我们的开发板上会有一个红外接收头和一个遥控器。通过这两个小东西就可以完成红外实验。其中这个遥控器采用了NEC协议。除了这个协议还有RC5、RC6类型的。我们接下来就说说红外接受和红外发射。
-
红外发送与接收
发射段就是一个红外发光二极管,产生红外光信号;接收端接收光信号通过光敏电阻把光信号转换成电信号,进一步装换成数字信号。但是发射端的功率较弱,那么光信号就比较弱,为了解决这个问题我们就引入了载波的概念。 -
载波
载波就是一个车,我可以做上这个车到达下一个地方。这个车就是载波。那么上车和下车就是调制和解调 -
调制
调制就是按下遥控器的数字,然后通过把这个按键的信号转换成一个载波信号。信号遥控器发送的数据码由以下部分组成:引导码,8位的客户码,8位客户码的补码,8位的按键值,8位按键值的补码; -
解调
解调就是把这个载波信号通过接收端转换位时序信号,通过接收端的输出引脚把时序信号传送给MCU的外部中断,让主机来判断信号。那么我们就想,数字信号是由0和1组成的。发射段最终把他的光信号包含的信息也是最终转换成了接收端的0和1。他是怎么做到的呐。
上边阴影部分代表载波。载波-空闲-载波-空闲。第一部分空闲2.25ms-560微秒,第二部分空闲时间是1.12ms-560微秒。通过两个载波之间的空闲时间来判断是1或者0。通过0和1来传递信息 -
NEC协议
我们的单片机有一个红外遥控器,按下其中的一个按钮的时候就会发出如下的载波信号
每个按键被按下会发送不停的信号。但是最开始都是9ms载波加上4.5ms的空闲。之后是32位数据,一共四个字节:地址码,地址码反码,数据码,数据码反码。红外接收端把载波信号转换成时序信号共主机来判断。
另外我们需要注意的一点是发射端和接收端的0和1是相反的。 当红外接收端接收到载波信号的时候,out引脚输出低电平。如下图所示。这就是解调的过程。调制是把信息转换成载波信号,解调就是重新载波信号解调出来。
下边我们俩看看红外发送与接收需要那些东西
- 首先是需要定时器,因为通过定时器来计算两个载波之间空闲时间
- 外部中断1,我用的板子是MCS51 。红外接收端的OUT引脚连接的是
P3.2(外部中断1),因为接收端接收到载波信号输出低电平,所以我们需要外部中断1为低电平触发。 - 需要说明一点的是,在项目中,外部中断1中断级别0,是最高级别,比我们用的定时器中断级别要高,所以面对两个中断是,主机先处理外部中断。当载波接收完毕,就开始接收空闲信号,这个时候外部中断不被触发,开始执行定时器中端,开始计数,通过计数数值结合定时器单次溢出时间来判断时序信号的0和1.
上代码
#include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
sbit IR=P3^2; //红外接口标志
sbit dula=P2^6; //定义锁存使能端口 段锁存
sbit wela=P2^7; // 位锁存
unsigned char code Table[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9
unsigned char irtime; //红外用全局变量
bit irpro_ok,irok;
unsigned char IRcord[4];
unsigned char irdata[33];
void Ir_work(void);
void Ircordpro(void);
void tim0_isr (void) interrupt 1 using 1
{
irtime++; //用于计数2个下降沿之间的时间
}
void EX0_ISR (void) interrupt 0 //外部中断0服务函数
{
static unsigned char i; //接收红外信号处理
static bit startflag=0; //是否开始处理标志位
if(startflag)
{
if(irtime<63&&irtime>=33)//引导码 TC9012的头码,9ms+4.5ms
i=0;
irdata[i]=irtime;//存储每个电平的持续时间,用于以后判断是0还是1
irtime=0;
i++;
if(i==33)
{
irok=1;
i=0;
}
}
else
{
irtime=0;
startflag=1;
}
}
void Ir_work(void)//红外键值散转程序
{
switch(IRcord[2])//判断第三个数码值
{
case 0x0c:P0=Table[1];break;//1 显示相应的按键值
case 0x18:P0=Table[2];break;//2
case 0x5e:P0=Table[3];break;//3
case 0x08:P0=Table[4];break;//4
case 0x1c:P0=Table[5];break;//5
case 0x5a:P0=Table[6];break;//6
case 0x42:P0=Table[7];break;//7
case 0x52:P0=Table[8];break;//8
case 0x4a:P0=Table[9];break;//9
default:break;
}
irpro_ok=0;//处理完成标志
}
void Ircordpro(void)//红外码值处理函数
{
unsigned char i, j, k,cord,value;
k=1;
for(i=0;i<4;i++) //处理4个字节
{
for(j=1;j<=8;j++) //处理1个字节8位
{
cord=irdata[k];
if(cord>7)//大于某值为1,这个和晶振有绝对关系,这里使用12M计算,此值可以有一定误差
value=value|0x80;
if(j<8)
{
value>>=1;
}
k++;
}
IRcord[i]=value;
value=0;
}
irpro_ok=1;//处理完毕标志位置1
}
void TIM0init(void)//定时器0初始化
{
TMOD=0x02;//定时器0工作方式2,TH0是重装值,TL0是初值
TH0=0x00; //重载值
TL0=0x00; //初始化值
ET0=1; //开中断
TR0=1;
}
void EX0init(void)
{
IT0 = 1; //指定外部中断0下降沿触发,INT0 (P3.2)
EX0 = 1; //使能外部中断
EA = 1; //开总中断
}
void main(void)
{
EX0init(); //初始化外部中断
TIM0init();//初始化定时器
dula=0; //位锁存
P0=0xfe; //取位码 第一位数码管选通,即二进制1111 1110
wela=1; //位锁存
wela=0;
P0=0x3f; //取位码 第一位数码管选通,即二进制1111 1110
dula=1; //位锁存
while(1)//主循环
{
if(irok) //如果接收好了进行红外处理
{
Ircordpro();
irok=0;
}
if(irpro_ok) //如果处理好后进行工作处理,如按对应的按键后显示对应的数字等
{
Ir_work();
}
}
}
- 注释
代码中的Ir_work()函数中用到了按键时序信号和按键值的对应关系,比如:
0x0c对应按键1。这里每个遥控器有所不同,可以通过看说明书获得,也可以通过示波器测波形,也可以通过串口通信把数字打印在上位机上