Teensy板C++项目:中断与loop间volatile修饰符使用疑问
问题描述
我在Teensy板上运行一套C++代码,实现麦克风节拍检测并驱动自行车LED特效。但发现不同LED特效下节拍检测表现不一致:部分特效下检测正常,部分会导致检测变慢。我怀疑问题源于声音分析的中断使用方式。
我有一个全局变量env,同时在loop()方法和中断方法中使用,定义如下:
ApplicationEnvironment env;
ApplicationEnvironment类定义(简化版)如下:
class ApplicationEnvironment final : public EffectEnvironment { public: // Always returns adc0, since we're never using adc1. ADC_Module* const GetAdc() { return adc_->adc0; } void SetFilteredSignal(int16_t filtered_signal) { filtered_signal_ = filtered_signal; } void SetPeakSignalAmplitude(uint16_t peak_signal_amplitude) { unfiltered_signal_peak_amplitude_ = peak_signal_amplitude; } void SetPeakSignalFilteredAmplitude(uint16_t peak_signal_filtered_amplitude) { filtered_signal_peak_amplitude_ = peak_signal_filtered_amplitude; } uint16_t GetFilteredSignalPeakAmplitude() const override { return filtered_signal_peak_amplitude_; } // Sets whether the beat is currently detected or not. void SetBeatActive(bool beat_active) { beat_active_ = beat_active; } private: // ADC module, to handle ADC properties. // Read from ICRSoundProcessing() but does not seem to need 'volatile'. ADC* adc_; volatile int16_t filtered_signal_; // Values below represent the peak seen since their last respective update. volatile uint16_t unfiltered_signal_peak_amplitude_; // Values below represent the peak seen since their last respective update. volatile uint16_t filtered_signal_peak_amplitude_; volatile bool beat_active_; };
中断方法中调用以下方法:
env.GetAdc(); env.GetFilteredSignal() env.GetFilteredSignalPeakAmplitude(); env.SetFilteredSignal(...); env.SetPeakSignalAmplitude(...); env.SetPeakSignalFilteredAmplitude(...); env.SetBeatActive(...);
其中setter方法仅在中断中调用,getter方法在中断和loop()中均有调用。
我的问题如下:
- 是否需要将
env变量声明为volatile? - 更重要的是,考虑到getter方法会在
loop()中调用,是否需要将其声明为volatile? - 尽管setter方法仅在中断中调用,是否需要将其声明为volatile?
我认为adc_变量无需声明为volatile,因为它不会被中断方法修改。
解答
是否需要将
env变量声明为volatile?
不需要。env是全局对象,内存地址固定,中断和loop()访问的是同一个实例。你已经将对象内部被中断修改的成员标记为volatile,这就足够了——volatile的作用是阻止编译器优化对这些成员的读写,确保每次访问都直接操作内存,而非寄存器缓存。getter方法是否需要声明为volatile?
需要。因为getter读取的是volatile成员变量,只有将getter声明为volatile成员函数,才能保证编译器正确处理这些volatile成员的访问,避免优化导致读取到过期的缓存值。修改示例:
uint16_t GetFilteredSignalPeakAmplitude() const volatile override { return filtered_signal_peak_amplitude_; }
这很可能是部分LED特效下节拍检测变慢的关键原因之一。
- setter方法是否需要声明为volatile?
不需要。setter仅在中断中调用,且内部修改的是已标记volatile的成员变量。当前场景下无需额外标记setter为volatile,不过如果后续需要让setter支持volatile对象调用,也可以添加该修饰符。
另外,你关于adc_的判断是正确的——它仅在初始化时赋值,后续不会被中断或loop()修改,因此不需要volatile修饰。
内容的提问来源于stack exchange,提问作者All-in-one Mighty
相关产品推荐
相关产品推荐

