基于STM32L432的Mbed OS 5正弦波计数方法咨询
可靠的正弦波计数方案在Mbed OS 5中的实现
嘿,针对你遇到的正弦波计数问题——用InterruptIn加阈值判断AnalogIn导致突发计数而非单脉冲,我来分享几个Mbed OS 5里靠谱的解决思路,毕竟硬件级的方案才是避免这类问题的关键:
1. 利用定时器的输入捕获模式(对应AVR的输入捕获)
这应该是最贴合你AVR使用经验的方案,绝大多数支持Mbed的MCU(比如STM32、NXP Kinetis等)都有定时器输入捕获功能,能硬件级检测正弦波的边沿(比如过阈值的上升/下降沿),完全避免软件判断带来的抖动问题。
实现思路:
- 把正弦波输入引脚连接到定时器的捕获通道
- 配置定时器捕获特定边沿(比如当正弦波过中点电压的上升沿)
- 在捕获中断回调里完成计数操作,每个周期只会触发一次中断
举个STM32平台的示例代码(需要根据你的MCU型号调整引脚和定时器通道):
#include "mbed.h" // 假设使用TIM2的通道1,对应引脚PA_0 TIM_HandleTypeDef htim2; volatile int sine_count = 0; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { sine_count++; // 可选:清除捕获标志位,确保下一次捕获正常触发 __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_CC1); } } void init_input_capture() { __HAL_RCC_TIM2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF1_TIM2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); htim2.Instance = TIM2; htim2.Init.Prescaler = 0; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFF; // 最大计数周期,我们只关心捕获边沿 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_IC_Init(&htim2); TIM_IC_InitTypeDef sConfigIC = {0}; sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; // 捕获上升沿 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; sConfigIC.ICFilter = 0x0; // 可设置滤波值减少噪声影响 HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动带中断的捕获 } int main() { init_input_capture(); while (true) { printf("正弦波计数:%d\n", sine_count); ThisThread::sleep_for(1000ms); } }
2. 使用内置模拟比较器
如果你的MCU有内置模拟比较器,这也是个绝佳选择——它能直接把正弦波和参考电压(比如电源中点)做硬件比较,输出数字信号后再用InterruptIn检测边沿,比软件读AnalogIn判断阈值可靠得多,还能设置滞回电压避免信号在阈值附近波动导致的多次触发。
示例代码(部分平台支持Mbed的AnalogComparator类):
#include "mbed.h" // 输入引脚接正弦波,参考引脚接中点电压(比如通过分压电阻得到) AnalogComparator comp(PA_0, PB_1); volatile int sine_count = 0; void count_sine() { sine_count++; } int main() { // 设置比较器上升沿触发中断(对应正弦波从低于阈值到高于阈值) comp.rise(&count_sine); // 若需要滞回,可通过MCU寄存器配置(不同平台方法不同) while (true) { printf("正弦波计数:%d\n", sine_count); ThisThread::sleep_for(1000ms); } }
3. 改进软件采样方案(迫不得已时用)
如果以上硬件方案都没法用,你可以优化AnalogIn的采样逻辑,避免突发计数:
- 提高ADC采样频率,确保每个正弦波周期至少采样两次
- 在采样回调里做过零检测:只有当连续采样值从低于阈值跳到高于阈值(或反之)时,才计数一次,忽略阈值附近的波动
- 加入软件防抖:比如连续3次采样都处于阈值同一侧,才确认边沿触发
为啥你之前的方案会出问题?
你提到的突发计数,本质是正弦波在阈值电压附近时,AnalogIn的采样值会在阈值上下波动,导致InterruptIn被多次触发。硬件级的捕获/比较器能从根源上解决这个问题,因为它们是基于硬件边沿检测的,不会受采样波动影响。
内容的提问来源于stack exchange,提问作者Nicholas Skram
相关产品推荐
相关产品推荐

