续ARM单片机触摸传感器原理和应用(3)
使用以下这类代码序列计算三个频率
for(int f=0; f<FREQ_VALUES;f++)
{
if(f!=0){
if(OptimunTscCfgCTPH+1<=16 && OptimunTscCfgCTPL+1<=16){
OptimunTscCfgCTPH++;
OptimunTscCfgCTPL++;
}else{
// 假设 CTPH<= CTPL
OptimunTscCfgDIV *= 2;
OptimunTscCfgCTPH = 1 + (OptimunTscCfgCTPH/2);
OptimunTscCfgCTPL = 1 + (OptimunTscCfgCTPL/2);
}
}
FreqHopp[f].div = OptimunTscCfgDIV;
FreqHopp[f].ctph = OptimunTscCfgCTPH;
FreqHopp[f].ctpl = OptimunTscCfgCTPL;
}
主循环调用
while (1)
{
// Process TSC acquisition
if(TSCAcqDone != 0){
TSCAcqDone = 0;
// Get TSC/TSL keys status
TSL_obj_GroupProcess(&MyObjGroup);
// Process DXS …
// Process ECS algo …
// activate led if key is press …
//FTB, or Fast Transient Burst immunity: wait for 15ms …
// We move to next frequency
MoveToNextFreq();
// After last bank acquisition we restart all bank acquisition
TSCidxGroup = 0;
TSCAcqDone = 0;
TSL_acq_BankConfig(TSCidxGroup);
TSL_acq_BankStartAcq_IT();
}else{
__WFI();
}
}
用于在每次采集后改变频率的API
* Api
void MoveToNextFreq(void)
{
static int f=0;
f++;
f %= FREQ_VALUES; // Change TSC frequency DIV divider htsc.Init.PulseGeneratorPrescaler = INT_TO_PRESC(FreqHopp[f].div); htsc.Init.CTPulseHighLength = INT_TO_CTPH(FreqHopp[f].ctph); htsc.Init.CTPulseLowLength = INT_TO_CTPL(FreqHopp[f].ctpl);
// Re Init TSC IP with New parameters... HAL_TSC_Init(&htsc);
return;
}
注意:
• 当使用所有频率时,所有传感器上的测量值必须相等:在从频率F0变化到频率F1时,从测量的角度来看,任何传感器都不应观察到变化。
• 所选频率必须足够远离彼此,不能是简单的倍数关系。
• 在每个频率下必须执行相同数量的采集。建议使用四个样本来验证触摸检测,而不是三个样本。在两个频率下各执行两次采集。
4.2.8 优化采样电容Cs和串联电阻Rs
增加采样电容Cs会延长采集时间,但可以减少传导噪声的影响。 配置Rskey = 10 kΩ和Cskey = 47 nF可以在传导噪声的免疫力上取得良好的效果。
4.2.9 在VDD上的噪声或纹波测量
在使用MCU通用目的ADC的应用中,可以测量VIN的纹波,并由用户决定是否停止触摸传感采集。同样的测量也可以在VDD侧进行(见下图)。
图25. 在VDD上的VIN纹波测量
关于VDD去耦,可以使用额外的10纳法拉(nF)和1纳法拉(nF)电容器。这种去耦方法分散了去耦电容的阻抗,并扩大了去耦噪声的抑制范围。
图26. VDD上的去耦电容器
4.3 应用层提升可靠性
4.3.1 使用DXS(检测排除系统)
在触摸传感系统中,多重检测指的是系统对单一触摸事件的多次识别,这可能会导致误操作或系统响应的混乱。检测排除系统(DXS)是一种功能,用于确保对触摸事件只进行一次检测,从而提高系统的稳定性和用户交互的准确性。
在触摸系统的编程过程中,应提供了一种避免多重检测的方法。并通过设置USE_DXS来激活:
/*=======================================================================*/ /* Detection Exclusion System (DXS) */ /*=======================================================================*/ /** @defgroup Common_Parameters_DXS 12 - DXS *
@{ */
/** Detection Exclusion System (0=No, 1=Yes) */
#define TSLPRM_USE_DXS (1)
/** @} Common_Parameters_DXS */
4.3.2 确认传感器状态
通常所有传感器都处于TSL_STATEID_RELEASE状态。当一个传感器被按下时,其状态变为TSL_STATEID_DETECT。
用户可以使用如下所示的SensorNbInStateId API检测系统的行为。
/**
* @brief Count Sensor in stateId
* @param StateId Sensor state id, can NOT be OR-ed.
* @retval Sensor number in StateId state
*/
int SensorNbInState(TSL_StateId_enum_T StateId){
int cntStateId=0,cntTKey=0;
// Parse TKey sensors
#if TSLPRM_TOTAL_TOUCHKEYS > 0
for (cntTKey=0;cntTKey<TSLPRM_TOTAL_TOUCHKEYS;cntTKey++){ if(MyTKeys[cntTKey].p_Data->StateId == StateId){
cntStateId++;
}
} #endif // Parse LinRots sensors
#if TSLPRM_TOTAL_LINROTS > 0
for (cntTKey=0;cntTKey<TSLPRM_TOTAL_LINROTS;cntTKey++){ if(MyLinRots[cntTKey].p_Data->StateId == StateId){
cntStateId++;
}
}
#endif
return cntStateId;
}
在主循环中,不在RELEASE(释放)状态的传感器数量被计数,如果超过n个传感器不在稳定状态,则停止采集。这可能会在有噪声干扰或当手同时触摸所有传感器时发生。
下面详细说明的加上DXS(检测排除系统)算法,提高了系统的噪声免疫力。
#define NOISE_COMPUTE_WINDOWS 10 /* Use to monitor noise */
#define NOISE_COMPUTE_THRESHOLD 20 /* Threshold level to decide when noise is too high */
#define NOISE_SUSPEND_DELAY 5000 /* Suspend delay when we detect noise (5s) */
#define NOISE_SENSOR_NOT_IN_RELEASE 3 /* Suspend acquisition when sensors are not in release state */
while (1)
{
if(TSCAcqDone != 0 && WaitEndOfNoiseDuration == 0){
TSCAcqDone = 0;
// Get TSC/TSL keys status
TSL_obj_GroupProcess(&MyObjGroup);
// Point to DXS first object
// Process ECS algo // Application sensor processing
// For FTB test, wait for 15ms // Handle Frequency Hopping
// Handle noise on application level
nbSensorInRelease = SensorNumberInState(TSL_STATEID_RELEASE);
// Handle next acquisition if((((TSLPRM_TOTAL_TOUCHKEYS+TSLPRM_TOTAL_LINROTS)- nbSensorInRelease) >= NOISE_SENSOR_NOT_IN_RELEASE)){
// Noise detect, we suspend sensors acquisition
WaitEndOfNoiseDuration = NOISE_SUSPEND_DELAY;
}else{
// After last bank acquisition we restart all bank acquisition
TSCidxGroup = 0;
TSCAcqDone = 0;
TSL_acq_BankConfig(TSCidxGroup);
TSL_acq_BankStartAcq_IT();
}
}else{
__WFI();
// We assume only 1ms tick interrupt occurs
if(WaitEndOfNoiseDuration > 0) {
WaitEndOfNoiseDuration--;
if(WaitEndOfNoiseDuration==0){
nbSensorInRelease = 0;
WaitEndOfNoiseDuration = 0;
// We restart all acquisition
TSCidxGroup = 0;
TSCAcqDone = 0;
TSL_acq_BankConfig(TSCidxGroup);
TSL_acq_BankStartAcq_IT();
}
}
}
4.2.10 使用噪声水平测量的差异值
/**
* @brief Get noise level
* @param None
* @retval mean noise level
*/
int phaseMeasurementNoise=0;
int GetNoiseLevel(void)
{
int meanNoiseLevel=0,
noiseLevelTKEYS=0,
noiseLevelLROT=0;
uint32_t id = 0, channel=0, div=0; //Reset array
if(phaseMeasurementNoise==0){
#if TSLPRM_TOTAL_TOUCHKEYS > 0
for (channel=0;channel<TSLPRM_TOTAL_TOUCHKEYS;channel++){
for (id=0;id<NOISE_COMPUTE_WINDOWS;id++){ NoiseTKEYS[channel][id] = 0;
}
}
#endif
#if TSLPRM_TOTAL_LINROTS > 0
for (channel=0;channel<TSLPRM_TOTAL_LINROTS;channel++){
for (id=0;id<NOISE_COMPUTE_WINDOWS;id++){
NoiseLROT[channel][id] = 0;
}
}
#endif
}
phaseMeasurementNoise++;
//Start to measure noise, level.....
#if TSLPRM_TOTAL_TOUCHKEYS > 0
// Parse TKey sensors
for (channel=0;channel<TSLPRM_TOTAL_TOUCHKEYS;channel++){
NoiseTKEYS[channel][phaseMeasurementNoise%NOISE_COMPUTE_WINDOWS] =MyTKeys[channel].p_ChD->Delta; }
#endif
#if TSLPRM_TOTAL_LINROTS > 0
// Parse LinRots sensors
for (channel=0;channel<TSLPRM_TOTAL_LINROTS;channel++){
NoiseLROT[channel][phaseMeasurementNoise%NOISE_COMPUTE_WINDOWS] =MyLinRots[channel].p_ChD->Delta; }
#endif
// Compute noise inside the rolling windows
#if TSLPRM_TOTAL_TOUCHKEYS > 0
for (channel=0;channel<TSLPRM_TOTAL_TOUCHKEYS;channel++){
for (id=0;id<NOISE_COMPUTE_WINDOWS;id++){
noiseLevelTKEYS += NoiseTKEYS[channel][id];
}
}
noiseLevelTKEYS = abs(noiseLevelTKEYS);
noiseLevelTKEYS /= NOISE_COMPUTE_WINDOWS;
div += TSLPRM_TOTAL_TOUCHKEYS;
// Will be used to compute noise mean level
#endif
#if TSLPRM_TOTAL_LINROTS > 0
for (channel=0;channel<TSLPRM_TOTAL_LINROTS;channel++){
for (id=0;id<NOISE_COMPUTE_WINDOWS;id++){
noiseLevelLROT += NoiseLROT[channel][id];
}
}
noiseLevelLROT = abs(noiseLevelLROT);
noiseLevelLROT /= NOISE_COMPUTE_WINDOWS;
div += TSLPRM_TOTAL_LINROTS; // Will be used to compute noise mean level
#endif