1)资料下载:点击资料即可下载

2)对正点原子Linux感兴趣的同学可以加群讨论:935446741

3)关注正点原子公众号,获取最新资料更新

http://weixin.qq.com/r/hEhUTLbEdesKrfIv9x2W (二维码自动识别)

上一章,我们介绍了 STM32F4 的输入捕获功能及其使用。这一章,我们将向大家介绍如

何通过输入捕获功能,来做一个电容触摸按键。在本章中,我们将用 TIM2 的通道 1(PA5)来

做输入捕获,并实现一个简单的电容触摸按键,通过该按键控制 DS1 的亮灭。从本章分为如下

几个部分:

16.1 电容触摸按键简介

16.2 硬件设计

16.3 软件设计

16.4 下载验证

16.1 电容触摸按键简介

触摸按键相对于传统的机械按键有寿命长、占用空间少、易于操作等诸多优点。大家看看

如今的手机,触摸屏、触摸按键大行其道,而传统的机械按键,正在逐步从手机上面消失。本

章,我们将给大家介绍一种简单的触摸按键:电容式触摸按键。

我们将利用探索者 STM32F4 开发板上的触摸按键(TPAD),来实现对 DS1 的亮灭控制。

这里 TPAD 其实就是探索者 STM32F4 开发板上的一小块覆铜区域,实现原理如图 16.1.1 所示:

图 16.1.1 电容触摸按键原理

这里我们使用的是检测电容充放电时间的方法来判断是否有触摸,图中 R 是外接的电容充

电电阻,Cs 是没有触摸按下时 TPAD 与 PCB 之间的杂散电容。而 Cx 则是有手指按下的时候,

手指与 TPAD 之间形成的电容。图中的开关是电容放电开关(由实际使用时,由 STM32F4 的

IO 代替)。

先用开关将 Cs(或 Cs+Cx)上的电放尽,然后断开开关,让 R 给 Cs(或 Cs+Cx)充电,

当没有手指触摸的时候,Cs 的充电曲线如图中的 A 曲线。而当有手指触摸的时候,手指和 TPAD

之间引入了新的电容 Cx,此时 Cs+Cx 的充电曲线如图中的 B 曲线。从上图可以看出,A、B

两种情况下,Vc 达到 Vth 的时间分别为 Tcs 和 Tcs+Tcx。

其中,除了 Cs 和 Cx 我们需要计算,其他都是已知的,根据电容充放电公式:

Vc=V0*(1-e^(-t/RC))

其中 Vc 为电容电压,V0 为充电电压,R 为充电电阻,C 为电容容值,e 为自然底数,t 为

充电时间。根据这个公式,我们就可以计算出 Cs 和 Cx。利用这个公式,我们还可以把探索者

开发板作为一个简单的电容计,直接可以测电容容量了,有兴趣的朋友可以捣鼓下。

在本章中,其实我们只要能够区分 Tcs 和 Tcs+Tcx,就已经可以实现触摸检测了,当充电时间在 Tcs 附近,就可以认为没有触摸,而当充电时间大于 Tcs+Tx 时,就认为有触摸按下(Tx

为检测阀值)。

本章,我们使用 PA5(TIM2_CH1)来检测 TPAD 是否有触摸,在每次检测之前,我们先配置

PA5 为推挽输出,将电容 Cs(或 Cs+Cx)放电,然后配置 PA5 为浮空输入,利用外部上拉电阻

给电容 Cs(Cs+Cx)充电,同时开启 TIM2_CH1 的输入捕获,检测上升沿,当检测到上升沿的时

候,就认为电容充电完成了,完成一次捕获检测。

在 MCU 每次复位重启的时候,我们执行一次捕获检测(可以认为没触摸),记录此时的值,

记为 tpad_default_val,作为判断的依据。在后续的捕获检测,我们就通过与 tpad_default_val 的

对比,来判断是不是有触摸发生。

关于输入捕获的配置,在上一章我们已经有详细介绍了,这里我们就不再介绍。至此,电

容触摸按键的原理介绍完毕。

16.2 硬件设计

本实验用到的硬件资源有:

