Linux应用开发【第九章】GPIO编程应用开发

文章目录 9 gpio编程应用开发 9.1 gpio编程基础介绍 9.2 gpio编程软件接口 9.2.1 控制接口 9.2.2 gpio信号 9.2.3 gpio控制器 9.3 imx6ull开发板gpio编号的确定 9.3.1 led的gpio编号计算 9.3.2 按键的gpio编号计算 9.3.3 特殊情况下的gpio编号计算 9.4 实际编程操作 9.4.1 导出gpio口 9.4.2 设置gpio方向 9.4.3 gpio输出实验-led输出控制 9.4.4 gpio输入试验-按键值读取 9.4.5 led和按键控制实验  
9 gpio编程应用开发 9.1 gpio编程基础介绍 ​ gpio(general-purpose io ports),即通用io接口。gpio的使用较为简单,主要分为输入和输出两种功能。gpio主要用于实现一些简单设备的控制。在作为输入型gpio的情况下,我们可以将该io连接外部按键或者传感器,用于检测外部状态。当作为输出时,我们可以通过输出高低电平来控制外部设备的运转。
​ 由于gpio的功能多种多样,我们需要首先将引脚设置为gpio。设置为gpio之后,我们需要设置gpio的方向。当设置为输出时,我们可以控制输出高电平或者低电平。当设置为输入时,我们可以读取gpio的电平来判断外部输入电平的高低。
9.2 gpio编程软件接口 ​ gpio编程有多种实现方式,在这里,我们通过sysfs方式来实现gpio的控制实现。
​ 如果要通过sysfs方式控制gpio,首先需要底层内核的支持。为了实现内核对sysfs gpio的支持,我们需要在内核中进行设置。在编译内核的时候,加入 device drivers-> gpio support ->/sys/class/gpio/… (sysfs interface)。作为gpio的引脚,不允许在内核中被用作其他用途。
​ 在系统正常运行之后,我们可以在/sys/class/gpio下看到sysfs控制相关的接口。有三种类型的接口, 分别是控制接口,gpio信号和gpio控制器三种接口。这部分的具体介绍可参考《kerneldocumentationgpiosysfs.txt》。
9.2.1 控制接口 ​ 控制接口用于实现在用户空间对gpio的控制,主要包括/sys/class/gpio/export和/sys/class/gpio/unexport两个接口。这这两个控制接口都是只写的,/sys/class/gpio/export实现将gpio控制从内核空间导出到用户空间,/sys/class/gpio/unexport用于实现取消gpio控制从内核空间到用户空间的导出。
​ 下面以引脚编号为19的gpio为例进行说明,在/sys/class/gpio/目录下,我们执行echo 19 > export之后,将会产生一个”gpio19”节点来控制引脚编号为19的gpio。我们执行echo 19 > unexport之后,将会删除之前通过export产生的”gpio19”节点。为了使用gpio,我们需要首先使用/sys/class/gpio/export导出gpio引脚编号。完成使用之后,通过/sys/class/gpio/unexport删除掉之前导出的gpio引脚。
9.2.2 gpio信号 ​ gpio信号,即为gpio本身,其路径为/sys/class/gpio/gpion/,拥有多个属性。通过对这些属性进行控制,就可以实现对gpio的控制。
“direction”属性,读取的值为”in”或者”out”。通过对该属性写入”in”或者”out”可以设置该gpio为输入或者输出。如果直接写入”out”,则会使gpio直接输出低电平。也可以通过写入”low”或者”high”来直接设置输出低电平或者高电平。
”value”属性,用于读取输入电平或者控制输出电平。如果gpio为输出,则对value写入0为输出低电平,写入非0为输出高电平。如果设置为输入的话,则读到0表示输入为低电平,1为高电平。
”edge”属性,用于设置触发电平,只有在gpio可以设置为中断输入引脚时才会出现该属性。
9.2.3 gpio控制器 ​ gpio控制器,用于表示gpio 控制实现的初始gpio,其路径为/sys/class/gpio/gpiochipn/。比如/sys/class/gpio/gpiochip42/ 则表示实现gpio控制器的初始化编号为42。gpio控制器的属性为只读属性,包括base、label和ngpio等多个。
”base”属性,和gpiochipn的n代表的含义相同,表示被该组gpio控制器实现的第一个gpio.
” ngpio”属性,用于表示该控制器支持多少个gpio,支持的gpio编号为从n到n+ngpio-1
” label”属性,用于判断控制器,并不总是唯一的
9.3 imx6ull开发板gpio编号的确定 ​ 每个芯片可以有n组gpio,每组gpio最多有32个gpio,即最多有n*32个gpio。但是在实际设计中,每组的gpio数量各有不同。在imx6ull中,实际每组拥有的gpio数量如下图所示,具体详见《imx6ullrm.pdf》手册1347页。
​ 从上图可以看到,在imx6ull中,共有5组gpio,起始gpio组为gpio1。因此在实际gpio编号计算中,第一组gpio1对应的编号为031。以此类推,imx6ull的gpion_x(n=15,x=0~31对应的编号实际为(n-1)*32+x。接下来,我们以板载的led和按键各自对应的gpio为例来说明如何在实际应用中计算gpio编号。
9.3.1 led的gpio编号计算 ​ 从原理图中找到对应led的设计,具体的连接如下图所示。从图中我们可以看到,led连接到的gpio为gpio5_3,其对应的gpio编号实际为(5-1)*32+3 = 131。因此,我们如果要在sys_gpio中操作led,我们就需要将编号131的gpio进行导出。
9.3.2 按键的gpio编号计算 ​ 从原理图中找到对应按键的设计,底板有2个按键,具体的连接如下图所示。从图中我们可以看到,两个按键连接到的gpio分别为gpio5_1和gpio4_14,第一个按键key1对应的gpio编号为(5-1) *32+1 = 129,第二个按键key2对应的gpio编号为(4-1) *32+14=110。因此,我们如果要在sys_gpio中读取按键key1和key2的值,,我们就需要将编号129和110的gpio进行导出。
9.3.3 特殊情况下的gpio编号计算 ​ 在有些情况下,起始的gpiochipn不是gpiochip0。这个时候 ,我们就需要在原有的gpio编号基础上加上起始gpiochipn值进行计算。下图所示的为其实gpiochip为gpiochip0的情况。
9.4 实际编程操作 ​ 在实际操作中,我们使用led和按键实现了gpio输出和输入的实验,相关的实验过程和相关代码如下。
9.4.1 导出gpio口 ​ 为了导出gpio口,我们需要向/sys/class/gpio/export写入需要导出的引脚编号。在使用之后,我们也可以使用/sys/class/gpio/unexport取消导出引脚编号。
​ 导出引脚编号的实现代码如下所示,具体详见《sysfs_gpio_1_export_gpio sysfs_gpio_export.c》的sysfs_gpio_export()函数。
32 int sysfs_gpio_export(unsigned int gpio)33 {34 int fd, len;35 char buf[max_buf];36 // /sys/class/gpio/export37 fd = open( /sys/class/gpio/export, o_wronly);//打开文件38 if (fd < 0) {39 perror(gpio/export);40 return fd;41 }42 43 len = snprintf(buf, sizeof(buf), %d, gpio);//从数字变换为字符串,即1 变为”1“44 write(fd, buf, len);//将需要导出的gpio引脚编号进行写入45 close(fd);//关闭文件46 47 return 0;48 } ​ 取消导出引脚编号的实现代码如下所示,具体详见《sysfs_gpio_export.c》的sysfs_gpio_unexport()函数。
59 int sysfs_gpio_unexport(unsigned int gpio)60 {61 int fd, len;62 char buf[max_buf];63 // /sys/class/gpio/unexport64 fd = open(/sys/class/gpio/unexport, o_wronly);//打开文件65 if (fd < 0) {66 perror(gpio/export);67 return fd;68 }69 70 len = snprintf(buf, sizeof(buf), %d, gpio);//从数字变换为字符串,即1 变为”1“71 write(fd, buf, len);//将需要取消导出的gpio引脚编号进行写入72 close(fd);//关闭文件73 return 0;74 } ​ 在实现导出和取消导出引脚编号的函数之后,我们来实现具体的引脚编号的导出。led和按键各自对应的引脚编号如下所示
11 #define gpio4_14 11012 #define gpio5_1 12913 #define gpio5_3 131 14 15 #define gpio_key1 gpio4_1416 #define gpio_key2 gpio5_117 #define gpio_led gpio5_3 ​ 在确定了各自对应的引脚编号,我们就可以进行导出了。具体实现代码在程序文件《sysfs_gpio_1_export_gpio/sysfs_gpio_export.c》中main函数,下为对应代码部分,我们将led和按键对应的引脚都进行了导出。
183 int main(int argc, char **argv) {184 unsigned int i;185 unsigned int value1,value2;186 187 printf(t********************************************n);188 printf(t******** sysfs_gpio_test_demo**************n);189 printf(t******** version date: 2020/05 **********n);190 printf(t********************************************n); 191 192 printf(gpio begin to export gpiorn);193 sysfs_gpio_export(gpio_key1);//export gpio key1194 sysfs_gpio_export(gpio_key2);//export gpio key2195 sysfs_gpio_export(gpio_led);//export gpio led196 printf(gpio export gpio okrn);197 198 199 return 0;200 } ​ 在将代码编译之后,我们将代码在板卡上进行运行。代码运行之后的的结果如下图所示,可以看到成功的将gpio110、gpio129和gpio131进行了导出。
9.4.2 设置gpio方向 ​ 为了实现导出的引脚的方向设置,我们需要对/sys/class/gpio/gpion/direction写入不同的值。写入“in”则表示设置为输入,写入“out”则表示设置为输出。设置引脚编号的的实现代码如下所示,具体详见《sysfs_gpio_2_export_gpio sysfs_gpio_export.c》的sysfs_gpio_set_dir ()函数。
86 int sysfs_gpio_set_dir(unsigned int gpio, unsigned int out_flag)87 {88 int fd, len;89 char buf[max_buf];90 // /sys/class/gpio/gpion/direction91 len = snprintf(buf, sizeof(buf), sysfs_gpio_dir /gpio%d/direction, gpio);92 93 fd = open(buf, o_wronly);//打开文件94 if (fd < 0) {95 perror(buf);96 return fd;97 }98 99 if (out_flag)//为1,则写入“out,即设置为输出100 write(fd, out, 4);101 else//为0,则写入“in,即设置为输入102 write(fd, in, 3);103 104 close(fd);//关闭文件105 return 0;106 } ​ 在实现引脚方向的设置函数之后,我们分别针对按键和led设置各自不同的方向。将按键设置为输入“in”,将led设置为输出“out”,对应的代码如下图所示。相关的代码在程序文件《sysfs_gpio_2_export_gpio/sysfs_gpio_export.c》中main函数,下为对应代码部分。
183 int main(int argc, char **argv) {184 unsigned int i;185 unsigned int value1,value2;186 187 printf(t********************************************n);188 printf(t******** sysfs_gpio_test_demo**************n);189 printf(t******** version date: 2020/05 **********n);190 printf(t********************************************n); 191 192 printf(begin to export gpio and directionrn);193 sysfs_gpio_export(gpio_key1);//export gpio key1194 sysfs_gpio_export(gpio_key2);//export gpio key2195 sysfs_gpio_export(gpio_led);//export gpio led196 197 sysfs_gpio_set_dir(gpio_key1, 0);//set as input198 sysfs_gpio_set_dir(gpio_key2, 0);//set as input199 sysfs_gpio_set_dir(gpio_led, 1);//set as output200 printf( export gpio and direction okrn);201 202 203 204 return 0;205 } ​ 在将代码编译之后,我们将代码在板卡上进行运行。代码运行之后的的结果如下图所示,我们可以看到按键gpio110和gpio129的方向设置成了输入,led2的gpio131设置成了输入。
9.4.3 gpio输出实验-led输出控制 ​ 为了设置引脚的输出电平高低,我们需要对/sys/class/gpio/gpion/value写入不同的值。写入‘1’则表示输出高电平,写入‘0’则表示输出低电平。设置引脚输出高低电平的的实现代码如下所示,具体详见《sysfs_gpio_3_export_gpio sysfs_gpio_export.c》的sysfs_gpio_set_value ()函数。
119 int sysfs_gpio_set_value(unsigned int gpio, unsigned int value)120 {121 int fd, len;122 char buf[max_buf];123 // /sys/class/gpio/gpion/value124 len = snprintf(buf, sizeof(buf), sysfs_gpio_dir /gpio%d/value, gpio);125 126 fd = open(buf, o_wronly);//打开文件127 if (fd < 0) {128 perror(buf);129 return fd;130 }131 132 if (value)//为1,则写入“1,即设置为输出高电平133 write(fd, 1, 2);134 else//为0,则写入“0,即设置为输出低电平135 write(fd, 0, 2);136 137 close(fd);//关闭文件138 return 0;139 } ​ 在实现引脚输出电平的控制函数之后,我们来实现led的控制。我们通过将“1”或“0”写入value来控制gpio输出高电平或者低电平,具体相关的代码在程序文件《sysfs_gpio_3_export_gpio/sysfs_gpio_export.c》中main函数,下为对应代码部分。
183 int main(int argc, char **argv) {184 unsigned int i;185 unsigned int value1,value2;186 187 printf(t********************************************n);188 printf(t******** sysfs_gpio_test_demo**************n);189 printf(t******** version date: 2020/05 **********n);190 printf(t********************************************n); 191 192 printf(led begin to initrn);193 sysfs_gpio_export(gpio_led);//export gpio led194 195 sysfs_gpio_set_dir(gpio_led, 1);//set as output196 printf(led init okrn);197 198 199 /* confirm init_b pin as high */200 while(1)201 {202 203 204 sysfs_gpio_set_value(gpio_led, 1);//output high 205 printf(led offrn);206 usleep(500000); //delay 207 sysfs_gpio_set_value(gpio_led, 0);//output low 208 printf(led onrn);209 usleep(500000);//delay210 }211 212 sysfs_gpio_unexport(gpio_led);//unexport gpio led213 214 return 0;215 } ​ 在将代码编译之后,我们将代码在板卡上进行运行。代码运行之后的的结果如下图所示, 可以看到规律性的打印led控制信息(实物可以看到led灯闪烁)。
9.4.4 gpio输入试验-按键值读取 ​ 为了读取引脚输入的电平高低,我们需要读取/sys/class/gpio/gpion/value的值。读到的是‘1’则表输入为高电平,读到的是‘0’则表示输入为低电平。读取引脚输入电平的、的的实现代码如下所示,具体详见《sysfs_gpio_4_export_gpio sysfs_gpio_export.c》的sysfs_gpio_get_value ()函数。
152 int sysfs_gpio_get_value(unsigned int gpio, unsigned int *value)153 {154 int fd, len;155 char buf[max_buf];156 char ch;157 // /sys/class/gpio/gpion/value158 len = snprintf(buf, sizeof(buf), sysfs_gpio_dir /gpio%d/value, gpio);159 160 fd = open(buf, o_rdonly);//打开文件161 if (fd < 0) {162 perror(gpio/get-value);163 return fd;164 }165 166 read(fd, &ch, 1);//读取外部输入电平167 168 if (ch != '0') {//为'1',则设置为1,即输入为高电平169 *value = 1;170 } else {//为'0',则设置为0,即输入为低电平171 *value = 0;172 }173 174 close(fd);//关闭文件175 return 0;176 } ​ 在实现引脚电平读取函数之后,我们来实现外部按键值得读取,我们通过读取value的值来读取按键值,具体相关的代码在程序文件《sysfs_gpio_4_export_gpio/sysfs_gpio_export.c》中main函数,下为对应代码部分。
183 int main(int argc, char **argv) {184 unsigned int i;185 unsigned int value1,value2;186 187 printf(t********************************************n);188 printf(t******** sysfs_gpio_test_demo**************n);189 printf(t******** version date: 2020/05 **********n);190 printf(t********************************************n); 191 192 printf(key begin to initrn);193 sysfs_gpio_export(gpio_key1);//export gpio key1194 sysfs_gpio_export(gpio_key2);//export gpio key2195 196 sysfs_gpio_set_dir(gpio_key1, 0);//set as input197 sysfs_gpio_set_dir(gpio_key2, 0);//set as input198 199 printf(key init okrn);200 201 202 /* confirm init_b pin as high */203 while(1)204 {205 206 sysfs_gpio_get_value(gpio_key1, &value1); //read key1 value 207 //printf(@@key1 value 1is %d nr,value1);208 if(value1==0)//key1 pressed209 {210 printf(@@key1 is pressed 0nr); 211 }212 sysfs_gpio_get_value(gpio_key2, &value2);//read key2 value 213 //printf(##key2 value 1is %d nr,value2);214 if(value2==0)//key2 pressed215 {216 printf(##key2 is pressed 0nr); 217 }218 usleep(100000);//delay219 220 }221 222 sysfs_gpio_unexport(gpio_key1);//unexport gpio key1223 sysfs_gpio_unexport(gpio_key2);//unexport gpio key2224 225 226 return 0;227 } ​ 在将代码编译之后,我们将代码在板卡上进行运行。代码运行之后的的结果如下图所示,我们可以看到在按键key1和key2按下之后打印的值各有不同。
9.4.5 led和按键控制实验 ​ 在前几个实验中,我们分别实现了led和按键各自的控制。在这个实验中,我们将前几个实验进行整合,控制led得闪烁,并读取按键得值。当按键按下时,打印相关信息。具体相关的代码在程序文件《sysfs_gpio_5_export_gpio/sysfs_gpio_export.c》中main函数,下为对应代码部分
183 int main(int argc, char **argv) {184 unsigned int i;185 unsigned int value1,value2;186 187 printf(t********************************************n);188 printf(t******** sysfs_gpio_test_demo**************n);189 printf(t******** version date: 2020/05 **********n);190 printf(t********************************************n); 191 192 printf(led&key begin to initrn);193 sysfs_gpio_export(gpio_key1);//export gpio key1194 sysfs_gpio_export(gpio_key2);//export gpio key2195 sysfs_gpio_export(gpio_led);//export gpio led196 sysfs_gpio_set_dir(gpio_key1, 0);//set as input197 sysfs_gpio_set_dir(gpio_key2, 0);//set as input198 sysfs_gpio_set_dir(gpio_led, 1);//set as output199 printf(led&key init okrn);200 201 202 /* confirm init_b pin as high */203 while(1)204 {205 206 sysfs_gpio_get_value(gpio_key1, &value1); //read key1 value 207 //printf(@@key1 value 1is %d nr,value1);208 if(value1==0)//key1 pressed209 {210 printf(@@key1 is pressed 0nr); 211 }212 sysfs_gpio_get_value(gpio_key2, &value2);//read key2 value 213 //printf(##key2 value 1is %d nr,value2);214 if(value2==0)//key2 pressed215 {216 printf(##key2 is pressed 0nr); 217 }218 //led flash 219 sysfs_gpio_set_value(gpio_led, 1);220 printf(led offnr); 221 usleep(500000);222 sysfs_gpio_set_value(gpio_led, 0);223 printf(led onnr); 224 usleep(500000);225 }226 227 sysfs_gpio_unexport(gpio_key1);//unexport gpio key1228 sysfs_gpio_unexport(gpio_key2);//unexport gpio key2229 sysfs_gpio_unexport(gpio_led);//unexport gpio led230 231 return 0;232 } ​ 在将代码编译之后,我们将代码在板卡上进行运行。代码运行之后的的结果如下图所示,可以看到led闪烁,按键key1和key2按下之后打印的值各有不同(因为led的闪烁导致按键需要经过一次led闪烁之后才能读取,因此按键必须一直按着才能读取到值的变化)。

半导体芯片厂商跟上深度学习发展脚步求新求变
洲明科技在华中地区首个LED电影放映系统落地武汉
由RFW122-M构成的短距离无线数据通信系统
防盗系统各部分电路故障的现象
2016年秋灯展揭示香港灯饰行业的发展现状和趋势
Linux应用开发【第九章】GPIO编程应用开发
公路数字化改造 促成车联网落地
现场总线控制系统(FCS)与传统控制系统比较
在Raspberry Pi中利用Node-RED控制LED
中国联通宣布将全面升级异地同享业务
关于RISC-V你需要知道的大事儿
iOS系统关闭后台刷新设置有哪些好处和弊端
Silicon Labs发布新版iWRAP软件以简化Bluetooth音频开发
边缘运算工业物联网和自驾车获得重要关注
采用功率放大器提高宽带通信系统的效率
华为云ModelArts利用骨干网络将加速AI在行业落地
itunes备份文件在哪_itunes备份文件路径_怎么改itunes备份路径(教程)
环球晶预计2024年营收仍将持续增长
LED标准密集出台 仍需跟踪完善
确保您的IC封装/PC电路板设计的散热完整性