单片机:实现红外接收(附带源码)

单片机:实现红外接收

1. 项目背景与目标

在嵌入式系统中,红外接收模块常用于遥控器、传感器、家电控制等应用。通过红外接收模块,我们能够接收并解析红外遥控信号,实现对外部设备的控制。常见的红外遥控协议包括NEC、RC5、SONY等。

本项目的目标是使用单片机(以STM32为例)实现红外接收功能,接收来自遥控器的红外信号,并对信号进行解码,最终执行相应的控制操作。我们将重点使用红外接收模块(如TSOP系列)接收红外遥控信号,并通过定时器和外部中断功能解析红外数据。

2. 硬件设计
2.1 硬件组件
  1. 红外接收模块:常见的模块如TSOP4838,它可以接收38kHz频率的调制红外信号,并输出数字信号(高电平和低电平),供单片机读取。
  2. 单片机:例如STM32系列单片机,作为主控芯片。
  3. LED指示灯:用于显示接收到的信号状态或执行的操作。
  4. 电源:为单片机和外设提供电源。
2.2 硬件连接
  • 红外接收模块的输出端连接到单片机的GPIO引脚,通常是一个输入引脚。
  • LED灯连接到GPIO输出引脚,用于显示接收到的信号或操作状态。
  • 3. 软件设计
    3.1 红外接收模块的工作原理

    红外接收模块通过调制的红外光波传输信号。例如,遥控器发射的信号通常是以38kHz频率调制的,接收模块会接收到这种调制信号并将其转换为数字信号。通过控制模块的输出引脚,单片机可以检测到信号的状态(高电平或低电平)。

    为了有效解析这些信号,我们需要记录红外信号的时序,并对其进行解码。常见的红外遥控协议(如NEC协议)会把数据分成多个脉冲宽度,表示1或0。

    3.2 定时器与中断
    1. 定时器:定时器用于测量红外接收模块的信号的高电平或低电平持续时间。每当信号发生变化时,定时器中断会触发,我们可以读取该时段的长度并用来解码。
    2. 外部中断:红外接收模块输出信号变化时,外部中断会触发,单片机通过中断处理程序来记录信号变化时间并进行解码。
    3.3 解码过程
    1. 信号检测:通过读取GPIO引脚的电平状态,检测信号的变化。
    2. 数据存储:记录信号的时间间隔,通过这些时间间隔来区分0和1,最终构建出遥控器的按键数据。
    3. 数据解析:根据红外协议解析数据包,得到遥控器发送的命令。
    4. 执行操作:根据解析的命令,执行相应的控制操作。
    3.4 代码实现

    以下是基于STM32单片机实现红外接收的代码示例(假设使用NEC协议):

    #include "stm32f4xx_hal.h"
    
    // 定义红外接收引脚
    #define IR_PIN           GPIO_PIN_0
    #define IR_PORT          GPIOA
    
    // 定义红外协议的时序
    #define NEC_HEADER_MARK  9000    // 起始信号,9ms高电平
    #define NEC_HEADER_SPACE 4500   // 起始信号,4.5ms低电平
    #define NEC_BIT_MARK      560    // 数据位标志,高电平500us
    #define NEC_ONE_SPACE     1690   // 1的数据位,低电平1.69ms
    #define NEC_ZERO_SPACE    560    // 0的数据位,低电平560us
    #define NEC_REPEAT_SPACE  110000 // 重复信号时间间隔,大约110ms
    
    // 存储接收到的数据
    uint32_t ir_data = 0;
    uint8_t bit_count = 0;
    
    // 定时器与外部中断变量
    volatile uint32_t tick = 0;  // 用于记录红外信号的时序
    
    // 定时器和外部中断处理函数声明
    void TIM2_IRQHandler(void);
    void EXTI0_IRQHandler(void);
    
    // 初始化定时器
    void Timer_Init(void) {
        __HAL_RCC_TIM2_CLK_ENABLE();
    
        TIM_HandleTypeDef htim2;
        htim2.Instance = TIM2;
        htim2.Init.Prescaler = 8399;   // 1kHz定时器时钟(10ms定时器中断)
        htim2.Init.Period = 10000;     // 自动重装载周期
        htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
        htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
        HAL_TIM_Base_Init(&htim2);
        HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断
    }
    
    // 初始化外部中断
    void IR_GPIO_Init(void) {
        GPIO_InitTypeDef GPIO_InitStruct = {0};
        
        // 启用GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        // 配置IR接收引脚(PA0)为外部中断输入
        GPIO_InitStruct.Pin = IR_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(IR_PORT, &GPIO_InitStruct);
        
        // 启用外部中断
        HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    }
    
    // 外部中断处理程序,处理红外信号的上升沿和下降沿
    void EXTI0_IRQHandler(void) {
        HAL_GPIO_EXTI_IRQHandler(IR_PIN);
    
        uint32_t time = tick;  // 记录信号发生变化的时刻
    
        // 判断信号是0还是1
        if (time > NEC_ONE_SPACE) {
            ir_data |= (1 << (31 - bit_count)); // 为1时设置对应位置
        }
        
        bit_count++;
    
        // 检查是否接收到完整的数据(32位)
        if (bit_count == 32) {
            // 解析接收到的数据
            // 可以根据数据执行操作,比如控制LED、启动电机等
            if (ir_data == 0xA5A5A5A5) {
                HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);  // 控制LED亮灭
            }
            bit_count = 0;  // 重置计数
            ir_data = 0;    // 重置接收数据
        }
    
        tick = 0;  // 重置时钟
    }
    
    // 定时器中断,记录定时器计数
    void TIM2_IRQHandler(void) {
        HAL_TIM_IRQHandler(&htim2);
        tick++;  // 增加时间计数
    }
    
    // 主函数
    int main(void) {
        HAL_Init();
    
        // 初始化GPIO,定时器和外部中断
        IR_GPIO_Init();
        Timer_Init();
    
        // 配置LED引脚(假设为PB0)
        __HAL_RCC_GPIOB_CLK_ENABLE();
        GPIO_InitTypeDef GPIO_InitStruct = {0};
        GPIO_InitStruct.Pin = GPIO_PIN_0;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        // 主循环
        while (1) {
            // 主循环不做任何操作,信号处理通过中断实现
        }
    }
    
    4. 代码解释
    1. 外部中断配置

    2. IR_GPIO_Init()函数配置了PA0引脚为外部中断模式(上升沿和下降沿触发)。当红外信号发生变化时,外部中断会触发,调用EXTI0_IRQHandler()进行信号处理。
    3. 定时器配置

    4. Timer_Init()函数配置了一个定时器(如TIM2)来记录时间间隔。定时器的频率为1kHz(即1ms),用于计算红外信号的高电平或低电平的持续时间。
    5. 信号解析

    6. EXTI0_IRQHandler()中断服务程序中,通过记录时间间隔来判断接收到的是0还是1。根据NEC协议的时序,分析每一位的数据并将其存储在ir_data变量中。当接收到完整的32位数据时,进行数据处理(如控制LED)。
    7. LED控制

    8. 如果接收到的数据为特定值(如0xA5A5A5A5),则通过HAL_GPIO_TogglePin()函数控制LED的亮灭。你可以根据不同的信号数据执行其他操作,如控制电机、传感器等。
    5. 总结

    本项目实现了使用STM32单片机接收红外信号的功能。通过配置外部中断和定时器,我们能够准确地解析红外遥控器发送的数据,并进行相应的操作(如控制LED灯)。该方案不仅可以用于红外遥控器的接收,还可以扩展到更多红外通信协议和应用场景中。

    作者:Katie。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 单片机:实现红外接收(附带源码)

    发表回复