Arm Community
Arm Community
  • Site
  • User
  • Site
  • Search
  • User
  • Groups
    • Research Collaboration and Enablement
    • DesignStart
    • Education Hub
    • Innovation
    • Open Source Software and Platforms
  • Forums
    • AI and ML forum
    • Architectures and Processors forum
    • Arm Development Platforms forum
    • Arm Development Studio forum
    • Arm Virtual Hardware forum
    • Automotive forum
    • Compilers and Libraries forum
    • Graphics, Gaming, and VR forum
    • High Performance Computing (HPC) forum
    • Infrastructure Solutions forum
    • Internet of Things (IoT) forum
    • Keil forum
    • Morello Forum
    • Operating Systems forum
    • SoC Design and Simulation forum
    • 中文社区论区
  • Blogs
    • AI and ML blog
    • Announcements
    • Architectures and Processors blog
    • Automotive blog
    • Graphics, Gaming, and VR blog
    • High Performance Computing (HPC) blog
    • Infrastructure Solutions blog
    • Innovation blog
    • Internet of Things (IoT) blog
    • Operating Systems blog
    • Research Articles
    • SoC Design and Simulation blog
    • Tools, Software and IDEs blog
    • 中文社区博客
  • Support
    • Arm Support Services
    • Documentation
    • Downloads
    • Training
    • Arm Approved program
    • Arm Design Reviews
  • Community Help
  • More
  • Cancel
中文社区
中文社区
中文社区博客 NucleoF429 基础应用3:GPIO Touch Key(触摸按键)
  • Blogs
  • Forum
  • 视频和文件
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • Jump...
  • Cancel
  • New
中文社区 requires membership for participation - click to join
More blogs in 中文社区
  • ARM中国大学计划博客

  • Arm新闻

  • 中文mbed博客

  • 中文社区博客

  • 恩智浦汽车电子MCU讨论区博客

 

Tags
  • nucleof429
  • touch key
Actions
  • RSS
  • More
  • Cancel
Related blog posts
Related forum threads

NucleoF429 基础应用3:GPIO Touch Key(触摸按键)

Xiaoya
Xiaoya
June 21, 2016
2 minute read time.

          这里先不说触摸(Touch)按键的优缺点,只从GPIO实现Touch Key的方法上来讲下这个Demo。Touch Key的实现方式有多种,效果和稳定性也相差很大。有些是硬件上专门做了设计,且有不少是有专利的。记得ST有支持Touch Key的型号,也有对应的库,但没用过。进入正题-->

1、Touch Key 硬件原理:

     Touch Key检测的原理网络上也有许多,方法各异,此文中的Touch Key 如下图:

实现原理是将Touch Pad 和 人体 等效为一个电容,Touch Pad的电容固定,和一个电阻并联到GND。IO输出High后,再转为Input,Touch Pad上的电荷通过电阻放电,只要Touch Pad的等效电容和电阻阻值不变,放电时间就是固定的。当有触摸时,人体等效电容相当于并联到了Touch Pad上,总的等效电容增大,放电时间增加,通过判断这个增加的“电容”大小,就能判断是否有触摸。如下图:红色的曲线为无触摸时的放电曲线,放电时间为t0,蓝色曲线为有触摸时的放电曲线,放电时间为t1,两者的差值为Δt。由Δt的大小可判断是否有触摸。VIL是IO读到低电平的阈值

下面是实际测量的电容放电波形:

可以看到,放电时间在10us左右(330K下拉电阻,有触摸时大概增加5us左右),这还是加了示波器探头的放电时间,如果不加探头,时间大概小一半,

下图是一个简易的Touch Pad。铜箔大小越1cm2,覆盖了透明胶带。下拉330K电阻,铜箔等效电容估计个位数pF。

以上是硬件方面和检测的原理。下面来看如何检测这个电容变化量?

2、Touch Key 软件原理:

     2.1、电路原理知道了,电容放电的波形也看到了,如何检测变化量呢?用ADC?有无触摸的电压变化量在mV级别,时间为us级别,用多大速度和分辨率的ADC暂且不管,主要是有很多MCU并不具备ADC模块,如果外接满足这个要求的ADC还不如用一颗触摸芯片来的简单。测量电压行不通,那就测量时间吧。(以下的方法可以等效为一个简易的ADC)

     上面有提到,有无触摸,放电波形会有5us的变化量。5us对于几十MHz的MCU来说能做太多事了。我这里采用的方法并不是采用中断,因为5us的变化量测量得需要上MHz的中断且获得的数据变化也不明显,所以我采用了do while(Loop)方式,IO输出High充电完成转为Input,通过电阻放电,直到IO读到低电平,在放电期间,放一个计数器,基本是以一条指令的时间来计数的,而且获得的数据变化量够大,便于后期计算,比如10MHz主频执行单周期指令计数,1us计数10次,5us计数50次,还可以连续做多次充放电,将计数累加,有无触摸就会有上百次的计数值变化,完全可以被检测出来。这是我的计数函数,在IO设置为Input后,查询IO是否为低,并且做计数,函数中连续做了16次累加,因为整个while循环中,执行while判断的时间是最长的,所以减少while判断可以计数更多的值,我的TouchKey是在PE0上,如果为High,计数值递增为1,如果换作其他IO时,在读到值之后做相应的移位,使每次递增为1即可,

