http://blog.rongpmcu.com/gpiozi-xi-tong-he-pinctrlzi-xi-tong/#
GPIO算是一种设备,有设备那我们就要对这个设备进行一个抽象,同时为了各种各样的GPIO设备会去抽象出一个gpio的核心层gpiolib.c,用于管理各种各种各样的gpio,
(话外:感觉内核对于设备的管理最精髓的就是核心层总线和driver,device分开,device,driver只需要向核心层去注册就好,核心层会去管理他们)
内核抽象的设备结构体就是:gpio_chip
gpio driver的工作就是去初始化实现这个结构体,然后注册这个结构体,此时gpio就可以工作了。
从层次来分,内核的gpio接口有两层,一层是提供给bsp用于实现gpio功能的,一层是提供给其他驱动去在内核态使用gpio的
如何使用gpio:
1.内核态:
使用gpio lib中提供的gpio子系统接口
gpio_request(30, "j15"); 申请该gpio
gpio_direction_input(30);置方向输入
gpio_get_value(20);输入模式下获取电平状态
gpio_direction_output(30);方向输出
gpio_set_value(20,0);输出模式下置高
gpio_set_value(20,1);输出模式下置低
gpio_free(20);释放gpio资源
上述就是一个完整的在内核中如果想操作一个gpio的流程
上述操作是基于内核的标准gpio子系统而注册gpiodriver后才可以使用,其他各种不标准的做法不可以使用上述即下述的操作
2.用户态:
http://blog.csdn.net/xgbing/article/details/51009384
http://blog.chinaunix.net/uid-27875-id-3979149.html
(1).将GPIO通过sysfs导出到用户空间
要想在用户空间访问GPIO,首先需要在sysfs中使能GPIO支持。
Device Drivers --->
[*] GPIO Support --->
[*] /sys/class/gpio/... (sysfs interface)
配置后gpiolib_sysfs.c才可以支持编译。
此时系统会有目录/sys/class/gpio这个文件夹出现
cd /sys/class/gpio
ls
export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
或者使用:for i in gpiochip* ; do echo `cat $i/label`: `cat $i/base` ; done
/soc/gpio@18000100: 0
/soc/gpio@18000120: 32
/soc/gpio@18000140: 64
/soc/gpio@18000160: 96
这个表示目前系统中有4组gpio,index从0开始一直到96+32 = 127
此时若要操作某个gpio,现将其导出
echo 139 >/sys/class/gpio/export
export_store: invalid GPIO 139
sh: write error: Invalid argument //因为139号引脚不存在,所以报错
echo 26 >/sys/class/gpio/export //没有报错
ls
export gpio26 gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport//此时已经有gpio26被导出在sysfs空间了
cd gpio26 //进入gpio发现会有这么多它的属性,此时我们可以cat这些属性查询他们的default状态
# ls
active_low direction power uevent
device edge subsystem value
我们对gpio26的direction属性做操作
echo out > /sys/class/gpio/gpio26/direction //置为输出状态
echo 1 > /sys/class/gpio/gpio26/value //设置为高电平
# cat value //查看设置成功
1
echo 26 > /sys/class/gpio/unexport //取消导出
s
export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport //此时已经看不到 gpio26 这个文件了,表示取消成功
上述我们看到要操作一个gpio的时候可以要使用echo 26 >/sys/class/gpio/export才可以导出gpio26这个文件,其实也可以预先让其默认让其导出,
在内核代码中当使用gpio_export()函数去使能:
gpio_request(26, "ACS5K CAN Control");
gpio_direction_output(26, 1); /* Default CAN_RESET high */
gpio_export(26, 0); /* export CAN_RESET as output only */0表示该gpio口的方向不能在用户空间通过sysfs来进行改变,1表示可以改变
static inline int gpio_export(unsigned gpio, bool direction_may_change)
函数原型static inline int gpio_export(unsigned gpio, bool direction_may_change)
gpio_export()的调用一定是要在request之后才可以,调用后,/sys/class/gpio/目录下就默认有gpio26这个目录了,
(2)将driver支持普通字符设备的file_operation操作,创建dev目录下节点,提供ioctl接口
gpio的中断是如何使用的?
gpio的中断是需要gpio的driver去支持的,当driver中注册了gpio的中断后,同时选中gpio的sysfs后就可以在/sys/class/gpio/gpio26目录下看到edge这个节点了
1.内核态:
?
2.用户态:
value 属性表示gpio引脚的电平,0(低电平)1(高电平),如果gpio被配置为输出,这个值是可写的,记住任何非零的值都将输出高电平, 如果某个引脚
能并且已经被配置为中断,则可以调用poll(2)函数监听该中断,中断触发后poll(2)函数就会返回。
edge属性 表示中断的触发方式,edge文件有如下四个值:
"none", "rising","falling","both"。
none表示引脚为输入,不是中断引脚
rising表示引脚为中断输入,上升沿触发
falling表示引脚为中断输入,下降沿触发
both表示引脚为中断输入,边沿触发
这个文件节点只有在引脚被配置为输入引脚的时候才存在。
当值是none时可以通过如下方法将变为中断引脚
echo "both" > edge;
对于是both,falling还是rising依赖具体硬件的中断的触发方式。此方法即用户态gpio转换为中断引脚的方式
用户态使用gpio监听中断
首先需要将该gpio配置为中断
echo "rising" > /sys/class/gpio/gpio12/edge
以下是伪代码
int gpio_id;
struct pollfd fds[1];
gpio_fd = open("/sys/class/gpio/gpio12/value",O_RDONLY);
if( gpio_fd == -1 )
err_print("gpio open");
fds[0].fd = gpio_fd;
fds[0].events = POLLPRI;
ret = read(gpio_fd,buff,10);
if( ret == -1 )
err_print("read");
while(1){
ret = poll(fds,1,-1);
if( ret == -1 )
err_print("poll");
if( fds[0].revents & POLLPRI){
ret = lseek(gpio_fd,0,SEEK_SET);
if( ret == -1 )
err_print("lseek");
ret = read(gpio_fd,buff,10);
if( ret == -1 )
err_print("read");
/*此时表示已经监听到中断触发了,该干事了*/
...............
}
}
记住使用poll()函数,设置事件监听类型为POLLPRI和POLLERR在poll()返回后,使用lseek()移动到文件开头读取新的值或者关闭它再重新打开读取新值。必须这样做否则poll函数会总是返回。