ShiMetaPi-Pico-G1:外设与接口(1)
1. GPIO子系统在 Linux 系统中GPIO 由专门的 GPIO 子系统统一管理该子系统基于内核的 GPIO 框架gpiolib实现为 GPIO 硬件提供标准化的抽象接口。通过该框架内核能够对不同平台的 GPIO 控制器进行统一封装使用户空间或内核驱动可以以一致的方式访问 GPIO 资源。 GPIO的控制方式有两种通过sysfs文件系统导出到用户空间进行控制从而以文件读写的方式实现对 GPIO 引脚的操作。(旧GPIO接口路径在 /sys/class/gpio/,现在 Linux 内核已经不推荐继续使用)使用基于字符设备的 GPIO 接口gpiochip。(新GPIO接口路径在/dev/gpiochipN,Linux 官方推荐的新方式)GPIO编号计算每组GPIO有8个GPIO管脚GPIO编号等于GPIO组合\*8组内偏移号例如GPIO4_2的GPIO编号为 4\*8234 使用字符设备驱动时GPIO1_4chip_path对应/dev/gpiochip1,line_offset对应42. character GPIO2.1 专业术语libgpiodlibgpiod 是 Linux 用户空间访问 GPIO通用输入输出的标准库是现代 GPIO 子系统gpiolib character device interface的官方用户态接口。ioctlioctl 是一种设备控制系统调用接口用于在 read/write 之外通过命令码request和结构化参数arg与设备驱动进行交互实现设备配置、状态查询和特殊控制功能。说明libgpiod 是 Linux 官方提供的 GPIO 用户空间参考库它对 GPIO character device ioctl 接口进行了封装提供统一的 GPIO line 申请、控制与事件 API。它在功能上相当于 GPIO 的用户态抽象层userspace abstraction layer但本质上仍依赖 ioctl 与内核 gpiolib 交互。同时用户完全可以绕过 libgpiod直接基于 ioctl 实现自己的 GPIO 抽象层。2.2 GPIO ioctl的使用2.2.1 头文件linux/gpio.h这个头文件里定义了常用结构体和 ioctlstruct gpiohandle_requeststruct gpiohandle_datastruct gpioevent_request以及这些 ioctl 命令GPIO_GET_CHIPINFO_IOCTLGPIO_GET_LINEINFO_IOCTLGPIO_GET_LINEHANDLE_IOCTLGPIO_GET_LINEEVENT_IOCTLGPIOHANDLE_GET_LINE_VALUES_IOCTLGPIOHANDLE_SET_LINE_VALUES_IOCTL2.2.2 相关结构体gpiohandle_request用于申请普通输入/输出 GPIO line。关键成员lineoffsets[]line 编号flags输入还是输出default_values[]默认输出值consumer_label给内核看的占用者名字fd成功后返回 line 句柄gpiohandle_data用于读写 line 电平。关键成员values[]每条 line 的值gpioevent_request用于申请中断事件 line。关键成员lineoffsetline 编号handleflags通常输入eventflags上升沿/下降沿/双边沿fd成功后返回事件 fd2.3 实现呼吸灯应用程序参考快速入门/部署第一个应用程序创建并编写gpio_hal.c#include gpio_hal.h #include errno.h #include fcntl.h #include stdio.h #include string.h #include sys/ioctl.h #include unistd.h int gpio_handle_init(gpio_handle_t *gpio_handle) { struct gpiohandle_request req; if (gpio_handle NULL || gpio_handle-chip_path NULL) { errno EINVAL; return -1; } gpio_handle-chip_fd -1; gpio_handle-line_fd -1; gpio_handle-chip_fd open(gpio_handle-chip_path, O_RDONLY); if (gpio_handle-chip_fd 0) { perror(open gpiochip); return -1; } memset(req, 0, sizeof(req)); req.lineoffsets[0] gpio_handle-line_offset; req.flags gpio_handle-gpio_mode; req.default_values[0] gpio_handle-default_value ? 1 : 0; req.lines 1; if (gpio_handle-consumer_label[0] ! \0) { strncpy(req.consumer_label, gpio_handle-consumer_label, sizeof(req.consumer_label) - 1); req.consumer_label[sizeof(req.consumer_label) - 1] \0; } else { strncpy(req.consumer_label, gpio-led, sizeof(req.consumer_label) - 1); req.consumer_label[sizeof(req.consumer_label) - 1] \0; } if (ioctl(gpio_handle-chip_fd, GPIO_GET_LINEHANDLE_IOCTL, req) 0) { perror(GPIO_GET_LINEHANDLE_IOCTL); close(gpio_handle-chip_fd); gpio_handle-chip_fd -1; return -1; } gpio_handle-line_fd req.fd; return 0; } int gpio_set_value(gpio_handle_t *gpio_handle, int value) { struct gpiohandle_data data; if (gpio_handle NULL || gpio_handle-line_fd 0) { errno EINVAL; return -1; } memset(data, 0, sizeof(data)); data.values[0] value ? 1 : 0; if (ioctl(gpio_handle-line_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, data) 0) { perror(GPIOHANDLE_SET_LINE_VALUES_IOCTL); return -1; } return 0; } void gpio_handle_close(gpio_handle_t *gpio_handle) { if (gpio_handle NULL) return; if (gpio_handle-line_fd 0) { close(gpio_handle-line_fd); gpio_handle-line_fd -1; } if (gpio_handle-chip_fd 0) { close(gpio_handle-chip_fd); gpio_handle-chip_fd -1; } }创建并编写gpio_hal.h#ifndef GPIO_HAL_H #define GPIO_HAL_H #ifdef __cplusplus extern C { #endif #include linux/gpio.h #define GPIO_CONSUMER_LABEL_LEN 32 typedef struct gpio_handle_t { int chip_fd; const char *chip_path; unsigned int line_offset; unsigned int gpio_mode; int default_value; int line_fd; char consumer_label[GPIO_CONSUMER_LABEL_LEN]; } gpio_handle_t; int gpio_handle_init(gpio_handle_t *gpio_handle); int gpio_set_value(gpio_handle_t *gpio_handle, int value); void gpio_handle_close(gpio_handle_t *gpio_handle); #ifdef __cplusplus } #endif #endif创建并编写main.c#include gpio_hal.h #include linux/gpio.h #include stdio.h #include string.h #include unistd.h #define LED_CHIP_PATH /dev/gpiochip1 #define LED_LINE 6 int main(void) { gpio_handle_t led_handle; memset(led_handle, 0, sizeof(led_handle)); led_handle.chip_path LED_CHIP_PATH; led_handle.line_offset LED_LINE; led_handle.gpio_mode GPIOHANDLE_REQUEST_OUTPUT; led_handle.default_value 0; snprintf(led_handle.consumer_label, sizeof(led_handle.consumer_label), led-gpio); if (gpio_handle_init(led_handle) 0) { perror(gpio_handle_init); return 1; } printf(LED GPIO set high\n); if (gpio_set_value(led_handle, 1) 0) { gpio_handle_close(led_handle); return 1; } sleep(5); printf(LED GPIO set low\n); if (gpio_set_value(led_handle, 0) 0) { gpio_handle_close(led_handle); return 1; } gpio_handle_close(led_handle); return 0; }创建并编写MakefileSDK_DIR : $(shell cd $(shell pwd)/../../.. /bin/pwd) include $(SDK_DIR)/build/base.mk TARGET : gpio SRCS : main.c gpio_hal.c OBJS : $(SRCS:.c.o) .PHONY: all clean install all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(SDK_LD_CFLAGS) -o $ $^ %.o: %.c $(CC) $(SDK_USR_CFLAGS) -c -o $ $ install: all mkdir -p $(XMEDIA_ROOTFS_DIR)/usr/bin cp -f $(TARGET) $(XMEDIA_ROOTFS_DIR)/usr/bin/$(TARGET) chmod 755 $(XMEDIA_ROOTFS_DIR)/usr/bin/$(TARGET) echo Installed $(TARGET) to $(XMEDIA_ROOTFS_DIR)/usr/bin/$(TARGET) clean: rm -f $(TARGET) $(OBJS)将应用程序编译进rootfs文件系统登录Pico-G1运行应用程序预期结果LED亮5秒后熄灭3 使用sysfs控制GPIO3.1 将GPIO导出到用户空间在板端 /sys/class/gpio 目录中每个 GPIO 设备都有其自己的文件夹。这些文件夹的名称是 gpio 加上引脚编号例如 /sys/class/gpio/gpio14 表示引脚编号为 14 的引脚即 GPIO1_6。用户可使用如下命令查看~ # echo 14 /sys/class/gpio/export ~ # ls /sys/class/gpio/ export gpiochip0 gpiochip24 gpiochip40 gpiochip56 gpiochip8 gpio14 gpiochip16 gpiochip32 gpiochip48 gpiochip64 unexport说明echo 14 sys/class/gpio 表示把 GPIO14 导出到用户空间,导出后就会生成/sys/class/gpio/gpio14 可以使用 echo 14 /sys/class/gpio/unexport 命令取消导出3.2 GPIO控制目录导出的GPIO对应的控制目录(即/sys/class/gpio/gpio14)里通常会有这些文件:~ # ls /sys/class/gpio/gpio14/ active_low direction power uevent device edge subsystem valueactive_low用于设置 GPIO 电平逻辑是否反向 示例echo 1 /sys/class/gpio/gpio14/active_low # 表示把GPIO14 的电平逻辑设置为反向direction用于设置 GPIO 的方向也就是它是输入还是输出 示例echo in /sys/class/gpio/gpio14/direction #输入 echo out /sys/class/gpio/gpio14/direction #输出power电源管理相关目录/文件一般不直接操作。uevent和设备事件相关通常不是用户日常手工控制 GPIO 时最常用的文件device表示这个 GPIO 所属的设备信息edgeecho both /sys/class/gpio/gpio14/edge配置中断触发边沿常见值none不产生中断rising上升沿触发中断falling下降沿触发中断both双边沿触发中断subsystem表示这个 GPIO 属于哪个子系统value用于读取或设置GPIO的电平值cat /sys/class/gpio/gpio14/value #读取电平echo 1 /sys/class/gpio/gpio14/value #设置为高电平 echo 0 /sys/class/gpio/gpio14/value #设置为低电平
上一篇/下一篇内容由系统自动关联
返回资讯列表 →