/*----------------------------------------------------------------------------*/
uint16_t Get_Touch_Value(void)
{
  //HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_SET);
  Touch_Count = 0;
  GPIO_InitTypeDef GPIO_InitStruct;

  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;

  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  //while(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_0)) //2
  while(GPIOE->IDR & GPIO_PIN_0) //1
  {
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);

  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);

  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);

  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  Touch_Count +=(GPIOE->IDR & GPIO_PIN_0);
  }
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_0,GPIO_PIN_SET);

  //HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_RESET);
  return Touch_Count;
}

2.2、因为手指本身有干扰,并且手指与Touch Pad的接触面积也不固定,导致增量时间时刻在变化,必须要经过滤波处理,如下图中的Touch按下部分,波形有杂波,在Touch隔离物较厚时,干扰问题会更严重且数据变化量较小,不经过滤波处理,误触发的几率会很大。

     我做了两次滤波:

1、间隔100us进行一次充放电采集,连续采集12个点,将这12个点进行从大到小排序,之后去掉队首最大的4个值和队尾最小的4个值,取中间的4个值做平均,通过这样将一段时间内的数据累加平均为一个点,能有效去掉脉冲干扰。

/*----------------------------------------------------------------------------*/
uint16_t Touch_Heap(void)
{
  uint8_t i,j;
  uint8_t DataTemp;
  uint32_t DataSum = 0;
  for(i=0; i<Heap_Num-1;i++)
  {
  for(j=0;j<Heap_Num-i;j++)
  {
  if(Data_Buffer[j]<Data_Buffer[j+1])
  {
  DataTemp = Data_Buffer[j];
  Data_Buffer[j] = Data_Buffer[j+1];
  Data_Buffer[j+1] = DataTemp;
  }
  }
  }
  for(i=Dis_Num;i<Heap_Num-Dis_Num;i++)
  {
  DataSum += Data_Buffer[i];
  }
  DataSum = DataSum/(Heap_Num-2*Dis_Num);

  return (uint16_t) DataSum;
}

2、间隔10ms进行一次连续的采集,采集后的点再进行平滑滤波,我这里采用了一个一阶低通滤波器,通过调整滤波系数能调节波形的延时和滤波效果。(ST提供了各种滤波器函数,效率高效果好,有兴趣的同学可以深入研究一下)

/*----------------------------------------------------------------------------*/
/* 
 LPF: Y(n) = α*X(n)+(1-α)*Y(n-1) 
 Y[n]:  = Y[n-1] + α * (X[n] - Y[n-1])
 LPF_Yn[0:1] = Yn-1:Yn  
*/ 

uint16_t IIR_LowPass_Filter(uint16_t LPF_Data)
{
  float LPF_Temp;
  LPF_Temp = LPF_Pre_Data+ LPF_Alpha*(LPF_Data - LPF_Pre_Data);
  LPF_Pre_Data = LPF_Temp;
  return (uint16_t)LPF_Temp;
}

经过两次滤波后,充放电数据波形改善很多。

3、Touch Key 状态判断:

     这里是指判断按下和释放,通常的做法是设置一个阈值,高于多少或者低于多少就认为是按下或释放,有些程序会做到动态追踪最低点等等,对于元件一致性好的产品,误差都在可控范围内,这样做通常没有太大问题。但对于一些成本敏感,元器件误差较大的产品,问题就会比较明显,因为每一个产品的元件都有误差,这些误差的范围有可能已超过了你的阈值范围,无论你阈值设置过大或过小,总有一部分产品会有问题,即使是同一个产品,不同的环境也会导致阈值超出范围,比如Touch Pad受到灰尘、磨损等改变了自身的等效电容,电阻阻值随温度进行变化等等。

     我这里采用数据变化的“趋势”来判断Touch 按下或释放,如图:

把数据点放入队列,依次计算前后两个点的差值,判断数值是增大还是减小,通过计数符合差值的点的次数,判断Touch按下还是释放,这也能有效排除干扰,最重要的是,这种方法受外界因素的影响比较小,元器件之间的差异被排除,因为这里只判断增量而非绝对值。

