外部中断+定时器的高性能红外解码分析STM8/32红外解码

时间:2021-06-21 23:31:20

红外解码对于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