开发板支持sysfs方式控制gpio的电平行为,下面简单介绍一下sysfs方式控制。
首先查看/sys/class/gpio文件夹,如存在该文件夹说明,系统支持sysfs方式控制gpio
root@igkboard:/sys/class/gpio# ls
export gpiochip0 gpiochip128 gpiochip32 gpiochip64 gpiochip96 unexport
sysfs 是最初基于 ramfs 的基于 ram 的文件系统。它提供了一种将内核数据结构、它们的属性以及它们之间的链接导出到用户空间的方法。
可以理解为驱动程序将一些驱动设备在内核程序的属性,通过sysfs的方式,导出到用户空间,最终以文本文件的方式显示。上一节ds18b20的驱动正是此方式,后面驱动编程时候,我们可以利用该机制个性化将我们自己的去驱动程序的属性导出到用户空间。
/sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号
/sys/class/gpio/unexport 用于通知系统注销已导出的GPIO
/sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号base,寄存器名称,引脚总数 导出一个引脚的操作步骤
该40pin扩展图中,已经标好每个gpio对应的/sys/class/gpio 导出时候的编号。但是再此还是需要知道是如何计算的,计算公式如下
假设需要导出的gpio是GPIO0X_IOY
计算其编号为 NUM = (X - 1) * 32 + Y
示例 :GPIO01_IO29 GPIO05_IO09
NUM_GPIO01_IO29 = 0 + 29 = 29
NUM_GPIO05_IO09 = 4 * 32 + 9 = 137
下面将GPIO05_IO09作为操作对象,进行示例测试。
root@igkboard:~# echo 137 > /sys/class/gpio/export
root@igkboard:/sys/class/gpio# ls
export gpio137 gpiochip0 gpiochip128 gpiochip32 gpiochip64 gpiochip96 unexport
root@igkboard:/sys/class/gpio# ls gpio137/
active_low device direction edge power subsystem uevent value
可以看到/sys/class/gpio文件夹下增加了gpio137文件夹,查看该文件夹。下面讲解三个常用属性接口 active_low、direction、value
direction:gpio的输入输出属性,可以为in或out。
active_low:gpio的有效电平为低使能属性,可以为1或0(一般为0)。active_low为0时,高电平为有效电平,value为1时,gpio电平为高电平,0时为低电平;active_low为1时,低电平为有效电平,value为1时,gpio电平为低电平,0时为高电平。为了符合软件编程习惯,一般设置active_low=0。
value:gpio的电平值,实际电平高低和有效电平属性相关。可以为1或0。
将GPIO05_IO09设置为输出或输入示例
root@igkboard:/sys/class/gpio/gpio137# echo out > direction
root@igkboard:/sys/class/gpio/gpio137# echo in > direction
root@igkboard:/sys/class/gpio/gpio137# echo out > direction
root@igkboard:/sys/class/gpio/gpio137# echo 1 > value
root@igkboard:/sys/class/gpio/gpio137# echo 0 > value
root@igkboard:/sys/class/gpio/gpio137# echo in > direction
root@igkboard:/sys/class/gpio/gpio137# cat value
0
root@igkboard:/sys/class/gpio/gpio137# cat value
1
root@igkboard:/sys/class/gpio# echo 137 > unexport
root@igkboard:/sys/class/gpio# ls
export gpiochip0 gpiochip128 gpiochip32 gpiochip64 gpiochip96 unexport
libgpiod是用于与linux GPIO交互的C库和工具,从 linux 4.8 后,官方不推荐使用 GPIO sysfs 接口,libgpiod库封装了 ioctl 调用和简单的API接口。
与sysfs方式相比,libgpiod可以保证所有分配的资源,在关闭文件描述符后得到完全释放,并且拥有sysfs方式接口中不存在的功能(如时间轮询,一次设置/读取多个gpio值)。此外libgpiod还包含一组命令行工具,允许用户使用脚本对gpio进行个性化操作。
当前igkboard开发板的linux内核支持libgpiod的库使用,登录开发板后在/usr/lib文件夹之中查看的该动态库的存在,查看gpiodetect工具版本。
root@igkboard:/usr/lib# ls libgpiod.so.2* -l
lrwxrwxrwx 1 root root 17 Mar 9 2018 libgpiod.so.2 -> libgpiod.so.2.2.2
-rwxr-xr-x 1 root root 25896 Mar 9 2018 libgpiod.so.2.2.2
root@igkboard:~# gpiodetect -v
gpiodetect (libgpiod) v1.6.3
Copyright (C) 2017-2018 Bartosz Golaszewski
License: LGPLv2.1
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
下面首先介绍libgpiod提供的命令行工具。
目前有六个命令行工具可用,实验室树莓派上也可以使用(需要sudo权限)
下面列出几个常用的功能示例,同样以点亮led灯和读取gpio管脚电平作为示例。
root@igkboard:~# gpiodetect
gpiochip0 [209c000.gpio] (32 lines)
gpiochip1 [20a0000.gpio] (32 lines)
gpiochip2 [20a4000.gpio] (32 lines)
gpiochip3 [20a8000.gpio] (32 lines)
gpiochip4 [20ac000.gpio] (32 lines)
root@igkboard:~# gpioinfo
gpiochip0 - 32 lines:
line 0: unnamed unused input active-high
...
line 8: unnamed unused output active-high
line 9: unnamed "regulator-sd1-vmmc" output active-high [used]
...
line 18: unnamed "w1" output active-high [used open-drain]
line 19: unnamed "cd" input active-low [used]
...
gpiochip4 - 32 lines:
line 0: unnamed unused input active-high
line 1: unnamed unused input active-high
line 2: unnamed "irq" input active-high [used]
line 3: unnamed "reset" input active-high [used]
line 4: unnamed "phy-reset" output active-high [used]
...
root@igkboard:~# gpioset gpiochip4 8=1 9=1
root@igkboard:~# gpioset gpiochip4 8=1 9=0
root@igkboard:~# gpioset 4 8=0 9=1
root@igkboard:~# gpioset 4 8=1 9=0
root@igkboard:~# gpioget gpiochip4 8 9
0 1
root@igkboard:~# gpioget 4 8 9
0 1
代表支持的gpio芯片的相关信息
struct gpiod_chip {
struct gpiod_line **lines; //每个 gpio芯片的gpiod_line 数组地址,每一个gpio口对应一个line
unsigned int num_lines; //该gpio芯片下的gpio线路数量
int fd; //设备描述符,即库中底层使用ioctl打开的gpio芯片设备节点的描述符
char name[32]; //芯片的名称
char label[32]; //芯片的标签
};
每个gpio芯片下的gpio口的信息
struct gpiod_line {
unsigned int offset; //gpio 的偏移量,如GPIO05_IO09 偏移 9
int direction; //gpio的方向
bool active_low; //是否是低电平有效,前面介绍过此属性
int output_value; //最后写入 GPIO 的逻辑值
__u32 info_flags;
__u32 req_flags;
int state; //和事件相关的一个状态值
struct gpiod_chip *chip;//所属芯片的地址
struct line_fd_handle *fd_handle;
char name[32]; //名字,编程时候可以给使用的gpio赋予名字
char consumer[32]; //使用者名字
};
struct gpiod_chip *gpiod_chip_open(const char *path);
功能描述:根据gpiochip路径打开需要的chip
参数解析:path:要打开的 gpiochip 的路径
返回值:成功返回GPIO 芯片句柄,失败则返回 NULL
示例代码:
struct gpiod_chip *chip;
chip = gpiod_chip_open("/dev/gpiochip4");
struct gpiod_line* gpiod_chip_get_line(struct gpiod_chip* chip,uint offset);
功能描述:获取给定偏移量值 GPIO 句柄
参数解析:chip:GPIO 芯片句柄
offset:GPIO 偏移量
返回值:成功返回GPIO 句柄,失败则返回 NULL
示例代码:
struct gpiod_line *line;
uint gpio_off_num = 9;
line = gpiod_chip_get_line(chip, gpio_off_num);
int gpiod_line_request_output(struct gpiod_line* line,const(char)* consumer,int default_val);
功能描述:设置输出方向并且初始化逻辑值
参数解析: line:GPIO 句柄
consumer:使用者的名称
default_val:初始值
返回值:成功返回0,失败则返回-1
示例代码:设置初始值为1,高电平
int ret;
ret = gpiod_line_request_output(line, "led_out", 1);
int gpiod_line_set_value(struct gpiod_line* line,int value);
功能描述:设置单个 GPIO 值
参数解析:line:GPIO 句柄
value:设定的值
返回值:成功返回0,失败则返回-1
示例代码:设置gpio逻辑值 为1或0
int ret;
ret = gpiod_line_set_value(line, 1);
ret = gpiod_line_set_value(line, 0);
int gpiod_line_request_input(struct gpiod_line *line, const char *consumer);
功能描述:设置gpio为输入方向
参数解析: line:GPIO 句柄
consumer:使用者的名称
返回值:成功返回0,失败则返回-1
int gpiod_line_get_value(struct gpiod_line *line);
功能描述:读取单个 GPIO 逻辑
参数解析:line:GPIO 句柄
返回值:成功返回0或1(即逻辑值),失败则返回-1
void gpiod_chip_close(struct gpiod_chip* chip);
功能描述:关闭 GPIO 芯片句柄并释放所有分配的资源
参数解析:chip: GPIO 芯片句柄
void gpiod_line_release(struct gpiod_line* line);
功能描述:释放gpio口
参数解析:line:GPIO 句柄
root@igkboard:~# ls /dev/gpiochip*
/dev/gpiochip0 /dev/gpiochip1 /dev/gpiochip2 /dev/gpiochip3 /dev/gpiochip4
示例:现需要对GPIO05_IO09 进行电平控制
易知需选择的chip是gpiochip4 所需要的偏移是9
struct gpiod_chip *chip;
struct gpiod_line *line_led;
int led_init(unsigned char gpio_chip_num, unsigned char gpio_off_num)
{
int ret;
char dev_name[16];
if(gpio_chip_num == 0 || gpio_chip_num > 5 || gpio_off_num == 0 || gpio_off_num > 32)
{
printf("[INFO] %s argument error.\n", __FUNCTION__);
return -1;
}
memset(dev_name, 0, sizeof(dev_name));
snprintf(dev_name, sizeof(dev_name), "/dev/gpiochip%d", gpio_chip_num-1);
chip = gpiod_chip_open(dev_name);
if(!chip)
{
printf("fail to open chip0\n");
return -1;
}
line_led = gpiod_chip_get_line(chip, gpio_off_num);
if(!line_led)
{
printf("fail to get line_led\n");
return -1;
}
ret = gpiod_line_request_output(line_led, "led_out", 1);
if(ret < 0)
{
printf("fail to request line_led for output mode\n");
return -1;
}
return ret;
}
led控制与释放函数的封装
int led_control(int cmd)
{
int ret;
if(cmd == 0)
{
ret = gpiod_line_set_value(line_led, 0);
}
else
{
ret = gpiod_line_set_value(line_led, 1);
}
if(ret < 0)
{
printf("fail to set line_led value\n");
return -1;
}
return ret;
}
int led_release(void)
{
gpiod_line_release(line_led);
gpiod_chip_close(chip);
}
完整代码如下
#include
#include
#include
#include //gpiod 头文件支持
struct gpiod_chip *chip;
struct gpiod_line *line_led;
int led_init(unsigned char gpio_chip_num, unsigned char gpio_off_num)
{
int ret;
char dev_name[16];
if(gpio_chip_num == 0 || gpio_chip_num > 5 || gpio_off_num == 0 || gpio_off_num > 32)
{
printf("[INFO] %s argument error.\n", __FUNCTION__);
return -1;
}
memset(dev_name, 0, sizeof(dev_name));
snprintf(dev_name, sizeof(dev_name), "/dev/gpiochip%d", gpio_chip_num-1);
chip = gpiod_chip_open(dev_name);
if(!chip)
{
printf("fail to open chip0\n");
return -1;
}
line_led = gpiod_chip_get_line(chip, gpio_off_num);
if(!line_led)
{
printf("fail to get line_led\n");
return -1;
}
ret = gpiod_line_request_output(line_led, "led_out", 1);
if(ret < 0)
{
printf("fail to request line_led for output mode\n");
return -1;
}
return ret;
}
int led_control(int cmd)
{
int ret;
if(cmd == 0)
{
ret = gpiod_line_set_value(line_led, 0);
}
else
{
ret = gpiod_line_set_value(line_led, 1);
}
if(ret < 0)
{
printf("fail to set line_led value\n");
return -1;
}
return ret;
}
int led_release(void)
{
gpiod_line_release(line_led);
gpiod_chip_close(chip);
}
int main(int argc, char *argv[])
{
if(0 > led_init(5, 9))
{
printf("led init error.\n");
return -1;
}
while(1)
{
led_control(1);
sleep(1);
led_control(0);
sleep(1);
}
led_release();
return 0;
}
编写makefile如下
CC=/opt/buildroot/cortexA7/bin/arm-linux-gcc
APP_NAME=gpiod_led_test
all:clean
@${CC} ${APP_NAME}.c -o ${APP_NAME} -lgpiod
clean:
@rm -f ${APP_NAME}
编译后文件tree
.
├── gpiod_led_test
├── gpiod_led_test.c
└── Makefile
将可执行文件传输到开发板上,利用chmod命令增加执行权限
root@igkboard:~# chmod +x gpiod_led_test
root@igkboard:~# ./gpiod_led_test
硬件连接将GPIO05_IO09连接发光二极管的阳极,GND连接发光二极管的阴极
可见 led灯发生闪烁现象,闪烁时间间隔大概1s