本帖最后由 林雨 于 2019-2-11 09:22 编辑
红外遥控解码器在刚开始学单片机的时候,就对那个小遥控器特别感兴趣,昨天终于把红外通信这一节学完了。而我呢,对红外光这种东西又有了更深层次的理解,收获也不小,下面我把我对红外通信的理解分享给大家,我们互相交流,互促进步。 所谓红外通信,就是利用红外线来传输信号的一种通信方式。它所使用的波长范围是0.7微米到一毫米之间,其中300微米到一毫米区域的波被称为亚毫米波,这种通信方式呢,在室内比较常见,因为大气会对红外线吸收和散射。红外通信设备虽然价格低廉,但是红外线具有容量大,保密性强,抗电磁干扰性能好,设备结构简单,体积小,重量轻等优势,也正是因为如此,它被广泛的使用,但是它不常被用来传输复杂的信号,因为信号复杂之后,它与其他的通信方式比如蓝牙等相比出错率比较高。 红外通信的实质是对二进制数字信号进行调制与解调,以便利用红外信道进行传输,红外通信的接口就是针对红外信道的调制解调器。接下来我说一下我对这种通讯方式的理解。我们知道信息都可以用0 ,1这两种数字来表示。那我们想要传输信息的时候,我们就可以把信息先转换成0,1这两种数字。当我们需要发送着0,1这两种数字串的时候,我们可以用“有,无”红外光来表示,比如说我们用“有”红外光表示0,“无”红外光表示1 ,那么接收红外光的接收头,就又能把0 ,1这两种数字还原出来,还原出来之后呢,按照一定的规律就能还原出来我们原始的信息。上面这个过程用正规一点的话说就是:“发送端将基带二进制信号调制为一系列的脉冲串信号,通过红外发射管发射红外信号接收端将接收到的光脉冲转换成电信号,在经过放大滤波,等处理后传送给解调电路进行解调,还原为二进制数字信号后输出。”就拿我们单片机来说,发送信号由遥控器来完成,而接收信号所需的各种硬件就都被集成到了我们的一体化接收头中。我们使用的红外通信的频率是38k,频率是38k那么它的波长就是26.3 微米了,处于红外线波长范围之内,并且38k这个频率抗干扰能力比较强,还有一些原因是因为当初硬件参数的原因。既然这是一种通信形式,那么少不了的就是协议,协议是人们制定出来共同遵守的,只有使用了相同的协议,那么他们的设备之间才能进行通信,现在使用较多的就是金沙滩上所说的那种NEC协议。的确,我发现我们家的三个调控板都是这种协议的,因为做出来的解码器这三个遥控器都能解码。
总结一下,【红外通信就是用红外光的“有,无”来代表“0,1”进行数据传输的一种通信形式,具体细节由专门的协议来规范。】 下面是一个例子,就是说用遥控器给开发板发射信号,开发板将信号解析出来,然后显示到数码管上。用到单片机的硬件资源,值得说的就是定时器还有外部中断。由于想加强一下自己使用定时器和中断的熟练程度,外部中断使用外部中断0(软件查询次序时候它排第0位),定时器使用T1(它的优先级控制寄存器是IP,可位寻址,我们使用它的PT1位)。代码分为两个文件来写:[mw_shl_code=c,true]#include <STC12C5A60S2.H>
sbit A0=P2^0;
sbit A1=P2^1;
sbit A2=P2^2;
sbit ENLED=P1^1;
unsigned char code LedChar[]={
0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8,
0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e
};//这是数码管的真值表
unsigned char LedBuff []={
0xff,0xff,0xff,0xff,0xff,0xff
};//数码管显示缓冲区
unsigned char T1RH = 0;
unsigned char T1RL = 0;//定义两个重载值给定时器1用。
extern bit IrFlag;
extern unsigned char Ircode[4];
extern void InitInfrared();//声明不在这个文件之类的变量或函数要用
//extern关键字,当然你也可以不加,加的
//话对你自己来说更容易区分。
void SetTimer_1(unsigned int ms);
/******************************************************************
* 函 数:主函数
* 描 述:主函数
* 参 数:无
* 返回值:无
*******************************************************************/
void main()
{
EA = 1;//打开总中断。
ENLED = 0;//使能LED显示区。
InitInfrared();//初始化接收头。
SetTimer_1(1);//定时一毫秒。
PT1 = 1;//将用于刷新数码管定时器的中断级别提高。
while (1)
{
if (IrFlag)//一旦检测到有一个字节接收完毕,就将那个
//字节送入缓冲区。
{
IrFlag = 0;
LedBuff[5] = LedChar[Ircode[0]>>4];
LedBuff[4] = LedChar[Ircode[0]&0x0f];
LedBuff[1] = LedChar[Ircode[2]>>4];
LedBuff[0] = LedChar[Ircode[2]&0x0f];
/* 由于一个字节是8位,而我们要显示的是16进制数,
4个二进制数是一个16进制数,所以用这种转换方式。*/
}
}
}
/******************************************************************
* 函 数:配置定时器函数
* 描 述:将配置定时器的过程函数化,每次调用即可。
这里有一点需要注意,我们的单片机性能不够强,当我们在算那个
初始值的时候,不要一下子列很长一个式子,这样单片机算的很费劲
即便我们心里很明白,也要设一些中间变量,要一步一步算,这样实
现的效果是一样的,但是单片机会算得更快一些。自己可以动手试一
下,比较一下两种情况。
* 参 数:无
* 返回值:无
*******************************************************************/
void SetTimer_1(unsigned int ms)
{
unsigned long i;
i = 33177600/12;
i = (i * ms)/1000;
i = 65536 - i;
T1RH = (unsigned char)(i>>8);
T1RL = (unsigned char)i;
TMOD &= 0x0f;
TMOD |=0x10;
TH1 = T1RH;
TL1 = T1RL;
ET1 = 1;
TR1 = 1;
}
/******************************************************************
* 函 数:数码管扫描函数
* 描 述:进行数码管的扫描显示,很容易,不用多说
* 参 数:无
* 返回值:无
*******************************************************************/
void LedScan()
{
static unsigned char i=0;
P0 = 0xff;
switch(i)
{
case 0:A0=0;A1=0;A2=0;i++0 = LedBuff[5];break;
case 1:A0=1;A1=0;A2=0;i++0 = LedBuff[4];break;
case 2:A0=0;A1=0;A2=1;i++0 = LedBuff[1];break;
case 3:A0=1;A1=0;A2=1;i=00 = LedBuff[0];break;
default: break;
}
}
/******************************************************************
* 函 数:定时器一中断服务函数
* 描 述:定时,用来扫描数码管,很容易,不用多说
* 参 数:无
* 返回值:无
*******************************************************************/
void Interrupt_Timer_1() interrupt 3
{
TH1 = T1RH;
TL1 = T1RL;
LedScan();
}[/mw_shl_code][mw_shl_code=c,true]#include <STC12C5A60S2.H>
sbit IRD = P3^2;//根据原理图可以知道接收头的输出引脚接到了这个引脚上。
bit IrFlag = 0;//接收完一个字节的标志。
unsigned char Ircode[4];//红外通信这种形式一次传出4个值。所以我们定义
//一个有4个元素的数组,他们分别是用户码,用户
//反码,键数据码,键数据反码。
/******************************************************************
* 函 数:红外接收头初始化函数
* 描 述:将红外接收头进行初始化
* 参 数:空
* 返回值:无
*******************************************************************/
void InitInfrared()
{
IRD = 1;//接收信息的时候首先要将这个引脚置为高电平。
TMOD = 0x01;//测算时间的时候我们使用定时器零的模式一。
ET0 = 0;//不允许定时器零触发中断
IT0 = 1;//外部中断0的触发方式配置为下降沿触发
EX0 = 1;//将外部中断允许位 置1,使能外部中断
}
/******************************************************************
* 函 数:测算高电平函数
* 描 述:当电平为高的时候用定时器算出高电平所耗费的时间
* 参 数:空
* 返回值:返回高电平耗费的时间Time_H
*******************************************************************/
unsigned int GetHighTime()
{
unsigned int Time_H;
TH0 = 0;
TL0 = 0;//计算时间,首先要清零。
TR0 = 1;//这里定时器的原则是谁使用定时器谁打开,使用完毕之后及时关闭。
while (IRD)//如果引脚有变化,及时算时间。
{
if (TH0>=0xc2)
{
break;//时间过长说是干扰信号。
}
}
TR0 = 0;//定时器使用完毕之后及时关闭。
Time_H = (TH0*256 + TL0);
return Time_H;
}
/******************************************************************
* 函 数:测算低电平函数
* 描 述:当电平为低的时候用定时器算出低电平所耗费的时间
* 参 数:空
* 返回值:返回低电平耗费的时间Time_L
*******************************************************************/
unsigned int GetLowTime()
{
unsigned int Time_L;
TH0 = 0;
TL0 = 0;
TR0 = 1;
while (!IRD)
{
if (TH0>=0xc2)
{
break;
}
}
TR0 = 0;
Time_L = (TH0*256 + TL0);
return Time_L;
}
/******************************************************************
* 函 数:外部中断0服务函数
* 描 述:中断函数,当中断被触发的时候进行接收信息操作
* 参 数:无
* 返回值:无
*******************************************************************/
void INT_Interrupt_0() interrupt 0
{
unsigned char i,j;//定义两个变量用于接收字节的时候循环。
unsigned char Byte;//定义一个字节型变量,暂存接收到的一个字节。
unsigned int Time;//定义一个变量来判断引导时间长度。
Time = GetLowTime();
if ((Time<23770)||(Time>25900))
{
return;
}
Time = GetHighTime();
if ((Time<11059)||(Time>13824))
{
return;
}//在这两个语句中,只要不符合协议中规定的引导时间长度就退出函数
for (i=0;i<4;i++)//循环接收4个字节
{
for (j=0;j<8;j++)//循环接收8个位
{
Time = GetLowTime();//接受每个位的时候都要算时间,时间
//长短代表了0和1。
if ((Time<940)||(Time>2157))
{
return;
}//不在这个范围之内说明是干扰性的。
Time = GetHighTime();
if ((Time>940)&&(Time<2157))
{
Byte>>=1;
}//在这个范围内判定为接收了一个1
else if ((Time>4037)&&(Time<5253))
{
Byte>>=1;
Byte|=0x80;
}//在这个范围之内判定为接收了一个0。
else
{
return;
}//不管循环到哪一次只要有别的干扰信号就退出。
Ircode = Byte;//将暂存值写到数组里边。
}
}
IrFlag = 1;//接收一个字节完毕之后标志置1。
}[/mw_shl_code] |