/*----------------------------------------------------------------------------*/
// 按下时:符合差值的计数增减,释放时,符合差值的计数减小。
void Check_Touch_State(void)
{
  uint8_t i;
  uint8_t Touch_DebCnt = Data_Num; //数据点个数
  int8_t Touch_D_Value = 0;
  for(i=0;i<Data_Num;i++)
  {
  Touch_Data_Buffer[i] = Touch_Data_Buffer[i+1]; //队列左移
  }
  Touch_Data_Buffer[i] = IIR_LowPass_Filter(Touch_Heap()); //最新数据点补在队列尾部
  if(Touch_POR_Deb > 200) //上电先做采集不判断按键状态,防止上电时数据为0,被误判为按下。
  {
  for(i=0;i<Data_Num;i++)
  {
  Touch_D_Value = Touch_Data_Buffer[i] - Touch_Data_Buffer[i+1];
  if(Touch_D_Value > Release_Sen_Value)
  {
  Touch_DebCnt--;
  }
  else if(Touch_D_Value < Press_Sen_Value )
  {
  Touch_DebCnt++;
  }
  }

  if(Touch_DebCnt > (Data_Num+3)) //判断递增次数(Press)
  {
  Press_Flag = 0x01;
  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
  }
  else if(Touch_DebCnt < (Data_Num-3)) //判断递减次数(Release)
  {
  Press_Flag = 0xF1;
  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
  }
  }
  else
  {
  Touch_POR_Deb++;

  }
}

以上是Touch Key的实现过程,作为一个原理性的Demo,还有很多细节工作未作,如果采用这种方式到产品中,还需要仔细优化和调试,如防止放电超时,功耗等问题,以上Demo是用NucleoF429上完成的,原理较为简单,可以方便移植到其他MCU上完成,(事实上我先是用NucleoF031K6完成之后再移植到F429上的)。

     以上Demo没有写关于CubeMX的部分,因为只需要一个或者两个中断,并不局限于具体的MCU。另外此文中的所有参数都是针对我的电路的,可以按照具体的电路和应用来调整参数。

     另外提一点CubeMX的使用技巧,我之前用CubeMX生成Code之后,再用CubeMX修改配置后重新生成Code,就会把我自己写的Code给覆盖掉,经微信群(微信群?中文社区微信交流群 )中ecson_2006 和 liposlt 的提醒,CubeMX中用户的Code只需要放在USER CODE BEGIN和USER CODE END 之间,就不会被CubeMX再次覆盖,省了不少事。感谢两位!按照惯例,此文的PDF档和Source Code在附件中,欢迎留言探讨。

---------------------------------------------------------------------------分割线---------------------------------------------------------------------------

下面是我抽奖送开发板的事,这个帖子发的晚了几天,很抱歉。抽奖的原因是版主songbin 发起2016Computex 酷评集赞有奖竞赛 我之后发帖拉票,承诺活动结束后从给我点赞的人中间抽出一人送NucleoL053R8开发板,需要说明的是,这个开发板好久不用,之前也说过会拿出来送给社区的同学,刚好碰到这个活动。二是我并没有限制只能给我点赞。。截至活动结束,我共有37个赞并从先后顺序编序号0~36号。

1.png 2.png 3.png 4.png 5.png 6.png

     因为只抽一人,所以选择一个固定的有记录方便查询的随机数作为基数来抽奖,找来找去发现 【双色球】比较符合预期,今天是2016/06/21星期三,明天周四是第72期的双色球开奖,我以红球作为基数,对36取余数,得到的结果即为中奖的同学。明天我会在这个帖子下面发布抽奖结果,有异议的同学可以留言。

songbin armiddu franco slotg 海king fengjunyang ecson_2006 momososo liposlt yanghanyu 。。。好吧,艾特人太慢了,页面老是奔溃,抽奖结果我再通知各位!

祝各位好运!  

6361.zip
Anonymous
中文社区博客
  • Arm A-Profile构架2022扩展

    Zenon Xiu (修志龙)
    Zenon Xiu (修志龙)
    原文:Arm A-Profile Architecture Developments 2022 - Architectures and Processors blog - Arm Community blogs - Arm Community  作者:Martin Weidmann翻译:修志龙(Zenon Xiu) 与arm构架授权和生态伙伴一起,arm持续演进其构架,开发新功能以满足现有和新市场的要求…
    • October 17, 2022
  • 深入理解 Arm A-profile的non-maskable interrupt -NMI

    Zenon Xiu (修志龙)
    Zenon Xiu (修志龙)
    原文: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/a-profile-non-maskable-interrupts  翻译: 修志龙 Zenon Xiu Arm A-profile构架一个长久以来的局限性是:缺乏对non-maskable interrupt (NMI…
    • August 24, 2022
  • Arm A-Profile 构架2021扩展

    Zenon Xiu (修志龙)
    Zenon Xiu (修志龙)
    原文: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/arm-a-profile-architecture-developments-2021 Martin Weidmann September 8, 2021 翻译注释:Zenon Xiu Arm与arm构架授权公司及生态伙伴一起…
    • August 17, 2022