如何通过pigpio SetAlertFunc正确修改变量?GPIO按键控制LED场景下全局变量的替代方案问询
替代全局变量管理pigpio回调中的按键状态
全局变量确实是个麻烦事——尤其是当你要把C代码打包成Python可用的库时,全局状态很容易在多线程或者多实例场景下出问题。好在pigpio提供了带用户数据指针的回调函数接口,完美解决这个问题。
核心思路
用gpioSetAlertFuncEx替代gpioSetAlertFunc,这个函数允许你传递一个自定义的结构体指针作为用户数据,把需要维护的状态(比如按键按下次数、LED配置等)都封装在这个结构体里,回调函数通过指针直接修改结构体里的变量,完全不需要全局变量。
修改后的完整代码
#include <stdio.h> #include <pigpio.h> #include <stdlib.h> #include <unistd.h> #define LED_1 13 #define LED_2 19 #define BUTTON 25 #define ON 1 #define OFF 0 #define DELAY 1 // 定义结构体存储所有需要的状态和配置 typedef struct { int current_state; int led1_pin; int led2_pin; int button_pin; } DeviceState; void initialise_pins(DeviceState* state); void clear_all(DeviceState* state); void start_button(DeviceState* state); void turn_on_lights(DeviceState* state); void increment_state(int gpio, int level, uint32_t tick, void* userdata); int main(){ if (gpioInitialise() < 0){ fprintf(stderr, "Error initializing pigpio library, exiting\n"); return 1; } // 初始化状态结构体 DeviceState* state = malloc(sizeof(DeviceState)); if (!state) { fprintf(stderr, "Failed to allocate memory for device state\n"); gpioTerminate(); return 1; } state->current_state = 0; state->led1_pin = LED_1; state->led2_pin = LED_2; state->button_pin = BUTTON; initialise_pins(state); clear_all(state); start_button(state); // 清理资源(实际运行中循环不会退出,这里是示例) free(state); gpioTerminate(); return 0; } void initialise_pins(DeviceState* state){ gpioSetMode(state->led1_pin, PI_OUTPUT); gpioSetMode(state->led2_pin, PI_OUTPUT); gpioSetMode(state->button_pin, PI_INPUT); gpioSetPullUpDown(state->button_pin, PI_PUD_DOWN); // 使用带用户数据的回调函数 gpioSetAlertFuncEx(state->button_pin, increment_state, state); } void clear_all(DeviceState* state){ gpioWrite(state->led1_pin, OFF); gpioWrite(state->led2_pin, OFF); } void turn_on_lights(DeviceState* state){ gpioWrite(state->led1_pin, ON); gpioWrite(state->led2_pin, ON); } void increment_state(int gpio, int level, uint32_t tick, void* userdata){ // 将用户数据转换为我们的结构体类型 DeviceState* state = (DeviceState*)userdata; if (level == 1){ state->current_state += 1; } } void start_button(DeviceState* state){ for (;;){ switch(state->current_state){ case 0: // 保持熄灭状态 clear_all(state); break; case 1: // 点亮LED turn_on_lights(state); break; case 2: // 闪烁LED clear_all(state); sleep(DELAY); turn_on_lights(state); sleep(DELAY); break; default: // 重置状态 state->current_state = 0; clear_all(state); break; } usleep(100000); // 用usleep更精确,对应原来的0.1秒 } }
关键改动说明
- 结构体封装状态:
DeviceState结构体包含了所有需要的状态和硬件引脚配置,不仅解决了全局变量问题,还让代码更易于扩展(比如要控制更多LED,直接加字段就行)。 - 使用
gpioSetAlertFuncEx:这个函数比gpioSetAlertFunc多了一个void* userdata参数,用来传递我们的结构体指针。 - 回调函数转换指针:在
increment_state里,把void*转换为DeviceState*,就可以安全地修改current_state了。 - 修复原代码的错误:原代码里重复定义了
main函数,这是编译错误,已经在修改后的代码中修正;另外把sleep(0.1)换成usleep(100000),因为sleep的参数是整数秒,原来的写法在很多编译器里会被截断为0,导致循环占用过高CPU。
为什么这适合Python扩展库
当你把这段代码编译为Python扩展时,每个Python实例可以拥有自己的DeviceState结构体实例,不会像全局变量那样导致不同实例之间的状态冲突。你可以在Python端通过扩展接口创建、管理这些状态实例,实现完全隔离的设备控制。
内容的提问来源于stack exchange,提问作者Ziv Ofir
相关产品推荐
相关产品推荐

