NEC 遥控器 源码处理流程分析

来源:互联网 发布:网络超市加盟 编辑:程序博客网 时间:2024/05/21 09:49

NEC 协议的遥控器

代码位置在 \kernel\drivers\input\remotectl\rkxx_remotectl.c


关于NEC协议的东西主要来源于网上,出处太多,也就不一一列举了,感谢提供资源的各位网友。

根据我个人的理解,NEC遥控器工作流程应该是这样的:

遥控器端:

当遥控器按下一个键之后,会形成载波发送到接收端,载波信息依次是: 

起始码

用户码              区别不同厂商的遥控器相互干扰

键值                  判断用户是按了哪一个键

结束码

另外需要说明的是,如果遥控器在压下一个键之后不放手,后面就不会发送同一个键被按下的载波(起始码,用户码......)了,而是会发一个 REPEAT的载波信号


当收到一个bit之后如何判断是0还是1,还是哪个状态主要是通过类似:if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX)) 代码判断,这个是怎么算出来的,我也不清楚。

接收机端:

依次解出载波,并识别出需要的信息


在接收端,一个 bit 会产生一个中断,这个 bit  所持续的时间(period)会成为判断这是哪一段数据(起始码?用户码?..具体原理我也不清楚),可参考

\kernel\arch\arm\mach-rk30\include\mach\remotectl.h

#ifndef __RKXX_REMOTECTL_H__#define __RKXX_REMOTECTL_H__#include <linux/input.h>/**********************************************************************                            宏定义                                *********************************************************************/#define TIME_BIT0_MIN  625  /*Bit0  1.125ms*/#define TIME_BIT0_MAX  1625#define TIME_BIT1_MIN  1650  /*Bit1  2.25ms*/#define TIME_BIT1_MAX  2650#define TIME_PRE_MIN   13000   /*4500*/#define TIME_PRE_MAX   14000   /*5500*/           /*PreLoad 4.5+0.56 = 5.06ms*/#define TIME_RPT_MIN   98100   /*101000*/#define TIME_RPT_MAX   98300   /*103000*/         /*Repeat  105-2.81=102.19ms*/  //110-9-2.25-0.56=98.19ms#define TIME_SEQ_MIN   11200   /*2650*/#define TIME_SEQ_MAX   11300   /*3000*/           /*sequence  2.25+0.56=2.81ms*/ //11.25ms#define TIME_SEQ1_MIN   10000   /*2650*/#define TIME_SEQ1_MAX   12000   /*3000*/           /*sequence  2.25+0.56=2.81ms*/ //11.25ms#define TIME_SEQ2_MIN   40000   /*101000*/#define TIME_SEQ2_MAX   47000   /*103000*/         /*Repeat  105-2.81=102.19ms*/  //110-9-2.25-0.56=98.19ms/**********************************************************************                          结构定义                                *********************************************************************/typedef enum _RMC_STATE{    RMC_IDLE,    RMC_PRELOAD,    RMC_USERCODE,    RMC_GETDATA,    RMC_SEQUENCE}eRMC_STATE;struct RKxx_remotectl_platform_data {//struct rkxx_remotectl_button *buttons;int nbuttons;int rep;int gpio;int active_low;int timer;int wakeup;void (*set_iomux)(void);};#endif



这里我主要分析在 接收端 的 处理流程:


每当从遥控器接受到一个 bit 就产生一次中断,中断处理函数是:

static irqreturn_t remotectl_isr(int irq, void *dev_id){    struct rkxx_remotectl_drvdata *ddata =  (struct rkxx_remotectl_drvdata*)dev_id;    struct timeval  ts;struct timeval temp_time;long plustime = 0;do_gettimeofday(&ts);ddata->cur_time = ts;temp_time = ts;ddata->period = 0;{plustime = temp_time.tv_sec - ddata->pre_time.tv_sec;if(plustime >= 0 && plustime <= 1){if(plustime == 1){temp_time.tv_usec += 1000000;}ddata->period = temp_time.tv_usec - ddata->pre_time.tv_usec;}}ddata->pre_time = ddata->cur_time;    tasklet_hi_schedule(&ddata->remote_tasklet);     return IRQ_HANDLED;}
这个函数会计算出这个 bit 持续的时间,即 ddata->period 值,然后调度  ddata->remote_tasklet  对应的函数,这个函数在 模块初始化函数(如下)中声明

