搜索
bottom↓
回复: 18

红外解码程序解码失败原因浅析

[复制链接]

出0入0汤圆

发表于 2016-11-3 16:53:17 | 显示全部楼层 |阅读模式
本帖最后由 rjx 于 2016-11-3 17:09 编辑

红外遥控接受解码失败的原因很多,但其中最重要的原因当属延时函数或定时器的延时时间误差太大。
很多人的遥控接受解码的程序大多是在网上找的或书上抄的。
我们知道,红外解码的关键是根据接收到的脉冲的宽度来区别0或1,这个宽度的判断,就是靠延时函数或定时器来完成的。因此延时函数的时间是否准确,就成为解码能否成功的关键。
因此,我们在引用别人的代码时,首先要注意原来的单片机用的是多大的晶振,如果原来的晶振是8M,你用4M,那延时函数的延时时间肯定不对了。
即使你用和原来的晶振一样,也未必能行,因为如果是单片机的型号不一样,那时间也会相差很多,因为AVR,PIC,51系列的机器周期和代码执行周期都是不一样的。
所以,在移植红外解码的程序时,自己要先编写一个简单的程序,在AVR Studio下跑一下,测试一下你使用的延时函数或定时器的时间,如有误差,一定要调整延时函数或定时器的参数,使之符合要求。
下面是一段解码程序,是一本书上抄来的,使用时发现不能解码,后仔细调整延时函数的参数(原值是155,后改为212,时钟是8M),最终才顺利成功。
最好的办法是利用单片机自带的定时器来判断0和1,定时器采用比较输出中断(自装),这是很准确的。

/********us延时程序,延时时间0.14ms(140us*),红外解码专用*******/
void Delayus(uint x)
{
        char i, j;
        for(i=0;i<x;i++)
      {for(j=0;j<210;j++);}
}


interrupt [EXT_INT0] void ext_int0_isr(void)  //红外解码,用INT0.
{
   uchar i,j,sum=0;  
   GICR |=(0<<INT0);
   Delayus(20);                                //延时20*0.14MS  ,2.8MS
   for(i = 0;i < 14;i++)
      {
         Delayus(1);
         if(IRIN)                            //9MS内有高电平,则判断为干扰,退出处理程序
         {
           GICR |=(1<<INT0);
                  return;                                    //返回
         }
      }
      
   while(!(IRIN));                          //等待9ms低电平过去
   
   for(i=0;i<4;i++)           
    {
      for(j=0;j<8;j++)         
      {
         while(IRIN);                         //等待4.5ms高电平过去
         while(!(IRIN));                 //等待变高电平
         while(IRIN)                          //计算高电平时间
          {
             Delayus(1);                        //延时0.14ms
             sum++;                                    //对0.14ms延时时间进行计数
             if(sum >= 30)              //高电平时间过长,则退出处理程序
             {
               GICR |=(1<<INT0);
               return;
             }   
          }
        
       IR_buf = IR_buf >> 1;      //接受一位数据
      
       if(sum >= 6)
          {IR_buf = IR_buf | 0x80;}   //若计数值大于6(高电平时间大于0.56),则为数据1
         
       sum = 0;                  //若计数小于6,数据最高位补"0",说明收到的是"0",同时计时清零
     }
   }
  if(IR_buf[2]!=~IR_buf[3])                //将键数据反码取反后与键数据码码比较,若不等,表示接收数据错误,放弃
    { GICR |=(1<<INT0);    return;}      
           
  HW_Status=1;

}  


用于测试延时函数时间的代码:在测试时,在相关位置设置几个断点,按F10执行,看时间是否正确。

hip type               : ATmega8L
Program type            : Application
AVR Core Clock frequency: 8.000000 MHz
Memory model            : Small
External RAM size       : 0
Data Stack size         : 256
*****************************************************/

#include <mega8.h>
typedef  unsigned int  uint;
typedef  unsigned char uchar;
// Declare your global variables here

void Delayus_2(uint x)
{
        char i,j;   
        for(i=0;i<x;i++)
      { for(j=0;j<212;j++); }   
}

