You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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秒
    }
}

关键改动说明

  1. 结构体封装状态:DeviceState结构体包含了所有需要的状态和硬件引脚配置,不仅解决了全局变量问题,还让代码更易于扩展(比如要控制更多LED,直接加字段就行)。
  2. 使用gpioSetAlertFuncEx:这个函数比gpioSetAlertFunc多了一个void* userdata参数,用来传递我们的结构体指针。
  3. 回调函数转换指针:在increment_state里,把void*转换为DeviceState*,就可以安全地修改current_state了。
  4. 修复原代码的错误:原代码里重复定义了main函数,这是编译错误,已经在修改后的代码中修正;另外把sleep(0.1)换成usleep(100000),因为sleep的参数是整数秒,原来的写法在很多编译器里会被截断为0,导致循环占用过高CPU。

为什么这适合Python扩展库

当你把这段代码编译为Python扩展时,每个Python实例可以拥有自己的DeviceState结构体实例,不会像全局变量那样导致不同实例之间的状态冲突。你可以在Python端通过扩展接口创建、管理这些状态实例,实现完全隔离的设备控制。

内容的提问来源于stack exchange,提问作者Ziv Ofir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 01:28:13