当时学51的时候红外遥控器的解码过程一直让我很迷惑,什么状态机什么标志位总是很迷惑。想了很长时间还是一知半解。知道最近又重温了一边我才完全明白了以下这段遥控器解码代码的妙处:
一、基础原理
原理很简单,但困扰我很长时间的一直是如何进行解码。
二、解码代码分析
大致思路通过一个变量设置三种状态0(空闲状态),1(检测开始和重发信号),2检测到(开始信号后开始发送字节),接下来来看代码:
状态0:
void Int0_Routine(void) interrupt 0
{
if(IR_State==0) //状态0,空闲状态
{
Timer0_SetCounter(0); //定时计数器清0
Timer0_Run(1); //定时器启动
IR_State=1; //置状态为1
}
这一部分外部中断的下降沿配置就不用多说。0状态的作用就好比中段来了进行初始化。这里定时器的作用是为后续检测相邻下降沿电平时长开始的计时。
状态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>12442-500 && IR_Time<12442+500)
{
IR_State=2; //置状态为2
}
1. 重发:
这个部分当时没想得很明白因为被当时被中断搞蒙了,现在来看不过就是如果一直按着不松手就就一直会在状态0和状态1之间重复。
//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
else if(IR_Time>10368-500 && IR_Time<10368+500)
{
IR_RepeatFlag=1; //置收到连发帧标志位为1
Timer0_Run(0); //定时器停止
IR_State=0; //置状态为0
}
再通过;
/**
* @brief 红外遥控获取收到连发帧标志位
* @param 无
* @retval 是否收到连发帧,1为收到,0为未收到
*/
unsigned char IR_GetRepeatFlag(void)
{
if(IR_RepeatFlag)
{
IR_RepeatFlag=0;
return 1;
}
return 0;
}
让其一直返回1
最后在主函数中;
if(IR_GetDataFlag() || IR_GetRepeatFlag()) //如果收到数据帧或者收到连发帧
来进行操作。
当时没注意到还会有发错的情况,学习还得是温故知新,才能查漏补缺锻炼自己的思维。
else //接收出错
{
IR_State=1; //置状态为1
}
}
状态2;
状态2在经过状态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>1032-500 && IR_Time<1032+500)
{
IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //数据对应位清0
IR_pData++; //数据位置指针自增
}
//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
else if(IR_Time>2074-500 && IR_Time<2074+500)
{
IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //数据对应位置1
IR_pData++; //数据位置指针自增
}
但是这个状态最有意思的还是这几行代码;
IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //数据对应位清0
IR_pData++; //数据位置指针自增
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
}
三、感悟
以上是我心血来潮对这段代码回顾的浅浅看法,其实我是想要通过自己看看能不能想出来更好的解决方法,尽管想了很长时间但是因为知识的欠缺思维能力还没有得到很好的锻炼暂时写不出来自己的方法,但是我认识到温故知新的重要性,能让我收获很多很多。不断提升自己不积硅步,无以至千里。开学了好在课排的不是很满又该全身心投入进去学习咯!