void main(void)
{

   PORTD=0x00;
   DDRD=0x01;

   while (1)
      {   PORTD=0x00;
          Delayus_2(1);
          PORTD=0x01;
          Delayus_2(20);
          PORTD=0x00;

      }
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2016-11-3 16:58:20 | 显示全部楼层
不是都在中断里计时么,难道用delay?

出0入0汤圆

发表于 2016-11-3 17:05:42 | 显示全部楼层
红外这种慢悠悠的东西,,,中断里面加定时器,绝不延误。超级简单。。。。

出0入0汤圆

发表于 2016-11-3 17:19:26 | 显示全部楼层
都用外部中断了还傻等,上升沿下降沿配合定时器解决

出0入0汤圆

发表于 2016-11-3 17:22:19 | 显示全部楼层
在中断里面读取当前的系统时间,利用两个中断的时间差就可以得到脉冲的宽度

出140入158汤圆

发表于 2016-11-3 18:51:32 | 显示全部楼层
太搞笑了,为解码而解码,傻等不商量

出0入10汤圆

发表于 2016-11-3 19:44:41 来自手机 | 显示全部楼层
竟然死等解码,我记得我以前是用定时器测脉宽时间,就玩过一次红外

出0入0汤圆

发表于 2016-11-3 19:45:07 | 显示全部楼层

现在知道红外解码程序还是挺花费时间的。楼主,请用这个BC7210A芯片5元来解决吧!

BC7210A 是一款低成本通用红外遥控解码芯片。
http://www.amobbs.com/thread-5661384-1-1.html

出0入0汤圆

 楼主| 发表于 2016-11-3 20:13:04 | 显示全部楼层
本帖最后由 rjx 于 2016-11-3 20:14 编辑

楼上的各位都是大佬,说话轻飘飘的,来点实际的,把你们自己的代码贴出来看看!这种讽刺挖苦有意思吗?是显示你的水平高吗?

出0入0汤圆

发表于 2016-11-3 20:28:04 | 显示全部楼层
建议楼主在本坛找一下,例子很多,你就知道楼上各位怎么会这样说了

出0入0汤圆

 楼主| 发表于 2016-11-3 20:32:12 | 显示全部楼层
本帖最后由 rjx 于 2016-11-3 20:38 编辑

我发这个帖子的本意是什么?难道各位不理解?红外解码的方法很多,有用延时函数的,有用定时器的,有用捕捉的。。。。。难道我不知道?
不管用什么方式,延时函数也好,定时器也好,捕捉也好,难道时间不准,时序不对可以解码吗???!!!
真是笑话。你以为再高明的代码,抄过来就能用?而不用管原来的时钟,各个参数?

出0入0汤圆

发表于 2016-11-3 21:39:45 来自手机 | 显示全部楼层
现在的单片机都带pwm捕获功能,实现红外软解玛最爽

出0入0汤圆

发表于 2016-11-3 21:52:07 | 显示全部楼层
中断函数里处理这么多,还延时,大忌

出0入0汤圆

 楼主| 发表于 2016-11-4 08:48:00 | 显示全部楼层
本帖最后由 rjx 于 2016-11-4 09:00 编辑

再发一段用M8 的Timer 2的CTC方式的红外解码函数,主频用4M,8分频,,采用比较中断来产生256US的时间间隔,这个是从某书上51单片机移植过来的,开始时并不能正确解码,必须修改几个参数才能正确解码(如红字的值)。已在硬件上验证通过。

// Timer/Counter 2 initialization
   // Clock source: System Clock
   // Clock value: 5 kHz
   TCCR2=0x0A;
   TCNT2=0x00;
   OCR2=0X7F;


// Timer 2 overflow interrupt service routine
interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
   irtime++;        

}

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
   static uchar i;
   static bit startflag;
   
        if(startflag)
          {
           if((irtime>=33)&&(irtime<60))
              i=0;      
            
             irdata=irtime;
             irtime=0;
             i++;
            
             if(i==33)
              {
                  irok=1;
                  i=0;                  
                  
              }
          }
        else
          {irtime=0;startflag=1;}  
      
}

