当前位置:研发设计首页 >> 技术文萃 >> 研发测试 >> 简单手势唤醒便携设备的设计思路及其代码
简单手势唤醒便携设备的设计思路及其代码
2016-08-02 21:05:06  作者:佚名  来源:互联网
  •   厨房里的突发奇想   如果做饭时使用触控设备,您可能会注意到按照设备列出的食谱烹饪并非想象得那么简单。技术达人(例如鄙人)走进厨房时,喜欢看着平板电脑或智能手机上的菜谱做饭。您可能会说:“好吧, ...

  厨房里的突发奇想

  如果做饭时使用触控设备,您可能会注意到按照设备列出的食谱烹饪并非想象得那么简单。技术达人(例如鄙人)走进厨房时,喜欢看着平板电脑或智能手机上的菜谱做饭。您可能会说:“好吧,这有什么难度?”由于屏幕始终开启会消耗很大电量,通常手持装置在1、2分钟后没有操作时将自动进入休眠状态。那么,当您需要参照食谱时,设备已进入休眠状态。此事,您面临两个选择:要么强制屏幕保持永久开启;要么用沾满食物的手开启装置,而在屏幕上留下斑斑油渍。当然,您可以在每次查看时把手清洗干净,但不断重复洗手、擦干即繁琐,又费水。

  我时常问自己:“怎样才能既不让屏幕始终开启,又不会弄脏装置?”实际上,有一种办法一举两得,即通过一个手势(不用接触屏幕)开启显示屏。听起来似乎很复杂,是吗?幸运的是,做起来可能比听起来容易一些。

  接近检测传感器

  许多触摸屏装置,尤其是智能手机,内部已经安装了红外(IR)接近检测传感器。这些传感器一般在通话期间自动打开/关闭屏幕,以避免意外操作手机的输入界面。这种传感器,加上精明的软件设计,就能实现利用一个手势唤醒装置的功能。

  基本的设计思路是:设备进入休眠状态时,触摸屏关闭,应用处理器处于低功耗模式,依靠接近检测传感器“观察”背景的变化,当接收到的信号足够大时,做出适当反应。这与接近检测传感器在通话期间关闭屏幕的功能几乎完全相同。只是,我们的应用对数据有了不同的解释。

  首先记录传感器在“正常”背景下的计数值,此时得到的数值可能为零,但实际设计中需要考虑系统失调(例如:散射或串扰)。然后将得到的数值设置为检测门限,当接收信号超过门限时触发中断或向应用处理器发送信号,以唤醒系统并打开屏幕。总体而言,这种方法非常简单、直观,可利用环境光检测器和IR接近检测传感器实现。

  本文介绍的方案采用MAX44000,接近检测的数据读取时间间隔可以设置在1.56ms至100ms (与环境光检测传感器轮流读取数据)。假设最大检测距离为10cm,LED的辐射角为±15°,那么,可以覆盖的面积大约为22cm2或跨距大约为5.35cm,只有该区域内的移动目标才能捕捉到。由此,能够以最慢(即最低功耗)的采样速度可靠检测的最快手势动作大约为0.53mps。在此,我们还假设传感器只需要采集到一次高于门限的信号,即可识别经过覆盖区域的目标。

  举手之劳...

  理论上讲,该方案的实施非常简单。当装置进入休眠模式时,将接近检测传感器置为环境扫描模式,并在检测到目标时发出中断信号,指示捕捉到超过预设门限的信号。可通过I2C接口轮询传感器的状态。不幸的是,这种方式会消耗过大功率,超出了大多数用户的预期。

  这也是接近检测传感器的设计重点,MAX44000传感器能够在许多方面摆脱应用处理器的干预,减轻处理器负荷(降低功耗)。

  使能MAX44000的内部接近检测中断(寄存器0x01的第1位),可将唤醒门限写入内部寄存器(0x0B和0x0C)。当接近检测传感器的读数超过该门限时,触发中断标识置位,将MAX44000的/INT引脚置为低电平。当应用处理器检测到该引脚驱动为低电平时,可唤醒装置退出低功耗模式,并打开屏幕,或完成其它需要的动作。

  …但不容忽视

  实际应用往往不如理论那么容易,非接触唤醒的具体实施并非只是简单地检测高于门限的信号。实际上,具体的设计需要考虑诸多因素。

  信号电平与电路布局

  最关键的考虑应该是触发唤醒条件的信号电平,需要在系统响应灵敏度与误报概率之间进行权衡。如果门限过低,则很容易检测到输入(手势工作),但会增大瞬态噪声或突发条件产生误报的概率。反之,过高的检测门限能够把误报概率降至几乎为零,但却只能检测到非常接近的目标,甚至对任何输入(即使您疯狂晃动手臂)都反应迟钝。

  解决这一问题的最佳方式是:首先降低系统噪声,可以通过光学方法或严谨的电路布局实现,降低的噪底有助于降低误报概率;其次,选择“平均”检测距离(例如:4cm至5cm)并利用参考目标测量信号,18%的灰板比较理想,但如果触摸屏上方安装了黑色玻璃,测量时也应该使用这样的玻璃,所测得的信号电平可以作为设置门限的最佳参考。通常可以遵循这样的原则:即将电平设置在满幅的8%至15%,即使电平发生变化。

  可以按照上述经验数据设置MAX44000传感器的接近检测门限寄存器,图1所示为信号强度随距离变化的关系曲线,采用18%灰板,驱动电流为100mA,传感器上方没有玻璃罩。蓝线为可以选择的唤醒门限。

  

