51单片机;红外解码
红外通信
注意:本文采用的是判断相邻下降沿时间来解码的,还有一种方式即分别判断载波和空闲的时间来进行解码
NEC协议的数据格式包括:引导码、用户码、用户码(用户反码)、按键码、按键码(按键反码)、最后一个停止位。停止位只要起隔离作用,一般不进行判断,程序中可以不予理会。
数据编码共4个字节32位,分别为:用户码、用户码(用户反码)、按键码、按键码(按键反码)(具体是源码还是反码由生产商决定,反码可用于对数据纠错)
解码:(其中载波为低电平,空闲为高电平)
引导码:9ms载波+4.5ms的空闲 信号时间为:13.5ms
比特值 “ 0 ”:560us的载波+560us的空闲 信号时间为:1.12ms
比特值 “ 1 ”:560us的载波+1.68ms的空闲 信号时间为:2.24ms
在程序中,将定时器配置为8位自动重装模式,用于计数下降沿的时间:
//定时器0初始化
void Init_Timer0()
{
TMOD = 0x02;//八位自动重装
TH0 = 0;//重载值
TL0 = 0;//初始值
ET0 = 1;//溢出中断允许位
TR0 = 1;//运行控制位
}
void Timer0_Break() interrupt 1
{
ir_time++;//用于记录相邻两个下降沿的时间
}
红外接收头接单片机的P3^2引脚(即外部中断0),配置外部中断为下降沿触发方式,在中断函数中将下降沿的时间进行记录,存到数组中以便对码值进行判断。
//外部中断0配置
void Init_EXT0()
{
IT0 = 1;//下降沿触发中断
EX0 = 1;//外部中断0中断允许位
EA = 1;//开总中断
}
//中断处理
void EX0_Break() interrupt 0
{
static bit Flag_Star;//开始处理标志
static uchar i;
if(Flag_Star)
{
//干扰信号的处理
//if(ir_time<63 && ir_time>=33)//9ms-17ms左右记为干扰信号
if(ir_time>48)//超过13.5ms的则为干扰信号
i = 0;//不记录进电平数组
ir_data[i] = ir_time;//将电信号时间进行记录
ir_time = 0;//清空计数
i++;
if(i==33)//记录一个完整的周期
{
i = 0;
ir_recode = 1;//记录完毕标志
}
}
else
{
ir_time = 0;
Flag_Star = 1;
}
}
电信号时间记录后,我们对记录到的时间进行处理。其中,引导码我们不予处理,主要处理4位数据编码,将其转换为对应的二进制编码。
//将接收到的时间转换为电平信号
void Ir_Single_Trun()
{
uchar i,j,k,value,temp;
k = 1;//略过引导码
for(i=0;i<4;i++)//4位
{
for(j=0;j<8;j++)//8个字节
{
temp = ir_data[k];//取出时间信息
if(temp > 7)//"1"的电平时间为2.24ms,2.24/0.000278
value |= 0x80;//为1,则最高位置1
if(j<7)
value >>= 1;//直接右移就会在最高位出来一个0
k++;
}
ir_code[i] = value;//将处理完的值存进码值数组
value = 0;
}
ir_turn = 1;//键值转换完毕
}
最后,我们将解码到的码值与编码值进行比较,判断出对应的操作。
我的红外遥控器编码值为:
//数码管显示处理 DataPort为P0口宏定义
void led_view()
{
switch(ir_code[2])//判断键码值
{
case 0x0c:DataPort=led_table[1];break;
case 0x18:DataPort=led_table[2];break;
case 0x5e:DataPort=led_table[3];break;
case 0x08:DataPort=led_table[4];break;
case 0x1c:DataPort=led_table[5];break;
case 0x5a:DataPort=led_table[6];break;
case 0x42:DataPort=led_table[7];break;
case 0x52:DataPort=led_table[8];break;
case 0x4a:DataPort=led_table[9];break;
default:DataPort=0x79;break;//未接收显示"E"
}
ir_turn = 0;//处理完成置0,以便下一次键值转换处理
}
最后在主函数中对各个函数进行调用。
//主函数
void main()
{
Init_Timer0();
Init_EXT0();
du = 0;
DataPort=0xfe;
we = 1;
we = 0;
DataPort=0x3f;
du=1;
while(1)
{
if(ir_recode)//时间记录完毕
{
Ir_Single_Trun();
ir_recode = 0;
}
if(ir_turn)
{
led_view();
}
}
}
从未停止