//红外码值处理函数
void Ircordpro(void)
{
   uchar  i,j,k;
   uchar  cord, value=0;   

   k=1;
    for(i=0;i<4;i++)
      {
       for(j=1;j<=8;j++)
        {
          cord=irdata[k];
          if(cord>7)
            { value=(value|0x80);    }
          else
            {value=value;   }
          if(j<8)
            {value=value>>1;  }     
          k++;
        }
         IR_buf=value;
         value=0;
      }
  irpro_ok=1;
}

出0入0汤圆

发表于 2016-11-4 11:31:32 | 显示全部楼层
红外解码如果用死延时等待的方式,那意义就不大了,毕竟一个系统不可能只有红外解码。
下面这个帖子是我发的,定时器中断+外部中断配合做的:
http://www.amobbs.com/thread-5662098-1-1.html
如果有不足之处,请指正下,谢谢。

出0入0汤圆

发表于 2016-11-4 11:49:20 | 显示全部楼层
哪用得了这么高档的东西,只能定时器采样,没有捕捉功能.
#define _IR_TIME_BASE_  (80)                    ; 红外接收时基,单位:us
#define _IR_ZERO_       (560)                   ; 0信号
#define _IR_ONE_        (1680)                  ; 1信号
#define _IR_HOLD_       (2240)                  ; 键长按信号
#define _IR_ZERO_MIN_   ((_IR_ZERO_ - _IR_ZERO_*30/100)/_IR_TIME_BASE_)
#define _IR_ZERO_MAX_   ((_IR_ZERO_ + _IR_ZERO_*40/100)/_IR_TIME_BASE_)
#define _IR_ONE_MIN_    ((_IR_ONE_ - _IR_ONE_*30/100)/_IR_TIME_BASE_)
#define _IR_ONE_MAX_    ((_IR_ONE_ + _IR_ONE_*30/100)/_IR_TIME_BASE_)
#define _IR_HOLD_MIN_   ((_IR_HOLD_ - _IR_HOLD_*30/100)/_IR_TIME_BASE_)
#define _IR_HOLD_MAX_   ((_IR_HOLD_ + _IR_HOLD_*30/100)/_IR_TIME_BASE_)
#define _IR_MAX_HIGH_   (108000/_IR_TIME_BASE_) ; 信号超时时间
修改定时中断时间后,直接改定义就好了.

出0入0汤圆

发表于 2016-11-4 13:41:04 | 显示全部楼层
liufabing 发表于 2016-11-4 11:49
哪用得了这么高档的东西,只能定时器采样,没有捕捉功能.
#define _IR_TIME_BASE_  (80)                     ...

我也觉得你这最好

出200入2554汤圆

发表于 2016-11-5 17:38:49 | 显示全部楼层
按照个人理解,红外解码的事情相当于给主板扩展了一个无线的键盘。
从这个角度来看,如果能将红外解码的任务负担优化至与键盘扫描相近,就可以比较好的安排系统资源了。