static int __devinit remotectl_probe(struct platform_device *pdev){    struct RKxx_remotectl_platform_data *pdata = pdev->dev.platform_data;    struct rkxx_remotectl_drvdata *ddata;    struct input_dev *input;    int i, j;    int irq;    int error = 0;    if(!pdata)         return -EINVAL;#ifdef CONFIG_RK30_KEYBOARD_LED_CTLrk29_keyboard_led_init();rk29_standby_led_init();#endif    ddata = kzalloc(sizeof(struct rkxx_remotectl_drvdata),GFP_KERNEL);    memset(ddata,0,sizeof(struct rkxx_remotectl_drvdata));    ddata->state = RMC_PRELOAD;    input = input_allocate_device();        if (!ddata || !input) {        error = -ENOMEM;        goto fail0;    }    platform_set_drvdata(pdev, ddata);    input->name = pdev->name;    input->phys = "gpio-keys/input0";    input->dev.parent = &pdev->dev;    input->id.bustype = BUS_HOST;    input->id.vendor = 0x0001;    input->id.product = 0x0001;    input->id.version = 0x0100;/* Enable auto repeat feature of Linux input subsystem */if (pdata->rep)__set_bit(EV_REP, input->evbit);    ddata->nbuttons = pdata->nbuttons;ddata->input = input;  wake_lock_init(&ddata->remotectl_wake_lock, WAKE_LOCK_SUSPEND, "rk29_remote");  if (pdata->set_iomux){  pdata->set_iomux();  }  error = gpio_request(pdata->gpio, "remotectl");if (error < 0) {printk("gpio-keys: failed to request GPIO %d,"" error %d\n", pdata->gpio, error);//goto fail1;}error = gpio_direction_input(pdata->gpio);if (error < 0) {pr_err("gpio-keys: failed to configure input"" direction for GPIO %d, error %d\n",pdata->gpio, error);gpio_free(pdata->gpio);//goto fail1;}    irq = gpio_to_irq(pdata->gpio);if (irq < 0) {error = irq;pr_err("gpio-keys: Unable to get irq number for GPIO %d, error %d\n",pdata->gpio, error);gpio_free(pdata->gpio);goto fail1;}error = request_irq(irq, remotectl_isr,IRQF_TRIGGER_FALLING , "remotectl", ddata);if (error) {pr_err("gpio-remotectl: Unable to claim irq %d; error %d\n", irq, error);gpio_free(pdata->gpio);goto fail1;}    setup_timer(&ddata->timer,remotectl_timer, (unsigned long)ddata);        tasklet_init(&ddata->remote_tasklet, remotectl_do_something, (unsigned long)ddata);        for (j=0;j<sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button);j++){     printk("remotectl probe j=0x%x\n",j);for (i = 0; i < remotectl_button[j].nbuttons; i++) {unsigned int type = EV_KEY;        input_set_capability(input, type, remotectl_button[j].key_table[i].keyCode);}  }error = input_register_device(input);if (error) {pr_err("gpio-keys: Unable to register input device, error: %d\n", error);goto fail2;}        input_set_capability(input, EV_KEY, KEY_WAKEUP);device_init_wakeup(&pdev->dev, 1);return 0;fail2:    pr_err("gpio-remotectl input_allocate_device fail\n");input_free_device(input);kfree(ddata);fail1:    pr_err("gpio-remotectl gpio irq request fail\n");    free_irq(gpio_to_irq(pdata->gpio), ddata);    del_timer_sync(&ddata->timer);    tasklet_kill(&ddata->remote_tasklet);     gpio_free(pdata->gpio);fail0:     pr_err("gpio-remotectl input_register_device fail\n");    platform_set_drvdata(pdev, NULL);return error;}

tasklet_init(&ddata->remote_tasklet, remotectl_do_something, (unsigned long)ddata); 就是这一行


也即就是,每从红外线遥控器获取到一个 bit  之后就调用一次  remotectl_do_something() ,代码如下:

static void remotectl_do_something(unsigned long  data){    struct rkxx_remotectl_drvdata *ddata = (struct rkxx_remotectl_drvdata *)data;mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(250));if((TIME_PRE_MIN < ddata->period) && (ddata->period < TIME_PRE_MAX)){if(ddata->state == RMC_SEQUENCE){remotectl_timer_reload(data);ddata->state = RMC_PRELOAD;}}    switch (ddata->state)    {        case RMC_IDLE:        {            ;        }        break;                case RMC_PRELOAD:        {            if ((TIME_PRE_MIN < ddata->period) && (ddata->period < TIME_PRE_MAX)){                                ddata->scanData = 0;                ddata->count = 0;                ddata->state = RMC_USERCODE;mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(120));            }else{                ddata->state = RMC_PRELOAD;            }            ddata->pre_time = ddata->cur_time;        }        break;                case RMC_USERCODE:        {            ddata->scanData <<= 1;            ddata->count ++;            if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX)){                ddata->scanData |= 0x01;            }            if (ddata->count == 0x10){//16 bit user code                if (remotectl_keybdNum_lookup(ddata)){                    ddata->state = RMC_GETDATA;                    ddata->scanData = 0;                    ddata->count = 0;                }else{                //user code error                    ddata->state = RMC_PRELOAD;                }            }        }        break;                case RMC_GETDATA:        {            ddata->count ++;            ddata->scanData <<= 1;                      if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX)){                ddata->scanData |= 0x01;            }            if (ddata->count == 0x10){                if ((ddata->scanData&0x0ff) == ((~ddata->scanData >> 8)&0x0ff)){                    if (remotectl_keycode_lookup(ddata)){                        ddata->press = 1;                         if (get_suspend_state()==0){                                input_event(ddata->input, EV_KEY, ddata->keycode, 1);                                input_sync(ddata->input);                            }else if ((get_suspend_state())&&(ddata->keycode==KEY_POWER)){                                input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);                                input_sync(ddata->input);                            }                        ddata->state = RMC_SEQUENCE;                    }else{                        ddata->state = RMC_PRELOAD;                    }                }else{                    ddata->state = RMC_PRELOAD;                }            }        }        break;                     case RMC_SEQUENCE:{            //printk( "S\n");                        if ((TIME_RPT_MIN < ddata->period) && (ddata->period < TIME_RPT_MAX)){                ;            }else if ((TIME_SEQ_MIN < ddata->period) && (ddata->period < TIME_SEQ_MAX)){            if (ddata->press == 1){                    ddata->press = 3;                }else if (ddata->press & 0x2){                    ddata->press = 2;                //input_event(ddata->input, EV_KEY, ddata->keycode, 2);            //input_sync(ddata->input);                }                mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(110));                //ddata->state = RMC_PRELOAD;            }        }        break;               default:            break;    } return;}
这个函数的处理流程,就是 完成一个状态到另一个状态的转换,比如初始化为  RMC_PRELOAD状态,当得到了正确的 起始码之后就进入了 RMC_USERCODE状态

犹如一个自动状态机:

RMC_PRELOAD--------------得到正确的起始码------------------------->>>>>>RMC_USERCODE

RMC_USERCODE-----------得到能识别的用户名---------------------->>>>>>RMC_GETDATA

RMC_GETDATA---------------得到能识别的键值------------------------->>>>>>RMC_SEQUECE

RMC_SEQUECE-------------得到正确的结束码-------------------------->>>>>>RMC_PRELOAD

其中,RMC_USERCODE,RMC_GETDATA,RMC_SEQUECE在识别出错的时候都返回到RMC_PRELOAD状态,结束解析。意思就是:比如,当收到的用户码不能识别,也即就是不是我们厂生产的遥控器在遥控我们的接收器,当然不能让它得到解析;又比如,用户码正确了,但是键值查不到,这可能是凑巧用户码一致但是确实不是这个遥控板,因为不同遥控板发送的键的扫描码可能是不一致的,当然也要结束本轮识别。

每当识别到了 键值 (即 RMC_GETDATA状态正确识别到了键值)就会向上层应用发送这个键值

                                input_event(ddata->input, EV_KEY, ddata->keycode, 1);
                                input_sync(ddata->input);

注意input_event函数的最后一个参数是 1 ,查资料得到,如果是 1  代表某个键按下去了,如果事件驱动器在收到input_event(...,1)之后的额定的时间内没有收到依然没有收到input_event(...,0)那么将产生连击事件。比如,系统在收到input_event(...,1)后如果200ms没收到input_event(...,0)就会认为键是压下去了,但还没弹起来,就是每隔Xms向上层应用发送 压下去的键的键值。

那么remotectl_do_something()又是如何实现连击的效果的呢?

首先,我们知道了每来一个电平我们就产生一次中断,中断处理程序会调用remotectl_do_something(),如果系统在收到一个 XX 键按下去之后再收到 REPEAT电平会做如处理:

        case RMC_SEQUENCE:{            if ((TIME_RPT_MIN < ddata->period) && (ddata->period < TIME_RPT_MAX)){                ;            }else if ((TIME_SEQ_MIN < ddata->period) && (ddata->period < TIME_SEQ_MAX)){            if (ddata->press == 1){                    ddata->press = 3;                }else if (ddata->press & 0x2){                    ddata->press = 2;                }                mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(110));            }        }
对,就是什么也不做,但是在收到这个REPEAT(TIME_SEQ_MIN < ddata->period) && (ddata->period < TIME_SEQ_MAX)电平的时候,remotectl_do_something()先是做了如下一件事,再去判断是否连击的,就是在它的前几行,代码是:

mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(250));

这句代码是什么意思呢?mod_timer()就是改变一个已经激活的定时器,重新设置它的超时时间。也就是说,如果收到REPEAT重复电平,但250ms之后没有收到任何电平了,250ms之后就会超时,超时就会执行注册这个定时器所对应的函数,这个函数在模块初始化函数remotectl_probe()中做了说明:

setup_timer(&ddata->timer,remotectl_timer, (unsigned long)ddata);

也即超时就会执行remotectl_timer(),该函数的代码是:

static void remotectl_timer(unsigned long _data){    struct rkxx_remotectl_drvdata *ddata =  (struct rkxx_remotectl_drvdata*)_data;    if(ddata->press != ddata->pre_press) {        ddata->pre_press = ddata->press = 0;        if (get_suspend_state()==0){//没有挂起            input_event(ddata->input, EV_KEY, ddata->keycode, 0);    input_sync(ddata->input);        }else if ((get_suspend_state())&&(ddata->keycode==KEY_POWER)){            input_event(ddata->input, EV_KEY, KEY_WAKEUP, 0);            input_sync(ddata->input);        }    }#ifdef CONFIG_PM    remotectl_wakeup(_data);#endif    ddata->state = RMC_PRELOAD;}

这个函数就是向事件驱动发送input_event(...,0),也就是通知事件驱动说,这个键弹起来了。

归纳一下,当连续收到重复电平的时候,在CASE重复电平分支里什么也不做,但是会不断地去更新 定时器,每次都设置超时时间为250ms,直到收到其他电平(后面会介绍)或者没收到任何电平(250ms之后执行默认弹起动作)。

应当说明一点,前面介绍了重复电平,是先收到一个正确的键值之后才会在下一个周期发送重复电平,所以收到重复电平的状态应该是 RMC_SEQUECE 状态,所以case这这个分支里做了是否是重复电平判断。

那么一个正常按键情况又是如何工作的呢?

收到起始码bit之后就会调用remotectl_do_something(),现在就来着重分析它:

mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(250));if((TIME_PRE_MIN < ddata->period) && (ddata->period < TIME_PRE_MAX)){if(ddata->state == RMC_SEQUENCE){remotectl_timer_reload(data);ddata->state = RMC_PRELOAD;}}
第一句,前面介绍了,不讲,接下来的这个判断作用是处理这个情况:

当用户按下去之后,不松,又突然松了重按一下键的情况。

不松时,状态为 RMC_SEQUENCE,突然又重按一下,得到了一个 TIME_PRE_MIN~TIME_PRE_MAX即得到一个起始码,这种情况下,模块不能等到REPEAT电平置的250ms超时时自动弹起,而是要马上弹起(),并把状态置为 RMC_PRELOAD,马上弹起的动作是由 remotectl_timer_reload()实现的,它的代码是:

static void remotectl_timer_reload(unsigned long _data){struct rkxx_remotectl_drvdata *ddata = (struct rkxx_remotectl_drvdata*)_data;if(ddata->press != ddata->pre_press){ddata->pre_press = ddata->press = 0;input_event(ddata->input, EV_KEY, ddata->keycode, 0);input_sync(ddata->input);}}
判断press=?pre_press后面会介绍,两个不等就代表 没有弹起,因为弹起是通过调用 remotectl_timer()实现的, 就会设置他们ddata->pre_press = ddata->press = 0;

继续分析remotectl_do_something(),接下来就进入了switch..case...这段代码,它依次识别遥控器发来的   起始码---用户码---键值----结束码 ,其中用户码16位,键值本身是8位,但是却发了两次 前8位为正常的键值,后8位是正常值得反码。这都是为了保证键值没错,键值错了比用户码识别错了还严重,因为用户码识别错了 ,大不了是本次没按灵,但是键值如果错了,可能导致不可预料的操作。

        case RMC_IDLE:        {            ;        }        break;

无关状态,直接跳过

        case RMC_PRELOAD:        {            if ((TIME_PRE_MIN < ddata->period) && (ddata->period < TIME_PRE_MAX)){                                ddata->scanData = 0;                ddata->count = 0;                ddata->state = RMC_USERCODE;mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(120));            }else{                ddata->state = RMC_PRELOAD;            }            ddata->pre_time = ddata->cur_time;            //mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(130));        }        break;
在准备状态(RMC_PRELOAD)如果收到了起始码,就意味着本轮识别开始,准备接受用户码了,因为NEC遥控器常常采用 38K Hz的载波,一个周期约 108ms,所以这设置自动弹起的时候使用了 120ms这个值,120ms之后应该把本次识别完成了,无论如何也要弹起,免得产生连击。

        case RMC_USERCODE:        {            ddata->scanData <<= 1;            ddata->count ++;            if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX)){                ddata->scanData |= 0x01;            }            if (ddata->count == 0x10){//16 bit user code                if (remotectl_keybdNum_lookup(ddata)){                    ddata->state = RMC_GETDATA;                    ddata->scanData = 0;                    ddata->count = 0;                }else{                //user code error                    ddata->state = RMC_PRELOAD;                }            }        }        break;
接受用户码,把 scanData左移一位,说白了把最低位置 0 ,然后判断收到的是1还是0 ,通过 if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX))判断,如果条件为真,表示收到了一个 1  这个时候  scanData |= 0x01; 说白了 就是把最低位置为 1 ,重复16次就收到了一个完整的用户码。

