旧乡故客
发表于: 2018-7-27 20:58:38 | 显示全部楼层

在本篇文章中,我们将开发一种算法,用于精确定位在圆形电容传感器上某处发生的触摸事件。


所需的硬件/软件

●    SLSTK2010A Sleepy Bee入门套件

●    Simplicity Studio


圆形传感器

SLSTK2010A评估板包括EFM8 Sleepy Bee微控制器和圆形电容式传感器。可以使用任何微控制器执行电容式触摸感应,但是通过专家设计的专用硬件模块可以获得更好的结果,这正是Sleepy Bee中集成的功能。前一篇题为“使用EFM8微控制器进行电容式触摸感应”的文章探讨了该外设的功能,在EFM8 Sleepy Bee参考手册中称为CS0(Capacitive Sense 0)。


圆形电容传感器提供非三维圆环形(或者,如果您喜欢的圆环形)区域,其中可以通过指尖的应用来接收用户输入。现实的限制条件限制了可以准确识别的单独触摸位置的数量,但总的来说我会说分辨率非常好 - 我猜测这些精心设计的固件可以可靠地区分5°增量,这意味着圆形传感器可提供多达72个独立的用户输入位置。


这是相当令人印象深刻的,但是当您想到仅使用三个电容感应通道实现这72个独立位置时,它会变得更加令人印象深刻。但是,这三个通道能提供此功能,只是因为它们连接到三个非常巧妙设计的电容式PCB传感器,如下图所示。

Keim_circular_sensor_1.jpg


每个传感器的中心部分对应于弯曲突起的尖端之间的径向部分。当您触摸中心部分时,该传感器的电容变化(也称为“上限三角形”)将最高,并且当指尖在任一方向上远离中心部分移动时,上限增量将稳定下降。


但是,正如您所看到的那样,当您离开一个中心部分时,您正朝着另一个中心部分移动。这意味着第一个通道上的下降增量与另一个通道上的上限增量一起发生。通过组合来自相邻传感器的三角测量,我们可以计算出在环面某处发生的任何触摸事件的近似角位置。


在我们继续之前,让我们简要讨论一下为什么这个圆形滑块在现代电子设备的背景下非常漂亮。毕竟,我们需要一些相当复杂的固件才能使用三个传感器获得合适的角度定位。为什么不使用十几个普通的圆形传感器在圆周上均匀分布?


好吧,每个传感器都需要一个微控制器上的引脚,所以现在你已经消耗了12个引脚,在此过程中你的分辨率会大大降低。固件价格便宜且不占用任何空间,而微控制器引脚(以及连接到它们的PCB走线)相对于工程师现在期望设计的微小设备占用了大量空间。从长远来看,硬件很昂贵,至少与固件相比。更少的引脚、更小的IC、更少的硬件、更多的固件 - 这是当前的趋势,至少对于消费电子产品而言。


了解我们的传感器

下图显示了代码中使用的传感器编号如何与物理布置相对应。

EFM8CS2_sensor_numbers.jpg


同样,这些是“传感器编号” - 即,分配给三个物理传感器的任意标识符。这些数字与“通道”不同,内部感应通道连接到物理传感器。当然,通道编号不一定以直观的方式对应于传感器电气连接的外部引脚。以下是映射表:

传感器编号

(Sensor Number)

通道

(Channel)

引脚号

(Pin)

位置

(Location)

1
2
P0.2
底部中间
2
3
P0.3
左上角
3
1
P1.5
右上角

建立基线(Baseline)

我们需要做的第一件事是将某个值设置为特定传感器的官方“未压缩”电容。这一步骤至关重要,因为电容式触摸感应围绕电容的变化而非绝对电容,其次,因为三个传感器的未压缩电容会有很大的变化。例如,我只是插入我的电路板,目前这三个(看似相同的)传感器的未压缩电容值约为15700、14800和14150。


