STM32外部中断EXTI实战用对射式红外传感器做个简易计数器附完整代码当我们需要实时监测物体通过某个区域的次数时对射式红外传感器配合STM32的外部中断功能是一个简单高效的解决方案。这个DIY项目不仅能帮助我们理解STM32的EXTI工作原理还能在实际应用中快速实现物体计数功能比如流水线上的产品计数、自动门的人流统计等场景。1. 硬件准备与电路连接对射式红外传感器通常由发射端和接收端组成。当物体通过两者之间时红外光被遮挡接收端输出电平发生变化。我们选用常见的E18-D80NK红外传感器模块它具有以下特点检测距离3-80cm可调输出信号数字电平遮挡时输出高电平未遮挡时输出低电平工作电压5V DC输出电流100mA连接方式如下表所示传感器引脚STM32连接说明VCC5V电源正极GNDGND电源负极DOPB14数字信号输出提示实际接线时建议在STM32的PB14引脚与传感器DO输出之间串联一个1kΩ电阻起到限流保护作用。2. STM32外部中断配置详解EXTIExternal Interrupt/Event Controller是STM32中用于处理外部中断和事件的专用外设。配置一个完整的外部中断需要以下几个步骤2.1 GPIO初始化首先需要配置连接传感器的GPIO引脚。我们使用PB14作为中断输入引脚// 使能GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure);2.2 AFIO配置AFIOAlternate Function I/O用于选择具体哪个GPIO引脚连接到EXTI// 使能AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 配置PB14连接到EXTI14 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);2.3 EXTI初始化配置EXTI14线的工作模式和触发方式EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line EXTI_Line14; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; // 下降沿触发 EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure);2.4 NVIC配置配置中断优先级和使能中断通道NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure);3. 中断服务函数实现当中断发生时处理器会跳转到对应的中断服务函数(ISR)执行。我们需要在中断函数中实现计数逻辑volatile uint16_t objectCount 0; // 使用volatile防止编译器优化 void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line14) SET) { objectCount; // 物体计数加1 EXTI_ClearITPendingBit(EXTI_Line14); // 清除中断标志 } }注意中断服务函数中应该尽可能减少处理时间避免影响系统实时性。复杂的处理可以放在主循环中执行。4. OLED显示实现为了实时显示计数结果我们使用常见的0.96寸OLED显示屏SSD1306驱动。以下是显示部分的实现4.1 OLED初始化首先需要初始化OLED显示屏#include oled.h void OLED_Init(void) { // OLED初始化代码 // ... OLED_ShowString(1, 1, Object Counter); OLED_ShowString(2, 1, Count:); }4.2 主循环实现在主循环中不断更新显示计数值int main(void) { // 硬件初始化 SystemInit(); OLED_Init(); CountSensor_Init(); while(1) { OLED_ShowNum(2, 7, objectCount, 5); // 显示5位数字 Delay_ms(100); // 适当延时 } }5. 完整项目代码整合将上述各部分整合为一个完整的项目以下是关键文件结构Project/ ├── Inc/ │ ├── count_sensor.h │ └── oled.h ├── Src/ │ ├── main.c │ ├── count_sensor.c │ └── oled.c └── STM32F10x_StdPeriph_Driver/ ├── inc/ └── src/count_sensor.h 头文件内容#ifndef __COUNT_SENSOR_H #define __COUNT_SENSOR_H #include stm32f10x.h void CountSensor_Init(void); uint16_t CountSensor_Get(void); #endifcount_sensor.c 源文件内容#include count_sensor.h #include stm32f10x_exti.h #include stm32f10x_gpio.h #include stm32f10x_rcc.h volatile uint16_t objectCount 0; void CountSensor_Init(void) { // GPIO初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // EXTI配置 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line EXTI_Line14; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); // NVIC配置 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); } uint16_t CountSensor_Get(void) { return objectCount; } void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line14) SET) { objectCount; EXTI_ClearITPendingBit(EXTI_Line14); } }6. 调试技巧与常见问题在实际开发中可能会遇到各种问题。以下是几个常见问题及解决方法6.1 中断不触发检查GPIO模式必须配置为输入模式如上拉输入GPIO_Mode_IPU验证EXTI线路确保GPIO引脚与EXTI线路对应关系正确PB14对应EXTI14检查NVIC配置确认中断通道和优先级设置正确6.2 计数不准确消抖处理传感器输出可能会有抖动可以在硬件并联电容或软件延时判断上做消抖处理中断标志清除确保在中断服务函数中清除了中断标志位变量修饰计数变量应使用volatile修饰防止编译器优化导致问题6.3 性能优化建议减少中断处理时间中断服务函数中只做必要的操作使用DMA传输如果显示数据量大可以考虑使用DMA传输低功耗设计在不需要频繁检测时可以进入低功耗模式7. 项目扩展思路这个基础计数器可以进一步扩展为更复杂的应用多传感器阵列使用多个传感器组成阵列实现物体方向检测速度计算结合定时器计算物体通过速度无线传输添加蓝牙/WiFi模块实现远程监控数据存储使用EEPROM或Flash存储历史数据报警功能当计数达到设定值时触发报警// 示例简单的报警功能实现 #define MAX_COUNT 100 void CheckAlarm(void) { if(objectCount MAX_COUNT) { // 触发报警如点亮LED、蜂鸣器等 GPIO_SetBits(GPIOC, GPIO_Pin_13); } else { GPIO_ResetBits(GPIOC, GPIO_Pin_13); } }在实际项目中我发现对射式红外传感器的安装位置和角度对检测稳定性影响很大。经过多次测试将传感器安装在距离检测面30-50cm、与移动方向成30°夹角时检测效果最为稳定可靠。