收到用户码之后,调用remotectl_keybdNum_lookup(),就是判断接收机是否支持这种遥控器,这里面代码很简单,就是一个键值映射,就不说了

        case RMC_GETDATA:        {            ddata->count ++;            ddata->scanData <<= 1;                      if ((TIME_BIT1_MIN < ddata->period) && (ddata->period < TIME_BIT1_MAX)){                ddata->scanData |= 0x01;            }            if (ddata->count == 0x10){                if ((ddata->scanData&0x0ff) == ((~ddata->scanData >> 8)&0x0ff)){                    if (remotectl_keycode_lookup(ddata)){                        ddata->press = 1;                         if (get_suspend_state()==0){                                input_event(ddata->input, EV_KEY, ddata->keycode, 1);                                input_sync(ddata->input);                            }else if ((get_suspend_state())&&(ddata->keycode==KEY_POWER)){                                input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);                                input_sync(ddata->input);                            }                        ddata->state = RMC_SEQUENCE;                    }else{                        ddata->state = RMC_PRELOAD;                    }                }else{                    ddata->state = RMC_PRELOAD;                }            }        }        break;
case RMC_USERCODE:一样,先收下 16 之后判断 前8位和其反码(后8位)是否一致,也就是判断键值在传输过程中是否出错。

如果没有出错,就去查找 usercode对应的遥控器有没有这个键,有这个键,表示按对了,就把这个按键的键值发给上层应用

        case RMC_SEQUENCE:{            if ((TIME_RPT_MIN < ddata->period) && (ddata->period < TIME_RPT_MAX)){                ;            }else if ((TIME_SEQ_MIN < ddata->period) && (ddata->period < TIME_SEQ_MAX)){            if (ddata->press == 1){                    ddata->press = 3;                }else if (ddata->press & 0x2){                    ddata->press = 2;                }                mod_timer(&ddata->timer,jiffies + msecs_to_jiffies(110));            }        }
