单片机:实现红外接收
1. 项目背景与目标
在嵌入式系统中,红外接收模块常用于遥控器、传感器、家电控制等应用。通过红外接收模块,我们能够接收并解析红外遥控信号,实现对外部设备的控制。常见的红外遥控协议包括NEC、RC5、SONY等。
本项目的目标是使用单片机(以STM32为例)实现红外接收功能,接收来自遥控器的红外信号,并对信号进行解码,最终执行相应的控制操作。我们将重点使用红外接收模块(如TSOP系列)接收红外遥控信号,并通过定时器和外部中断功能解析红外数据。
2. 硬件设计
2.1 硬件组件
- 红外接收模块:常见的模块如TSOP4838,它可以接收38kHz频率的调制红外信号,并输出数字信号(高电平和低电平),供单片机读取。
- 单片机:例如STM32系列单片机,作为主控芯片。
- LED指示灯:用于显示接收到的信号状态或执行的操作。
- 电源:为单片机和外设提供电源。
2.2 硬件连接
- 红外接收模块的输出端连接到单片机的GPIO引脚,通常是一个输入引脚。
- LED灯连接到GPIO输出引脚,用于显示接收到的信号或操作状态。
3. 软件设计
3.1 红外接收模块的工作原理
红外接收模块通过调制的红外光波传输信号。例如,遥控器发射的信号通常是以38kHz频率调制的,接收模块会接收到这种调制信号并将其转换为数字信号。通过控制模块的输出引脚,单片机可以检测到信号的状态(高电平或低电平)。
为了有效解析这些信号,我们需要记录红外信号的时序,并对其进行解码。常见的红外遥控协议(如NEC协议)会把数据分成多个脉冲宽度,表示1或0。
3.2 定时器与中断
- 定时器:定时器用于测量红外接收模块的信号的高电平或低电平持续时间。每当信号发生变化时,定时器中断会触发,我们可以读取该时段的长度并用来解码。
- 外部中断:红外接收模块输出信号变化时,外部中断会触发,单片机通过中断处理程序来记录信号变化时间并进行解码。
3.3 解码过程
- 信号检测:通过读取GPIO引脚的电平状态,检测信号的变化。
- 数据存储:记录信号的时间间隔,通过这些时间间隔来区分0和1,最终构建出遥控器的按键数据。
- 数据解析:根据红外协议解析数据包,得到遥控器发送的命令。
- 执行操作:根据解析的命令,执行相应的控制操作。
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. 代码解释
-
外部中断配置:
IR_GPIO_Init()
函数配置了PA0引脚为外部中断模式(上升沿和下降沿触发)。当红外信号发生变化时,外部中断会触发,调用EXTI0_IRQHandler()
进行信号处理。
-
定时器配置:
Timer_Init()
函数配置了一个定时器(如TIM2
)来记录时间间隔。定时器的频率为1kHz(即1ms),用于计算红外信号的高电平或低电平的持续时间。
-
信号解析:
EXTI0_IRQHandler()
中断服务程序中,通过记录时间间隔来判断接收到的是0还是1。根据NEC协议的时序,分析每一位的数据并将其存储在ir_data
变量中。当接收到完整的32位数据时,进行数据处理(如控制LED)。
-
LED控制:
- 如果接收到的数据为特定值(如
0xA5A5A5A5
),则通过HAL_GPIO_TogglePin()
函数控制LED的亮灭。你可以根据不同的信号数据执行其他操作,如控制电机、传感器等。
- 如果接收到的数据为特定值(如
5. 总结
本项目实现了使用STM32单片机接收红外信号的功能。通过配置外部中断和定时器,我们能够准确地解析红外遥控器发送的数据,并进行相应的操作(如控制LED灯)。该方案不仅可以用于红外遥控器的接收,还可以扩展到更多红外通信协议和应用场景中。