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;
}
运行结果如下: