MCU 51-10 Infrared Communication

红外遥控电路组成

一般而言,红外遥控系统由发射装置和接收装置两大部分组成。

红外发射

其中发射装置主要包括键盘电路、红外编码芯片、电源和红外发射电路组成(例如遥控器);

比如:我们按下CH+,对应的键值码是0x47, 然后到编码芯片进行编码调制,最后发送出去

 红外接收

红外接收设备可由红外接收电路、红外解码芯片、电源和应用电路组成。 (例如开发板上有红外接收电路,单片机充当解码芯片)

红外通信流程图

信号调制和解调

数字信号传输过程中为什么要进行调制解调?
①把信号调整成比较容易传输的频率(有些信号频率低,不适合远距离传输,可以加上频率很高的载波信号)
②可以在有限的线路上传输更多的信息
③可以在传输过程中抗干扰(接受到的有效信号与外界环境的干扰信号分离开),加密

通常为了使信号能更好的被传输,发送端将基带二进制信号调制为脉冲串信号,通过红外发射管发射。

 

NEC格式的红外编码:

数据格式 发射端的方波图,接收端的正好与之相反,数据传输从最低位开始

NEC格式的红外编码是连续的32位二进制码组。32位二进制码组之前的引导码,用于区分每次的传输;

在起始码之后的才是32位二进制码组,其中8位用户识别码和8位反码(反码主要用于)校验,用户识别码的作用主要是区分不同品牌的遥控器;接着就是8位操作码和反码,用于表示哪个按键被按下。

NEC格式的编码除引导码和起始码外,用于交互数据的信息采用脉宽调制的串行码,在38kHz的载波下:

以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;

以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”

红外解码:(以扫地机举例)

一般,进行红外解码我们需要先将接受到的有效信号与外界环境的干扰信号分离开,这就是载波的作用。

现在市面上有很多一体化红外接收头,内部是红外接收二极管 +放大电路 +解调器。当接收到红外信号后,先将其放大,然后把38kHz的信号保留下来

需要注意的是一般一体化红外接收头接收到信号时输出是低电平,没有接收信号时是高电平。

解码工作主要是识别引导码,还有数据“0”“1”。我们看波形图,只要识别红外接收头“OUT”引脚高低电平持续的时间就可以了。这个可以用定时器中断完成,也可以用延时来判断。由于只完成解码工作,我们就用延时的方法更方便。

红外接收头没有接到信号时是高电平,等待其变低。之后,每隔900us测量一次,若在10次内电平变高,说明这不是引导码,退出重新开始;若10次内并没有变高,说明这就是引导码,继续下一步。接着可以不判断起始码,就是直接等待引脚电平变高再变低,就可以接收数据了。
接收数据,就是进行判断“0”和“1”。从位的定义我们可以发现“0”、“1”均以0.56ms的低电平开始,不同的是高电平的宽度不同,“0”为0.56ms,“1”为1.68ms,所以必须根据高电平的宽度区别“0”和“1”。
每一位“0”或者“1”在接收头接收信号即低电平的时间都是一样的,直接等待低电平过去,在高电平时延时600us,如果600us时还是高电平,说明是“1”;如果是低电平即高电平过去了,说明是“0”。将这些数据结合到一起,就是接收到的编码了

(1)编码信息的格式为:开头码+8位数据+停止位;

 

(2)充电座上共有4个红外发射灯,每个灯发射的编码信息都不同;区别主要在“8位数据”不同;A = 0xA2,B = 0x2A,C = 0x34,C' =0x43;
(3)充电座在同一个发射周期只能有一个红外灯在进行发射信号;主要是为了防止两个灯发射时会产生相互干扰,导致无法解码成功;
(4)编码信息为:开头码(引导码):9ms的38kHz PWM波 + 4.5ms的关闭

数据段:
Bit = 0:500us 的 38kHz PWM波 + 600 us 的关闭;

 Bit = 1:500us 的 38kHz PWM波 + 1.7ms的关闭;

