一,为什么要和硬件通信

1.1,做软件开发的可能大多只是在手机上做服务器/客户端这种应用,说白了这些只是对数据的处理,对数据做存储和读取,以及分析的工作。

1.2 但随着智能领域的发展,人们已不满足手动去直接接触硬件,毕竟这个过程需要先走到机器跟前,伸出手按下某一个按钮来控制机器某一行为,更多的想要通过一个小小的手机去控制更多的东西,比如控制车,控制灯,控制任何一种机器,甚至远程相隔几千里外去操控机器。这个时候单纯的上层应用已不能实现需求,需要和硬件建立某种持久的联系,方便时刻去控制硬件。

二,GPIO 介绍

2.1 gpio是什么

GPIO(General Purpose I/O Ports)意思为通用输入/输出端口,通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态-是高电平或是低电平

GPIO是芯片的引脚,管脚是可编程的,可对引脚的工作模式进行设置:输入模式(检测输入信号),输出模式(输出0或1)。可用来做嵌入式开发,驱动开发等等。

2.2 简单的控制灯的示例

如上图,P21这个GPIO口,输出1的时候LED403点亮,输出0或者没有输出的时候,LED403熄灭。需要亮灯灯的时候调用GPIO口拉高的函数,需要熄灯的时候调用GPIO拉低的函数,即可实现控制。函数的操作,最终变成了向这个GPIO的硬件寄存器写入数据,硬件的状态会跟随寄存器的数据改变而改变

三 GPIO运用实例

3.1 项目背景

用到gpio的是个门禁人脸识别开锁系统,android开发板上运行人脸识别软件,开发板连接开门锁,这个锁跟我们平时的电机锁并无区别,人脸识别成功后,发送gpio指令来控制锁的开关

3.2 gpio运行过程

首先,看看系统中有没有“/sys/class/gpio”这个文件夹。如果没有请在编译内核的时候加入进去:

Device Drivers
—> GPIOSupport
—> /sys/class/gpio/… (sysfsinterface)。

/sys/class/gpio 的使用说明:

1、gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映射;

2、控制GPIO的目录位于/sys/class/gpio;

3、 /sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号,首先给它赋读写权限;
adb shell chmod 0666 /sys/class/gpio/export

4、/sys/class/gpio/unexport 用于通知系统取消导出GPIO引脚编号,首先给它赋读写权限;
adb shell chmod 0666 /sys/class/gpio/export

5、/sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号base,寄存器名称,引脚总数等。

