红外遥控:利用红外光进行通讯的设备,有红外LED讲调制后的信号发出,由专门的红外接收头进行解调输出。由单工异步的通讯方式进行通讯。通信协议标准是NEC标准。(红外LED标准940nm)
硬件电路
解释:IN引脚是我们要发送波形信息的引脚,38KHZ是调制的信号加载到IN的波形上的。采用两个PNP三极管的串联,使当两个三级管的基极同时为低电平时LED才亮。
亮灭变化:当IN为高电平时,LED不发光,当IN为低电平时,LED按照38KHZ的频率,当为低电平时发光,高电平时不发光。
加上38KHZ的调制波形是为了与自然光区别,当红外光接收器接收信号时会通过滤波电路把38KHZ的波形滤掉,保留信息发送引脚发送的波形信号。
使用一个IN引脚模拟上面所讲的时序也行
接收装置:
为了保证红外光接收装置接收信息的准确性,不使用IF语句,使用响应更为快送的外部中断处理。
tip:
在实际运用中,我们只需控制IN引脚发送的信号,38KHZ的频率是底层配置好的。
基本发送与接收
IN空闲状态:红外LED不亮,接收头输出高电平。
IN低电平:红外LED以38KHZ闪烁发光,接收头输出低电平
IN高电平:红外LED不亮,接收头输出高电平。
遥控器键码值
NEC协议
1、起始信号:由9ms的低电平和4.5ms 的高电平组成。
2、数据格式:地址码+地址码反码+命令+命令反码(都是8位的数据,发送时低位在前高位在后)
//外部中断0中断函数,下降沿触发执行
void Int0_Routine(void) interrupt 0
{
if(IR_State==0) //状态0,空闲状态
{
Timer0_SetCounter(0); //定时计数器清0
Timer0_Run(1); //定时器启动
IR_State=1; //置状态为1
}
else if(IR_State==1) //状态1,等待Start信号或Repeat信号
{
IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间
Timer0_SetCounter(0); //定时计数器清0
//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
if(IR_Time>13500-500 && IR_Time<13500+500)
{
IR_State=2; //置状态为2
}
//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
else if(IR_Time>11250-500 && IR_Time<11250+500)
{
IR_RepeatFlag=1; //置收到连发帧标志位为1
Timer0_Run(0); //定时器停止
IR_State=0; //置状态为0
}
else //接收出错
{
IR_State=1; //置状态为1
}
}
else if(IR_State==2) //状态2,接收数据
{
IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间
Timer0_SetCounter(0); //定时计数器清0
//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
if(IR_Time>1120-500 && IR_Time<1120+500)
{
IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //数据对应位清0
IR_pData++; //数据位置指针自增
}
//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
else if(IR_Time>2250-500 && IR_Time<2250+500)
{
IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //数据对应位置1
IR_pData++; //数据位置指针自增
}
else //接收出错
{
IR_pData=0; //数据位置指针清0
IR_State=1; //置状态为1
}
if(IR_pData>=32) //如果接收到了32位数据
{
IR_pData=0; //数据位置指针清0
if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3])) //数据验证
{
IR_Address=IR_Data[0]; //转存数据
IR_Command=IR_Data[2];
IR_DataFlag=1; //置收到连发帧标志位为1
}
Timer0_Run(0); //定时器停止
IR_State=0; //置状态为0
}
}
}
代码实现思路
使用状态位IR_State,IR_State0表示空闲状态,IR_State1表示等待Start信号或Repeat信号,IR_State2表示接收信号。
上篇说到为了响应迅速故使用外部中断,由于每个数据位的发送都是以下降沿结束,所以使用下降沿触发的外部中断。因为数据位0或1是由高低电平的时间决定,所以使用定时器定时时间来确定数据位是多少。
红外线接收装置的OUT引脚连接到了外部中断引脚上,当接收装置接收到红外线信号时将会进入外部中断函数。
首次进入到中断函数中,已经产生了外部中断,此时把IR_State置为1进而判断是Start信号还是Repeat信号。进入中断后可能是开始信号也可能是重复信号,差别是中断时间的不同 ,因此判断时间的多少就可以识别出是开始信号还是结束信号。判断时间时在保证时间不会交集的情况下可以判断时间的范围,因为定时也不一定准确,
若判断的时间长度为开始信号时间的长度,IR_State置为2,判断的时间长度为重复信号时间的长度,IR_State置为0,如果这两个都不是,说明接收出错,则把这次接收结果舍去,IR_State清零,准备下次接收。
当IR_State2时,说明已接受到开始信号,准备开始接收数据。组成数据的“0”,“1”码是根据高低电平的持续时间确定的,所以只要检测定时器的计数值就能确定数据。
采用了以下算法:
else if(IR_State==2) //状态2,接收数据
{
IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间
Timer0_SetCounter(0); //定时计数器清0
//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
if(IR_Time>1120-500 && IR_Time<1120+500)
{
IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //数据对应位清0
IR_pData++; //数据位置指针自增
}
//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
else if(IR_Time>2250-500 && IR_Time<2250+500)
{
IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //数据对应位置1
IR_pData++; //数据位置指针自增
}
else //接收出错
{
IR_pData=0; //数据位置指针清0
IR_State=1; //置状态为1
}
if(IR_pData>=32) //如果接收到了32位数据
{
IR_pData=0; //数据位置指针清0
if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3])) //数据验证
{
IR_Address=IR_Data[0]; //转存数据
IR_Command=IR_Data[2];
IR_DataFlag=1; //置收到连发帧标志位为1
}
Timer0_Run(0); //定时器停止
IR_State=0; //置状态为0
}
}
IR_Data[IR_pData/8] 划分了四个8位数据,IR_Data[0],IR_Data[1],IR_Data[2],IR_Data[3]分别代表地址码,地址码反码,命令,命令反码
8*****************************-----*********************8
当判断的时间长度为“0”的时间长度时:
则把该数据位清0。
if(IR_Time>1120-500 && IR_Time<1120+500)
{
IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //数据对应位清0
IR_pData++; //数据位置指针自增
}
8*****************************-----*********************8
当判断的时间长度为“0”的时间长度时:
则把该数据位置1。
else if(IR_Time>2250-500 && IR_Time<2250+500)
{
IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //数据对应位置1
IR_pData++; //数据位置指针自增
}
8*****************************-----*********************8
当判断的时间长度既不是“0”的时间长度也不是“1”的是时间长度时,说明数据接收错误,数据位置指针清0,重新接收。
else //接收出错
{
IR_pData=0; //数据位置指针清0
IR_State=1; //置状态为1
}
8*****************************-----*********************8
因为共需要接收4个8位数据,共32位,所以当地址指针等于32时说明接收完毕,然后就能进入判断:
if(IR_pData>=32) //如果接收到了32位数据
{
IR_pData=0; //数据位置指针清0
if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3])) //数据验证
{
IR_Address=IR_Data[0]; //转存数据
IR_Command=IR_Data[2];
IR_DataFlag=1; //置收到连发帧标志位为1
}
Timer0_Run(0); //定时器停止
IR_State=0; //置状态为0
}