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

ESP32类成员变量在定时器回调函数中无法正常更新的问题

ESP32类成员变量在定时器回调中无法递增的解决方法

我在Arduino环境下编写了ESP32代码,期望每次调用myTestFn()时类成员变量iCounter能递增,但它始终保持为1。将iCounter声明移到Test类外部时能正常更新,请问如何让Test类的成员变量在myTestFn()中正常更新?

#include <Arduino.h>
#include "esp_timer.h"

//volatile int iCounter; <-- 取消注释此行后iCounter可在myTestFn()中更新

class Test {
    public:       
        Test(void) {
            esp_timer_handle_t testTimer;
            esp_timer_create_args_t timerConfig;
            timerConfig.arg = this;
            timerConfig.callback = reinterpret_cast<esp_timer_cb_t>( testFn );
            timerConfig.dispatch_method = ESP_TIMER_TASK;
            timerConfig.name = "Test_Timer";
            esp_timer_create( &timerConfig, &testTimer );
            esp_timer_start_periodic( testTimer, 1000000 );
        }
        
        static void testFn(void *arg) {
          Test *obj = (Test *)arg;
          obj->myTestFn();
        }
          
        void myTestFn(void) {
            iCounter = iCounter +1;
            Serial.printf("Test succeeded! Counter: %d Time elapsed: %lu.%03lu sec\n", iCounter, millis() / 1000, millis() % 1000);
        }
    private:
        volatile int iCounter; // <-- 此处无法正常工作,iCounter在myTestFn()中不更新
};

void setup() {
    Serial.begin(115200);
    while(!Serial);
    Test justATestObject;
}

void loop() {
}

问题根源

核心问题是justATestObject是setup()函数内的局部变量:当setup()执行完毕后,这个对象会被销毁,其占用的内存会被系统回收或复用。而定时器的回调函数还在尝试通过之前保存的this指针访问这个已销毁对象的iCounter成员,此时访问的是无效内存,导致数值始终显示为1(实际是随机的无效值)。

当把iCounter移到全局作用域时,它不受对象生命周期影响,所以能正常递增。

解决方案

只需确保Test对象在程序运行的整个周期内都存在,有两种常用方式:

  • 将对象声明为全局变量
    把Test justATestObject;移到setup()函数外面,变成全局变量:

    #include <Arduino.h>
    #include "esp_timer.h"
    
    class Test {
        // 类定义保持不变
    };
    
    Test justATestObject; // 全局对象,生命周期覆盖整个程序
    
    void setup() {
        Serial.begin(115200);
        while(!Serial);
    }
    
    void loop() {
    }
    
  • 使用动态分配创建对象
    在setup()中用new创建对象,避免局部变量被销毁:

    void setup() {
        Serial.begin(115200);
        while(!Serial);
        Test* justATestObject = new Test(); // 动态分配,对象会一直存在直到手动delete
    }
    

这样修改后,定时器回调访问的是有效的对象成员,iCounter就能正常递增了。

内容的提问来源于stack exchange,提问作者Michael Hanagan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 01:01:04