应用层操作gpio的3种方法之一:通过sysfs文件系统
先上结论:1. 通过sysfs方式访问gpio,在命令行界面进行操作。2.编写一个简单的GPIO驱动,通过系统调用进行操作。3. 通过Input子系统进行操作。如果还有其他方法,欢迎补充~!《一》通过sysfs方式访问gpio,在命令行界面进行操作。《二》编写一个简单的GPIO驱动,通过系统调用进行操作。《三》通过Input子系统进行操作。...
通过sysfs方式访问gpio,在命令行界面进行操作。
Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。这里对于Sysfs不展开,有需要详细查看Sysfs文件系统的,可以参考我的博文“”。
=========================================================================
(1). 在应用层通过Sysfs访问Gpio子系统,首先需要操作接口,没有操作接口一切都无从谈起。
先进入/sys/class/目录,看看系统中有没有“/sys/class/gpio”这个文件夹。
如果没有请在 make ARCH=arm menuconfig 中打开(以ARM平台为例):
Device Drivers -> GPIO Support -> /sys/class/gpio/… (sysfs interface)
这是系统提供的gpio的驱动,保存退出后重新编译内核,将新内核烧写到目标板子中。
(2). 然后就是计算引脚值。
- 一般芯片引脚的表达为:GPIOx_yZ
- 引脚值的计算公式为: num = x*32 + (y - a) * 8 + Z
- 其中x代表0,1,2,3.....n; y代表a,b,c...;Z代表0,1,2,3.....n
- 以GPIO 0_A7 为例(此处A与a不区分大小写):num = 0*32 + (a - a) * 8 + 7 = 7
- 因此该引脚值num为:7
-------------------------------------------------------------------------------
- 针对芯片引脚的表达为:Py_Z
- 引脚值的计算公式为: num = (y-a)*32 + Z
- 其中y代表a,b,c...;Z代表0,1,2,3.....n
- 以PH_6为例(此处H与h不区分大小写):num = (h - a) * 32 + 6 = 7 * 32 + 6 = 230
- 一般内核中,PA定义为0,PB定义为1,PC定义为2,以此类推,则PH定义为7
- 因此该引脚值num为:230
(3). 接口和引脚值提供好了,剩下的就是操作,有6种操作方式。
先 cd 进入 /sys/class/gpio 目录,以230引脚为例
- 导出230引脚: echo 230> export
导出成功后会有引脚目录出现,如下:
- 设置方向:
cd 进入gpio230目录, ls 查看一下,会出现几个操作符,如下:
其中: direction 代表引脚方向设置,value 代表设置或获取的引脚高低电平值。
通过字符串 "in" 或 "out" 可以设置引脚输入输出方向
echo "in" > direction -----------> 输入方向
echo "out" > direction -----------> 输出方向
- 查看引脚方向: cat direction
- 设置输出值
在引脚为输出状态下,通过数0或1,可以设置引脚输出高低电平。
echo 1 > value -------> 输出高电平
echo 0 > value -------> 输出低电平
- 查看输出值: cat value
在引脚为输入状态下,可以通过数0或1,判断引脚的高低电平状态。
0 ----------引脚电平为低
1 ----------引脚电平为高
- 取消导出: echo 230> unexport
取消导出后,该gpio230目录消失
(4). 另外就是作为中断引脚使用
作为中断引脚使用时,需要先看这个引脚是否为外部GPIO的中断引脚源,一般几组引脚中只有一到两组引脚是具有GPIO外部中断功能。
以引脚编号230为例子:进入进入gpio230目录
如果目录中存在 “edge” 变量,则代表该引脚可以作为中断引脚来使用。
edge 表示中断的触发方式,edge文件有如下四个值:"none", "rising", "falling","both"。
none:表示引脚为输入,不是中断引脚
rising:表示引脚为中断输入,上升沿触发
falling:表示引脚为中断输入,下降沿触发
both:表示引脚为中断输入,边沿触发
这个文件节点(即该 “edge” 变量)只有在引脚被配置为输入引脚的时候才存在。
当值是none时可以通过如下方法将变为中断引脚
echo "both" > /sys/class/gpio/gpioN/edge
对于是both,falling还是rising依赖具体硬件的中断的触发方式。
此方法即用户态gpio转换为中断引脚的方式:
先将GPIO配置为输入,然后使用poll()来阻塞程序直到GPIO的输入电平发生改变,关键是使用POLLPRI而不是POLLIN来侦听事件;或者使用select()。
=========================================================================
同样,也可以在应用程序中,通过系统调用进行操作,但是要注意所要操作节点的权限问题。
如下:
// 导出打开
int export_fd = open("/sys/class/gpio/export", O_WRONLY);
// 导出关闭
close(export_fd);
// 方向设置打开
int direction_fd = open("/sys/class/gpio/gpio7/direction", O_WRONLY);
// 写方向
write(direction_fd, "in", sizeof("in"))
write(direction_fd, "out", sizeof("out"))
// 方向设置关闭
close(direction_fd);
// 引脚值设置/读取打开
int gpiovalue_fd = open("/sys/class/gpio/gpio7/value", O_RDONLY);
// 设置引脚值
write(gpiovalue_fd, "1", sizeof("1"))
write(gpiovalue_fd, "0", sizeof("0"))
// 引脚值设置关闭
close(gpiovalue_fd);
// 读取引脚值
read(gpiovalue_fd, &value, sizeof(value))
注意:如果在循环中,不重新open和close情况下,循环 读写 某个IO口的状态,每次 读写 前
需要用lseek将指针重置回文件起始处,因为本质上IO口的状态值是保存在文件中。
if(lseek(gpio_fd,0,SEEK_SET) < 0){
return;
} else {
.....
}
例子:用户态使用gpio控制LED
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
// Led pin number is 242
int main(int argc,char **argv)
{
int gpio_fd = -1;
int ret;
char gpio[]="242";
char dir[]="out";
// 导出引脚
gpio_fd = open("/sys/class/gpio/export",O_WRONLY);
if(gpio_fd < 0){
printf("open gpio/export failed\n");
return -1;
}
ret = write(gpio_fd,gpio,strlen(gpio));
if(ret < 0){
printf("write to gpio/export failed\n");
return -1;
}
close(gpio_fd);
// 配置引脚方向为输出
gpio_fd = open("/sys/class/gpio/gpio242/direction",O_RDWR);
if(gpio_fd < 0){
printf("open gpio242/direction failed\n");
return -1;
}
ret = write(gpio_fd,dir,strlen(dir));
if(ret < 0){
printf("write to gpio242/direction failed\n");
return -1;
}
close(gpio_fd);
// 配置引脚输出电平
gpio_fd = open("/sys/class/gpio/gpio242/value",O_RDWR);
if(gpio_fd < 0){
printf("open gpio242/value failed\n");
return -1;
}
// 配置引脚依次输出高低电平
char off[] = "1";
char on[] = "0";
for(int i=0;i < 10;i++){
printf("led off\n");
ret = write(gpio_fd,off,strlen(off));
if(ret < 0){
printf("write to gpio242/value failed\n");
return -1;
}
sleep(1);
printf("led on\n");
ret = write(gpio_fd,on,strlen(on));
if(ret < 0){
printf("write to gpio242/value failed\n");
return -1;
}
sleep(1);
}
close(gpio_fd);
// 关闭引脚
gpio_fd = open("/sys/class/gpio/unexport",O_WRONLY);
if(gpio_fd < 0){
printf("open gpio/unexport failed\n");
return -1;
}
ret = write(gpio_fd,gpio,strlen(gpio));
if(ret < 0){
printf("write to gpio/unexport failed\n");
return -1;
}
close(gpio_fd);
// 结束
printf("test gpio led ok\n");
return 0;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)