Android通过JNI操作GPIO

简介: Android通过JNI操作GPIO

平台


 Android 11 + RK3566


概述


在应用层通过JNI操作主板上的GPIO.

一些小提示:


注意文件操作权限, 若在adb shell下可以成功操作, APP下操作失败, 注意排查下权限问题:

相关的几个文件 export和unexport, 默认情况下, 权限并没有给这么宽, 参考下面的修改, 以保证APP能有正确认的操作权限.

rk3566:/sys/class/gpio # ll /sys/class/gpio
-rw-rw-rw-  1 root root 4096 2022-07-20 10:36 export
-rw----rw-  1 root root 4096 2022-07-20 10:19 unexport


第二部分, 在成功export后, 对应的节点下的权限, 下面对应的是gpio93代码中需要用到的两个文件节点:


ll /sys/class/gpio/gpio93/                                                              
total 0
-rw-rw-rw- 1 root root 4096 2022-07-20 10:36 direction
-rw-rw-rw- 1 root root 4096 2022-07-20 10:36 value


代码


目录结构:


src
└── main
    ├── AndroidManifest.xml
    ├── cpp
    │   ├── CMakeLists.txt
    │   └── jniapi.cpp
    ├── java
    │   └── com
    │       └── android
    │           └── apitester
    │               ├── Gpio.java
    │               └── utils
    │                   └── JniApi.java
    ├── res
    │   ├── layout
    │   │   ├── activity_gpio.xml


GPIO.java


package com.android.apitester;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.Toast;
import com.android.apitester.utils.JniApi;
public class Gpio extends Activity implements View.OnClickListener {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_gpio);
  findViewById(R.id.btGpio).setOnClickListener(this);
  }
  @Override
  public void onClick(View view) {
  JniApi api = new JniApi();
  int gpio = Integer.valueOf(((EditText)findViewById(R.id.etGpio)).getText().toString());
  int val = ((Switch)findViewById(R.id.swValue)).isChecked() ? 1 : 0;
  int res = api.setGpio(gpio, val);
  String result = "SUCCESS";
  if(res == -1){
    result = "OPEN EXPORT failed";
  }else if(res == -2){
    result = "open DIRECTION failed";
  }else if(res == -3){
    result = "open VALUE failed";
  }else if(res == -4){
    result = "write VALUE failed";
  }else if(res == -5){
    result = "open UNEXPORT failed";
  }
  Toast.makeText(this, result, Toast.LENGTH_LONG).show();
  }
}


activity_gpio.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <EditText android:id="@+id/etGpio"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"
        android:gravity="center_horizontal"
        android:singleLine="true"
        android:maxLines="1"/>
    <Switch android:id="@+id/swValue"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="GPIO High/Low:"/>
    <Button android:id="@+id/btGpio"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="OK"/>
</LinearLayout>


JniApi.java


package com.android.apitester.utils;
public class JniApi {
  static {
  System.loadLibrary("apitester");
  }
  public native int setGpio(int gpio, int value);
}


jniapi.cpp


#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>
//#include <cutils/log.h>
#include <string.h>
#include <jni.h>
#include <android/log.h>
#define LOG_TAG "ApiTester"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO,  LOG_TAG, __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN,  LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
//
// Created by anson on 2022/7/20.
//
extern "C"
JNIEXPORT jint JNICALL
Java_com_android_apitester_utils_JniApi_setGpio(JNIEnv *env, jobject thiz, jint gpio, jint value) {
    char direction_file[64];
    char value_file[64];
    const char* export_file = "/sys/class/gpio/export";
    const char* unexport_file = "/sys/class/gpio/unexport";
    LOGD("setGpio gpio:%d to value:%d", gpio, value);
    //export gpio.
    FILE *export_fp=fopen(export_file,"w");
    if(!export_fp){
        LOGE("open file %s failed", export_file);
        return -1;
    }
    fprintf(export_fp,"%d", gpio);
    fclose(export_fp);
    //set direction output
    sprintf(direction_file,"/sys/class/gpio/gpio%d/direction", gpio);
    FILE *direction_fd=fopen(direction_file,"w");
    if(!direction_fd){
        LOGE("open file %s failed", direction_file);
        return -2;
    }
    fprintf(direction_fd,"out");
    fclose(direction_fd);
    //write value
    sprintf(value_file,"/sys/class/gpio/gpio%d/value", gpio);
    int value_fd = open(value_file, O_RDWR);
    if(value_fd <= 0){
        LOGE("open file %s failed", value_file);
        return -3;
    }
    ssize_t size = write(value_fd, value == 0 ? "0" : "1", 1);
    if(size <= 0){
        LOGE("write gpio %s failed", value_file);
        close(value_fd);
        return -4;
    }
    //unexport gpio
    FILE *unexport_fd = fopen(unexport_file,"w");
    if(!unexport_fd){
        LOGE("open file %s failed", unexport_file);
        return -5;
    }
    fprintf(unexport_fd, "%d", gpio);
    fclose(unexport_fd);
    return 1;
}


CMakeLists.txt


# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
project("apitester")
# file(GLOB native_srcs "src/main/cpp/*.cpp" "src/main/cpp/dalvik/*.cpp" "src/main/cpp/art/*.cpp" "src/main/cpp/art/*.S")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
             apitester
             # Sets the library as a shared library.
             SHARED
             # Provides a relative path to your source file(s).
        jniapi.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
              log-lib
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
                       apitester
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )


扩展


关于Studio 中创建JNI

创建JNI比较简单: 模块名, 右键 > Add C++ to Module

image.png


伴随而来的一些问题

1. CMake中版本兼容问题, 更改为已安装的版本 3.10.2, 修改build.gradle和 CMakeLists.txt

image.png

2. 创建后, 在Android视图下, 只有一个CPP文件夹

image.png

其它的问题现象还有:

打开CPP文件:android studio this file does not belong to any project target等.

不管如何sync或增加/修改文件, 甚至重启Studio也不能解决, 始终看不到CPP目录下的文件.


最后解决:

找到项目目录下的setttings.gradle


注释当前的模块 -> sync now
去掉模块注释 -> sync now


3. 新增加JNI函数: 选中函数 ALT + ENTER

image.png

之后会有一个文件选择列表, 选中想要添加的位置即可自动生成代码, 相当方便


相关文章
|
14天前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
19天前
|
Android开发
Android JNI与CAN通信遇到的问题总结
Android JNI与CAN通信遇到的问题总结
37 1
|
19天前
|
Java Android开发
Android系统 获取用户最后操作时间回调实现和原理分析
Android系统 获取用户最后操作时间回调实现和原理分析
21 0
|
19天前
|
Linux Android开发
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
13 0
|
20天前
|
Android开发
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
60 1
|
19天前
|
传感器 Java 开发工具
[NDK/JNI系列03] Android Studio集成NDK开发环境
[NDK/JNI系列03] Android Studio集成NDK开发环境
22 0
|
19天前
|
Linux Shell Android开发
自动化脚本之GPIO/LED相关适用于Android/Linux
自动化脚本之GPIO/LED相关适用于Android/Linux
15 0
|
19天前
|
XML Java API
Android 浅度解析:系统框架层修改,编译,推送相关操作
Android 浅度解析:系统框架层修改,编译,推送相关操作
27 0
|
5月前
|
Java 开发工具 Android开发
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
171 0
|
2月前
|
Android开发
[Android jni] Bitmap与Mat对象的相互转换
[Android jni] Bitmap与Mat对象的相互转换
60 0