电容触摸按键实验
电容触摸按键的基本原理(原理图层面)
新电容的产生与作用
我们学过模电的同学知道:我们的手其实相当于一块可以存储感应电荷的金属板,当我们的手靠近屏幕时,我们的手与屏幕下的金属板Cx构成了一个平行电容板,这个电容与Cs杂散电容相并联,“并联电容C=C1+C2”。
充放电性能的变化
我们看见“VCC是一定的;电容C越大代表一定电压下,存储的电荷数量越多(Q=CU),反之,充电到U所花费的时间也更多;电阻R的值一定代表着对电荷的阻碍作用是固定的”。
充电到V=Vth,花费时间TA明显小于TB;我们真实情况下的矩形脉冲如下图所示:
脉冲如何被捕获
当电容从0充电到Vth时,就相当于一个上升沿脉冲。其实这个充放电过程很快,我们一般用的RC充放电电路是为了观察现象因此将时间常数做的很大,但是现实的话我们要快速识别必须将时间常数缩小到一定范围内才OK。
原理图层面的连接
STM_ADC其实只是这个引脚功能的其中一个,我们这里并不是用的引脚的ADC功能,以下为该引脚的复用:
我们这里使用的是PA1的TIM5_CH2功能,用于快速捕获TPAD的有效脉冲沿变化。
硬件配置的大致流程
注:没有按下的时候,充电时间为T1(default)。按下TPAD,电容变大,所以充电时间为T2。我们可以通过检测充放电时间,来判断是否按下。如果T2-T1大于某个值,就可以判断有按键按下。
电容触摸按键的基本原理(软件配置层面)
函数说明
TPAD_InitConfig()函数
void TPAD_InitConfig() // 读取默认电容充电时间
{
u16 RecordChargeTime[10] = {0};
u8 i = 0, temp = 0;
uart_init(115200); // 初始化USART1
TIM_CAP_InitConfig(0xFFFF, 72-1); // 初始化TIM5_CH2的输入配置
for(; i<10; i++)
{
RecordChargeTime[i] = TPAD_GetValue(); // 连续计算10次默认充电时间
}
for(i=0; i<9; i++)
{
if(RecordChargeTime[i] < RecordChargeTime[i+1]) // 冒泡排序(大->小)
{
temp = RecordChargeTime[i];
RecordChargeTime[i] = RecordChargeTime[i+1];
RecordChargeTime[i+1] = temp;
}
}
for(i=1, temp=0; i<9; i++)
{
temp += RecordChargeTime[i]; // 去掉MAX与MIN求平均充电时间
}
TPAD_DefaultValue = temp/8; // 求出TPAD无动作时的默认充电时间
printf("Default:%dusrn",TPAD_DefaultValue); // 向串口打印未触摸时的电容充电时长
}
TPAD_Reset()函数
这里值得注意的是:PA1端口输入输出模式的变化以及TIM_ClearFlag()函数的使用。
PA1端口输入输出模式的变化:
我们会疑问“PA1端口为何要变化输入输出模式?”,在我们上述流程中提及过,我们要对电容充电所造成的上升沿重新计数就必须先将TPAD(PA1)端口置0进行充分的放电。那我们如何放电呢?
在图中,PA1与TPAD通过跳线帽连接在一起,然后为了读取TPAD中产生的上升沿,我们必须将PA1设置为TIM5_CH2的功能,此时PA1为浮空输入模式,但是我们读取完有效的脉冲沿之后,若继续读取必须先进行放电才可以。此时,我们配置PA1为推挽输出模式,放完电后再变成浮空输入模式。
TIM_ClearFlag()库函数的含义:
我们使用库函数的同学会有些疑问:TIM_ClearFlag()函数与 TIM_ClearITPendingBit()函数的区别在哪里?这里我来解说一下。
TIM_ClearFlag()函数是用于“清除标志位的”,比如:当检测到上升沿脉冲(有效的脉冲沿),相应的TIM_IT_CC2标志会出现,其实这些标志位和TIM_ClearITPendingBit ()函数中的中断标志位是一摸一样的,只不过由于未开启中断,事件一旦发生仅仅会使得标志位置1,不会触发中断。 其实如下的程序中我们就是想清除标志位,因此也可以使用TIM_ClearITPendingBit(TIM5, TIM_IT_CC2),这两个函数的功能就是去除相应的标志位,功能相同可以相互转换使用。
TIM_ClearFlag()与TIM_ClearITPendingBit()的函数体对比
TIM_ClearFlag()函数体
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_IT(TIM_IT));
/* Clear the IT pending Bit */
TIMx->SR = (uint16_t)~TIM_IT; // 操作SR寄存器
}
TIM_ClearITPendingBit()的函数体
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
{
/* Check the parameters */
assert_param(IS_TIM_ALL_PERIPH(TIMx));
assert_param(IS_TIM_CLEAR_FLAG(TIM_FLAG));
/* Clear the flags */
TIMx->SR = (uint16_t)~TIM_FLAG; // 操作SR寄存器
}
我们从TIM_ClearFlag()与TIM_ClearITPendingBit()的函数体对比发现,完全一摸一样,可以互换使用。
//复位一次
void TPAD_Reset(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
//设置GPIOA.1为推挽使出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_1); //PA.1输出0,放电
delay_ms(5); // 给予充足的时间进行放电
TIM_SetCounter(TIM5,0); //计数器的初始计数值置零
TIM_ClearFlag(TIM5, TIM_IT_CC2); // 清除TIM5_CH2的上升沿标志位
//重新设置GPIOA.1为浮空输入
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
TPAD_GetValue()函数
u16 TPAD_GetValue() // 获取电容充电时间
{
TPAD_Reset(); // 复位一次
while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2) == RESET)//不断轮询等待捕获上升沿
{
if(TIM_GetCounter(TIM5)>MAX_ARR-500)
{
return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值
}
};
return TIM_GetCapture2(TIM5);
}
这里有几行代码特别值得关注:
if(TIM_GetCounter(TIM5)>MAX_ARR-500)
{
return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值
}
这里的提出了一个“检查错误”的概念,我们可以在检查是否有上升沿脉冲触发的同时检测“电容充电时间是否过长”,如果充电时间过长直接return值结束轮询(while(1)),此时我们认为有效脉冲沿持续时间为此时的计数器的值。
TPAD_GetMaxValue()函数
u16 TPAD_GetMaxValue(u8 SampleNumber) // 捕获4次取MAX
{
u8 temp = TPAD_GetValue();
while(1)
{
temp = temp>TPAD_GetValue()?temp:TPAD_GetValue();
if(--SampleNumber) break;
}
return temp; // 求出TPAD无动作时的默认充电时间
}
TPAD_Scan()
u8 TPAD_Scan() // 扫描TPAD状态且不支持连续按
{
u16 TPAD_MaxValue = TPAD_GetMaxValue(3); // 连续采集3次
static u8 PressedFlag = 1;
uart_init(115200); // 初始化USART1
printf("Moment:%dusrn",TPAD_MaxValue); // 向串口打印当前充电时长
if((TPAD_MaxValue >= TPAD_Threshold + TPAD_DefaultValue)&&PressedFlag)
{
PressedFlag = 0;
return 1;
}
else if(TPAD_MaxValue < TPAD_Threshold + TPAD_DefaultValue)
{
PressedFlag = 1;
return 0;
}
else
{
return 0;
}
}
不支持连续按的精髓代码:
if((TPAD_MaxValue >= TPAD_Threshold + TPAD_DefaultValue)&&PressedFlag)
{
PressedFlag = 0;
return 1;
}
else if(TPAD_MaxValue < TPAD_Threshold + TPAD_DefaultValue)
{
PressedFlag = 1;
return 0;
}
else
{
return 0;
}
// 希望大家理解代码执行的逻辑,我在这里就不仔细讲解了。
我们要清楚的一点是:我们前面编写的所有函数都是为了编写TPAD_Scan()做准备,我们真正在main函数中使用的只有TPAD_Scan()函数。我们在编写函数时的好习惯是:注意哪些是辅助函数,哪些是要在main函数中使用得到的函数,函数的功能一定不要过于集中也不要过于分散。
程序示例
Main.c
#include "tpad.h"
#include "led.h"
#include "timer.h"
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
int main()
{
extern u16 TPAD_DefaultValue; // TPAD的默认充电时间
u8 TPAD_Status = 0, temp = 0;
delay_init(); // systick时钟初始化
LED_InitConfig(); // 初始化LED1&LED0
TPAD_InitConfig(); // 计算TPAD_DefaultValue
uart_init(115200); // 初始化USART1
while(1)
{
TPAD_Status = TPAD_Scan(); // 实时捕获TPAD的动作
if(TPAD_Status == 1)
{
LED1 = !LED1; // 触摸按键动作触发LED1状态翻转
}
temp++;
if(temp == 15)
{
LED0 = !LED0; // LED0作为状态灯使用
}
}
}
Led.c
#include "led.h"
#include "stm32f10x.h"
void LED_InitConfig()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); // 使能LED1,LED0的时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
上一篇:STM32输入捕获实验示例详解
下一篇:OLED自定义显示图像实验(基础->进阶)
推荐阅读最新更新时间:2024-11-11 14:56
推荐帖子
- 如何加载驱动!!!!!
- 请问各位,我编好了1个iic设备的驱动(.h和.c)后,怎么能把它加载到xworks中呢都有什么方法,请高人详解还有就是xxDRV()和xxDevCreate()一般用在什么地方来注册设备如何加载驱动!!!!!不懂,帮顶!需要看你是基于工程的,还是基于命令行方式的。VxWorks是通过网络来下载引导的:方法:1、基于目标板BSP建立一个可下载的工程2、把你驱动的原文件加入到工程中去,然后编译3、编译完成,上电你的目标板,利用网络下载、引导VxWorks
- yangyali314 嵌入式系统
- WiMAX技术综述
- 人类的科学总是不断进步的,技术总是为应运而生的。WiMAX作为一项新兴技术,能够在比Wi-Fi更广阔的地域范围内提供“最后一公里”宽带连接性,由此支持企业客户享受T1类服务以及居民用户拥有相当于线缆/DSL的访问能力。凭借其在任意地点的1~6英里覆盖范围,WiMAX将可以为高速数据应用提供更出色的移动性。此外,凭借这种覆盖范围和高吞吐率,WiMAX还能够提供为电信基础设施、企业园区和Wi-Fi热点提供回程。本文主要针对WiMAX技术产生的背景,技术特点,主要应用,存在问题以及发展趋
- maker RF/无线
- 请问香版,st72324如何进行位操作,cosmicc如何操作,急
- st72324是不是在指定的区域才能位操作cosmicc下有没有直接的操作指令请问香版,st72324如何进行位操作,cosmicc如何操作,急仔细查下,有与或操作吧,不过cosmic的帮助手册有讲位操作的例如var|=0x10;var&=~0x10;直接使用与或操作即可,编译器会选用位操作指令的能不能结构定义如PADDR.1PADDR.2这样能不能结构定义但不是你说的那样,你那
- fjjwwb123456 stm32/stm8
- 无线信号的常识性知识
- 什么是信号,哪里有信号?通常我们在用手机的时候经常会说信号差,手机没信号了,生活中处处都有信号,手机信号是“无线电波”“无线电信号”,它是看不见摸不着的东西。今天我们就来说一说关于信号的知识。图1信号差,难以通话 通常可以用示波器、频谱分析仪等设备来检测无线电信号。可用频率、幅度、相位等参数来描述无线电信号。对于一般的维修人员来说,很少关注信号的相位。 1、信号的频率 频率,指单位时间内信号发生周期性变化的次数。频率的国际单位是赫兹(Hz)。
- Jacktang RF/无线
- micropython的EFM32移植
- https://github.com/osresearch/micropython/tree/efm32/ports/efm32micropython的EFM32移植
- dcexpert MicroPython开源版块
- Multisum 12.0 和13.0破解安装,以及安装包
- 前几天买了SSD,一重装了系统,一些软件就不能用了,所以花时间又重新装了一些软件,发现有的软件安装还是挺繁琐的,对于之前完全没有接触过的新手,也想起之前自己在网络上找资源的艰辛,不多说,我就是来送福利的,另附Multisum12.0Multisum13.0Timson,如果您要查看本帖隐藏内容请回复的安装包和破解说明汉化教程。Multisum12.0和13.0破解安装,以及安装包找了很久,学习下载多多学习学习:pleased:刚好才装遇到此事,看来福利来了,
- logitech66 模拟电子
设计资源 培训 开发板 精华推荐
- MTUDK2-ST-MDOT,基于 868MHz、915MHz 收发器的 MultiConnect mDot 开发套件
- DJI SkyEra 徽章 0.1版本
- LT1615,锂离子至 15V 升压转换器
- 具有放电功能的 150mA 超低噪声 LDO 稳压器的典型应用
- TRK-USB-MPC5602P,用于MC9S08JM60 S08微控制器的StarterTRAK USB开发系统
- 使用 Silicon Labs 的 Si5311-BM 的参考设计
- VL53L1激光测距
- LTC2376-18、18 位、250ksps、低功率 SAR ADC 的典型应用电路
- 使用 Analog Devices 的 LT3470IDDB 的参考设计
- LTC4245 具有 I2C 兼容监视功能的多电源热插拔控制器的典型应用