1) 指示灯 DS0 和 DS1

2) 定时器 TIM2

3) 触摸按键 TPAD

前面两个之前均有介绍,我们需要通过 TIM2_CH1(PA5)采集 TPAD 的信号,所以本实

验需要用跳线帽短接多功能端口(P12)的 TPAD 和 ADC,以实现 TPAD 连接到 PA5。如图 16.2.1

所示:

图 16.2.1 TPAD 与 STM32F4 连接原理图

硬件设置(用跳线帽短接多功能端口的 ADC 和 TPAD 即可)好之后,下面我们开始软件

设计。

16.3 软件设计

软件设计方面,相比上一实验,我们删掉了timer.c和timer.h文件,同时新建了tpad.c和tpad.h

文件。因为 tpad 我们也是使用的定时器输入捕获来实现,所以我们相比上个实验并没有增加任

何库函数相关的文件。

接下来我们看看 tpad.c 文件代码:

#include "tpad.h"

#include "delay.h"

#include "usart.h"

TIM_HandleTypeDef TIM2_Handler; //定时器 2 句柄

#define TPAD_ARR_MAX_VAL 0XFFFFFFFF //最大的 ARR 值(TIM2 是 32 位定时器)

vu16 tpad_default_val=0;

//空载的时候(没有手按下),计数器需要的时间

//初始化触摸按键

//获得空载的时候触摸按键的取值.

//psc:分频系数,越小,灵敏度越高.

//返回值:0,初始化成功;1,初始化失败

u8 TPAD_Init(u8 psc)

{

u16 buf[10];

u16 temp;

u8 j,i;

TIM2_CH1_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);//设置分频系数

for(i=0;i<10;i++)//连续读取 10 次

{

buf[i]=TPAD_Get_Val();

delay_ms(10);

}

for(i=0;i<9;i++)//排序

{

for(j=i+1;j<10;j++)

{

if(buf[i]>buf[j])//升序排列

{

temp=buf[i];

buf[i]=buf[j];

buf[j]=temp;

}

}

}

temp=0;

for(i=2;i<8;i++)temp+=buf[i];//取中间的 8 个数据进行平均

tpad_default_val=temp/6;

printf("tpad_default_val:%drn",tpad_default_val);

if(tpad_default_val>(vu16)TPAD_ARR_MAX_VAL/2)return 1;

//初始化遇到超过 TPAD_ARR_MAX_VAL/2 的数值,不正常!

return 0;

}

//复位一次

//释放电容电量,并清除定时器的计数值

void TPAD_Reset(void)

{

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_5; //PA5

GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出

GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速

HAL_GPIO_Init(GPIOA,&GPIO_Initure);

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); //PA5 输出 0,放电

delay_ms(5);

__HAL_TIM_CLEAR_FLAG(&TIM2_Handler,TIM_FLAG_CC1|

TIM_FLAG_UPDATE);

//清除标志位

__HAL_TIM_SET_COUNTER(&TIM2_Handler,0);

//计数器值归 0

GPIO_Initure.Mode=GPIO_MODE_AF_PP;

//推挽复用

GPIO_Initure.Pull=GPIO_NOPULL;

//不带上下拉

GPIO_Initure.Alternate=GPIO_AF1_TIM2;

//PA5 复用为 TIM2 通道 1

HAL_GPIO_Init(GPIOA,&GPIO_Initure);

}

//得到定时器捕获值

//如果超时,则直接返回定时器的计数值.

//返回值:捕获值/计数值(超时的情况下返回)

u16 TPAD_Get_Val(void)

{

TPAD_Reset();

while(__HAL_TIM_GET_FLAG(&TIM2_Handler,TIM_FLAG_CC1)==RESET)

//等待捕获上升沿

{

if(__HAL_TIM_GET_COUNTER(&TIM2_Handler)>TPAD_ARR_MAX_VAL-

500)

return __HAL_TIM_GET_COUNTER(&TIM2_Handler);

//超时了,直接返回 CNT 的值

};

return HAL_TIM_ReadCapturedValue(&TIM2_Handler,TIM_CHANNEL_1);

}

//读取 n 次,取最大值

