LINUXGPIO框架与测试
在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
# echo 1 > /sys/class/gpio/export
# ls /sys/class/gpio/
export gpio1 gpiochip0 unexport
# cd gpiochip0
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
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 \c"
echo 1 > /sys/class/gpio/gpio$1/value
echo
echo -e "TRIGGER \c"
echo 0 > /sys/class/gpio/gpio$1/value
sleep 1
echo
echo -e "TRIGGER \c"
echo 1 > /sys/class/gpio/gpio$1/value
echo
linux下的sleep函数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 "
echo
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 "
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 ) 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);
return0;
}
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;
}
}
/*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);
return0;
}
/*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);
return0;
}
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){
case0:
ptr = 0;
break;
break;
case2:
ptr = 12;
break;
case3:
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);
return0;
}
//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);
}
return0;
}
(4)中断
edge 表⽰中断的触发⽅式,edge⽂件有如下四个值:"none", "rising", "falling","both"。// none表⽰引脚为输⼊,不是中断引脚
// rising表⽰引脚为中断输⼊,上升沿触发
// falling表⽰引脚为中断输⼊,下降沿触发
// both表⽰引脚为中断输⼊,边沿触发
// 这个⽂件节点只有在引脚被配置为输⼊引脚的时候才存在。当值是none时可以通过如下⽅法将变为中断引脚

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。