6、首先需要计算此引脚编号,引脚编号 = 控制引脚的寄存器基数 + 控制引脚寄存器位数(一般平台不同计算方法也不相同,主要根据平台来计算;
查看gpio对应配置:cat /sys/kernel/debug/gpio //这里可以对应计算出gpio号
查看gpio复用或者是否已被申请使用:/sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins

7、向/sys/class/gpio/export写入此编号,比如12号引脚,在shell中可以通过以下命令实现,命令成功后生成/sys/class/gpio/gpio12目录,如果没有出现相应的目录,说明此引脚不可导出:
echo 12 > /sys/class/gpio/export;

8、direction文件,定义输入输入方向,可以通过下面命令定义为输出,direction接受的参数:in,out, high, low。in/out设置为输入或者输出口,high/low同时设置方向为输出,并将value设置为相应的1/0;

echo out > direction //设置该gpio为输出口

9、value文件是端口的数值,为1或0:为1设置为高电平,为0设置为低电平。
echo 1 > value

adb shell chmod 0666 /sys/class/gpio/export //export赋权限0666

adb shell chmod 0666 /sys/class/gpio/unexport //unexport赋权限0666

adb shell echo 62 > /sys/class/gpio/export //通知系统需要导出控制的GPIO62

adb shell chmod 0666 /sys/class/gpio/gpio62/direction //给GPIO62的direction属性赋权限0666

adb shell chmod 0666 /sys/class/gpio/gpio62/value //给GPIO62的value属性赋权限0666

adb shell echo out > /sys/class/gpio/gpio62/direction //往GPIO62的direction属性写入out,设置GPIO为输出

adb shell echo 1 > /sys/class/gpio/gpio62/value //往GPIO62的value属性写入1,设置GPIO为高电平

四 Runtime.exec()介绍

4.1 Runtime.exec()字面理解运行时执行某种操作,它的主要作用就是调用外部可执行程序或者命令,比如shell命令。通俗讲需要运行JVM外的程序,需要用到Runtime

4.2 执行外部程序命令步骤

​​​​​​​第一步:通过Runtime.getRuntime()获取该类的一个实例,Runtime类是单实例的,每个Java应用程序都有一个该类的实例。

Runtime runtime=Runtime.getRuntime();

​​​​​​​第二步:使用exec方法执行字符串命令并返回一个process对象,process是一个进程,可以做一些进程相关的事情。

Process process =runtime.exec("su");

exect相关方法

// 在单独的进程中执行指定的外部可执行程序的启动路径或字符串命令public Process exec(String command)// 在单独的进程中执行指定命令和变量public Process exec(String[] cmdArray)// 在指定环境的独立进程中执行指定命令和变量public Process exec(String command, String[] envp)// 在指定环境的独立进程中执行指定的命令和变量public Process exec(String[] cmdArray, String[] envp)// 在指定环境和工作目录的独立进程中执行指定的字符串命令public Process exec(String command, String[] envp, File dir)// 在指定环境和工作目录的独立进程中执行指定的命令和变量public Process exec(String[] cmdarray, String[] envp, File dir)// 参数说明:cmdarray // 包含所调用命令及其参数的数组。数组第一个元素是命令,其余是参数envp // 字符串数组,其中每个元素的环境变量的设置格式为 name=value,如果子进程应该继承当前进程的环境,则该参数为nulldir // 子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为null// 参数cmdArray 示例:shutdown -s -t 3600String arr[] = {"shutdown","-s","-t","3600"};Process process = Runtime.getRuntime().exec(arr[]);/*注意:在调用这个方法时,不能将命令和参数放在一起,eg:String arr[] = {"shutdown -s -t 3600"};这样会导致程序把“shutdown -s -t 3600”当成是一条命令的名称,然后去查找“shutdown -s -t 3600”这条命令,它当然会找不到,所以就会报错*/

常用方法:

// 导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。int waitFor()/*如果已终止该子进程,此方法立即返回。如果没有终止该子进程,调用的线程将被阻塞,直到退出子进程,0 表示正常终止 */// 杀掉子进程void destroy()// 返回子进程的出口值,值 0 表示正常终止int exitValue()// 获取子进程的错误流InputStream getErrorStream()// 获取子进程的输入流InputStream getInputStream()// 获取子进程的输出流OutputStream getOutputStream()

第三步:通过 Process实例.getInputStream() 和 Process实例.getErrorStream() 获取的输入流和错误信息流是缓冲池向当前Java程序提供的,而不是直接获取外部程序的标准输出流和标准错误流

而缓冲池的容量是一定的,因此若外部程序在运行过程中不断向缓冲池输出内容,当缓冲池填满,那么外部程序将暂停运行直到缓冲池有空位可接收外部程序的输出内容为止。(采用xcopy命令复制大量文件时将会出现该问题)

所以需要当前的Java程序不断读取缓冲池的内容,从而为腾出缓冲池的空间。

DataOutputStream os = new DataOutputStream(process.getOutputStream());

第四步:输出流发送指令消息

dataOutputStream.writeBytes(instruct);dataOutputStream.flush();dataOutputStream.close();

4.3 示例

publicvoid chmod(String instruct) {try {Runtime runtime=Runtime.getRuntime();Process process =runtime.exec("su");DataOutputStream dataOutputStream = new DataOutputStream(process.getOutputStream());dataOutputStream.writeBytes(instruct);dataOutputStream.flush();dataOutputStream.close();} catch (Exception ex) {ex.printStackTrace();}}

五 完成流程Demo示例​​​​​​​

/** * 控制门禁锁的打开和关闭 */public class GpioEntranceGuardTest {//gpio引脚编码变量,不同的功能有不同的引脚编码privateString gpio_number="178";/** * 初始化gpio */publicvoid gpioInt() {//写入编号String exportPath = "echo " + gpio_number + " > /sys/class/gpio/export";chmod(exportPath);//定义输入输出方向String directionPath = "echo out > " + " /sys/class/gpio/gpio" + gpio_number+ "/direction";chmod(directionPath);//赋予引脚编号的读写权限String permissionGpio = "chmod 0777 /sys/class/gpio/"+gpio_number+"/value";chmod(permissionGpio);}/** * 获取gpio编号对应的值,即是高电屏,或低电平 * @return */publicint getValue() {File localFile = new File("/sys/class/gpio/gpio" + gpio_number+ "/value");if (!localFile.exists())System.out.println(localFile.getAbsoluteFile() + " not exist!");while (true) {try {FileReader localFileReader = new FileReader(localFile);char[] arrayOfChar = new char[1];int i = localFileReader.read(arrayOfChar, 0, 1);localFileReader.close();if (i == 1) {int j = arrayOfChar[0];if (j == 48)return 0;return 1;}} catch (FileNotFoundException localFileNotFoundException) {localFileNotFoundException.printStackTrace();return -1;} catch (IOException localIOException) {localIOException.printStackTrace();return -1;}}}//发送指令,设置gpio值,即设置高电屏,或低电平publicvoid setValue(int paramInt) {String exportPath1 = "echo " + paramInt + " > /sys/class/gpio/gpio"+gpio_number+"/value";chmod(exportPath1);}/** * 执行外部程序指令 * @param instruct 指令 */publicvoid chmod(String instruct) {try {Runtime runtime=Runtime.getRuntime();Process process =runtime.exec("su");DataOutputStream dataOutputStream = new DataOutputStream(process.getOutputStream());dataOutputStream.writeBytes(instruct);dataOutputStream.flush();dataOutputStream.close();} catch (Exception ex) {ex.printStackTrace();}}