当然这是后话,任务不重的话用阻塞式扫描也是可以的(中断用不着,功能类似 Windows API 串口访问: ReadFile)
顺手贴上自己的代码,省的只说不练(#1是阻塞式,#2是中断式)。

#1 阻塞式红外解码(硬件边沿检测+定时器)
  1. DWORD IrGetRaw(void)
  2. {
  3.         // 阻塞式查询遥控器按键,返回32位按键键码或0(空闲)
  4.         BYTE  tmp;
  5.         DWORD dwCmd;
  6.        
  7.         // 查询红外跳变沿,有则开始接收,无则立即返回0
  8.         // 采样过程使用TIMER0,遇到超时则返回0
  9.         // 超时时间 T= 1024*256*Tosc= 35.6ms @7.3728MHz
  10.        
  11.         // 跳变沿初始检查
  12.         if(IRIN_IDLE())
  13.         {
  14.                 return 0;
  15.         }
  16.        
  17.         // 已收到首个下降沿, 随后每个脉冲均代表一个bit
  18.         // 脉冲串的结束条件为:脉冲超时(35.6ms @7,3728M)
  19.         dwCmd= 0;
  20.         while(1)
  21.         {
  22.                 // 对当前脉冲进行计时
  23.                 TCCR2A= 0;
  24.                 TCCR2B= 0;
  25.                 TCNT2 = 0;                        // Nmax=256
  26.                 TIMSK2= 0;
  27.                 TIFR2 = BIT(TOV2);
  28.                 TCCR2B= 7;                        // P=1024
  29.                 IRIN_RESET();
  30.                 while(IRIN_IDLE())
  31.                 {
  32.                         if(TIFR2&BIT(TOV2))
  33.                         {
  34.                                 // 等待超时:关闭计时,复位并返回结果
  35.                                 TCCR2B= 0;
  36.                                 IRIN_RESET();
  37.                                 return dwCmd;
  38.                         }
  39.                 }
  40.                 TCCR2B= 0;
  41.                 tmp= (TIFR2&BIT(TOV2))? 255:TCNT2;
  42.                
  43.                 // 对计时结果进行处理
  44.                 if(tmp<12)
  45.                 {
  46.                         dwCmd= (dwCmd>>1);
  47.                 }
  48.                 else if(tmp<30)
  49.                 {
  50.                         dwCmd= (dwCmd>>1)|0x80000000;
  51.                 }               
  52.         }
  53.        
  54.         return 0;
  55. }
复制代码


#2 中断式红外解码(硬件中断+定时器)
  1. #pragma interrupt_handler OnIrCntCarry: iv_TIMER2_COMP
  2. void OnIrCntCarry()
  3. {
  4.         // 红外计时定时器:200us @ 14.7456MHz
  5.         gIrdaTimCnt++;
  6.        
  7.         // S=10ms, 0/1=1ms/2ms, E=40ms
  8.         if(gIrdaTimCnt>=80)
  9.         {
  10.                 // 红外接收超时(>=16ms)
  11.                 // 认为数据包接收完成
  12.                 TCCR2= 0;
  13.                 TCNT2= 0;
  14.                 gIrdaTimCnt= 0;
  15.                 gIrdaRxFlag= 1;
  16.         }
  17. }

  18. #pragma interrupt_handler OnIrdaPulse: iv_INT0
  19. void OnIrdaPulse()
  20. {
  21.         // 收到外部红外脉冲
  22.         if( gIrdaRxFlag )
  23.         {
  24.                 TCCR2= 0;
  25.                 TCNT2= 0;
  26.                 gIrdaTimCnt= 0;
  27.         }
  28.         else if( TCCR2==0 )
  29.         {
  30.                 // 接收到首个红外脉冲
  31.                 TCCR2= 0;
  32.                 TCNT2= 0;
  33.                 gIrdaTimCnt= 0;
  34.                 gIrdaBitCnt= 0;
  35.                 TIFR = BIT(OCF2);        // 清除多余标志位
  36.                 TCCR2= BIT(WGM21)|BIT(CS21)|BIT(CS20);
  37.         }
  38.         else
  39.         {
  40.                 // 正在接受脉冲比特流
  41.                 TCCR2= 0;
  42.                
  43.                 // 红外物理层解码
  44.                 // S=10ms, 0/1=1ms/2ms, E=40ms
  45.                 if(gIrdaTimCnt<8)
  46.                 {
  47.                         // 收到0
  48.                         gIrdaBitCnt++;
  49.                         gIrdaRxData.dat>>=1;
  50.                 }
  51.                 else if(gIrdaTimCnt<20)
  52.                 {
  53.                         // 收到1
  54.                         gIrdaBitCnt++;
  55.                         gIrdaRxData.dat>>=1;
  56.                         gIrdaRxData.inf.cmdh|=0x80;                // 仅限LE处理器
  57.                 }
  58.                
  59.                 // 准备接收下一位
  60.                 TCNT2= 0;
  61.                 gIrdaTimCnt= 0;
  62.                 TIFR = BIT(OCF2);        // 清除多余标志位
  63.                 TCCR2= BIT(WGM21)|BIT(CS21)|BIT(CS20);
  64.         }
  65. }
复制代码


出0入0汤圆

发表于 2016-12-26 11:28:58 | 显示全部楼层
最近在弄RC-6解码,资源比较少,学习下
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 13:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表