//n:连续获取的次数

//返回值:n 次读数里面读到的最大读数值

u16 TPAD_Get_MaxVal(u8 n)

{

u16 temp=0;

u16 res=0;

u8 lcntnum=n*2/3;//至少 2/3*n 的有效个触摸,才算有效

u8 okcnt=0;

while(n--)

{

temp=TPAD_Get_Val();//得到一次值

if(temp>(tpad_default_val*5/4))okcnt++;//至少大于默认值的 5/4 才算有效

if(temp>res)res=temp;

}

if(okcnt>=lcntnum)return res;//至少 2/3 的概率,要大于默认值的 5/4 才算有效

else return 0;

}

//扫描触摸按键

//mode:0,不支持连续触发(按下一次必须松开才能按下一次);

//1,支持连续触发(可以一直按下)

//返回值:0,没有按下;1,有按下;

u8 TPAD_Scan(u8 mode)

{

static u8 keyen=0; //0,可以开始检测;>0,还不能开始检测

u8 res=0;

u8 sample=3; //默认采样次数为 3 次

u16 rval;

if(mode)

{

sample=6;

//支持连按的时候,设置采样次数为 6 次

keyen=0; //支持连按

}

rval=TPAD_Get_MaxVal(sample);

if(rval>(tpad_default_val*4/3)&&rval<(10*tpad_default_val))

//大于 tpad_default_val+(1/3)*tpad_default_val,且小于 10 倍 tpad_default_val,则有效

{

if(keyen==0)res=1; //keyen==0,有效

//printf("r:%drn",rval);

keyen=3;

//至少要再过 3 次之后才能按键有效

}

if(keyen)keyen--;

return res;

}

//定时器 2 通道 1 输入捕获配置

//arr:自动重装值(TIM2 是 32 位的!!)

//psc:时钟预分频数

void TIM2_CH1_Cap_Init(u32 arr,u16 psc)

{

TIM_IC_InitTypeDef TIM2_CH1Config;

TIM2_Handler.Instance=TIM2;

//通用定时器 3

TIM2_Handler.Init.Prescaler=psc;

//分频

TIM2_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;

//向上计数器

TIM2_Handler.Init.Period=arr;

//自动装载值

TIM2_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;

HAL_TIM_IC_Init(&TIM2_Handler);

TIM2_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING;

//上升沿捕获

TIM2_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到 TI1 上

TIM2_CH1Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频

TIM2_CH1Config.ICFilter=0; //配置输入滤波器,不滤波

HAL_TIM_IC_ConfigChannel(&TIM2_Handler,&TIM2_CH1Config,

TIM_CHANNEL_1);//配置 TIM2 通道 1

HAL_TIM_IC_Start(&TIM2_Handler,TIM_CHANNEL_1); //开始捕获 TIM2 的通道 1

}

//定时器 2 底层驱动,时钟使能,引脚配置

//此函数会被 HAL_TIM_IC_Init()调用

//htim:定时器 2 句柄

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)

{

GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_TIM2_CLK_ENABLE();

//使能 TIM2 时钟

__HAL_RCC_GPIOA_CLK_ENABLE();

//开启 GPIOA 时钟

GPIO_Initure.Pin=GPIO_PIN_5;

//PA5

GPIO_Initure.Mode=GPIO_MODE_AF_PP;

//推挽复用

GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH;

//高速

GPIO_Initure.Alternate=GPIO_AF1_TIM2;

//PA5 复用为 TIM2 通道 1

HAL_GPIO_Init(GPIOA,&GPIO_Initure);

}

函数TIM2_CH1_Cap_Init和上一章输入捕获实验中函数TIM5_CH1_Cap_Init的配置过程几

乎是一模一样的,不同的是上一章实验开 TIM5_CH1_Cap_Init 函数最后调用的是函数

HAL_TIM_IC_Start_IT,使能输入捕获通道的同事开启了输入捕获中断,而该函数最后调用的

函数是 HAL_TIM_IC_Start,只是开启了输入捕获通道,并没有开启输入捕获中断。

函数 HAL_TIM_IC_MspInit 是输入捕获通用 MSP 回调函数,该函数的作用是使能定时器

