在ARM开发过程中,引脚复用严重,导致问题的原因软硬件并存,因此在实际的开发中常常需要使用GPIO来探测所配置的路径或设备是否畅通,可用,以此来判定是设备的问题,还是驱动的问题或是程序的问题。
1. 在Linux内核中提供一个标准的GPIO LIB框架,它位于:
--<*>Device Drivers
--<*>GPIO Support
--
GPIO Support选项的选入,内核会将标准GPIO库进行编译。标准GPIO库的源码位于内核中的如下位置:
/driver/gpio/gpiolib.c
在该文件中,提供如下的接口函数用于配置和操作GPIO:
int gpio_request(unsigned gpio, const char *label); //获取GPIO Pin的使用权,并为该Pin命名为label。
void gpio_free(unsigned gpio); //释放GPIO Pin的使用权。
int gpio_direction_input(unsigned gpio); //设置GPIO Pin为输入模式。
int gpio_direction_output(unsigned gpio, int value);//设置GPIO Pin为输出模式,并指定输出值value。
int gpio_get_value(unsigned gpio); //获得 GPIO Pin 上的电平。
void gpio_set_value(unsigned gpio, int value); //设置 GPIO Pin 上的电平。
int gpio_to_irq(unsigned gpio); //通过获得GPIO Pin 对应的 irq number。
通过追踪代码会发现,他们调用的正是我们实现的驱动里边的接口。
对于上述函数的gpio参数的计算方式为: GPIOx_n-->gpio=x*32+n。
2.上层应用说明
(a)启动内核查看系统中有没有 “/sys/class/gpio” 这个文件夹。如果没有请在编译内核的时候通过make menuconfig加入
GPIO Support —> /sys/class/gpio/… (sysfs interface)
(b)文件说明
export:用于通知系统需要导出控制的GPIO引脚编号,导出成功会出现 gpio*,如下的gpio1
base device label ngpio power subsystem uevent
/*base :引脚的起始编号, label:寄存器名称, ngpio:引脚总数*/
# cd ../gpio1
active_low direction power uevent device edge subsystem value
# echo out > direction
# echo 1 > value
# cd ..
# echo 1 > unexport
(c)命令行说明:
# echo 44 > /sys/class/gpio/export /*44引脚 导出*/
# echo out > /sys/class/gpio/gpio44/direction /*44引脚设置输出为方向*/
# cat /sys/class/gpio/gpio44/direction /*查看方向*/
# echo 1 > /sys/class/gpio/gpio44/value /*设置输出值*/
# cat /sys/class/gpio/gpio44/value /*查看输出值*/
# echo 44 > /sys/class/gpio/unexport /*取消导出*/
3 测试
(1)在内核启动之后,通过shell 脚本或命令行的形式操作gpiolib中的标准接口。
# GPIO BASE
GPIO0_BASE=0
GPIO1_BASE=1
GPIO2_BASE=2
GPIO3_BASE=3
# gpio_out/gpio_in/test_value
function gpio_sysfs_inout_test() {
echo out > /sys/class/gpio/gpio$1/direction
echo in > /sys/class/gpio/gpio$2/direction
echo $3 > /sys/class/gpio/gpio$1/value
inv=`cat /sys/class/gpio/gpio$2/value`
if [ $inv == $3 ]; then
echo "GPIO$1 ----($3)----->GPIO$2 PASS"
else
echo "GPIO$1 ----($3)----->GPIO$2 FAIL"
fi
}
# gpio-event-mon
function check_alive(){
count=`ps -ef |grep gpio-event-mon |grep -v "grep" |wc -l`
#echo $count
if [ 0 == $count ];then
return false
else
return true
fi
}
# out in
function gpio_sysfs_interrupt_test() {
echo $1 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio$1/direction
echo 0 > /sys/class/gpio/gpio$1/value
echo
usr/bin/gpio-event-mon -n gpiochip0 -o $2 -r -f -c 3 &
sleep 1
echo
echo -e "TRIGGER RISSING EDGE...\c"
echo 1 > /sys/class/gpio/gpio$1/value
echo
echo -e "TRIGGER FAILLING EDGE...\c"
echo 0 > /sys/class/gpio/gpio$1/value
sleep 1
echo
echo -e "TRIGGER RISSING EDGE...\c"
echo 1 > /sys/class/gpio/gpio$1/value
echo
sleep 1
count=`ps -ef |grep gpio-event-mon |grep -v "grep" |wc -l`
#echo $count
if [ 0 == $count ];then
result=PASS
else
result=FAIL
killall gpio-event-mon
fi
echo
echo "GPIO$2 INTERRUPT TEST ---->$result"
echo $1 > /sys/class/gpio/unexport
}
echo $GPIO0_BASE > /sys/class/gpio/export
echo $GPIO1_BASE > /sys/class/gpio/export
echo $GPIO2_BASE > /sys/class/gpio/export
echo $GPIO3_BASE > /sys/class/gpio/export
export_done=false
[ -e /sys/class/gpio/gpio${GPIO0_BASE} ] && \
[ -e /sys/class/gpio/gpio${GPIO1_BASE} ] && \
[ -e /sys/class/gpio/gpio${GPIO2_BASE} ] && \
[ -e /sys/class/gpio/gpio${GPIO3_BASE} ] && export_done=true
if [ $export_done == "false" ]; then
echo "Fail to export $GPIO0_BASE $GPIO1_BASE $GPIO2_BASE $GPIO3_BASE"
exit 0
fi
echo
echo "GPIO IN and OUT TEST START...."
echo
gpio_sysfs_inout_test $GPIO0_BASE $GPIO1_BASE 0
gpio_sysfs_inout_test $GPIO0_BASE $GPIO1_BASE 1
gpio_sysfs_inout_test $GPIO0_BASE $GPIO1_BASE 0
gpio_sysfs_inout_test $GPIO1_BASE $GPIO0_BASE 0
gpio_sysfs_inout_test $GPIO1_BASE $GPIO0_BASE 1
gpio_sysfs_inout_test $GPIO1_BASE $GPIO0_BASE 0
gpio_sysfs_inout_test $GPIO2_BASE $GPIO3_BASE 0
gpio_sysfs_inout_test $GPIO2_BASE $GPIO3_BASE 1
gpio_sysfs_inout_test $GPIO2_BASE $GPIO3_BASE 0
gpio_sysfs_inout_test $GPIO3_BASE $GPIO2_BASE 0
gpio_sysfs_inout_test $GPIO3_BASE $GPIO2_BASE 1
gpio_sysfs_inout_test $GPIO3_BASE $GPIO2_BASE 0
echo $GPIO0_BASE > /sys/class/gpio/unexport
echo $GPIO1_BASE > /sys/class/gpio/unexport
echo $GPIO2_BASE > /sys/class/gpio/unexport
echo $GPIO3_BASE > /sys/class/gpio/unexport
echo
echo "GPIO INTERRUPT TEST START...."
echo
gpio_sysfs_interrupt_test $GPIO0_BASE $GPIO1_BASE
gpio_sysfs_interrupt_test $GPIO1_BASE $GPIO0_BASE
gpio_sysfs_interrupt_test $GPIO2_BASE $GPIO3_BASE
gpio_sysfs_interrupt_test $GPIO3_BASE $GPIO2_BASE
(2)通过写应用程序
#include stdlib.h
#include stdio.h
#include string.h
#include unistd.h
#include fcntl.h
#include poll.h
#define MSG(args...) printf(args)
static int gpio_export(int pin);
static int gpio_unexport(int pin);
static int gpio_direction(int pin, int dir);
static int gpio_write(int pin, int value);
static int gpio_read(int pin);
static int gpio_export(int pin)
{
char buffer[64];
int len;
int fd;
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
MSG("Failed to open export for writing!\n");
return(-1);
}
len = snprintf(buffer, sizeof(buffer), "%d", pin);
if (write(fd, buffer, len) < 0) {
MSG("Failed to export gpio!");
return -1;
}
close(fd);
return 0;
}
static int gpio_unexport(int pin)
{
char buffer[64];
int len;
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (fd < 0) {
MSG("Failed to open unexport for writing!\n");
return -1;
}
len = snprintf(buffer, sizeof(buffer), "%d", pin);
if (write(fd, buffer, len) < 0) {
MSG("Failed to unexport gpio!");
return -1;
}
close(fd);
return 0;
}
/*dir: 0-->IN, 1-->OUT*/
static int gpio_direction(int pin, int dir)
{
static const char dir_str[] = "in\0out";
char path[64];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);
fd = open(path, O_WRONLY);
if (fd < 0) {
MSG("Failed to open gpio direction for writing!\n");
return -1;
}
if (write(fd, &dir_str[dir == 0 ? 0 : 3], dir == 0 ? 2 : 3) < 0) {
MSG("Failed to set direction!\n");
return -1;
}
close(fd);
return 0;
}
/*value: 0-->LOW, 1-->HIGH*/
static int gpio_write(int pin, int value)
{
static const char values_str[] = "01";
char path[64];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_WRONLY);
if (fd < 0) {
MSG("Failed to open gpio value for writing!\n");
return -1;
}
if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0) {
MSG("Failed to write value!\n");
return -1;
}
close(fd);
return 0;
}
static int gpio_read(int pin)
{
char path[64];
char value_str[3];
int fd;
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_RDONLY);
if (fd < 0) {
MSG("Failed to open gpio value for reading!\n");
return -1;
}
if (read(fd, value_str, 3) < 0) {
MSG("Failed to read value!\n");
return -1;
}
close(fd);
return (atoi(value_str));
}
// none表示引脚为输入,不是中断引脚
// rising表示引脚为中断输入,上升沿触发
// falling表示引脚为中断输入,下降沿触发
// both表示引脚为中断输入,边沿触发
// 0-->none, 1-->rising, 2-->falling, 3-->both
static int gpio_edge(int pin, int edge)
{
const char dir_str[] = "none\0rising\0falling\0both";
char ptr;
char path[64];
int fd;
switch(edge){
case 0:
ptr = 0;
break;
case 1:
ptr = 5;
break;
case 2:
ptr = 12;
break;
case 3:
ptr = 20;
break;
default:
ptr = 0;
}
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin);
fd = open(path, O_WRONLY);
if (fd < 0) {
MSG("Failed to open gpio edge for writing!\n");
return -1;
}
if (write(fd, &dir_str[ptr], strlen(&dir_str[ptr])) < 0) {
MSG("Failed to set edge!\n");
return -1;
}
close(fd);
return 0;
}
//GPIO1_17
int main()
{
int gpio_fd, ret;
struct pollfd fds[1];
char buff[10];
unsigned char cnt = 0;
//LED引脚初始化
gpio_export(115);
gpio_direction(115, 1);
gpio_write(115, 0);
//按键引脚初始化
gpio_export(49);
gpio_direction(49, 0);
gpio_edge(49,1);
gpio_fd = open("/sys/class/gpio/gpio49/value",O_RDONLY);
if(gpio_fd < 0){
MSG("Failed to open value!\n");
return -1;
}
fds[0].fd = gpio_fd;
fds[0].events = POLLPRI;
ret = read(gpio_fd,buff,10);
if( ret == -1 )
MSG("read\n");
while(1){
ret = poll(fds,1,0);
if( ret == -1 )
MSG("poll\n");
if( fds[0].revents & POLLPRI){
ret = lseek(gpio_fd,0,SEEK_SET);
if( ret == -1 )
MSG("lseek\n");
ret = read(gpio_fd,buff,10);
if( ret == -1 )
MSG("read\n");
gpio_write(115, cnt++%2);
}
usleep(100000);
}
return 0;
}
(4)中断
edge 表示中断的触发方式,edge文件有如下四个值:"none", "rising", "falling","both"。
// none表示引脚为输入,不是中断引脚
// rising表示引脚为中断输入,上升沿触发
// falling表示引脚为中断输入,下降沿触发
// both表示引脚为中断输入,边沿触发
// 这个文件节点只有在引脚被配置为输入引脚的时候才存在。 当值是none时可以通过如下方法将变为中断引脚
// echo "both" > /sys/class/gpio/gpioN/edge;对于是both,falling还是rising依赖具体硬件的中断的触发方式。
// 此方法即用户态gpio转换为中断引脚的方式
先将GPIO配置为输入,然后使用poll()来阻塞程序直到GPIO的输入电平发生改变,关键是使用POLLPRI而不是POLLIN来侦听事件;或者使用select()。