我建立了未压缩的电容值如下:

  1. Accumulated_Capacitance_Sensor1 = 0;
  2. Accumulated_Capacitance_Sensor2 = 0;
  3. Accumulated_Capacitance_Sensor3 = 0;

  4. for (n = 0; n < 16; n++)
  5. {
  6.         Accumulated_Capacitance_Sensor1 += Measure_Capacitance(SENSOR_1);
  7.         Delay_us(1000);
  8.         Accumulated_Capacitance_Sensor2 += Measure_Capacitance(SENSOR_2);
  9.         Delay_us(1000);
  10.         Accumulated_Capacitance_Sensor3 += Measure_Capacitance(SENSOR_3);
  11.         Delay_10ms(5); Delay_us(6000);
  12. }

  13. Sensor1_Unpressed = (Accumulated_Capacitance_Sensor1 >> 4);
  14. Sensor2_Unpressed = (Accumulated_Capacitance_Sensor2 >> 4);
  15. Sensor3_Unpressed = (Accumulated_Capacitance_Sensor3 >> 4);
复制代码

你可以在这里看到我做了很多平均。 CS0硬件配置为每次测量平均64个样本,然后我平均这些已经平均的测量值中的16个。也许这有点矫枉过正,但所有的上限检测功能都是基于这些未压缩的值,因此我想确保它们不会被瞬态噪声源或某种测量故障所破坏。


注意:重要的是要避免这些初始未压缩测量之间的主要差异,然而在正常操作期间对电容感应通道进行采样。我特别提到维护信道序列并确保测量之间的间隔相似。例如,如果在正常操作期间您对传感器1,传感器2,传感器3进行采样,每次测量之间的间隔为1 ms,则您不希望通过进行16次快速传感器1测量来确定您的未压缩值,然后立即采取16个快速传感器2测量,然后立即采取16个快速传感器3测量。这将导致正常操作测量与建立非压缩电容测量之间的小但明显的差异,使得您的官方未压缩值将与正常编程操作期间观察到的实际典型未压缩值不同。


检测按下