这段代码比起前面的就有点复杂,不解释后面介绍。

总结起来,remotectl_do_something干的工作就是:

收到起始码--->接受用户码----->用户码判定---->接受键值----->键值传输查错控制----->判断键值---->向上层应用发送键值------>接受结束码 

press,pre_press 意思

在程序中多次判断 这两个值是否相等,这两个值究竟又是什么意思呢?

据我分析,press有4个值 分别是 0,1,2,3,其中:

0  代表  初始化状态

1  代表  识别到了一个按键

3   代表  完完整整识别了一个载波,也就是 结束码也得到了

2   代表 我现在没搞懂,在 case RMC_SEQUENC 里面有一段诡异的代码:

        if (ddata->press == 1){                    ddata->press = 3;                }else if (ddata->press & 0x2){                    ddata->press = 2;                }

这段代码至今未懂,恳请能够分析出这段代码的网友能不吝指点一二。

再分析一段代码:关于遥控器在系统深度睡眠状态的唤醒代码  有BUG

#ifdef CONFIG_PMvoid remotectl_wakeup(unsigned long _data){    struct rkxx_remotectl_drvdata *ddata =  (struct rkxx_remotectl_drvdata*)_data;    long *time;    int i;    time = ddata->remotectl_suspend_data.scanTime;    if (get_suspend_state()){                static int cnt;               ddata->remotectl_suspend_data.suspend_flag = 0;        ddata->count = 0;        ddata->state = RMC_USERCODE;        ddata->scanData = 0;                for (i=0;i<ddata->remotectl_suspend_data.cnt;i++){            if (((TIME_BIT1_MIN<time[i])&&(TIME_BIT1_MAX>time[i]))||((TIME_BIT0_MIN<time[i])&&(TIME_BIT0_MAX>time[i]))){                cnt = i;                break;;            }        }                for (;i<cnt+32;i++){            ddata->scanData <<= 1;            ddata->count ++;            if ((TIME_BIT1_MIN < time[i]) && (time[i] < TIME_BIT1_MAX)){                ddata->scanData |= 0x01;            }                        if (ddata->count == 0x10){//16 bit user code                                          if (ddata->state == RMC_USERCODE){                    if (remotectl_keybdNum_lookup(ddata)){                        ddata->scanData = 0;                        ddata->count = 0;                        ddata->state = RMC_GETDATA;                    }else{                        ddata->state = RMC_PRELOAD;                    }                }else if (ddata->state == RMC_GETDATA){                    if ((ddata->scanData&0x0ff) == ((~ddata->scanData >> 8)&0x0ff)){                        if (remotectl_keycode_lookup(ddata)){                             if (ddata->keycode==KEY_POWER){                                input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);                                input_sync(ddata->input);                                input_event(ddata->input, EV_KEY, KEY_WAKEUP, 0);                                input_sync(ddata->input);                            }                            ddata->state = RMC_PRELOAD;                        }else{                            ddata->state = RMC_PRELOAD;                        }                    }else{                        ddata->state = RMC_PRELOAD;                    }                }else{                    ddata->state = RMC_PRELOAD;                }            }        }    }    memset(ddata->remotectl_suspend_data.scanTime,0,50*sizeof(long));    ddata->remotectl_suspend_data.cnt= 0;     ddata->state = RMC_PRELOAD;    }#endif

