在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()。