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 <jni.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <android/log.h>

#include <errno.h>
#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<String> 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<String> 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

Logo

更多推荐