和 GPIO 时钟,配置 GPIO 复用映射关系。该函数功能和输入捕获实验中该函数作用基本类似。

函数 TPAD_Get_Val 用于得到定时器的一次捕获值。该函数先调用 TPAD_Reset,将电容放

电,同时设置通过程序__HAL_TIM_SET_COUNTER(&TIM2_Handler,0)将计数值 TIM2_CNT 设

置为 0,然后死循环等待发生上升沿捕获(或计数溢出),将捕获到的值(或溢出值)作为返回

值返回。

函数 TPAD_Init 用于初始化输入捕获,并获取默认的 TPAD 值。该函数有一个参数,用来

传递分频系数,其实是为了配置 TIM2_CH1_Cap_Init 的计数周期。在该函数中连续 10 次读取

TPAD 值,将这些值升序排列后取中间 6 个值再做平均(这样做的目的是尽量减少误差),并赋

值给 tpad_default_val,用于后续触摸判断的标准。

函数 TPAD_Scan 用于扫描 TPAD 是否有触摸,该函数的参数 mode,用于设置是否支持连

续触发。返回值如果是 0,说明没有触摸,如果是 1,则说明有触摸。该函数同样包含了一个静

态变量,用于检测控制,类似第七章的 KEY_Scan 函数。所以该函数同样是不可重入的。在函

数中,我们通过连续读取 3 次(不支持连续按的时候)TPAD 的值,取最大值和 tpad_default_val*4/3

比较,如果大于则说明有触摸,如果小于,则说明无触摸。其中 tpad_default_val 是我们在调用

TPAD_Init 函数的时候得到的值,然后取其 4/3 为门限值。该函数,我们还做了一些其他的条件

限制,让触摸按键有更好的效果,这个就请大家看代码自行参悟了。

函数 TPAD_Reset 顾名思义,是进行一次复位操作。先设置 PA5 输出低电平,电容放电,

同时清除中断标志位并且计数器值清零,然后配置 PA5 为复用功能浮空输入,利用外部上拉电

阻给电容 Cs(Cs+Cx)充电,同时开启 TIM2_CH1 的输入捕获。

函数 TPAD_Get_MaxVal 就非常简单了,它通过 n 次调用函数 TPAD_Get_Val 采集捕获值,

然后进行比较后获取 n 次采集值中的最大值。

tpad.h 头文件部分代码比较简单,这里不做介绍。

接下来我们看看主函数代码如下:

int main(void)

{

u8 t=0;

HAL_Init(); //初始化 HAL 库

Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz

delay_init(168); //初始化延时函数

uart_init(115200); //初始化 USART

LED_Init();

//初始化 LED

TPAD_Init(8); //初始化触摸按键

while(1)

{

if(TPAD_Scan(0)) //成功捕获到了一次上升沿(此函数执行时间至少 15ms)

{

LED1=!LED1;

//LED1 取反

}

t++;

if(t==15)

{

t=0; LED0=!LED0;

//LED0 取反,提示程序正在运行

}

delay_ms(10);

}

}

该 main 函数比较简单,TPAD_Init(8)函数执行之后,就开始触摸按键的扫描,当有触摸的

时候,对 DS1 取反,而 DS0 则有规律的间隔取反,提示程序正在运行。注意在修改 main 函数

之后,还需要在 main.c 里面添加 tpad.h 头文件,否则会报错哦。

这里还要提醒一下大家,不要把 uart_init(115200);去掉,因为在 TPAD_Init 函数里面,我们

有用到 printf,如果你去掉了 uart_init,就会导致 printf 无法执行,从而死机。

至此,我们的软件设计就完成了。

16.4 下载验证

在完成软件设计之后,将我们将编译好的文件下载到探索者 STM32F4 开发板上,可以看

到 DS0 慢速闪烁,此时,我们用手指触摸 ALIENTEK 探索者 STM32F4 开发板上的 TPAD(右

下角的白色头像),就可以控制 DS1 的亮灭了。不过你要确保 TPAD 和 ADC 的跳线帽连接上了

哦!如图 16.4.1 所示:

图 16.4.1 触摸区域和跳线帽短接方式示意图

同时大家可以打开串口调试助手,每次复位的时候,会收到 tpad_default_val 的值,一般为

250 左右。

正点原子stm32f429 pcb_正点原子【STM32-F407探索者】第十六章 电容触摸按键实验相关推荐

  1. 判断按键值_「正点原子NANO STM32开发板资料连载」第十六章电容触摸按键实验...

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十六章电容 ...

  2. pdfstamper生成pdf无法显示汉字_正点原子STM32F4/F7水星开发板资料连载第四十六章 汉字显示实验...

    1)实验平台:正点原子水星 STM32F4/F7 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载 ...

  3. 【正点原子MP157连载】第十六章 基本定时器实验-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  4. 光盘显示0字节可用_正点原子STM32F4/F7水星开发板资料连载第四十六章 汉字显示实验

    1)实验平台:正点原子水星 STM32F4/F7 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载 ...

  5. 【正点原子STM32连载】 第二十六章 USMART调试组件实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  6. 【正点原子STM32连载】 第十五章 按键输入实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

    1)实验平台:正点原子stm32f103战舰开发板V4 2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420 3)全套实验源码+手册+视 ...

  7. 嵌入式linux系统蜂鸣器实验,「正点原子Linux连载」第四十六章Linux蜂鸣器实验

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 大家将imx35_gpio_hwdata ...

  8. STM32学习心得十九:电容触摸按键实验及相关代码解读

    记录一下,方便以后翻阅~ 主要内容 1) 电容触摸按键原理: 2)部分实验代码解读. 实验内容 手触摸按键后,LED1灯翻转. 硬件原理图 上图,TPAD与STM_ADC用跳线帽相连,即TPAD与PA ...

  9. STM32学习心得二十六:DAC数模转换实验

    记录一下,方便以后翻阅~ 主要内容: 1) DAC数模转换原理: 2) 寄存器和库函数介绍: 3) 相关实验代码解读. 实验功能:系统启动后,按WK_UP键,输出电压加200点,对应电压值200*3. ...

最新文章

  1. 初学Python,我给你总结了常见的17个错误
  2. 基于SmartQQ协议的QQ聊天机器人-7
  3. 指针:自定义函数length,调用它计算字符串的长度
  4. Boost::context模块fiber的circle测试程序
  5. matlab 泡泡图,使用matlab绘制2维、3维气泡图
  6. 打开软件就遍历桌面和D盘的软件名通过UDP广播的方式发出。服务器监听指定的端口可以获得文件夹名称。不会暴露自己的IP
  7. python分数序列求和_Python练习题 019:求分数序列之和
  8. jmeter元件的作用域与执行顺序
  9. SimpleFs文件系统初步二(测试用的块设备构建)
  10. 创建ASP.NET MVC5 应用程序
  11. Windows新建桌面和切换
  12. 树莓派3B+无显示屏安装教程(一)
  13. 医院his系统服务器需要配置,创智和宇 His医院系统单机(有备份数据库)配置...
  14. 基于Python中docx与docxcompose批量合并多个Word文档文件并逐一添加分页符
  15. 51nod 1526 分配笔名(字典树)
  16. mysql中的unix_timestamp函数
  17. osi是什么?计算机有哪几层?每一层有什么作用?
  18. python入门-if嵌套-火车站安检
  19. DHT11温湿度传感器编程详解
  20. 洛谷oj P2802 回家(BFS)

热门文章

  1. Linux下进程间通信的六种机制详解
  2. 《Java从入门到放弃》框架入门篇:hibernate基本配置
  3. unrecognized font family iconfont2
  4. ansible加密敏感数据
  5. Viewpager中改变PagerTabStrip的颜色(背景色,指示条颜色,文字颜色)
  6. 在ppt中直接插入视频和图片-通过托拽
  7. Spring Boot MongoDB 入门
  8. 遇到洋妞不敢搭讪,程序员的羞涩你不懂
  9. 技术人, 请不要封闭自己
  10. Kafka 压测:3 台廉价服务器竟支撑 200 万 TPS