这段代码的意思是,从scanTime中取出 32个 我们需要的 用户码+键值,如果他们能通过验证,并且是键值是 POWER 键,那么我就向上层应用发送一个 电源键被按下和弹起的动作,这样就能唤醒系统了系统睡眠之后遥控器的键值 之所以要从 scanTime[] 里面取,是因为这段代码。请看代码,在函数  irqreturn_t remotectl_isr()  的最后

#ifdef CONFIG_PM   wake_lock_timeout(&ddata->remotectl_wake_lock, HZ);   if ((get_suspend_state())&&(ddata->remotectl_suspend_data.cnt<50))       ddata->remotectl_suspend_data.scanTime[ddata->remotectl_suspend_data.cnt++] = ddata->period;#endif
如果系统进入休眠状态,那么就把收到的 bit 持续时间存入到  scanTime  中,并且用 cnt  进行计数

但取出scanTime 值的这段代码有问题:

首先,它是过滤掉无关的码,因为用户码和键值的 bit持续时间 都是在 最小0/1 ~最大0/1 之间,所以找到第一个 相关的bit所在位置,代表从这 ( i 值) 开始读用户码和键值

        for (i=0;i<ddata->remotectl_suspend_data.cnt;i++){            if (((TIME_BIT1_MIN<time[i])&&(TIME_BIT1_MAX>time[i]))||((TIME_BIT0_MIN<time[i])&&(TIME_BIT0_MAX>time[i]))){                cnt = i;                break;;            }        }
