4.4 51单片机-NEC红外线遥控器解码
Posted DS小龙哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.4 51单片机-NEC红外线遥控器解码相关的知识,希望对你有一定的参考价值。
4.4 NEC红外线遥控器解码
4.4.1 接收头原理图介绍
图4-4-1
实验板上的红外线接收头是接在单片机的P3.2 IO口上,要使用红外线接收功能,需要将红外线接收头的跳线帽接上。
图4-4-2
4.4.2 NEC红外线协议介绍
红外线遥控是目前使用最广泛的一种通信和遥控手段。由于红外线遥控装置具有体积小、功耗低、功能强、成本低等特点,因而,继彩电、录像机之后,在录音机、音响设备、空凋机以及玩具等其它小型电器装置上也纷纷采用红外线遥控。工业设备中,在高压、辐射、有毒气体、粉尘等环境下,采用红外线遥控不仅完全可靠而且能有效地隔离电气干扰。
红外线是波长介于微波和可见光之间的电磁波,波长在 760 纳米到 1 毫米之间,是波形比红光长的非可见光。
家电遥控器通信距离往往要求不高,而红外的成本比其它无线设备要低的多,所以家电遥控器应用中红外始终占据着一席之地。遥控器的基带通信协议很多,大概有几十种,常用的就有 ITT 协议、 NEC 协议、 Sharp 协议、 Philips RC-5 协议、 Sony SIRC 协议等。用的最多的就是 NEC 协议了,本小节以 NEC协议标准进行讲解,实验板自带的遥控器也是NEC协议的。
NEC协议,通常是使用 38K左右的载波进行调制,再发送的;这里的调制就是用待传送信号去控制某个高频信号的幅度、相位、频率等参量变化的过程,即用一个信号去装载另一个信号。比如:我们的红外遥控信号要发送的时候,先经过 38K 调制。
图4-4-3 信号调制示例
原始信号就是要发送的一个数据“0”位或者一位数据“1”位,而38K 载波就是频率为 38K 的方波信号,调制后信号就是最终我们发射出去的波形。使用原始信号来控制 38K 载波,当信号是数据“0”的时候, 就发送38K载波,当信号是数据“1”的时候,就不发送任何载波信号,那么数据接收方只需要检测是否收到38KHZ的载波就行判断是否收到了数据。实验板上带了一个一体化接收头HS0038B,可以识别38KHZ的载波信号,当HS0038B收到38KHZ载波就输出低电平,没有收到38KHZ载波就输出高电平;程序就可以判断HS3008的引脚电平配合指定的协议格式来分析收到的数据。
图4-4-4 红外线接收头
NEC协议的数据格式包括了引导码、用户码、用户码反码、按键码、按键反码。
其中数据编码总共是4个字节 32 位(除了引导码外就是数据位),数据格式中的反码用于数据校验,可以判断数据在传输过程中有没有丢包。
图4-4-5 NEC协议传输格式
NEC协议解码总结:
引导码:9ms的低电平+4.5ms的高空闲。
逻辑 1: 560us 低+1680us 高电平
逻辑 0: 应该是 560us 低+560us 高电平。
遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平。
红外线遥控器向红外线接收头按下一个按键之后,红外线接收头先收到的是9ms的高电平脉冲,接着是4.5ms的低电平,之后就是接收8bit的用户码、8bit的用户码的反码,8bit 的按键码,8bit 的按键码的反码。如果一直按着1个键,这样遥控器发送的是以110ms为周期的重复码,就是说,发了一次按键码之后,不会再发送按键码,而是每隔110ms时间,发送一段重复码,重复码由9ms高电平和2.25ms的低电平以及560us的高电平组成。
4.4.3 采集NEC协议的信号
为了更加直观的理解NEC协议格式,可以使用逻辑分析仪或者示波器采集HS0038红外线接收头收到的数据,再通过软件分析收到的数据。
图4-4-6 逻辑分析采集的NEC数据
图4-4-7 逻辑分析采集过程
逻辑分析仪软件下载地址: Logic analyzer software from Saleae
图4-4-8 逻辑分析软件
4.4.4 NEC红外线协议解码示例
下面代码采用定时器+外部中断的方式解码红外线数据,外部中断采用外部中断0,接在P3.2口上,配置外部中断0的触发方式为下降沿触发。
解码思路: 红外线接收头没有收到38KHZ方波时,默认输出高电平,当收到38KHZ方波时输出低电平,这时就触发了下降沿,接着就进入到外部中断0 的中断服务函数;解码的过程就在中断服务函数里实现,高低电平的持续时间通过定时器0进行计数,当前实验板的晶振是12MHZ,刚好定时器的计数器+1的时间就是1us,配置定时器0的工作方式为16位模式,最大计数可以到65535,NEC协议最长的一段时间也就9000us,完全满足需求;解码完成之后设置标志位,在主函数里将解码的数据通过串口打印出去。
(硬件平台说明:CPU是STC90C516RD 、晶振频率12MHZ 、工作在12T模式下、一个机器周期为1us时间)
示例代码:
#include <reg51.h>
u8 Infrared_RX_Flag=0; //红外接收标志,收到一帧正确数据后置1
u8 Infrared_RX_Buff[4];//红外代码接收缓冲区
sbit Infrared_GPIO=P3^2;//红外接收引脚--外部中断0
/*
函数功能: 开始红外线解码之前的相关初始化
实验板的晶振频率是12MHZ
51单片机标准架构下一个机器周期是12个时钟周期,如果晶振频率是12MHZ,那一个机器周期的时间就是12/12微秒。
也就是说定时器的计数器+1的时间就是12/12=1us。
*/
void Infrared_Init(void)
{
Infrared_GPIO=1;//红外接收引脚默认保持高电平输出
TMOD&=0xF0; //清除配置
TMOD|=0x01; //配置定时器0,工作在16位计数模式
TR0=0; //停止定时器0计数
ET0=0; //禁止定时器0中断
IT0=1; //开启外部中断0,下降沿触发
EX0=1; //允许外部中断0中断
}
/*
函数功能: 检测高电平持续的时间
*/
u16 Infrared_GetTimeH(void)
{
TH0=0; //定时器0重装值为0
TL0=0; //定时器0重装值为0
TR0=1; //启动定时器0开始计数
while(Infrared_GPIO)//等待高电平结束
{
if(TH0>0x40)//防止超时
{
break;
}
}
TR0=0;//停止定时器0计数
return TH0<<8|TL0;//T0计数值合成为16位整数返回
}
/*
检测低电平持续的时间
*/
u16 Infrared_GetTimeL(void)
{
TH0=0;//定时器0的高8位重装值
TL0=0;//定时器0的低8位重装值
TR0=1;//开启定时器0
while(Infrared_GPIO==0)//等待低电平结束
{
if(TH0>0x40)//防止超时
{
break;
}
}
TR0=0;//停止定时器0计数
return TH0<<8|TL0;//T0计数值合成为16位整数返回
}
/*
外部中断0中断服务函数
*/
void EXTI0_IRQHandler() interrupt 0
{
u8 i, j;
u16 time;
u8 byte;
time=Infrared_GetTimeL(); //获取出现低电平的时间
if((time<7800)||(time>9300))//判断低电平时间是否符合9ms范围
{ //超过此范围则说明为误码,直接退出
IE0=0; //清除外部中断0中断标志
return;
}
time=Infrared_GetTimeH(); //获取出现高电平的时间
if((time<3500)||(time>4700))//高电平是否符合4.5ms范围
{ //超过此范围则说明为误码,直接退出
IE0=0; //清除外部中断0中断标志
return;
}
//接收32位数据位
for(i=0;i<4;i++)
{
for(j=0;j<8;j++)
{
time=Infrared_GetTimeL(); //获取低电平持续时间,标准的间隔时间为560us范围
if((time<300)||(time>700)) //判断范围是否合理
{
IE0=0;//清除外部中断0中断标志
return;
}
//1和0是靠高电平持续的长短来区分的
time=Infrared_GetTimeH(); //获取高电平持续时间
if(time>300&&time<700) //0的标准时间为560us
{
byte>>=1;
}
else if(time>1400&&time<1800) //1的标准时间是1680us
{
byte>>=1;
byte|=0x80;
}
else //不在上面的判断范围内说明是错误码,直接退出
{
IE0=0;//清除外部中断0中标
return;
}
}
Infrared_RX_Buff[i]=byte;//接收完一个字节后保存到缓冲区
}
Infrared_RX_Flag=1;//接收完毕后设置标志
IE0=0;//退出前清除外部中断0中断标志
}
int main()
{
UART_Init(); //初始化串口波特率为4800
Infrared_Init(); //初始化红外功能
while(1)
{
if(Infrared_RX_Flag) //接收到红外数据
{
Infrared_RX_Flag=0; //清楚标志
printf("user1:%d,user2:%d\\r\\n",(int)Infrared_RX_Buff[0],(int)((u8)(~Infrared_RX_Buff[1])));
printf("key1:%d,key2:%d\\r\\n",(int)Infrared_RX_Buff[2],(int)((u8)(~Infrared_RX_Buff[3])));
}
}
}
以上是关于4.4 51单片机-NEC红外线遥控器解码的主要内容,如果未能解决你的问题,请参考以下文章
46.Linux-分析rc红外遥控平台驱动框架,修改内核的NEC解码函数BUG