由于刚好有小项目需要用到触摸按键,stm8s可以节约一块触摸按键的片子,并且成本比较低。在某些对成本比较敏感的应用还是有价值的。现将自己网上找到的资料与自己学习的心得记下与大家分析。ST的底层库对滚动条也支持,效果还是不错的
一、原理分析
ST的电容式触摸按键方案通过一个电阻和感应电极的电容CX构成的阻容(RC)网络的充电/放电时间来检测人体触摸所带来的电容变化。
如上图所示,当人手按下时相当于感应电极上并联了一个电容Cx,增加了感应电极上的电容,感应电极进行充放电的时间会增加,从而检测到按键的状态。
先用开关将 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为检测阀值)。
实际应用中感应电极可以直接在PCB板上绘制成按键、滚轮或滑动条的应用样式,也可以做成弹簧件插在PCB板上,即使隔着绝缘层(玻璃、树脂)也不会对其检测性能有所影响。
二、硬件设计
REF_LOAD:对按键进行充放电的引脚,所有按键必须共用一个LOAD(保证一致性)。也可以用VCC代替。
SH_MCKEY:屏蔽引脚,这个根据实际情况来觉得是否接,一般调试的时候预留着
PD2-PD6 PC6 PC7为用户按键部分
注意:不要将按键检测脚安排在True open drain引脚和OSC1/PA1,OSC2/PA2引脚上(前者无法输出高电平,后者的内部结构与一般IO口不同,不适宜作为按键检测脚使用)
三、软件分析
MCU需要资源
1个16位定时器 (用于采集按键信号: 测量RC充放电时间)
1个8位定时器 (主要作为后处理的时基信号)
void main(void)
{
volatile u16 temp;
CLK_HSICmd(ENABLE);//使能HSI
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);//
CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);//主频16M
Delay(10000);
TSL_Init(); //按键库初始化
Extra_Init();
while(1)
{
temp = uMainCounting;
TSL_Action();//获取按键状态
Runing_Session[0] = uMainCounting - temp;
temp = uMainCounting;
Key_State_Machine();
Runing_Session[1] = uMainCounting - temp;
}
}
void TSL_Init(void)
{
disableInterrupts();
DetectionIntegrator = DETECTION_INTEGRATOR_DEFAULT;
EndDetectionIntegrator = END_DETECTION_INTEGRATOR_DEFAULT;
ECSTimeStep = ECS_TIME_STEP_DEFAULT;
ECSTemporization = ECS_TEMPO_DEFAULT;
RecalibrationIntegrator = RECALIBRATION_INTEGRATOR_DEFAULT;
DetectionTimeout = DTO_DEFAULT;
ECS_K_Fast = ECS_IIR_KFAST_DEFAULT;
ECS_K_Slow = ECS_IIR_KSLOW_DEFAULT;
ECSTimeStepCounter = ECSTimeStep;
ECSTempoCounter = 0;
ECSTempoPrescaler = 0;
TSL_IO_Init();
#if (RTOS_MANAGEMENT == 0)
TSL_Timer_Init();
#endif
#if NUMBER_OF_SINGLE_CHANNEL_KEYS > 0
TSL_SCKey_Init();
#endif
#if NUMBER_OF_MULTI_CHANNEL_KEYS > 0
TSL_MCKey_Init();
#endif
enableInterrupts();
TSLState = TSL_IDLE_STATE;
}