首先在windows安装Android Studio,配置好环境,还要在虚拟机上使用同样的步骤安装Android Studio,虚拟机上安装的AS只有一个功能,就是要使用它的ndk交叉编译libgpiod动态库(在虚拟机上也可以不安装Android Studio,直接下载ndk来交叉编译,方法自行百度,本文档介绍第一种方法),其它工作都在windows上进行,交叉编译完成后,会生成gpio.h头文件和libgpiod.so动态库,在windows系统打开AS,配置项目的CMakeLists.txt文件,导入交叉编译好的libgpiod.so,再导入gpio.h头文件到cpp文件中,就能调用libgpiod库的接口。项目需要用到gpio18,gpio20,gpio22三个引脚,把这三个引脚空闲出来,再给gpiochip0赋予777权限,gpiochip0是管理GPIO0(一组引脚有32个,gpio0-32)这一组的gpio,要控制gpio18,gpio20,gpio22就需要有gpiochip0的操作权限。
总结以上,开发流程为:安装AS,配置AS环境,连接开发板, 空闲gpio引脚,开gpiochip0权限,使用NDK交叉编译libgpiod,设计UI界面,配置CMakeLists.txt,写JNI,最后编译运行。
安装下载ndk
从凌云实验室文件服务器上下载并安装下面烧录软件。
1、CP210x_VCP_Windows.zip 解压缩并安装开发板的Console调试串口驱动;
2、DriverAssitant_v5.1.1.zip , 解压缩安装RK3568开发板驱动;
3、RKDevTool_Release_v2.86.zip ,直接解压缩即可运行的 RK3568开发板烧录软件;
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="76dp"
android:text="ON/OFF"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Lamp" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginTop="180dp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@android:drawable/presence_invisible" />
<ImageView
android:id="@+id/Lamp"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginTop="180dp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@android:drawable/presence_online" />
<TextView
android:id="@+id/textView"
android:layout_width="150dp"
android:layout_height="35dp"
android:gravity="center"
android:text="RK3568-LED"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.532"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.04" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.myapplication;
public class HardControl {
public static native int ledOpen();//打开设备
public static native int ledCtrl_on(int which, int status);//打开led灯
public static native int ledCtrl_off(int which, int status);//关闭led灯
static
{
System.loadLibrary("myapplication");
System.loadLibrary("gpiod");
}
}
package com.example.myapplication;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
ImageView L1;
Button bnt1;
boolean isLightOn = false; // 指示灯状态
HardControl hardControl = new HardControl();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bnt1 = findViewById(R.id.button1);// 按钮灯开
hardControl.ledOpen();
bnt1.setOnClickListener(this);
L1 = (ImageView)this.findViewById(R.id.Lamp);
}
@Override
public void onClick(View view) {
int i=view.getId();
if (i==bnt1.getId())
{
if(isLightOn)
{
L1.setVisibility(View.INVISIBLE);
hardControl.ledCtrl_off(1,1);
isLightOn = false;
}
else
{
L1.setVisibility(View.VISIBLE);
hardControl.ledCtrl_on(1,0);
isLightOn = true;
}
}
}
protected void onDestroy() {
super.onDestroy();
// 当Activity销毁时,关闭LED控制
hardControl.ledCtrl_close();
}
}
#下载安装相关编译工具
sudo apt update
sudo apt-get install autoconf-archive
sudo apt-get install autoconf automake libtool
git clone https://github.com/brgl/libgpiod/tree/v1.6.x
unzip libgpiod-1.6.x.zip
cd libgpiod-1.6.x
bash autogen.sh #运行脚本,会生成configure这个文件
(重点,环境变量没设置对交叉编译会失败)
export NDK=/home/af/Android/Sdk/ndk/26.2.11394342 # 根据自己的NDK路径设置
#export TOOLCHAIN=${NDK}/toolchains/llvm/prebuilt/darwin-x86_64
export TOOLCHAIN=${NDK}/toolchains/llvm/prebuilt/linux-x86_64
#export TARGET=armv7a-linux-androideabi
#export TARGET=i686-linux-android
#export TARGET=x86_64-linux-android
export TARGET=aarch64-linux-android #TARGET=aarch64-linux-android 是我们选择的交叉编译的目标架构,根据需要选择不通的架构
export API=29
export AR=${TOOLCHAIN}/bin/llvm-ar #打包器,用于库操作,可以通过该工具从一个库中删除或者增加目标代码模块。
export CC=${TOOLCHAIN}/bin/${TARGET}${API}-clang # 编译器,对C源文件进行编译处理,生成汇编文件。
export AS=${CC} #汇编器,将汇编文件生成目标文件(汇编文件使用的是指令助记符, AS将它翻译成机器码)
export CXX=${TOOLCHAIN}/bin/${TARGET}${API}-clang++
export LD=${TOOLCHAIN}/bin/ld #链接器,为前面生成的目标代码分配地址空间,将多个目标文件链接成一个库或者是可执行文件。
export RANLIB=${TOOLCHAIN}/bin/llvm-ranlib
export STRIP=${TOOLCHAIN}/bin/llvm-strip # 以最终生成的可执行文件或者库文件作为输入,然后消除掉其中的源码。
"aarch64-linux-android29-clang" 是一个基于 Clang 编译器的工具链,用于在 Android 平台上编译 ARMv8 架构的代码。它的主要特点包括:
1、支持使用 LLVM/Clang 编译器进行编译,具有优秀的代码优化能力和兼容性;
2、支持 ARMv8 架构指令集,可以生成针对 ARMv8 架构的优化代码;
3、针对 Android 平台进行了优化,可以使用 Android 平台上的系统库和头文件;
4、适用于 Android 9.0 及以上版本(API level 29)。
5、使用该工具链可以在 Android 平台上编译出高效、稳定、兼容性好的 ARMv8 架构代码,适用于开发 Android 平台上的应用程序和系统级组件。
sudo ./configure CC=${CC} CXX=${CXX} RANLIB=${RANLIB} AR=${AR} STRIP=${STRIP} --prefix=/home/af/android_libgpiod --host=${TARGET}
# --prefix指定安装目录
sudo make
sudo make install
########导入libgpiod###########
#在CMakeLists.txt文件中添加一个导入库(imported library)的声明,
#IMPORTED: 这是一个可选的参数,表示库是一个导入库(imported library),即该库不是由CMake构建生成的,而是由外部提供的。
add_library(libgpiod
SHARED
IMPORTED)
set_target_properties( libgpiod
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libgpiod.so)
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
${log-lib}
libgpiod
)
ledOpen ledCtrl_on ledCtrl_off是JNI接口,鼠标选中函数名,按alt+enter键,点击Create JNI...,然后会在cpp文件里自动生成JNI函数接口名,在里面写代码
```cpp
#include <jni.h>
#include
#include <android/log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/select.h>
#include "include/gpiod.h"
// Write C++ code here.
//
// Do not forget to dynamically load the C++ library into your application.
//
// For instance,
//
// In MainActivity.java:
// static {
// System.loadLibrary("myapplication");
// }
//
// Or, in MainActivity.kt:
// companion object {
// init {
// System.loadLibrary("myapplication")
// }
// }
const char *chipname = "gpiochip0"; //GPIO1
struct gpiod_chip *chip;
unsigned int line_num ;
struct gpiod_line *line18 ;
unsigned int line_num_18 = 18; // 偏移GPIO 20 GPIO1_20
int ret;
bool isused;
const char *chip_name;
int set_led_value(int status)
{
__android_log_print(ANDROID_LOG_DEBUG, "函数:set_led_value", "开始设置0/1控制亮灭:%d",status);
//设置GPIO18的值
ret = gpiod_line_set_value(line18, status);//设置输出状态
if (ret < 0) {
__android_log_print(ANDROID_LOG_DEBUG, "Set On/Off", "Set line output failed. val:%d!",status);
return 0;
}
ret = gpiod_line_get_value(line18);
__android_log_print(ANDROID_LOG_DEBUG, "GPIO18的值", "GPIO18的值为:%d!",ret);
if (ret == 0)
{
__android_log_print(ANDROID_LOG_DEBUG, "--LED状态--", "状态为:--打开!--");
}
else
{
{
__android_log_print(ANDROID_LOG_DEBUG, "--LED状态--", "状态为:--关闭!--");
}
}
return 0;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_HardControl_ledOpen(JNIEnv *env, jclass clazz) {
// TODO: implement ledOpen()
// __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "------native ledOpen-------");
//1、OPEN:打开chipgpio0
chip = gpiod_chip_open_by_name(chipname);
if (!chip) {
__android_log_print(ANDROID_LOG_INFO, "Open chipgpio0", "Open chip by name failed. name: %s\n", chipname);
return 0;
}
__android_log_print(ANDROID_LOG_DEBUG, "Open chipgpio0", "Successfully opened chip:%s",chipname);
//2、获取打开的设备名
chip_name = gpiod_chip_name(chip);
__android_log_print(ANDROID_LOG_DEBUG, "gpiod_chip_name", "The GPIO chip name as represented in the kernel: %s",chip_name);
//3、获取打开的chipgpio0的引脚个数
line_num = gpiod_chip_num_lines(chip);
__android_log_print(ANDROID_LOG_DEBUG, "gpiod_chip_num_lines", "Line number:%u",line_num);
//4、获取GPIO18引脚句柄
line18 = gpiod_chip_get_line(chip,line_num_18);
if ( !line18 )
{
__android_log_print(ANDROID_LOG_DEBUG, "gpiod_chip_get_line", "Get line failed. line_num: %u",line_num_18);
return 0;
}
//5、判断GPIO18是否被占用
isused = gpiod_line_is_used(line18);
if (isused)
{
// GPIO线正在被使用
__android_log_print(ANDROID_LOG_DEBUG, "GPIO18 isused", "GPIO18线正在被使用");
return 0;
}
else
{
// GPIO线没有被使用
__android_log_print(ANDROID_LOG_DEBUG, "GPIO18 is not used", "GPIO18线没有被使用");
}
//6、设置GPIO18为输出模式
ret = gpiod_line_request_output(line18,NULL ,1);
if (ret < 0) {
__android_log_print(ANDROID_LOG_DEBUG, "设置为输出模式", "输出模式设置失败");
return 0;
}
__android_log_print(ANDROID_LOG_DEBUG, "设置为输出模式", "输出模式设置成功!");
return 2;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_HardControl_ledCtrl_1on(JNIEnv *env, jclass clazz, jint which,
jint status) {
// TODO: implement ledCtrl_on()
__android_log_print(ANDROID_LOG_DEBUG, "ledCtrl_on", "status 值:%d",status);
set_led_value(status);
return 0;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_HardControl_ledCtrl_1off(JNIEnv *env, jclass clazz, jint which,
jint status) {
// TODO: implement ledCtrl_off()
__android_log_print(ANDROID_LOG_DEBUG, "ledCtrl_off", "status 值:%d",status);
set_led_value(status);
return 0;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_myapplication_HardControl_ledCtrl_1close(JNIEnv *env, jclass clazz) {
// TODO: implement ledCtrl_close()
set_led_value(1);
gpiod_line_release(line18);
gpiod_chip_close(chip);
}
```