目录
电容触摸按键实验 电容触摸按键的基本原理(原理图层面)
新电容的产生与作用
我们学过模电的同学知道:我们的手其实相当于一块可以存储感应电荷的金属板触摸电容,当我们的手靠近屏幕时,我们的手与屏幕下的金属板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的有效脉冲沿变化。
硬件配置的大致流程
第一步
TPAD引脚的电压先置零,进行电容Cs的放电
第二步
TPAD引脚设置为浮空输入(由于TPAD与PA1接在了一起,因此就是将PA1置为浮空输入模式,为了识别有效的脉冲沿)
第三步
TIM5计数器初始值置0并开启上升沿计数模式
第四步
开启TIM5_CH2的输入捕获模式
第五步
等待充电完成,读取此时的捕获值,“脉冲沿持续时间 = (捕获值-初始值)*单位递增时间”
注:没有按下的时候,充电时间为T1(default)。按下TPAD,电容变大,所以充电时间为T2。我们可以通过检测充放电时间,来判断是否按下。如果T2-T1大于某个值,就可以判断
有按键按下。
电容触摸按键的基本原理(软件配置层面)
函数名称
返回值类型
函数功能
TPAD_Init()
void
用于捕获无接触情况下的电容充电时间,并且配置TIM5_CH2引脚的初始属性
TPAD_Get_Val()
int
获取一次电容充电时间
TPAD_Get_MaxVal()
int
获取4次读取“电容充电时间”的最大值
TPAD_Scan()
char(true or false)
用于扫描判断TPAD是否动作
TPAD_Reset()
void
重置计数器与捕获值(将前一次读取结果重置)
函数说明 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:%dus\r\n",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);
}
上一篇:麦克风电容 动圈和电容的区别 教你怎么选合适自己的麦克风