关于红外遥控器NEC协议讲解和基于STM32的NEC解码程序思路(最后附程序源码)
红外遥控器是通过发射红外光来进行通信的。通过对红外LED进行调制输出给红外接收器,经过红外接收器来调解接收红外遥控信号。NEC通信协议是一种单工遥控协议只能一方发送一方接收的协议。
NEC编码格式
由于自然界充斥着大量红外光,为了避免自然界红外光的干扰,红外发射器会以38KHz的频率闪烁。此时红外接收头输出低电平,红外接收头通过滤波电路等将闪烁的光信号转化为低电平。。当红外发射器不亮时,此时红外接收头输出高电平。
红外NEC编码的每一个字节都是有一段高电平和一段低电平组成的。逻辑1是由一段560微秒的低电平和1690微秒的高电平组成,逻辑0是由一段560微秒的低电平和560微秒的高电平组成。标准的NEC格式是由32字节组成。红外接收端经过滤波电路输出相应的高低电平。

本次主要讨论红外NEC解码,就只用考虑接收端的信号处理。接收端使用的是集成好的接收头电路,可以直接在淘宝就能购买到。vcc接3.3v电源。OUT接PB0引脚。

此外还需要一个支持NEC协议的遥控器(家里的空调遥控器大概率不是NEC协议)。
一帧NEC编码一共由32bit组成。包含了8bit的地址码和8bit的命令码。每一帧信号首先有一个起始码(也叫做同步码),起始码后紧接着就是32bit命令信息。起始码由9ms低电平 + 2.5ms高电平组成。除了32字节的指令,当长摁按键还会发送一个连发码,连发码是由9ms低电平 + 2.5ms高电平 + 0.56ms低电平 + 97.94ms高电平组成。
一帧NEC命令码由8位地址码+8位地址反码+8位命令码+8位命令反码,由下图构成。反码是和正常码为取反的关系,主要用来判断信号传输是否有错误。


上图是我在网上找的一帧红外信号。各位可以自己尝试解一下这一帧包含了什么数据可以将解码的数据发在评论区。
经过以上了解我们可以知道红外NEC编码主要是通过高电平和低电平的持续时间来表示各种数据的,根据这个方式我们可以利用STM32单片机中的外部中断和定时器(配置可参考上一章内容)来解码红外信号。接下来主要讲解外部中断和解码程序的思路。
整体程序思路
整体程序设计参考江协科技(51单片机红外解码)的类似状态机的思路来对红外信号解码。分为等待解码状态、开始解码状态、解码状态,三种状态。首先当一段NEC命令码发送时,首先接收到的
是起始码,当接收到起始码后便开始对信号进行解码。整体流程可参考下图。

由上边NEC编码格式可知每个字节的信息的开始和结束都是一段下降沿,根据这个特征只需在每次下降沿进入中断后读取距离上次下降沿的时间就可以知道每一帧的内容。
|
命令 |
两个下降沿的间隔时间 |
|
起始码 |
13500us |
|
连发码 |
11250us |
|
逻辑0 |
1120us |
|
逻辑1 |
2250us |
也就是测量图中红色箭头之间的时间间隔来判断每一字节。

外部中断配置
所有端口都有外部中断能力。为了使用外部中断线,端口必须配置成输入模式。首先要配置引脚位上拉输入模式然后配置外部中断寄存器(AFIO)最后要配置嵌套向量中断控制器(NVIC)。

首先就要打开AFIO和GPIO的时钟,将GPIO配置为上拉输入模式(本次程序使用GPIOB_PIN0)。然后打开AFIO的PIN0中断线输入,这样就打通了第一个通路。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//打开GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//打开AFIO时钟
GPIO_InitTypeDef GPIO_InitSturcture;
GPIO_InitSturcture.GPIO_Mode=GPIO_Mode_IPU;//上拉输入模式
GPIO_InitSturcture.GPIO_Pin=GPIO_Pin_0;//选择PIN0引脚
GPIO_InitSturcture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitSturcture);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//配置AFIO中断线
配置外部中断寄存器要将中断模式选择为外部中断触发。
EXTI_Initsturcture.EXTI_Line=EXTI_Line0;//选择中断唤醒线
EXTI_Initsturcture.EXTI_LineCmd=ENABLE;//中断线使能
EXTI_Initsturcture.EXTI_Mode=EXTI_Mode_Interrupt;//选择为外部中断
EXTI_Initsturcture.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发中断
最后配置中断向量NVIC,选择优先级。由于红外中断比较紧急一般选择较大的优先级。这里要注意对外部中断通道的选择。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//中断通道选择
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//中断通道使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
NVIC_Init(&NVIC_InitStructure);//初始化NVIC
不同的GPIO引脚与不同的中断线和AFIO相连。事件更新后使用的中断服务函数有所不同,具体可参考下图的映射关系。

|
状态参数(IR_State) |
状态 |
|
0 |
等待解码 |
|
1 |
起始码 |
|
2 |
解码完成 |
使用PIN0引脚作为中断输入引脚使用EXTI0_IRQHandler中断服务函数。
定时器配置
定时器主要是对两次中断的间隔进行计时。根据对NEC红外编码的时序每个信号最小时间为1ms左右,因此将时钟分频为1微秒就可以对每一帧信号进行精确计时,时钟配置函数参考上一章内容。1微秒也就是1000KHz=72MHz÷72。将计数器设为最大值65536,每次中断可直接获取计数器的值Get_Counter(TIM2)来判断两次中断间隔时间。
Timer_Init(6553,72);//配置时钟函数。
解码程序思路
解码过程通过计算下降沿的时间间隔来实现,因此整个函数封装在外部中断函数当中。根据上边的状态机程序思路,首先规定状态参数如下表。
|
状态参数(IR_State) |
状态 |
|
0 |
等待解码 |
|
1 |
起始码 |
|
2 |
解码完成 |
当第一次进入中断后,此时红外解码器状态为0,首先清零计数器和打开计时器,然后将状态置1。再次进入中断后开始判断计数器的值来确定是否为起始码或是长摁信号。若为起始信号便开始解码。

总结
在TIMER定时器的函数中,若使用的是上篇文章的函数记得要删除NVIC相应的内容。也可直接使用本期附件的函数。若想使用别的引脚作为输入引脚,要记得查询表格改变中断服务函数。下期会更新格力空调遥控器的解码教程。
void IR_Init(void);//初始化引脚和中断
uint8_t Get_IR_RepeatFlag(void);//获取长摁标志位
uint8_t Get_IR_Command(void)//获取命令
uint8_t Get_IR_Adress(void)//获取地址
uint8_t Get_IR_DataFlag(void);//获取解码标志位
使用这几个函数就可以直接解码红外信号。大家有什么问题可以评论区交流。
通过网盘分享的文件:红外解码
链接: https://pan.baidu.com/s/1jrqypAeLfm5198iBS8vyKg 提取码: 1111
更多推荐

所有评论(0)