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

硬件连接注意事项:

  1. 传感器VCC接3.3V,GND共地
  2. 输出信号线接STM32 GPIO引脚(如PB14)
  3. 长距离传输时建议增加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 常见问题排查

调试时可能遇到的问题及解决方案:

  1. 中断不触发

    • 检查GPIO时钟和AFIO时钟是否开启
    • 确认EXTI线配置与GPIO引脚对应
    • 测量传感器输出信号是否正常
  2. 重复计数

    • 增加硬件RC滤波(如100Ω电阻+0.1μF电容)
    • 软件防抖:在中断中二次检测信号状态
  3. 计数遗漏

    • 检查传感器响应时间是否满足物体速度
    • 尝试改用双边沿触发模式

4. 完整应用实例

4.1 系统初始化流程

完整的初始化序列应按照以下顺序:

  1. 硬件外设时钟使能
  2. GPIO初始化
  3. EXTI配置
  4. NVIC配置
  5. 外设初始化(如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 高级功能扩展

对于更复杂的应用场景,可以考虑:

  1. 多传感器协同:使用多个中断通道实现双向计数
  2. 速度计算:结合定时器测量物体通过时间间隔
  3. 无线传输:通过蓝牙/WiFi模块上报计数数据
  4. 数据存储:利用EEPROM保存累计计数
// 示例:通过USART发送计数数据
void Send_Count_Data(void)
{
    printf("Current count: %lu\r\n", object_count);
}

在实际项目中,外部中断的响应时间直接影响计数精度。测试发现,STM32F103在72MHz主频下,从中断发生到进入ISR通常需要12-16个时钟周期,完全能满足大多数工业场景的需求。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