搜索
bottom↓
回复: 15

ATmega 8 ICP 解码NEC

[复制链接]

出0入0汤圆

发表于 2014-9-13 09:28:48 | 显示全部楼层 |阅读模式
利用ATmega8的ICP解码遥控器,精准快速,希望对初学者有帮助。
以下是部分代码:
#define IR_state_0 0 //无遥控信号
#define IR_state_1 1 //引导码前段
#define IR_state_2 2 //引导码后段
#define IR_state_3 3 //数据码前段
#define IR_state_4 4 //数据码后段

volatile uInt stamp=0; //暂存时间印记
volatile uChar  count=0; //32位数据计数
volatile uChar state=0; //状态机
volatile uChar times=0;
volatile uChar IRCOM[4]; //保存接收的遥控数据
volatile uLong  Nec_ircom=0;
volatile uChar  ADDRESS=0;
volatile uChar  IRCODE=0;
volatile uChar err_flag=0;

//************************端口初始化*********************************

void STF_INIT(void)
{
                PORTB=0X00;
        PORTB|=(1<<Freq_ICP);
        DDRB|=(1<<MOSI )|(1<<SCK)|(1<<DISPPL);
        DDRB&=~(1<<Freq_ICP);
       
        PORTD=0x00;
        DDRD|=(1<<LcdRs)|(1<<LcdEn)|(1<<RXD)|(1<<TXD);
                DDRD &= ~((1<<KEY_sta)|(1<<IRIN));

        PORTC=0x00;
        DDRC=0xff;       
}

//******************test*************************
void Test (void)
{
                 STF_INIT();
        Timer1_INIT();
        Reset_IR();
        sei();
        while(1);
}

/**************************************
*
* NEC红外协议解码
*
**************************************/

void NEC_Decode(void)
{
     switch(state)
        {
          case IR_state_0:        //第一次中断,数据丢弃(下降沿中断)
                   state = IR_state_1;//设置为状态1
                   break;
       
          case IR_state_1: //接收引导码前半段(9ms)(上升沿中断)
                   if ((stamp >8500)&&(stamp<9500))
                       state = IR_state_2; //设置为状态2
                   else
                       Reset_IR();//干扰信号,复位接收状态
                   break;
                  
           case IR_state_2:      //接收引导码后半段(4.5ms)(下降沿中断)
                   if ((stamp >4000)&&(stamp<5000))
                           state = IR_state_3; //设置为状态3
                   else
                            Reset_IR();//干扰信号,复位接收状态
                   break;
                  
        case IR_state_3://过滤掉数据码前半部分(560us)(上升沿中断)
                      state = IR_state_4; //设置为状态4
                      break;
                          
        case IR_state_4://接收数据后半段(0=565us,1=1690us)(下降沿中断)
                     count++;
                     if(count<=32)//接收32位数据(低位在前)
                     {
                        state = IR_state_3;      //回到状态3
                        Nec_ircom=Nec_ircom>>1;
                            if(stamp >1600&&stamp<1800) //保存数据"1"
                             {
                                Nec_ircom|=0x80000000;
                              }
                     }
                    else
                     {
                  
                          IRCOM[3] =( Nec_ircom&0xff);//地址
                                  IRCOM[2] = ((Nec_ircom>>8)&0xff); //地址反
                                  IRCOM[1] = (Nec_ircom>>16)&0xff;//数据
                                  IRCOM[0] = (Nec_ircom>>24)&0xff;//数据反
                  
                                  IRCOM[0] = ~IRCOM[0];
                                 
                                  if(IRCOM[1]==IRCOM[0])
                                  {   
                                    LcdClear(1);
                                        LcdClear(2);
                                        LcdWcom(0x80);
                                         printf("addr:%2X code:%2X",IRCOM[3],IRCOM[1]);
                                         
                                  }
                                  else
                                   {   
                                       LcdWcom(0xc0);
                                           printf("Decoding error !");
                                        }
                  
                                  ADDRESS = IRCOM[3];
                                  IRCODE = IRCOM[1];
                               Reset_IR();   //完成接收,复位接收状态
                     }
                       break;
         }


}

/**************************************
*
* 复位状态设置
*
**************************************/
void Reset_IR (void)
{
    TCCR1B &=~( _BV(ICES1)); // 设置为下降沿触发
    state = IR_state_0; //复位为状态0
    count=0;
        err_flag=0;
}

/**************************************
*
* 初始化定时器1
*
**************************************/

void Timer1_INIT(void)
{
      TCCR1B=0x00;
     TCCR1A = 0x00;         //普通模式
   TCNT1H = 0x00;          //8MHz  
     TCNT1L = 0x00;
   TIMSK |=_BV(TICIE1);   //输入捕捉中断标志
   TIFR |= (1<<TOV1);    //清中断标志
   TCCR1B|= (1<<ICNC1)|((1<<CS11)); //开启输入捕捉口的噪声抑制器,8分频
   TCCR1B&=~(1<<ICES1); //下降沿触发
}


/**************************************
*
* 定时器1 输入捕捉信号中断
*
**************************************/

ISR(TIMER1_CAPT_vect)
{
        stamp= ICR1L;               //保存时间印记
        stamp+=(uInt)(ICR1H<<8);
                 TCNT1H = 0x00;          //8MHz  
                  TCNT1L = 0x00;                       //清零计数器
        TCCR1B ^= _BV(ICES1);          //切换捕获的触发方式
        NEC_Decode();
}


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

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

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2014-9-13 09:33:05 | 显示全部楼层
好像是AVR GCC的,时间可以直接读取的,也可以不用分的, stamp=ICR1,虽然说要先读低位先,看看反汇编

出0入0汤圆

 楼主| 发表于 2014-9-13 09:41:34 | 显示全部楼层