接下来是,开始取值:

        for (;i<cnt+32;i++){            ddata->scanData <<= 1;            ddata->count ++;            if ((TIME_BIT1_MIN < time[i]) && (time[i] < TIME_BIT1_MAX)){                ddata->scanData |= 0x01;            }
在for开始执行前,i 值和 cnt 相等的,所以这个 for 循环需要运行 32 次,错就错在这。试想,如果在睡眠状态,没有收到完整的32位用户码+键值,那么势必要从 scanTime[cnt]取得一些非存入的不可预料的值。也就是说,如果存入scanTime  的值的个数是 cnt 个, cnt 小于32,我们又必须从scanTime 中取得32个,那么32-cnt这些值是不可预料的。 

而正好,这个错误正好发生在目前公司的RK3188的板子上了。

通过实验,发现如果 cnt 大于36 (2位起始码+16位用户码+16位键值 +2位结束码)那么系统正常唤醒,反之就不能唤醒。

通过实验,在睡眠状态,连续快速按 两次开机键就能正常唤醒,因为第二次可能存入了完整的 36 位值了

更改代码:

        for (i=0;i<ddata->remotectl_suspend_data.cnt;i++){        if (ddata->count>=32)        break;           if ((TIME_BIT1_MIN < time[i]) && (time[i] < TIME_BIT1_MAX)){                ddata->scanData <<= 1;ddata->scanData |= 0x01;                ddata->count ++;;            }else if ((TIME_BIT0_MIN < time[i]) && (time[i] < TIME_BIT0_MAX)){              ddata->scanData <<= 1;              ddata->count ++;;            }        }
这样,保证取到的数据都是需要的数据,直到把 scanTime  取完也取不出32 个,那么循环也要结束,与其取错误数据倒不如不取。
这段代码,在板子上依然存在问题,就是在待机状态开机键只能捕捉到正确的键值,用户码最多能正确捕捉到后7位

在厂家给的遥控器驱动里面,我发现,对于唤醒,并没有对用户码进行验证,直接判断是否是电源键而发生动作,这正好被我们测试出来了,通过电视机的遥控器居然能把我们的板子唤醒,蛋都碎了~~~哎,目前系统工程师说,可能在深度睡眠状态 板子工作电压都会变,导致收不到前面几位用户码,于是,只好折衷下,只判断后7位,通过了4个遥控器测试。代码如下:

for (i=0;i<sizeof(remotectl_button)/sizeof(struct rkxx_remotectl_button);i++){//Note: just test the last 7 bit of usercode(16bit).if( (remotectl_button[i].usercode&0x7F) == ((ddata->scanData>>16)&0x7F) ){remotectl_get_pwr_scanData(ddata,&power_scanData,i);if ( (ddata->scanData&0xFFFF) == (power_scanData&0xFFFF) ){    input_event(ddata->input, EV_KEY, KEY_WAKEUP, 1);input_sync(ddata->input);input_event(ddata->input, EV_KEY, KEY_WAKEUP, 0);input_sync(ddata->input);break;}}}


希望解决这个问题网友,能联系我,谢谢  bortskaffe@163.com

继续更新,瑞芯微的专家已经回复,因为android在深度睡眠的状态CPU频率会调整到很低,导致收不到完整的用户码。

此问题在很多板子上都有出现,要彻底解决就只有改遥控器的power键,每次按的时候发送两次一样的波,等于是按了两次power键,程序按一次处理。

1 0