概述
以前学习了LED和按键驱动,实际上,在Linux中实现这些设备驱动,有一种更为推荐的方法,就是GPIO子系统和Input子系统。GPIO子系统可以控制IO的初始化、输出高低电平值,读取IO的输入电平;Input子系统处理输入事务,任何输入设备的驱动程序都可以通过Input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互。例如控制LED、读取按键、触摸屏、鼠标都可以通过这些子系统接口实现。
GIPO子系统介绍
gpio子系统相关描述可在内核源码 Documentation/gpio 了解。
在/sys/class/gpio目录下展示该子系统设备。如果没有请在编译内核的时候加入 Device Drivers-> GPIO Support ->/sys/class/gpio/… (sysfs interface)。
export 和 unexport:把某个GPIO导出用户空间和取消导出用户空间。
- 例如:echo 19 > export,把GPIO19导出到用户空间。
- GPION,其中N值的计算方式:
假设一组端口有32个GPIO,N=index = GPIOx_y= (x-1)*32 +y;
例如一:GPIOB_6,对应的N值为(2-1)*32+6=38;
例如二:GPIOD_3,对应的N值为(4-1)*32+3=99。
进入子系统设备目录,包含一些常用属性:
direction:gpio端口的方向,内容可以是 in 或者 out;
- 示例:echo out > /sys/class/gpio/pioD3/direction
- 如果写入 low 或者 hight,则不仅设置为输出,还同时设置了输出电平(本小点要了解当前内核是否支持)
value:gpio引脚的电平,0为低电平,非0为高电平。
- 如果配置为输入,则该文件可写;
- 如果配置为中断,则可以调用poll(2)函数监听该中断,中断触发后poll(2)函数就会返回。
edge: 中断的触发方式,该文件有4个值。
- none:输入,非中断
- rising:中断输入,上升沿触发
- falling:中断输入,下降沿触发
- both:中断输入,边沿触发
gpio子系统与led子系统是不同,但是类似。 - led只能输出,gpio能输出也能输入
- gpio输入还支持中断功能
- 使用gpio前,需要从内核空间暴露到用户空间
注意:在用户空间控制使用gpio时,注意不要滥用和内核一些绑定好、已经有合适内核启动的引脚冲突。参考 Documentation/driver-api/gpio/drivers-on-gpio.rst
示例:通过GPIO子系统控制LED(GPIOA29)
system函数进行操作:
void main(void)
{
if(access("/sys/class/gpio/pioA29/value",F_OK) != 0)
{
/*使能LED设备*/
system("echo 29 > /sys/class/gpio/export");
}
/*设置LED输出*/
system("echo out > /sys/class/gpio/pioA29/direction");
/*打开LED*/
system("echo '1' > /sys/class/gpio/pioA29/value");
sleep(1);
/*关闭LED*/
system("echo '0' > /sys/class/gpio/pioA29/value");
}
文件操作实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
FILE *p=NULL;
int i=0;
p = fopen("/sys/class/gpio/export","w");
fprintf(p,"%d",29);
fclose(p);
p = fopen("/sys/class/gpio/pioA29/direction","w");
fprintf(p,"out");
fclose(p);
for(i=0;i<100;i++)
{
p = fopen("/sys/class/gpio/pioA29/value","w");
fprintf(p,"%d",1);
sleep(1);
fclose(p);
p = fopen("/sys/class/gpio/pioA29/value","w");
fprintf(p,"%d",0);
sleep(1);
fclose(p);
}
p = fopen("/sys/class/gpio/unexport","w");
fprintf(p,"%d",38);
fclose(p);
return 0;
}
示例:通过GPIO子系统中断读取IO值(GPIOC2)
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
/*按键按下时,打按键值*/
int main()
{
FILE *fp=NULL; /*定义一个文件指针*/
if(access("/sys/class/gpio/pioC2",F_OK) != 0)
{
/*使能GPIO子系统设备*/
fp=fopen("/sys/class/gpio/export","w");
if(fp == NULL)
{
perror("open export fail\r\n");
return -1;
}
fprintf(fp,"%d",66);
fclose(fp);
}
/*设置为输入*/
fp=fopen("/sys/class/gpio/pioC2/direction","w");
if(fp == NULL)
{
perror("open direction fail\r\n");
return -1;
}
fprintf(fp,"in");
fclose(fp);
/*设置中断触发边沿*/
fp=fopen("/sys/class/gpio/pioC2/edge","w");
if(fp == NULL)
{
perror("open edge fail\r\n");
return -1;
}
fprintf(fp,"both");
fclose(fp);
/*对IO值进行poll侦听*/
int fd=open("/sys/class/gpio/pioC2/value",O_RDONLY);
if(fd<0)
{
perror("open '/sys/class/gpio/pioC2/value' failed!\n");
return -1;
}
struct pollfd fds[1];
fds[0].fd=fd;
fds[0].events=POLLPRI;
while(1)
{
if(poll(fds,1,0)==-1)
{
perror("poll failed!\n");
return -1;
}
if(fds[0].revents&POLLPRI)
{
if(lseek(fd,0,SEEK_SET)==-1)
{
perror("lseek failed!\n");
return -1;
}
char buffer[16];
int len;
if((len=read(fd,buffer,sizeof(buffer)))==-1)
{
perror("read failed!\n");
return -1;
}
buffer[len]=0;
printf("%s",buffer);
}
}
}
分类:
嵌入式
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2020-11-06 Socket缓冲区大小修改与系统设置
2020-11-06 shell 将变量当命令执行问题【多条命令同时执行问题】
2020-11-06 将make的输出重定向到文件
2018-11-06 windows下网络丢包模拟软件(Network Emulator for Windows Toolkit)
2017-11-06 使用boost的deadline_timer实现一个异步定时器
2017-11-06 C++11 lambda 表达式解析
2017-11-06 C++11使用emplace_back代替push_back