1. 项目概述
最近在跟着江协科技的STM32教程系统学习嵌入式开发,今天要分享的是第5-2节关于对射式红外传感器和旋转编码器的实战应用。这两个外设在工业控制和智能设备中非常常见,比如流水线物品计数、旋钮菜单选择等场景都会用到。
作为STM32的经典输入设备,它们的工作原理和代码实现有几个关键点需要特别注意:
- 对射式红外传感器需要通过外部中断实现精准计数
- 旋转编码器的正交信号解码需要处理抖动问题
- 两种设备都需要考虑消抖处理和信号稳定性
我在实际调试过程中发现,很多新手容易在中断配置和信号处理上踩坑,本文将结合我的实战经验,详细解析这两个外设的完整开发流程。
2. 硬件准备与接线说明
2.1 对射式红外传感器接线
对射式红外传感器通常由发射端和接收端组成,当有物体通过时会阻断红外线,接收端输出电平变化。推荐使用E18-D80NK这款常见型号,它具有3-80cm的检测距离。
接线方式:
- VCC → 3.3V
- GND → GND
- OUT → PA0(配置为外部中断输入)
注意:OUT信号线需要接10K上拉电阻,确保默认高电平稳定。我在实际测试中发现,不加这个电阻会导致误触发。
2.2 旋转编码器接线
旋转编码器通过两个相位差90°的信号(A/B相)来检测旋转方向和步数。推荐使用EC11这类增量式编码器,性价比高且易于使用。
接线配置:
- CLK(A相)→ PB10
- DT(B相) → PB9
- SW(按键)→ 可接其他GPIO(本文未使用)
- VCC → 3.3V
- GND → GND
3. 对射式红外传感器实现
3.1 外部中断配置
STM32CubeMX配置步骤:
- 选择PA0引脚为GPIO_EXTI0
- 配置中断触发方式为下降沿(Falling edge)
- 在NVIC中使能EXTI0中断
关键代码:
c复制// stm32f1xx_it.c中实现中断服务函数
void EXTI0_IRQHandler(void) {
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
Counter++; // 全局计数变量
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
}
}
3.2 消抖处理实战
红外传感器在实际使用中容易因环境光干扰产生抖动,我的解决方案是:
-
硬件消抖:
- 在OUT信号线上并联104电容
- 增加RC滤波电路(1K电阻+0.1uF电容)
-
软件消抖:
c复制// 修改后的中断服务函数
void EXTI0_IRQHandler(void) {
static uint32_t last_time = 0;
uint32_t current = HAL_GetTick();
if((current - last_time) > 10) { // 10ms消抖
Counter++;
last_time = current;
}
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
}
4. 旋转编码器实现
4.1 正交解码原理
旋转编码器通过A/B两相的相位差判断方向:
- 顺时针:A相先变化
- 逆时针:B相先变化
典型信号时序:
code复制顺时针旋转:
A相: _|‾|__|‾|__
B相: __|‾|_|‾|_
逆时针旋转:
A相: __|‾|_|‾|_
B相: _|‾|__|‾|__
4.2 中断方式实现
配置PB9/PB10为外部中断输入,采用双边沿触发:
c复制// 中断服务函数
void EXTI15_10_IRQHandler(void) {
static uint8_t last_AB = 0;
uint8_t current_A = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10);
uint8_t current_B = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9);
uint8_t current_AB = (current_A << 1) | current_B;
// 状态机判断方向
if(last_AB == 0b00 && current_AB == 0b10) {
Counter++; // 顺时针
}
else if(last_AB == 0b00 && current_AB == 0b01) {
Counter--; // 逆时针
}
last_AB = current_AB;
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_9 | GPIO_PIN_10);
}
4.3 定时器编码器模式(进阶方案)
更高效的方式是使用STM32内置的编码器接口模式,以TIM4为例:
c复制// CubeMX配置:
// TIM4 -> Encoder Mode -> TI1=PB6, TI2=PB7
// 代码中使用
HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
int32_t count = __HAL_TIM_GET_COUNTER(&htim4);
5. 常见问题与解决方案
5.1 红外传感器误触发
问题现象:无物体通过时计数器自动增加
排查步骤:
- 检查电源稳定性(示波器观察3.3V纹波)
- 测量OUT信号电压(无遮挡时应>3V)
- 增加环境光屏蔽罩
5.2 编码器计数不准确
问题现象:旋转时计数方向随机变化
解决方案:
- 检查A/B相接线是否反接
- 降低旋转速度测试(机械编码器有最大转速限制)
- 在GPIO引脚增加10nF滤波电容
5.3 中断响应延迟
优化方案:
- 提高中断优先级
c复制HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); - 精简中断服务函数代码
- 使用DMA传输替代中断
6. 项目优化建议
经过实际项目验证,我有几个提升稳定性的建议:
-
对射式红外传感器:
- 改用槽型光电开关(如ITR9608)提高抗干扰能力
- 增加LED指示灯实时显示触发状态
- 采用光电耦合器隔离输出信号
-
旋转编码器:
- 使用硬件消抖专用芯片(如MAX6816)
- 实现加速度检测(快速旋转时增加步长)
- 添加按键功能(按下编码器作为确认键)
-
系统级优化:
c复制// 使用看门狗防止程序跑飞 IWDG_HandleTypeDef hiwdg; void MX_IWDG_Init(void) { hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = IWDG_PRESCALER_32; hiwdg.Init.Reload = 0xFFF; HAL_IWDG_Init(&hiwdg); }
我在一个工业计数器项目中实测,经过这些优化后,设备在强电磁干扰环境下也能稳定工作,计数误差小于0.1%。