红外解码对于STC等51系列单片机可以采用常规延时办法实现,但是有一个非常尴尬的弊病:中断内延时
void INT1_service(void) interrupt 2//外部中断
{
uchar k;
EX1 = 0;
for (k = 0;k < 8;k++)
{
delay1ms();//延时
if (Rec == 1)
{
EX1 = 1;
return ;
}
}
while (Rec == 0);
ds4ms();//延时
Delay500us();//延时
for (k = 0;k < 16;k++)
{
while (Rec == 0);
ds850us();
if (Rec == 0)
continue;
delay1ms();
}
for (k = 0;k < 8;k++)
{
shujua = shujua >> 1;
while (Rec == 0);
ds850us();
if (Rec == 1)
{
shujua = shujua | 0x80;
delay1ms();
}
}
for (k = 0;k < 8;k++)
{
shujub = shujub >> 1;
while (Rec == 0);
ds850us();
if (Rec == 1)
{
shujub = shujub | 0x80;
delay1ms();
}
}
if (shujua == ~shujub)
{
EX1 = 1;
Rec = 1;
return;
}
EX1 = 1;
Rec = 1;
return ;
}
大家可以看到在中断中采用延时函数,这对程序效率影响极为大,在STC系列单片机中可以运行,至今还没出现过问题,但是这个代码移植到STM8系列后问题百出,经常定时器卡死,单片机死机,问题就在于中断内停留过长时间。
正确做法:
红外解码可以分解为两个步骤:
一,解出引导码。
二,解出数据码。
下面程序分析(STM8程序)
INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6)
{
if(IRFJS==0){
switch(EXTIstatus)//解码步骤选择
{
case 0: TIM2_SetCounter(0x0000);//当检测到下降沿打开定时器并清零 计数值
TIM2_Cmd(ENABLE);
IRFED=0;
EXTIstatus=1;break;//进入到第一步
case 1:
IRFED=TIM2_GetCounter();//当第二个下降沿来临后进入中断 查看是否为引导码
TIM2_SetCounter(0x0000);
if((IRFED>12000)&&(IRFED<14500))EXTIstatus=2;//判断时间是否在区间内 引导码低电平9ms+高电平4.5ms,共计13500us因此范围在这个值以内就算解到引导码。
else EXTIstatus=0;//如果不符合则退出去 重新解引导码
IRFED=0;//计数值清零
break;
case 2: //第二步解出数据码
IRFED=TIM2_GetCounter();//取得数据位
TIM2_SetCounter(0x0000);//清零计数值 进行下一个下降沿计数
if((IRFED>900)&&(IRFED<1400))//判断为低
{//因为红外解码 高电平周期为2140us左右 低电平为1120us左右因此都取一个区间
DataBuf[EXTItablecount]<<=1;//为低电平左移一位
EXTIbytecount++;//该数据位前进一位
}
else if((IRFED>2000)&&(IRFED<2600))//判断为高
{ DataBuf[EXTItablecount]<<=1;//左移一位
DataBuf[EXTItablecount]=DataBuf[EXTItablecount]+1;//加1
EXTIbytecount++;//数据位进一位
}
else{//如果都不对 则为误动作 退出去
TIM2_SetCounter(0x0000);
TIM2_ClearFlag(TIM2_FLAG_UPDATE);
TIM2_Cmd(DISABLE);
EXTItablecount=0;
EXTIbytecount=0;
flot=0;
EXTIstatus=0;
}
if(EXTIbytecount==8){// 共计8位数据 解完 完成一个字节
EXTIbytecount=0;
EXTItablecount++; // 加一个字节
}//如此循环把所有解完
if(EXTItablecount==2){//如果解到两个字节则完成解码
TIM2_SetCounter(0x0000);
TIM2_ClearFlag(TIM2_FLAG_UPDATE);
TIM2_Cmd(DISABLE);
EXTItablecount=0;
EXTIbytecount=0;
flot=1;
EXTIstatus=0;}
break;
default :
{ TIM2_SetCounter(0x0000);
TIM2_ClearFlag(TIM2_FLAG_UPDATE);
TIM2_Cmd(DISABLE);
EXTItablecount=0;
EXTIbytecount=0;
flot=1;
EXTIstatus=0;
EXTIstatus=0;
}
break;
}
}
}
在此附上STM8S定时器2 的初始化信息
TIM2_TimeBaseInit(TIM2_PRESCALER_16, 20000);//16分频 也就是1us加一
TIM2_ARRPreloadConfig(ENABLE);//打开自动装载
TIM2_Cmd(DISABLE);//关闭 解码的时候再打开
还有一个至关重要的点:
为了防止误动作导致红外解码跳到了其他步骤,请在主函数加入定时器超时归零的函数
if(TIM2_GetCounter()>15000)EXTIstatus=0;//定时器超时 解码步骤归零
代码在多种单片机使用非常稳定请大家拿去使用。
交流QQ群:496998875 有问题可以在里边问。
希望大家深刻理解红外解码的过程,红外编码NEC协议信息大家可以访问这里。
https://www.cnblogs.com/yulongchen/archive/2013/04/12/3017409.html