newmaker.com

  图1. MAX44000接近检测传感器信号强度随距离变化的关系曲线,

  采用18%灰板,100mA驱动电流,没有玻璃罩。

  噪声和低通滤波

  需要考虑噪声问题时,可利用低通滤波器处理信号;另外,MAX44000还有几个控制位可以用作触发中断标识之前的屏蔽,采用这种设置时,需要检测到一定数量超出门限的采样值时才会触发中断标示,能够在一定程度上降低噪声的影响。

  一种稍微复杂的方法是将传感器的读数储存在数据队列中,然后利用定制的FIR软件对其进行滤波处理。但这种方法需要提高接近检测传感器的采样速率,否则则会降低能够捕捉到的传感器可视范围内的手势动作速率,特别是把采样速率设置在100ms时。利用器件的控制位屏蔽检测时,速率可最多降低16倍(通常选择4x屏蔽即可)。

  手势速度

  手势动作的快慢是我们需要考虑的另一因素。最大速度取决于:1. 传感器的可视范围;2. 手与传感器之间的距离;3. 采样率;4. 检测门限。前两项很容易确定:传感器的检测角度,结合传感器与目标之间的距离,利用基本的三角形即可计算出传感器可视范围内目标的移动距离。例如,如果传感器的视角为30度,最大有效检测距离10cm,那么,传感器可视范围内允许的目标移动距离为5.35cm,覆盖面积大约为78cm2。直线距离结合采样率,即可决定速度限值。 具体地说,如果采样率为T,那么目标跨越可视区域的时间不得小于T。例如,如果T为100ms (MAX44000的最低采样速率),那么按照上例,理论上最大允许的速率为1mps (这实际上已经相当快了)。您可能希望捕获到多个采样值来确认触发唤醒,这样的话,会降低允许的速率下限。

  检测门限也影响最大允许速率。一般来说,门限越低,能够捕捉到的手势动作就越快。如上所述,应谨慎选择门限,以免产生误报。

  人为因素

  这种应用还会受到人手以及挥手动作等人为因素的影响。应通过一些案例确定一般大多数人的习惯,包括他们在屏幕前挥动手掌的速度以及与屏幕之间的距离,另外,是否戴手套也会产生一定的影响。不同的应用场合(不同装置)也会影响到设计需求,例如智能手机、平板电脑或汽车仪表盘,对存在具体的设计考虑。当然,设计过程中还应考虑用户界面和经验参数。

  最后,还要对真假手势做出判断,即装置需要判断接收到的信号是来自于一个手势动作,还是简单的装置移动(例如:放置在外套、口袋或背包中,或者是屏幕朝下放置)。单纯依靠上述检测原理,很难做出正确的“真伪”鉴别,除非在装置内提供更多的背景信息。关于这一问题的讨论超出了本文范围。

  设计中可以选择只有装置进入特定的应用程序时启动唤醒方案,也可以由用户手动操作使能。此外,许多此类装置都有一个加速度传感器,能够检测到屏幕是否背面朝下放置。如果用户手动将装置置于休眠模式,则可禁用该功能(例如关机状态)。

  设计实例

  为方便起见,本文附带了三段演示程序代码。第一段代码用于手动操作MAX44000的接近检测数据读取,概念上简单实现唤醒功能;第二段代码在第一段的基础上进行了扩展,增加了之前讨论的滤波功能;最后一段代码演示利用MAX44000中断唤醒触控装置。

  示例代码1

  __interrupt void TimedInterrupt( void )

  {

  uint8 proximity_counts;

  ....

  ....

  if ( device_status == SLEEP_MODE )

  {

  // read one byte from register 0x16

  proximity_counts = read_i2c_register(MAX44000_ADDR,0x16,1);

  if (proximity_counts > WAKEUP_THRESHOLD)

  {

  device_status = WAKE_MODE;

  ...

  }

  else

  {

  // do whatever it is you need to in sleep mode

  ...

  ...

  }

  }

  ...

  ...

  }

  示例代码2

  // example interrupt function where this might be implemented

  __interrupt void TimedInterrupt( void )

  {

  uint8 proximity_counts;

  uint8 filtered_counts;

  ....

  ....

  if ( device_status == SLEEP_MODE )

  {

  // read one byte from register 0x16

  proximity_counts = read_i2c_register(MAX44000_ADDR,0x16,1);

  // weights[QUEUE_SIZE] contains the filter weights for the FIR filter

  // data_queue[QUEUE_SIZE] is a FIFO queue meant to be the input to the filter

  filtered_counts = fir_filter(proximity_counts,weights,data_queue);

  if (filtered_counts > WAKEUP_THRESHOLD)

  {

  device_status = WAKE_MODE;

  ...

  }

  else

  {

  // do whatever it is you need to in sleep mode

  ...

  ...

  }

  }

  ...

  ...

  }

  /**

  * fir_filter()

  *

  * Implements an FIR filter in the form

  * y = w[0]*x[0] + w[1]*x[1] + ...+ w[QUEUE_SIZE]*x[QUEUE_SIZE]

  *

  * Arguments:

  * uint8 input - newest datapoint taken (that is, x[0])

  * uint8 *weights - w[0]...w[QUEUE_SIZE]

  * uint8 *queue - the discrete sequence x[0]...x[QUEUE_SIZE]

  *

  * Returns:

  * The FIR-filtered output, y

  */

  uint8 fir_filter(uint8 input, uint8 *weights, uint8 *queue)

  {

  uint8 i;

  int sum = 0;

  // pop first entry in the queue, then

  // push new data into the last position

  push_into_queue(queue,input);

  // input is now x[0]

  for (i=0; i {

  sum += weights[i]*queue[i];

  }

  return (sum/QUEUE_SIZE);

  }

  示例代码3

  // this handles hardware-level interrupts on the micro

  __interrupt void irq_handler( void )

  {

  ...

  // if the hardware interrupt came from the MAX44000 sensor

  // pulling its \INT pin low

  if ( irq_source == MAX44000 )

  {

  // if the device is in sleep mode

  if (device_status == SLEEP_MODE)

  {

  device_status = WAKE_MODE; // wake up the device

  ...

  // reconfigure whatever else you need here as the system wakes up

  }

  // otherwise, handle it however it is you wish

  else

  {

  ...

  }

  }

  ...

  }

  /**

  * configure_max44000_for_sleep_mode()

  *

  * Sets up the MAX44000 to trigger a hardware interrupt when the proximity

  * counts go above some set threshold.

  *

  * Arguments:

  * uint8 upper_threshold - the set threshold (8-bit mode)

  *

  * Returns:

  * n/a

  */

  void configure_max44000_for_sleep_mode(uint8 upper_threshold)

  {

  uint8 max44000_thresh_registers[] = {0x0B,0x0C};

  uint8 max44000_upper_thresh[] = {0x40,0};

  max44000_upper_thresh[1] = upper_threshold;

  // do a consecutive write of 0 followed by upper_threshold to

  // registers 0xB and 0xC, respectively

  // MAX44000_ADDR is usually 0x94

  // interrupt will trigger only if proximity value is above the threshold

  write_i2c_register(MAX44000_ADDR,max44000_thresh_registers,

  max44000_upper_thresh,2);

  // write to bits 2 and 3 of register 0x0A here if you wish to set the

  // persist time to anything other than one sample

  // writes to register 0x01 to enable interrupts on the MAX44000

  max44000_enable_interrupt();

  return;

  }



版权所有:智造网 京ICP证100778号 京公网安备110102003025 虚假新闻举报电话:010-88379107