tjiang 发表于 2014-9-13 09:28:48

ATmega 8 ICP 解码NEC

利用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 uCharcount=0; //32位数据计数
volatile uChar state=0; //状态机
volatile uChar times=0;
volatile uChar IRCOM; //保存接收的遥控数据
volatile uLongNec_ircom=0;
volatile uCharADDRESS=0;
volatile uCharIRCODE=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 =( Nec_ircom&0xff);//地址
                                  IRCOM = ((Nec_ircom>>8)&0xff); //地址反
                                  IRCOM = (Nec_ircom>>16)&0xff;//数据
                                  IRCOM = (Nec_ircom>>24)&0xff;//数据反
                  
                                  IRCOM = ~IRCOM;
                               
                                  if(IRCOM==IRCOM)
                                  {   
                                  LcdClear(1);
                                        LcdClear(2);
                                        LcdWcom(0x80);
                                       printf("addr:%2X code:%2X",IRCOM,IRCOM);
                                       
                                  }
                                  else
                                   {   
                                     LcdWcom(0xc0);
                                           printf("Decoding error !");
                                        }
                  
                                  ADDRESS = IRCOM;
                                  IRCODE = IRCOM;
                             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();
}


askme 发表于 2014-9-13 09:33:05

好像是AVR GCC的,时间可以直接读取的,也可以不用分的, stamp=ICR1,虽然说要先读低位先,看看反汇编

tjiang 发表于 2014-9-13 09:41:34

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

是的,可以直接读取

askme 发表于 2014-9-13 09:53:08

tjiang 发表于 2014-9-13 09:41
是的,可以直接读取

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

tjiang 发表于 2014-9-13 10:02:19

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

溢出修正怎么设置?

黄晨0410 发表于 2014-9-13 10:25:44

学习一下下

amigenius 发表于 2014-9-13 12:30:02

用捕获这么奢侈,整个定时器都给您霸占了。其实用定时中断查询就好了,噪声容限更高,定时器还能作其它用途。

tjiang 发表于 2014-9-13 15:53:21

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

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

amigenius 发表于 2014-9-15 12:35:43

tjiang 发表于 2014-9-13 15:53
也可以用定时器,在资源足够的情况下,用ICP捕捉更容易编写

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

amigenius 发表于 2014-9-15 12:45:54

用定时器查询,十多行代码就能解决了,孰简单?

tjiang 发表于 2014-9-15 17:00:26

amigenius 发表于 2014-9-15 12:45
用定时器查询,十多行代码就能解决了,孰简单?

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

wshg 发表于 2014-9-15 18:04:41

对啊,对啊,分享一下啊

amigenius 发表于 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;                //接收到的数据
uint8PulseWidth;                //捕捉到的脉冲宽度
#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 uint8BitCt=0;        //                //接收位计数器
        static uint8tmpByte=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=tmpByte;}
                if (BitCt>=(IR_BUF_LEN*8)){BitCt=0;IR_Recv_int=1;IR_Recv_Step=0;}
        }
}

tjiang 发表于 2014-9-16 09:31:04

amigenius 发表于 2014-9-15 19:38
#define        LeadCodeMin                                70               
#define        LeadCodeMax                                130               
#define        OneCodeMin                                12               


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

cs128815 发表于 2014-9-16 09:40:21

不错 收藏了

mk_avatar 发表于 2014-9-22 12:56:14

mark!!!!!!
页: [1]
查看完整版本: ATmega 8 ICP 解码NEC