Linux下要同时检测多个GPIO输入的方法有很多,这里我使用libgpiod库中的API实现多个GPIO输入检测,可以达到类似STM32利用外部中断实现输入事件检测的效果,示例代码如下所示:
/* 示例使用的libgpiod库版本为V1.2.1 */
//示例功能是通过libgpiod库捕获GPIO输入事件#include <stdbool.h>
#include <stdio.h>
#include "gpiod.h"/* GPIO组路径 */
#define GPIO_CHIP_PATH "/dev/gpiochip0"/* GPIO组 */
struct gpiod_chip *Test_Gpio_Chip;/* 电源切换信号输入 */
struct gpiod_line *Gpio_Line_In_Switch;
#define LINE_IN_SWITCH_ID 113 /* PD17 *//* 脉冲信号输入 */
struct gpiod_line *Gpio_Line_In_PPS;
#define LINE_IN_PPS_ID 115 /* PD19 *//*** 配置GPIO输入* * 本函数用于配置GPIO线脚为输入模式,并设置其触发事件。需要指定GPIO芯片、引脚ID号、触发事件以及GPIO线脚的客户端名称。* * @param chip 指向GPIO芯片的指针。* @param line 指向GPIO线脚的指针的地址,函数执行成功后,会在这里返回配置好的线脚指针。* @param line_id 要配置的GPIO线脚的ID。* @param edge 触发事件的类型,0表示下降沿触发,1表示上升沿触发,2表示同时触发。* @param consumer 指定使用这个GPIO线脚的客户端名称。* @return 成功返回0,否则返回错误码。* @note 触发事件类型为0表示下降沿触发,1表示上升沿触发,2表示同时触发。*/
int gpio_input_config(struct gpiod_chip* chip, struct gpiod_line** line, int line_id, int edge, const char* consumer)
{struct gpiod_line* tmp_line;int ret;if (!chip || !consumer || !line){return -1;}tmp_line = gpiod_chip_get_line(chip, line_id);if (!tmp_line) {return -2;}if(edge == 0){ret = gpiod_line_request_falling_edge_events(tmp_line, consumer);}else if(edge == 1){ret = gpiod_line_request_rising_edge_events(tmp_line, consumer);}else{ret = gpiod_line_request_both_edges_events(tmp_line, consumer);}if (ret < 0) {gpiod_line_release(tmp_line);return -3;}*line = tmp_line;return ret;
}/*** GPIO清理函数* * 本函数用于释放GPIO资源,包括释放GPIO线脚和关闭GPIO芯片。*/
void gpio_cleanup(void)
{if(Gpio_Line_In_PPS){gpiod_line_release(Gpio_Line_In_PPS);}if(Gpio_Line_In_Switch){gpiod_line_release(Gpio_Line_In_Switch);}if(Gnss_Gpio_Chip){gpiod_chip_close(Gnss_Gpio_Chip);}
}/*** GPIO初始化函数* * 本函数用于初始化GPIO,将GPIO配置为输入。* * @return 成功返回0,否则返回-1。*/
int gpio_init(void)
{int init_sta = 0;int ret;/* 打开芯片GPIO组 */Test_Gpio_Chip = gpiod_chip_open(GPIO_CHIP_PATH);if (!Test_Gpio_Chip ) {return -1;}/* PPS引脚输入配置 */ret = gpio_input_config(Test_Gpio_Chip, &Gpio_Line_In_PPS, LINE_IN_PPS_ID, 0, "pps");if (ret == 0) {init_sta++;}else{printf ("gpio %d input config failed: %d\n", LINE_IN_PPS_ID, ret);}/* 电源切换信号输入配置 */ret = gpio_input_config(Test_Gpio_Chip, &Gpio_Line_In_Switch, LINE_IN_SWITCH_ID, 0, "switch");if (ret == 0) {init_sta++;}else{printf ("gpio %d input config failed: %d\n", LINE_IN_SWITCH_ID, ret);} /* 如果配置失败,则关闭芯片并返回错误 */if (init_sta == 0){if(Test_Gpio_Chip){gpiod_chip_close(Test_Gpio_Chip);}return -1;}return 0;
}/*** GPIO输入事件处理线程* * 本函数用于处理GPIO输入事件,通过调用gpiod_line_event_wait和gpiod_line_event_read函数来获取事件。* * @param arg 线程参数,这里没有使用。*/
void* gpio_input_event_thread(void* arg)
{struct gpiod_line_bulk gpiod_line_bulk, event_bulk;struct gpiod_line_event event;int ret;gpiod_line_bulk_add(&gpiod_line_bulk, Gpio_Line_In_PPS);gpiod_line_bulk_add(&gpiod_line_bulk, Gpio_Line_In_Switch); while(1){/* 无限阻塞,直到输入事件到来 */ret = gpiod_line_event_wait_bulk(&gpiod_line_bulk, NULL, &event_bulk);if (ret < 0){printf("gpiod line event wait error\n");}else if(ret == 0){printf("gpiod line event wait timeout\n");}else{/* 解析检测到的事件 */for(unsigned int i=0; i<event_bulk.num_lines; i++){if(gpiod_line_event_read(event_bulk.lines[i], &event) == 0){printf("line %d event happen\n", gpiod_line_offset(event_bulk.lines[i]));/* 还可以根据event中的内容判断当前检测到的事件是上升沿还是下降沿*/} }}}
}int main(void)
{if (gpio_init() != 0) {printf("gpio init failed\n");return 1;}gpio_input_event_thread(NULL);gpio_cleanup();return 0;
}
运行结果如下: