Java 通过GPIO控制LED灯的亮灭(Firefly-RK3399)(二)
Note: 为Android 开发,开发工具AndroidStudio
上篇简要介绍了GPIO在Linux 下的操作,这篇主要描述在Java下的应用
1.JNI 方式
2.Java 运行Linux命令
JNI
JNI要通过调用C文件,来读写操作GPIO,而且
对于相关的目录文件还需要权限才能执行,搞得一脸懵
jni 操作gpio需要权限 (AS中Terminal窗口):
chmod -R 777 /sys/class/gpio/gpio1157/
将当前目录下所有文件都给予777权限,777就是高权限(读、写、执行)
如果还是读写失败,需要每一步进行授予权限,详情参考:
android通过JNI控制GPIO
Android NDK开发中jni配置及调用GPIO
c文件如下(仅供参考):
//
// Created by K_EJ on 2019/3/20.
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "com_ejior_platform_led_GPIOControl.h"
#define TAG "jni_gpio"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)
#define IN 0
#define OUT 1
#define LOW 0
#define HIGH 1
#define BUFFER_MAX 5 //3 1157 为5
#define DIRECTION_MAX 66
#include "com_ejior_platform_led_GPIOControl.h"
JNIEXPORT jint JNICALL Java_com_ejior_platform_led_GPIOControl_exportGpio
(JNIEnv *env, jobject jclass, jint gpio){
char buffer[BUFFER_MAX];
int len;
int fd;
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
LOGE("Failed to open export for writing!\n");
return fd;
}
len = snprintf(buffer, BUFFER_MAX, "%d", gpio);
return len;
if (write(fd, buffer, len) < 0) {
LOGE("Fail to export gpio!\n");
return (0);
}
close(fd);
return 1;
};
/*
* Class: com_ejior_platform_led_GPIOControl
* Method: setGpioDirection
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ejior_platform_led_GPIOControl_setGpioDirection
(JNIEnv *env,jobject jclass, jint gpio, jint direction)
{
static const char dir_str[] = "in\0out";
char path[DIRECTION_MAX];
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", gpio);
//path
fd = open(path, O_WRONLY);
if (fd < 0) {
LOGE("failed to open gpio direction for writing!\n");
return 0;
}
if (write(fd, &dir_str[direction == IN ? 0 : 3], direction == IN ? 2 : 3) < 0) {
LOGE("failed to set direction!\n");
return 0;
}
close(fd);
return 1;
};
/*
* Class: com_ejior_platform_led_GPIOControl
* Method: readGpioDirection
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ejior_platform_led_GPIOControl_readGpioStatus
(JNIEnv *env,jobject jclass , jint gpio)
{
char path[DIRECTION_MAX];
char value_str[3];
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/value", gpio);
fd = open(path, O_RDONLY);
if (fd < 0) {
LOGE("failed to open gpio value for reading!\n");
return -1;
}
if (read(fd, value_str, 3) < 0) {
LOGE("failed to read value!\n");
return -1;
}
close(fd);
return (atoi(value_str));
};
/*
* Class: com_ejior_platform_led_GPIOControl
* Method: writeGpioStatus
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ejior_platform_led_GPIOControl_writeGpioStatus
(JNIEnv *env, jobject jclass, jint gpio, jint value)
{
static const char values_str[] = "01";
char path[DIRECTION_MAX];
int fd;
snprintf(path,DIRECTION_MAX,"/sys/class/gpio/gpio%d/value",gpio);
fd = open(path,O_WRONLY);//path
if(fd<0){
LOGE("failed to open gpio value for writing!\n");
return 0;
}
if(write(fd,&values_str[value == LOW? 0:1],1) < 0){
LOGE("failed to write value!\n");
return 0;
}
close(fd);
return 1;
};
/*
* Class: com_ejior_platform_led_GPIOControl
* Method: unexportGpio
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_ejior_platform_led_GPIOControl_unexportGpio
(JNIEnv *env, jobject jclass, jint gpio)
{
char buffer[BUFFER_MAX];
int len;
int fd;
fd = open("/sys/class/gpio/unexport",O_WRONLY);
if(fd <0){
LOGE("Failed to open unexport for writing!\n");
}
len = snprintf(buffer, BUFFER_MAX, "%d", gpio);
if (write(fd, buffer, len) < 0) {
LOGE("Fail to unexport gpio!");
return 0;
}
close(fd);
return 1;
};
Note:需要配置NDK环境 >++<
Java 方式
也就是用java通过输出流写入Linux 命令
**Note:**设备需要root,如果不root,2种方式,1.系统签名(需要编译源码) 2. 放在系统目录下/system(Note,引用的第三方so库,需要放入/system/lib下,在rk3399开发板上遇到系统打不开,定格在Recovery页面,需要重新烧录固定。。。)
root 设备
详情:
【android工具篇】Firefly-RK系列
里面介绍的方法有的并不能达到获取权限的目的,工具类反而用的到
需要用到工具类ShellUtils,来写入Linux命令,如下:
public class ShellUtils {
public static final String COMMAND_SU = "su";
public static final String COMMAND_SH = "sh";
public static final String COMMAND_EXIT = "exit\n";
public static final String COMMAND_LINE_END = "\n";
private ShellUtils() {
throw new AssertionError();
}
/**
* 查看是否有了root权限
*
* @return
*/
public static boolean checkRootPermission() {
return execCommand("echo root", true, false).result == 0;
}
/**
* 执行shell命令,默认返回结果
*
* @param command command
* @return
* @see ShellUtils#execCommand(String[], boolean, boolean)
*/
public static CommandResult execCommand(String command, boolean isRoot) {
return execCommand(new String[]{command}, isRoot, true);
}
/**
* 执行shell命令,默认返回结果
*
* @param commands command list
* @return
* @see ShellUtils#execCommand(String[], boolean, boolean)
*/
public static CommandResult execCommand(List commands, boolean isRoot) {
return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, true);
}
/**
* 执行shell命令,默认返回结果
*
* @param commands command array
* @return
* @see ShellUtils#execCommand(String[], boolean, boolean)
*/
public static CommandResult execCommand(String[] commands, boolean isRoot) {
return execCommand(commands, isRoot, true);
}
/**
* execute shell command
*
* @param command command
* @param isNeedResultMsg whether need result msg
* @return
* @see ShellUtils#execCommand(String[], boolean, boolean)
*/
public static CommandResult execCommand(String command, boolean isRoot, boolean isNeedResultMsg) {
return execCommand(new String[]{command}, isRoot, isNeedResultMsg);
}
/**
* execute shell commands
*
* @param commands command list
* @return
* @see ShellUtils#execCommand(String[], boolean, boolean)
*/
public static CommandResult execCommand(List commands, boolean isRoot, boolean isNeedResultMsg) {
return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, isNeedResultMsg);
}
/**
* execute shell commands
*/
public static CommandResult execCommand(String[] commands, boolean isRoot, boolean isNeedResultMsg) {
int result = -1;
if (commands == null || commands.length == 0) {
return new CommandResult(result, null, null);
}
Process process = null;
BufferedReader successResult = null;
BufferedReader errorResult = null;
StringBuilder successMsg = null;
StringBuilder errorMsg = null;
DataOutputStream os = null;
try {
process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);
os = new DataOutputStream(process.getOutputStream());
for (String command : commands) {
if (command == null) {
continue;
}
// donnot use os.writeBytes(commmand), avoid chinese charset
// error
os.write(command.getBytes());
os.writeBytes(COMMAND_LINE_END);
os.flush();
}
os.writeBytes(COMMAND_EXIT);
os.flush();
result = process.waitFor();
// get command result
if (isNeedResultMsg) {
successMsg = new StringBuilder();
errorMsg = new StringBuilder();
successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String s;
while ((s = successResult.readLine()) != null) {
successMsg.append(" "+s);
}
while ((s = errorResult.readLine()) != null) {
errorMsg.append(s);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
if (successResult != null) {
successResult.close();
}
if (errorResult != null) {
errorResult.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (process != null) {
process.destroy();
}
}
return new CommandResult(result, successMsg == null ? null : successMsg.toString(), errorMsg == null ? null : errorMsg.toString());
}
public static class CommandResult {
/**
* 运行结果
**/
public int result;
/**
* 运行成功结果
**/
public String successMsg;
/**
* 运行失败结果
**/
public String errorMsg;
public CommandResult(int result) {
this.result = result;
}
public CommandResult(int result, String successMsg, String errorMsg) {
this.result = result;
this.successMsg = successMsg;
this.errorMsg = errorMsg;
}
@Override
public String toString() {
return "CommandResult{" +
"result=" + result +
", successMsg='" + successMsg + '\'' +
", errorMsg='" + errorMsg + '\'' +
'}';
}
}
}
简单写了GPIO的调用类
public class GPIOUtils {
// private static final
public static final String GPIO_DEFINE_NUM = "GPIO1_B0";
//GPIO4_D5 rk3399 gpio编号157 GPIO1_B0 gpio编号40
// value 1 有色 0白色
public final static int GPIO_VALUE_LOW = 0;//低电平
public final static int GPIO_VALUE_HIGH = 1;
private static final String execCommand = "echo s% > /sys/class/gpio/export";
public void setGpio(String gpioNum) {
String export = String.format(execCommand, gpioNum);
ShellUtils.execCommand(export, true);
}
/**
* 默认 GPIO_DEFINE_NUM
*
* @param value
*/
public static void setDefineGpio(int value) {
int gpioPin = GPIOUtils.gpioParse(GPIOUtils.GPIO_DEFINE_NUM);//157
if (scanGpio() == -1) {
} else {
gpioPin = scanGpio() + gpioPin;
}
Logger.infod("Define Gpio", scanGpio() + "===" + gpioPin);
setGpioControl(gpioPin, GPIOControl.GPIO_DIRECTION_OUT_STR, value);
}
/**
* 通知系统取消导出
*/
public static void setDefUnexportGpio() {
int gpioPin = GPIOUtils.gpioParse(GPIOUtils.GPIO_DEFINE_NUM);//157
if (scanGpio() == -1) {
} else {
gpioPin = scanGpio() + gpioPin;
}
gpioRemove(gpioPin);
}
/**
* 查看gpio目录,获取Gpio的第一个值,
* 因为Gpiochip可能含有gpiochip1000 gpiochip1032 gpiochip1064 gpiochip1096 gpiochip1128
* 而不是gpiochip0 gpiochip160 gpiochip64
* gpiochip128 gpiochip32 gpiochip96
*/
public static int scanGpio() {
String lsgpio = "ls /sys/class/gpio/";
ShellUtils.CommandResult commandResult = ShellUtils.execCommand(lsgpio, true, true);
String result = commandResult.successMsg;
Log.d("Scan Gpio", result == null ? "" : result);
String[] resultArray = result.split(" ");
List<String> strings = new ArrayList<>();
for (int i = 0; i < resultArray.length; i++) {
String s = resultArray[i];
if (s.contains("gpiochip")) {
String subStr = s.substring(s.lastIndexOf("gpiochip") + 8, s.length());
strings.add(subStr);
}
}
if (strings.size() <= 0) {
return -1;
}
String lastStr = strings.get((strings.size() - 1));
if (lastStr.length() >= 4) {
return Integer.parseInt(lastStr.substring(0, 1)) * (int) Math.pow(10, (lastStr.length() - 1));
}
return -1;
}
/**
* system remount为读写权限
* @return
*/
public static boolean reRemount() {
// remount命令 将文件系统remount为读写权限 --- remount回只读: mount -o ro,remount /system
String remount = "mount -o remount /system";// mount -o rw,remount /system
ShellUtils.CommandResult commandResult = ShellUtils.execCommand(remount, true);
Log.d("GpioControl Remount==", commandResult.toString());
return commandResult.result == 0;
}
/**
* system remount 为只读
* @return
*/
public static boolean reRemountRead() {
// remount命令 将文件系统remount为读写权限 --- remount回只读: mount -o ro,remount /system
String remount = "mount -o ro,remount /system";// mount -o rw,remount /system
ShellUtils.CommandResult commandResult = ShellUtils.execCommand(remount, true);
Log.d("Gpio RemountRead==", commandResult.toString());
return commandResult.result == 0;
}
/**
* 本项目中 value 1 有色 0 白色
*/
public static boolean setGpioControl(int gpio, String direction, int value) {
String exportPath = "";
String directionPath = "";
if (!isGpioOccupied(gpio)) {
exportPath = "echo " + gpio + " > /sys/class/gpio/export";
directionPath = "echo " + direction + " > " + " /sys/class/gpio/gpio" + gpio + "/direction";
}
String valuePath = "echo " + value + " > /sys/class/gpio/gpio" + gpio + "/value";
String[] command = {exportPath, directionPath, valuePath};
ShellUtils.CommandResult commandResult = ShellUtils.execCommand(command, true);
Log.d("GpioControl Set==", commandResult.toString());
return commandResult.result == 0;
}
public static boolean isGpioOccupied(int gpio) {
String catGpio = "cat /sys/kernel/debug/gpio | grep gpio-" + gpio;
ShellUtils.CommandResult commandResult = ShellUtils.execCommand(catGpio, true);
Log.d("Cat Gpio==", commandResult.toString());
return commandResult.result == 0;
}
/**
* 获取GPIO编号
*
* @param gpioStr
* @return
*/
public static int gpioParse(String gpioStr) {//GPIO4_D5
if (gpioStr != null && gpioStr.length() == 8) {
gpioStr = gpioStr.toUpperCase();
if (gpioStr.charAt(4) >= '0' && gpioStr.charAt(4) <= '8') {
if (gpioStr.charAt(6) >= 'A' && gpioStr.charAt(6) <= 'D') {
return gpioStr.charAt(7) >= '0' && gpioStr.charAt(7) <= '7' ?
(gpioStr.charAt(4) - 48) * 32 + (gpioStr.charAt(6) - 65) * 8 + (gpioStr.charAt(7) - 48) : -1;
} else {
return -1;
}
} else {
return -1;
}
} else {
System.out.println("input gpio error!");
return -1;
}
}
public static boolean gpioRemove(int gpio) {
String unexportPath = "echo " + gpio + " > /sys/class/gpio/unexport";
ShellUtils.CommandResult commandResult = ShellUtils.execCommand(unexportPath, true);
Log.d("GpioRemove==", commandResult.toString());
return commandResult.result == 0;
}
public String gpioRead(int gpio) {
String valuePath = "cat /sys/class/gpio/gpio" + gpio + "/value";
ShellUtils.CommandResult commandResult = ShellUtils.execCommand(valuePath, true, true);
Log.d("GpioRead==", commandResult.toString());
return commandResult.result == 0 ? commandResult.successMsg : commandResult.errorMsg;
}
/*
String command2 = "echo out > /sys/class/gpio/gpio1157/direction";
String command3 = "echo 0 > /sys/class/gpio/gpio1157/value";
String[] command = {command1,command2,command3};
ShellUtils.execCommand(command,true).result+""*/
}
调用如下,因为是2个Led切换,低电平白色,高电平蓝色:
//初始白色
GPIOUtils.reRemount();
GPIOUtils.setDefineGpio(GPIOUtils.GPIO_VALUE_LOW);//设置低电平
//设置高电平 变色
GPIOUtils.setDefineGpio(GPIOUtils.GPIO_VALUE_HIGH);
//退出
GPIOUtils.setDefUnexportGpio();//通知系统取消导出该编号Gpio
GPIOUtils.reRemountRead();
以上就是对Led灯的控制方法,java方式设备必须root
资源:Demo