结束码为:500us 的 38kHz PWM波;
一整段编码信息如下:

(5)充电座每个红外发射头进行轮流发射,每个灯发射之间延时75ms

/*NEC协议红外通信
单片机解码后通过串口以9600的比特率发送出去
*/

#include <reg52.h>

/*====================================
 自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
 硬件接口位声明
====================================*/
sbit IR  = P3^2;     //定义红外脉冲数据接口    外部中断O输入口

uchar IRtime;         //检测红外高电平持续时间(脉宽)
uchar IRcord[4];    //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)
uchar IRdata[33];   //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)
bit IRpro_ok, IRok;  //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕

void init()       //初始化定时器0 和外部中断0
{
    TMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装
    TH0 = 0x00;  //高8位装入0那么定时器溢出一次的时间是256个机器周期
    TL0 = 0x00;
    EA = 1;      //总中断
    ET0 = 1;       //定时器0中断
    TR0 = 1;     //启动定时器0

    IT0 = 1;       //设置外部中断0为跳沿触发方式,来一个下降沿触发一次
    EX0 = 1;       //启动外部中断0

    TH1 = 0xfd;  //此溢出率为波特率9600
    TL1 = 0xfd;
    TR1 = 1;     //启动定时器1
    SM1 = 1;     //设置串口工作方式1,10位异步收发器
}

//计算脉宽持续的时间
void time0() interrupt 1 //定义定时器0 { IRtime++; //检测脉宽,1次为278us } void int0() interrupt 0 //定义外部中断0 { static uchar i; // 声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata static bit startflag; //开始储存脉宽标志位 if(startflag) //开始接收脉宽检测 { if( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us 这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000 如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/ i = 0; //如果是引导码那么执行i=0把他存到IRdata的第一个位 IRdata[i] = IRtime; //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断 IRtime = 0; //计数清零,下一个下降沿的时候在存入脉宽 i++; //计数脉宽存入的次数 if(i == 33) //如果存入34次 数组的下标是从0开始i等于33表示执行了34次 { IRok = 1; //那么表示脉宽检测完毕 i = 0; //把脉宽计数清零准备下次存入 } } else { IRtime = 0; //引导码开始进入把脉宽计数清零开始计数 startflag = 1; //开始处理标志位置1 } } void IRcordpro() //提取它的33次脉宽进行数据解码 { uchar i, j, k, cord, value; /*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位 cord用于取出脉宽的时间判断是否符合1的脉宽时间*/ k = 1; //从第一位脉宽开始取,丢弃引导码脉宽 for(i = 0; i < 4; i++) { for(j = 0; j < 8; j++) { cord = IRdata[k]; //把脉宽存入cord if(cord > 5) //如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1 value = value | 0x80; /*接收的时候是先接收最低位, 把最低位先放到value的最高位在和0x08按位或一下 这样不会改变valua的其他位的数值只会让他最高位为1*/ if(j < 7) { value = value >> 1; //value位左移依次接收8位数据。 } k++; //每执行一次脉宽位加1 } IRcord[i] = value; //每处理完一个字节把它放入IRcord数组中。 value = 0; //清零value方便下次在存入数据 } IRpro_ok = 1; //接收完4个字节后IRpro ok置1表示红外解码完成 } void main() { uchar i; init(); //执行初始化定时器0和外部中断0 while(1) //大循环 { if(IRok) //判断脉宽是否检测完毕 { IRcordpro();//根据脉宽解码出4个字节的数据 IRok = 0; //重新等待脉宽检测 if(IRpro_ok) //判断是否解码完毕 { for(i = 0; i < 4; i++) { SBUF = IRcord[i]; while(!TI); TI = 0; } IRpro_ok = 0; } } } }
原文地址:https://www.cnblogs.com/darren-pty/p/13324119.html