基于STM32F103与MH-Sensor红外对射模块的测速码盘系统设计与实现
1. 项目背景与硬件选型测速系统在嵌入式开发中非常常见无论是智能小车的轮速检测还是工业设备的转速监控都需要可靠的测速方案。我最近用STM32F103和MH-Sensor红外对射模块搭建了一个测速系统实测效果不错这里把完整的设计过程和踩过的坑都分享给大家。选择STM32F103是因为它性价比高GPIO和定时器资源丰富完全能满足测速需求。MH-Sensor红外对射模块价格便宜某宝上5块钱左右反应灵敏配合20孔测速码盘使用效果很好。这里有个小技巧码盘的孔数越多测速分辨率越高但也要考虑电机转速转速太高时孔数太多可能导致计数来不及。硬件清单如下STM32F103C8T6最小系统板MH-Sensor Series红外对射模块20孔测速码盘外径5cm电机及驱动模块杜邦线若干2. 硬件连接与工作原理2.1 接线示意图实际接线非常简单只需要3根线红外模块VCC → STM32 3.3V红外模块GND → STM32 GND红外模块DO → STM32 PB14这里特别注意MH-Sensor模块有AO和DO两个输出口我们要用DO数字输出而不是AO。DO输出的是干净的高低电平信号可以直接给STM32的GPIO读取。2.2 工作原理详解当码盘转动时红外对射模块会周期性检测到通光/遮光的变化码盘通光孔对准传感器时红外光透过DO输出高电平码盘实体部分遮挡传感器时红外光被阻挡DO输出低电平这样每转过一个孔就会产生一个完整的脉冲信号。通过统计单位时间内的脉冲次数就能计算出转速。公式很简单 转速(rpm) (脉冲数 × 60) / (码盘孔数 × 采样时间)比如1秒内检测到34个脉冲码盘20孔那么转速就是(34×60)/20102rpm。3. STM32软件设计3.1 GPIO外部中断配置我们需要在PB14引脚上配置外部中断检测红外模块的高低电平变化。关键配置步骤如下// 初始化代码片段 void CountSensor_Init(void) { // 开启GPIOB和AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); // 配置PB14为上拉输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOB, GPIO_InitStructure); // 映射外部中断线 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); // 配置EXTI为下降沿触发 EXTI_InitStructure.EXTI_Line EXTI_Line14; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; EXTI_Init(EXTI_InitStructure); // 配置NVIC中断 NVIC_InitStructure.NVIC_IRQChannel EXTI15_10_IRQn; NVIC_Init(NVIC_InitStructure); }实际使用中发现如果电机振动较大可能会产生信号抖动。我在中断服务函数里加了二次判断有效解决了这个问题void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line14) SET) { // 再次确认引脚电平防抖动 if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) 0) { CountSensor_Count; } EXTI_ClearITPendingBit(EXTI_Line14); } }3.2 定时器配置与速度计算使用TIM3定时器实现1秒间隔的中断在中断里读取脉冲计数并计算速度void Timer_Init(void) { // TIM3基础配置 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_Period 10000 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler 7200 - 1; // 10kHz TIM_TimeBaseInit(TIM3, TIM_TimeBaseInitStructure); // 使能更新中断 TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); NVIC_InitStructure.NVIC_IRQChannel TIM3_IRQn; NVIC_Init(NVIC_InitStructure); TIM_Cmd(TIM3, ENABLE); } // TIM3中断服务函数 void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { // 计算转速(rpm) current_rpm (CountSensor_Count * 60) / 20; CountSensor_Count 0; // 清零计数器 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } }4. 实测效果与优化建议实测这套系统在100-2000rpm范围内表现稳定误差小于3%。但在低速时50rpm精度会下降这是因为低速时采样到的脉冲数太少。可以通过以下方法优化增加码盘孔数比如改用60孔码盘低速分辨率能提高3倍延长采样时间从1秒改为3秒采样但会降低响应速度使用M法测速在固定时间内计数脉冲数当前方案使用T法测速测量两个脉冲间的时间间隔低速时更准我在项目中还加了移动平均滤波对速度值做平滑处理代码很简单#define FILTER_LEN 5 uint16_t speed_buf[FILTER_LEN]; uint8_t buf_index 0; // 在定时器中断中加入滤波 current_rpm (CountSensor_Count * 60) / 20; speed_buf[buf_index] current_rpm; if(buf_index FILTER_LEN) buf_index 0; // 计算平均值 uint32_t sum 0; for(int i0; iFILTER_LEN; i) { sum speed_buf[i]; } filtered_rpm sum / FILTER_LEN;这套系统稍作修改就能用于很多场景比如我后来把它用在了智能小车的闭环速度控制上配合PID算法可以实现精确的速度调节。如果用在工业设备上建议选用金属码盘和工业级红外传感器这样抗干扰能力更强。