[PIC®/AVR®/dsPIC®产品]

求教:红外遥控长按键的处理

[复制链接]
2388|17
手机看帖
扫描二维码
随时随地手机跟帖
rjx|  楼主 | 2019-3-31 11:14 | 显示全部楼层 |阅读模式
下面的代码单次按可以,长按键时要判断连发码。搞了几天也搞不出来。诚心求教

uchar IR_buf[4]={0x00,0x00,0x00,0x00};  //IR_buf[0]、IR_buf[1]为用户码低位、用户码高位接收缓冲区
                                                             // IR_buf[2]、IR_buf[3]为键数据码和键数据码反码接收缓冲区

/********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)
{
   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[i] = IR_buf[i] >> 1;      //接受一位数据

       if(sum >= 6)
          {IR_buf[i] = IR_buf[i] | 0x80;}   //若计数值大于6(高电平时间大于0.56),则为数据1

       sum = 0;                  //若计数小于6,数据最高位补"0",说明收到的是"0",同时计时清零
     }
   }
  if(IR_buf[2]!=~IR_buf[3])                //将键数据反码取反后与键数据码码比较,若不等,表示接收数据错误,放弃
    { GICR |=(1<<INT0);    return;}      
           
  HW_Status=1;
}  


void RR()    //遥控器键控
{
        if(HW_Status==1)
     {   static uchar m;
                 if((IR_buf[2]==0xfb)||(IR_buf[2]==e))              //音量加
                        {
                                if( VOLUME_1<98)
                                        {++ VOLUME_1;}
                                else if( VOLUME_1>=98)
                                        { VOLUME_1=98;}                           
                        }
                if((IR_buf[2]==0xfa)||(IR_buf[2]==b))                                        //音量减
                        {
                                if( VOLUME_1>0)
                                        {-- VOLUME_1;}                         
                       
                        }

}

使用特权

评论回复
捉虫天师| | 2019-3-31 21:10 | 显示全部楼层
按键这边你要能区分出长按键和非长按键。
比如按下某个键发送某个码率,如果你一直按着不松手,这个时候这边就计时器计时,当大于多少时候认定为长按键,这个时候发送一个长按键确认字符。

使用特权

评论回复
捉虫天师| | 2019-3-31 21:10 | 显示全部楼层
接收端的处理是:接收到一个长按键确认符,就结合之前接收到的那个字判断具体功能。

使用特权

评论回复
diy1997| | 2019-4-1 10:09 | 显示全部楼层
红外长按有重复发射的,有发射重复码的。

使用特权

评论回复
rjx|  楼主 | 2019-4-1 10:17 | 显示全部楼层
我也试过加入连发码的判断,但不成功,楼上能详细指导一下吗?

使用特权

评论回复
WALX53| | 2019-4-1 12:33 | 显示全部楼层
可以参考下我写的,任意IO解码,占用一个定时器。移植过STC,AVR,STM8,N76E003,HC89S003等,非常方便简单。
贴一下关键代码,楼主可参考:
#if USE_IR_DECODE
uchar volatile  Irprot_LastState = 0; // 记录IR端口状态
uchar volatile  codeCnt = 0;          // 数据码位计数
uchar volatile  irTime = 0;           // 码时间,用于时间计时
uchar volatile  IR_data[4];           // 接收数据缓存
uchar volatile  IR_REPEAT_DELAY = 0;

#define IR_H        P0_1 = 1
#define IR_L        P0_1 = 0

#define IR_IO_Init  SET_P01(GPIO_IN_PH_NO_SMT);
#define GET_IR_IO   (P0_1)

#define IR_DELAY    3  // 重复码延迟个数
#endif

// 初始化定时器T3。488us中断一次
void IR_decode_init(void)
{
    #if USE_IR_DECODE
    IR_IO_Init;                         // 初始IR接收IO
    #endif
    TH3 = (65536-IR_intrpt_Num)/256;
    TL3 = (65536-IR_intrpt_Num)%256;

    T3CON = BIT2;                       // Start T3 count
    ET3 = 1;                            // enable Timer3 interrupt
}

void Timer3_ISR (void) interrupt T3_VECTOR
{
#if USE_IR_DECODE
//=================================================================================
    if(irTime<250) irTime++;                             // ir解码后码值存放时间约97.6ms, 200*488us
    else { codeCnt = 0xff; IR_REPEAT_DELAY = 0; }   

    if(GET_IR_IO)   Irprot_LastState = 1;                // 记录IO状态
    else if(Irprot_LastState)                            // 有下降沿   
    {
        Irprot_LastState = 0;                             // 记录IO状态
        if(irTime<6)                                      // 小于6*488us=2.684ms的间隔才进行处理
        {   codeCnt++;  codeCnt &= 0x1f;
        #if IR_DECODE_MSB_MODE
            IR_data[codeCnt>>3] <<= 1;
            if( irTime&BIT2 )   {IR_data[codeCnt>>3]++; }     // 大于3*488us=1.464ms的间隔为数据1
        #else
            IR_data[codeCnt>>3] >>= 1;
            if( irTime&BIT2 )   {IR_data[codeCnt>>3]|=0x80;}
        #endif  
        }
        else if( (irTime<28)&&(codeCnt==0x3f))            // 重复码间隔时间28*488=13.664 > 11.25ms
        {
            if( IR_REPEAT_DELAY>IR_DELAY ) { codeCnt = 0x7f;}
            else IR_REPEAT_DELAY++;
        }
        irTime = 0;                                       // 下降沿处理完成,将时间清0
    }
#endif
}


void main(void)
{
     System_init();              // 系统初始化
    UART1_init();      
    IR_decode_init();
      
    while(1)
    {
         if(IR_Receive_OK)
        {   IR_RST;
            UART_Put_HEX(IR_data[2]);
        }
        if(IR_Det_Repeat)   // 检测到重复按键,重复按键值存于变量IR_data【2】
        {
            UART_Put_Str("IR repeat\r\n");
            IR_RST;
        }
      
    }
}

使用特权

评论回复
WALX53| | 2019-4-1 12:35 | 显示全部楼层
补上几个宏:

#define IR_Receive_OK  (codeCnt==0x1F)      // IR解码完成一个数据
#define IR_Det_Repeat  (codeCnt==0x7f)      // IR接收到重复码
#define IR_FREE        (codeCnt==0xff)      // IR空闲,没有接收到任何数据
#define IR_RST          codeCnt =0x3f

使用特权

评论回复
diy1997| | 2019-4-1 18:32 | 显示全部楼层
rjx 发表于 2019-4-1 10:17
我也试过加入连发码的判断,但不成功,楼上能详细指导一下吗?

我是用定时器和外中断实现接收解码的。

重复码是9MS的低电平,2.25MS的高电平。

而引导码是9MS的低电平,4.5MS的高电平。

所以我是在引导码那里做判定的。


使用特权

评论回复
oxygenzz| | 2019-4-2 15:35 | 显示全部楼层
楼主是使用哪一款接收模块?

可以试下SMT - 信号测量定时器这个外设来对接收数据进行解码。
这款外设可以测量脉冲宽度,周期,占空比等信息。可以在CPU休眠状态下工作,有利于系统低功耗设计。

PIC16F18446 上就有这一外设,现在有活动,楼主可以去申请一款评估板来试一下。
https://bbs.21ic.com/icview-2683188-1-1.html

使用特权

评论回复
rjx|  楼主 | 2019-4-4 19:09 | 显示全部楼层
diy1997 发表于 2019-4-1 18:32
我是用定时器和外中断实现接收解码的。

重复码是9MS的低电平,2.25MS的高电平。

能把相关的代码发来参考一下吗?谢谢!

使用特权

评论回复
diy1997| | 2019-4-5 13:02 | 显示全部楼层
rjx 发表于 2019-4-4 19:09
能把相关的代码发来参考一下吗?谢谢!

这样的代码网上一大把。

我的重复码是在引导码那里处理的,

所以只要收到低9高2.25就会执行重复上次成功收到的编码。

一旦长按时没成功收到编码数据(被其它红外设备干扰或处理机制的问题),

也是会响应后面的重复码的,但重复执行的却是上一次成功收到的编码。


if (Receive ==0)//低电平
        {
                Count++;        //计数
               
                if (Stage ==1)//阶段:判断是否引导码/重复码高电平
                {
                        if ((Count >24 && Count < 28) || (Count >11 && Count < 15))
                        {

                                if (Count >24 && Count < 28)
                                {
                                        Stage        =2;//是码头高电平,进入解码阶段
                                        Count =0;
                                        RepeatCount =0;//重复码计数清零
                                }

                                if (Count >11 && Count < 15)//是重复码高电平
                                {
                       

                                        RepeatCount ++;

                                        if (RepeatCount >6)//重复执行间隔时间
                                        {
                                                RepeatCount =0;
                                               
                                                CommandToUpdate =1;
       
                                                       
                                    }

                                       
                                }
                        }
                        else
                        {
                                returnStatus ();
                        }
                }


                if (Stage ==3)//阶段:判断是否数据码高电平
                {
                        if (Count >1 && Count < 5)
                        {
                                State_Data [DATA_Length] <<=1;//收到1位数据0
                                Stage        =2;
                               
                                State_Count ++;

               
                        }
                       

                        if (Count >7 && Count < 13)
                        {
                                State_Data [DATA_Length]=(State_Data [DATA_Length]<<1) | 0x01;//收到1位数据1
                                Stage        =2;
                               

                                State_Count ++;

                       
                        }

                       

                        if (Stage ==3)
                        {
               
                                returnStatus ();
                        }

                        Count =0;

                        if (State_Count >7)//完成一个字节数据接收
                        {
                                State_Count =0;
                               
                                DATA_Length ++;

                                if (DATA_Length >3)//收到4个字节数据
                                {
                       

                                       
                                               
                                               

                                                CommandToUpdate =1;

                                               

                                               
                                        }

                                       
                                       
                                }
                               
                        }
                       
                       
                }
        }

        if (Receive==1)//高电平       
        {
                Count++;        //计数

                if (Stage ==0)//阶段:判断是否引导码低电平
                {
                        if (Count >49 && Count < 56)
                        {
                                Stage        =1;//是码头低电平,进入引导码高电平阶段
                                Count =0;
                        }
                        else
                        {
               
                                returnStatus ();
                        }
                }

                if (Stage ==2)//阶段:判断是否数据码低电平
                {
                        if (Count >1 && Count < 5)
                        {
                                Stage        =3;
                                Count =0;
                        }
                        else
                        {
                       
                                returnStatus ();
                        }
                }

        }

使用特权

评论回复
dongnanxibei| | 2019-4-10 15:20 | 显示全部楼层
这个两种方式,一个是发送端确认长按键,一个是接收端确认

使用特权

评论回复
susceptibility| | 2019-4-10 16:30 | 显示全部楼层
红外用的比较少,学习下

使用特权

评论回复
rjx|  楼主 | 2019-4-10 21:02 | 显示全部楼层
11楼 diy1997, 感谢提供的参考代码,这几天正在研究,有几个问题请教一下:
1、计数是用的定时器吗?
2、能把几个重要的变量的定义注释一下吗/
3、能把函数 returnStatus ();的具体内容贴出来吗?
非常感谢了!

使用特权

评论回复
diy1997| | 2019-4-11 15:02 | 显示全部楼层
rjx 发表于 2019-4-10 21:02
11楼 diy1997, 感谢提供的参考代码,这几天正在研究,有几个问题请教一下:
1、计数是用的定时器吗?
2、能 ...

1,是用定时器,具体数据根据定时时间修改。

2,都注释了。

uchar Count;//接收电平时间计数
uchar Stage;//接收阶段
uchar State_Data[9]; //接收数据寄存器
uchar DATA_Length;//数据长度
uchar State_Count;//接收位计数


uchar FaultCodes;//故障代码
uchar FaultPlaces;//故障位置
uchar FaultData;//故障数据

bit CommandToUpdate;//指令更新标志

uchar RepeatCount;//重复码计数


3,void returnStatus ()
{
       

        EX0        =1;
        TR0        =0;

        return;
}

使用特权

评论回复
rjx|  楼主 | 2019-4-11 15:48 | 显示全部楼层
非常感谢diy1997不厌其烦的解答,不好意思,再问:你在11楼里贴出的代码,是中断里的一段吧?能把这个中断函数全部贴出来吗?我是个菜鸟,望能得到帮助,先谢谢了!

使用特权

评论回复
diy1997| | 2019-4-11 21:23 | 显示全部楼层
兄弟,你还没搞好吗?我的是这样的:

外中断下降沿触发,进入外中断后停止响应外中断,

把那几个变量清零后开启定时器。

定时中断里根据高低电平时间计数来接收。

除了定时时间和地址码判断,定时中断里就那些了。

使用特权

评论回复
zhuotuzi| | 2019-4-12 00:05 | 显示全部楼层
接收端可以判断。对接收到的码计数,如果在小于多少间隔又收到某个码进行统计。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

rjx

3

主题

10

帖子

0

粉丝