Bootstrap

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

单片机:实现红外接收

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. 外部中断配置

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

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

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

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

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

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;