在计算角度位置之前,我们需要认识到指尖与圆形传感器接触。我们通过扫描通道,计算上限增量并将其与阈值进行比较来实现此目的。

  1. Sensor1_Measurement = Measure_Capacitance(SENSOR_1);
  2. Sensor1_Delta = Sensor1_Measurement - Sensor1_Unpressed;
  3. if(Sensor1_Delta < 0)
  4.         Sensor1_Delta = 0;
  5. Delay_us(1000);

  6. Sensor2_Measurement = Measure_Capacitance(SENSOR_2);
  7. Sensor2_Delta = Sensor2_Measurement - Sensor2_Unpressed;
  8. if(Sensor2_Delta < 0)
  9.         Sensor2_Delta = 0;
  10. Delay_us(1000);

  11. Sensor3_Measurement = Measure_Capacitance(SENSOR_3);
  12. Sensor3_Delta = Sensor3_Measurement - Sensor3_Unpressed;
  13. if(Sensor3_Delta < 0)
  14.         Sensor3_Delta = 0;

  15. if(Sensor1_Delta > TOUCH_DELTA_THRESHOLD
  16.                 || Sensor2_Delta > TOUCH_DELTA_THRESHOLD
  17.                 || Sensor3_Delta > TOUCH_DELTA_THRESHOLD)
  18. {
  19.         ...
复制代码

通过实验,我发现不太坚固的手指按压时电容的最小增加大约是6000个计数(我将帽感增益设置为4倍)。 我通过触摸两个传感器之间的中途来做到这一点; 这导致单个传感器的电容增加最小,因为两个传感器之间的总增加量相等。

EFM8CS2_minimumcounts.jpg


因此,我们可以假设与圆形传感器的任何部分接触的指尖将导致至少6000计数的上限。 基于此,您可以使用4000或5000计数作为上限 - 增量阈值,但我选择2000,仅仅因为2000仍然远高于典型的噪声幅度并且将给予我们更高的灵敏度(可能一些手指具有比其他手指更小的电容。)。


接下来,我们简单地将每个传感器的电容增量与阈值进行比较,如果至少有一个超过阈值,我们继续处理传感器数据以确定触摸事件的角位置。

跳转到指定楼层
旧乡故客
发表于: 2018-7-27 21:49:49 | 显示全部楼层

计算位置

在进一步讨论之前,我应该申明以下内容:我开发的角位置算法可能远非最优。但那不是重点。关键是,这种算法是一种常识思维过程的结果,它反映了从意图、思考、观察和分析到实施的“直觉路径”。我更倾向于从这个直观的路径开始,因为它无疑会将引导我们深入理解手头的问题,并且在寻找由其他人创建的可能更好的算法实现之前,寻求更深入的理解是个好的途径。


我首先确定触摸事件发生的120°段;这是通过寻找具有最低测量值的传感器来完成的。例如,如果传感器1具有最低的测量值,则指尖位于传感器2和3之间。

EFM8CS2_findminimum.jpg


  1. if(Sensor1_Delta > TOUCH_DELTA_THRESHOLD
  2.                 || Sensor2_Delta > TOUCH_DELTA_THRESHOLD
  3.                 || Sensor3_Delta > TOUCH_DELTA_THRESHOLD)
  4. {
  5.         switch(Find_Minimum(Sensor1_Delta, Sensor2_Delta, Sensor3_Delta))
  6.         {
  7.                 case SENSOR_1: Segment = UPPER_MIDDLE;
  8.                 break;

  9.                 case SENSOR_2: Segment = LOWER_RIGHT;
  10.                 break;

  11.                 case SENSOR_3: Segment = LOWER_LEFT;
  12.                 break;
  13.         }
  14.         
  15.         ...
复制代码

算法的下一部分的关键是假设电容的总增加是相同的,和角位置无关。假设手指位于传感器2的中心,并且传感器2的计数增加了15000,传感器3的计数增加了零。在此假设下,如果传感器2和传感器3的计数增加了7500,则触摸事件在两者之间。我不知道这个假设有多准确,但总体思路似乎足以产生良好的结果。


接下来,我们简单地将传感器2(或传感器3或传感器1)的增量值除以总增量值,然后我们将该比率乘以120°。最后,我们将该角度添加到与120°段的“开始”对应的角度。

EFM8CS2_angles.jpg


这种技术的主要缺点如下:传感器中心附近的角度(即30°、150°、270°)被“跳过”,因为电容增量值一直不会变为零 - 例如,如果您的指尖位于传感器3的中心,传感器2的读数可能是400计数而不是零计数。结果是传感器2的值与总增量值的比率不会从0%一直延伸到100%。为了纠正这一点,我假设(根据观察结果)初始计算的比率将从5%延伸到95%,然后我将比率从5-95范围映射到0-100范围。映射常数通过求解以下联立方程确定:

0.05X + Y = 0

0.95x + Y = 1

我对这种方法提供的性能感到满意,但需要进行一些额外的微调以完全消除在30°、150°和270°过渡区域发生的“跳跃”测量。

  1. if(Segment == UPPER_MIDDLE)
  2. {
  3.         TotalDelta = Sensor2_Delta + Sensor3_Delta;

  4.         Ratio = (float)Sensor2_Delta/TotalDelta;
  5.         Ratio = (Ratio*1.1111) - 0.05556;

  6.         AngularPosition = (Ratio*120) + 30;
  7. }

  8. else if(Segment == LOWER_RIGHT)
  9. {
  10.         TotalDelta = Sensor3_Delta + Sensor1_Delta;

  11.         Ratio = (float)Sensor3_Delta/TotalDelta;
  12.         Ratio = (Ratio*1.1111) - 0.05556;

  13.         AngularPosition = (Ratio*120) + 270;

  14.         if(AngularPosition >= 360)
  15.                 AngularPosition = AngularPosition - 360;
  16. }

  17. else if(Segment == LOWER_LEFT)
  18. {
  19.         TotalDelta = Sensor2_Delta + Sensor1_Delta;

  20.         Ratio = (float)Sensor1_Delta/TotalDelta;
  21.         Ratio = (Ratio*1.1111) - 0.05556;

  22.         AngularPosition = (Ratio*120) + 150;
  23. }
复制代码

总结

该程序有两种模式:在默认模式下,它显示当前或最近触摸事件的角度位置。 在第二种模式中,它显示每个通道的电容增量值。

EFM8CS2_deltadisplay.jpg


在以后的文章中,我们将这种角度位置功能结合到基于圆形触摸传感器的用户界面中。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 29 | 回复: 32



手机版|

GMT+8, 2024-11-21 17:08 , Processed in 0.065608 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表