askme 发表于 2014-9-13 09:33
好像是AVR GCC的,时间可以直接读取的,也可以不用分的, stamp=ICR1,虽然说要先读低位先,看看反汇编 ...

是的,可以直接读取

出0入0汤圆

发表于 2014-9-13 09:53:08 | 显示全部楼层
tjiang 发表于 2014-9-13 09:41
是的,可以直接读取

还有捕捉到时间不用清零,加上溢出修正就可以了,这样定时器又可以当定时器用,而不是单单的为了解码一个功能

出0入0汤圆

 楼主| 发表于 2014-9-13 10:02:19 | 显示全部楼层
askme 发表于 2014-9-13 09:53
还有捕捉到时间不用清零,加上溢出修正就可以了,这样定时器又可以当定时器用,而不是单单的为了解码一个 ...

溢出修正怎么设置?

出0入0汤圆

发表于 2014-9-13 10:25:44 | 显示全部楼层
学习一下下

出140入158汤圆

发表于 2014-9-13 12:30:02 | 显示全部楼层
用捕获这么奢侈,整个定时器都给您霸占了。其实用定时中断查询就好了,噪声容限更高,定时器还能作其它用途。

出0入0汤圆

 楼主| 发表于 2014-9-13 15:53:21 | 显示全部楼层
amigenius 发表于 2014-9-13 12:30
用捕获这么奢侈,整个定时器都给您霸占了。其实用定时中断查询就好了,噪声容限更高,定时器还能作其它用途 ...

也可以用定时器,在资源足够的情况下,用ICP捕捉更容易编写

出140入158汤圆

发表于 2014-9-15 12:35:43 | 显示全部楼层
tjiang 发表于 2014-9-13 15:53
也可以用定时器,在资源足够的情况下,用ICP捕捉更容易编写

本姑娘,其实定时器查询更简单,连捕获寄存器设置都不用折腾了

出140入158汤圆

发表于 2014-9-15 12:45:54 | 显示全部楼层
用定时器查询,十多行代码就能解决了,孰简单?

出0入0汤圆

 楼主| 发表于 2014-9-15 17:00:26 | 显示全部楼层
amigenius 发表于 2014-9-15 12:45
用定时器查询,十多行代码就能解决了,孰简单?

那麻烦仁兄慷慨解囊,分享一下你写的代码。

出0入0汤圆

发表于 2014-9-15 18:04:41 | 显示全部楼层
对啊,对啊,分享一下啊

出140入158汤圆

发表于 2014-9-15 19:38:07 | 显示全部楼层
tjiang 发表于 2014-9-15 17:00
那麻烦仁兄慷慨解囊,分享一下你写的代码。

#define        LeadCodeMin                                70               
#define        LeadCodeMax                                130               
#define        OneCodeMin                                12               
#define        OneCodeMax                                25               
#define        ZeroCodeMin                                4               
#define        ZeroCodeMax                                12               

uint8 IR_RecvCt=0;                                // 捕捉数据暂存
uint8 IR_Recv_Step=0;                //接收状态
uint8 IR_Recv_int=0;                //接收到一次有效的遥控指令就置1
uint8 IR_RecvBuf[IR_BUF_LEN];                //接收到的数据
uint8  PulseWidth;                //捕捉到的脉冲宽度
#define IR_RecvCt_Srv()  {IR_RecvCt++;if(IR_RecvCt>=250){IR_RecvCt=0;IR_Recv_Step=0;}}  
void IR_Recv_Srv(void){                //遥控解码,定时中断
        uint8 i;                                       
        static uint8  BitCt=0;        //                //接收位计数器
        static uint8  tmpByte=0;                //正在接收的字节
        static uint8 preIRin=1;
        uint8 IRin;
        uint8 IRin_falling=0;
        IR_RecvCt_Srv();
        IRin=IR_INPUT();                //定义您的输入
        if(IRin==0 && preIRin) IRin_falling=1;
        preIRin=IRin;
        if(IRin_falling){
                PulseWidth=IR_RecvCt;IR_RecvCt=0;
                if (IR_Recv_Step==0){BitCt=0;IR_Recv_Step=1;return;}
                if(IR_Recv_Step==1){
                        BitCt=0;
                        if ((PulseWidth<=LeadCodeMax)&&(PulseWidth>=LeadCodeMin)){IR_Recv_Step=2;}
                        return;
                }
                if (IR_Recv_int!=0){IR_Recv_Step=0;return;} //如果主循环还未响应遥控信号,则不接收新的遥控信号
                if(IR_Recv_Step!=2){IR_Recv_Step=0;}
                if (PulseWidth<ZeroCodeMin){IR_Recv_Step=0;return;}       
                if (PulseWidth>=OneCodeMax){IR_Recv_Step=0;return;}
                tmpByte>>=1;
                if (PulseWidth>=OneCodeMin){tmpByte|=0x80;}
                BitCt++;i=BitCt>>3;
                if (((BitCt&0x07)==0)&&(i>0)){IR_RecvBuf[i-1]=tmpByte;}
                if (BitCt>=(IR_BUF_LEN*8)){BitCt=0;IR_Recv_int=1;IR_Recv_Step=0;}
        }
}

出0入0汤圆

 楼主| 发表于 2014-9-16 09:31:04 | 显示全部楼层
amigenius 发表于 2014-9-15 19:38
#define        LeadCodeMin                                70               
#define        LeadCodeMax                                130               
#define        OneCodeMin                                12               

定时器中断未设定,代码说明不规范,仁兄,还需请您再调整一下代码,可否?

出0入0汤圆

发表于 2014-9-16 09:40:21 | 显示全部楼层
不错 收藏了

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-7-23 19:01

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

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