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_CTL
rk29_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_USERCODE,RMC_GETDATA,RMC_SEQUECE在识别出错的时候都返回到RMC_PRELOAD状态,结束解析。意思就是:比如,当收到的用户码不能识别,也即就是不是我们厂生产的遥控器在遥控我们的接收器,当然不能让它得到解析;又比如,用户码正确了,但是键值查不到,这可能是凑巧用户码一致但是确实不是这个遥控板,因为不同遥控板发送的键的扫描码可能是不一致的,当然也要结束本轮识别。RMC_PRELOAD--------------得到正确的起始码------------------------->>>>>>RMC_USERCODE
RMC_USERCODE-----------得到能识别的用户名---------------------->>>>>>RMC_GETDATA
RMC_GETDATA---------------得到能识别的键值------------------------->>>>>>RMC_SEQUECE
RMC_SEQUECE-------------得到正确的结束码-------------------------->>>>>>RMC_PRELOAD
每当识别到了 键值 (即 RMC_GETDATA状态正确识别到了键值)就会向上层应用发送这个键值
input_event(ddata->input, EV_KEY, ddata->keycode, 1);
input_sync(ddata->input);
那么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));
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_PM void 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位,通过了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; } } }
希望解决这个问题网友,能联系我,谢谢 [email protected]
继续更新,瑞芯微的专家已经回复,因为android在深度睡眠的状态CPU频率会调整到很低,导致收不到完整的用户码。
此问题在很多板子上都有出现,要彻底解决就只有改遥控器的power键,每次按的时候发送两次一样的波,等于是按了两次power键,程序按一次处理。