STM32外部中断实战:用红外传感器实现物体计数(附完整代码)
·
STM32外部中断实战:用红外传感器实现物体计数(附完整代码)
在工业自动化领域,物体计数是一个基础但至关重要的功能。想象一下传送带上的产品需要精确统计,或者仓库入口需要记录货物进出数量——这些场景都需要可靠的非接触式计数方案。STM32微控制器配合红外传感器,正好能构建一个高性价比的解决方案。
红外传感器通过发射和接收红外光束来检测物体。当物体阻挡光束时,接收端会产生电平变化。利用STM32的外部中断功能,我们可以实时捕捉这个变化,实现精准计数。相比轮询方式,中断机制能极大降低CPU负载,确保系统及时响应。
1. 硬件设计与传感器选型
1.1 红外传感器工作原理
常见的对射式红外传感器包含发射器和接收器两部分:
- 发射端:通常采用940nm波长的红外LED,通过调制信号减少环境光干扰
- 接收端:光敏三极管或专用接收芯片,输出数字或模拟信号
关键参数对比:
| 参数 | 数字输出型 | 模拟输出型 |
|---|---|---|
| 响应时间 | 快(μs级) | 较慢(ms级) |
| 抗干扰 | 较强 | 需额外电路 |
| 接口复杂度 | 简单 | 需要ADC |
| 成本 | 低 | 中等 |
对于计数应用,推荐使用数字输出的槽型光电传感器,如EE-SX670。这类传感器内置信号调理电路,直接输出TTL电平,简化了电路设计。
1.2 STM32硬件连接
以STM32F103C8T6为例,典型连接方式如下:
// 传感器接线定义
#define IR_SENSOR_PIN GPIO_Pin_14
#define IR_SENSOR_PORT GPIOB
硬件连接注意事项:
- 传感器VCC接3.3V,GND共地
- 输出信号线接STM32 GPIO引脚(如PB14)
- 长距离传输时建议增加RC滤波电路
提示:实际布线时,信号线应远离电机等干扰源,必要时使用屏蔽线。
2. 外部中断配置详解
2.1 GPIO与AFIO初始化
首先配置GPIO为上拉输入模式,这样可以确保引脚在无信号时保持确定状态:
void Hardware_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
// 启用GPIOB和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// 配置PB14为上拉输入
GPIO_InitStruct.GPIO_Pin = IR_SENSOR_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IR_SENSOR_PORT, &GPIO_InitStruct);
// 将PB14映射到EXTI14
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
}
2.2 EXTI中断配置
配置EXTI线14为上升沿触发(假设传感器遮挡时输出高电平):
void EXTI_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line14;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
}
2.3 NVIC优先级设置
采用优先级分组2(2位抢占优先级,2位响应优先级):
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
3. 中断服务程序实现
3.1 计数逻辑处理
在中断服务函数中实现计数功能,注意防抖处理:
volatile uint32_t object_count = 0; // 使用volatile防止编译器优化
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) != RESET)
{
// 简单延时防抖(实际项目建议用硬件防抖或定时器)
Delay_us(500);
if(GPIO_ReadInputDataBit(IR_SENSOR_PORT, IR_SENSOR_PIN) == SET)
{
object_count++;
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
3.2 常见问题排查
调试时可能遇到的问题及解决方案:
-
中断不触发:
- 检查GPIO时钟和AFIO时钟是否开启
- 确认EXTI线配置与GPIO引脚对应
- 测量传感器输出信号是否正常
-
重复计数:
- 增加硬件RC滤波(如100Ω电阻+0.1μF电容)
- 软件防抖:在中断中二次检测信号状态
-
计数遗漏:
- 检查传感器响应时间是否满足物体速度
- 尝试改用双边沿触发模式
4. 完整应用实例
4.1 系统初始化流程
完整的初始化序列应按照以下顺序:
- 硬件外设时钟使能
- GPIO初始化
- EXTI配置
- NVIC配置
- 外设初始化(如OLED显示)
void System_Init(void)
{
Hardware_Init(); // 硬件接口初始化
EXTI_Configuration(); // 外部中断配置
NVIC_Configuration(); // 中断控制器配置
OLED_Init(); // 显示模块初始化
}
4.2 主程序实现
主循环中实时显示计数结果:
int main(void)
{
System_Init();
OLED_ShowString(1, 1, "Object Count:");
while(1)
{
OLED_ShowNum(1, 14, object_count, 5);
// 其他任务处理
Delay_ms(10);
}
}
4.3 高级功能扩展
对于更复杂的应用场景,可以考虑:
- 多传感器协同:使用多个中断通道实现双向计数
- 速度计算:结合定时器测量物体通过时间间隔
- 无线传输:通过蓝牙/WiFi模块上报计数数据
- 数据存储:利用EEPROM保存累计计数
// 示例:通过USART发送计数数据
void Send_Count_Data(void)
{
printf("Current count: %lu\r\n", object_count);
}
在实际项目中,外部中断的响应时间直接影响计数精度。测试发现,STM32F103在72MHz主频下,从中断发生到进入ISR通常需要12-16个时钟周期,完全能满足大多数工业场景的需求。
更多推荐
所有评论(0)