TSM12M(TSM16C)驱动程序(STM32HAL库为例)
完全个人调试过来,后移植过复旦微MCU(FM33G026)也完全可行,全篇纯手打
开的这个专栏会一直记录自己用过的驱动,有触摸芯片,RFID芯片,语音芯片,显示屏驱动,ADC,各种通讯模块等等,感兴趣的可以一起交流学习
一、简介
TSM12是一种12通道的电容式触摸传感器芯片,采用I2C通信协议。这里建议使用GPIO模拟I2C,即软件I2C,因为很多MCU的硬件I2C不太稳定,调试时问题较多,而且管脚受限制,虽然硬件I2C的速度会更快。(TSM16基本兼容)
二、硬件连接
引脚 | 说明 |
---|---|
EN | 片选信号 |
INT | 中断信号 |
SDA | 数据信号 |
SCL | 时钟信号 |
tips:
- RST引脚拉高复位,一般用不到(可以软件复位)因此可直接接地,节省引脚。
- ID_SEL接地,所以地址是0xD0,接电源则是0xF0。
- 在CSX引脚并一个小电容到地可调节灵敏度,越小越灵敏。不要也行。
- P_CDEG(2:0) ,如上图为110,仅仅只是控制CS1的灵敏度
三、驱动层
1、配置GPIO
在头文件中引脚宏定义(程序中有些繁杂的宏定义这里不列出,看懂即可,像speed等都进行了宏定义,延时函数用的正点原子的延时函数)
/* 片选 */
#define TSM12_EN_PORT GPIOA
#define TSM12_EN_PIN GPIO_PIN_8
/* 中断 */
#define TSM12_INT_PORT GPIOB
#define TSM12_INT_PIN GPIO_PIN_15
/* 数据 */
#define TSM12_SDA_PORT GPIOB
#define TSM12_SDA_PIN GPIO_PIN_7
/* 时钟 */
#define TSM12_SCL_PORT GPIOB
#define TSM12_SCL_PIN GPIO_PIN_6
使用带参宏快速配置引脚,可以减少代码量,或者创建一个配置函数同理
/* 带参宏的方式 */
static GPIO_InitTypeDef GPIO_InitStructure;
#define IO_CFG(GPIOx,PINx,MODE,PULL,SPEED) (GPIO_InitStructure.Pin = PINx,\
GPIO_InitStructure.Mode = MODE,\
GPIO_InitStructure.Pull = PULL,\
GPIO_InitStructure.Speed = SPEED,\
HAL_GPIO_Init(GPIOx,&GPIO_InitStructure))
/* 配置函数的方式 */
void IO_config(GPIOx_Type* GPIOx, uint32_t pin,uint32_t mode,uint32_t pull,uint32_t speed)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = pin;
GPIO_InitStructure.Mode = mode;
GPIO_InitStructure.Pull = pull;
GPIO_InitStructure.Speed = speed;
HAL_GPIO_Init(GPIOx,&GPIO_InitStructure);
}
配置引脚(后面都以带参宏的方式配置为例)
/* 片选,低电平有效 */
#define TSM12_EN_OUT_PP() (IO_CFG(TSM12_EN_PORT,TSM12_EN_PIN,GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_SPEED_VH))
#define TSM12_EN_OUT_1() (TSM12_EN_OUT_PP(),HAL_GPIO_WritePin(TSM12_EN_PORT,TSM12_EN_PIN,GPIO_PIN_SET))
#define TSM12_EN_OUT_0() (TSM12_EN_OUT_PP(),HAL_GPIO_WritePin(TSM12_EN_PORT,TSM12_EN_PIN,GPIO_PIN_RESET))
/* 中断,上拉输入 */
#define TSM12_INT_IPU() (IO_CFG(TSM12_INT_PORT,TSM12_INT_PIN,GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_SPEED_VH))
#define TSM12_INT_READIN() (HAL_GPIO_ReadPin(TSM12_INT_PORT,TSM12_INT_PIN))
#define TSM12_INT_EXTI_FALLING_EN() (IO_CFG(TSM12_INT_PORT,TSM12_INT_PIN,GPIO_MODE_IT_FALLING,GPIO_PULLUP,GPIO_SPEED_VH),\
HAL_NVIC_SetPriority(EXTI4_15_IRQn,2,0),HAL_NVIC_EnableIRQ(EXTI4_15_IRQn))
/* SCL */
#define TSM12_SCL_OUT_PP() (IO_CFG(TSM12_SCL_PORT,TSM12_SCL_PIN,GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_SPEED_VH))
#define TSM12_SCL_OUT_1() (TSM12_SCL_OUT_PP(),HAL_GPIO_WritePin(TSM12_SCL_PORT,TSM12_SCL_PIN,GPIO_PIN_SET))
#define TSM12_SCL_OUT_0() (TSM12_SCL_OUT_PP(),HAL_GPIO_WritePin(TSM12_SCL_PORT,TSM12_SCL_PIN,GPIO_PIN_RESET))
/* SDA 因为还要读取中断时发送的通道值,因此还要配置输入*/
#define TSM12_SDA_OUT_PP() (IO_CFG(TSM12_SDA_PORT,TSM12_SDA_PIN,GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_SPEED_VH))
#define TSM12_SDA_OUT_1() (TSM12_SDA_OUT_PP(),HAL_GPIO_WritePin(TSM12_SDA_PORT,TSM12_SDA_PIN,GPIO_PIN_SET))
#define TSM12_SDA_OUT_0() (TSM12_SDA_OUT_PP(),HAL_GPIO_WritePin(TSM12_SDA_PORT,TSM12_SDA_PIN,GPIO_PIN_RESET))
#define TSM12_SDA_IPU() (IO_CFG(TSM12_SDA_PORT,TSM12_SDA_PIN,GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_SPEED_VH))
#define TSM12_SDA_READIN() (HAL_GPIO_ReadPin(TSM12_SDA_PORT,TSM12_SDA_PIN))
2、启动、停止、应答信号
数据有效性:SCL为高时数据有效
启动与停止信号
tips:
- 处理数据时注意先EN片选(我注释掉是因为在其他调用以下函数的函数中先片选了,看自己习惯)
/********************************************************
* @brief TSM12_I2C_Start()
* 发送TSM12起始信号
* @param
* @arg:
* @return
* none
* @note 硬件总在SCL高电平检测SDA状态,所以先操作SDA
******************************************************/
static void TSM12_I2C_Start(){
TSM12_SCL_OUT_PP();
TSM12_SDA_OUT_PP();
// TSM12_EN_OUT_0(); //片选
TSM12_SDA_OUT_1(); //SDA先拉高
TSM12_SCL_OUT_1();
delay_us(5);
TSM12_SDA_OUT_0();
delay_us(5);
TSM12_SCL_OUT_0();
delay_us(5);
}
/***********************************************
* @brief TSM12_I2C_Stop()
* 发送TSM12停止信号
* @param
* @arg:
* @return
* none
* @note
***********************************************/
static void TSM12_I2C_Stop(){
TSM12_SCL_OUT_PP();
TSM12_SDA_OUT_PP();
// TSM12_EN_OUT_0(); //片选
TSM12_SDA_OUT_0(); //SDA先拉低
TSM12_SCL_OUT_1();
delay_us(5);
TSM12_SDA_OUT_1();
delay_us(5);
// TSM12_EN_OUT_1(); //结束TSM12发送数据
}
应答信号
/***************************************************
* @brief TSM12_I2C_MasterSendAck(BOOL isAck)
* 主机发送TSM12应答或未应答信号
* @param
* @arg:BOOL isAck:是否应答
* M_ACK 0
* M_NACK 1
* @return
* none
* @note 接收方成功接收数据写0,反之写1
**************************************************/
static void TSM12_I2C_MasterSendAck(BOOL isAck){
TSM12_SCL_OUT_PP();
TSM12_SDA_OUT_PP();
if(isAck == M_ACK) //先发送应答信号
{
TSM12_SDA_OUT_0();
}else{
TSM12_SDA_OUT_1();
}
TSM12_SCL_OUT_1();
delay_us(5);
TSM12_SCL_OUT_0();
}
/**************************************************
* @brief TSM12_I2C_SlaveAck(void)
* 检测从机应答信号
* @param
* @arg:
* @return BOOL
* M_ACK 0
* M_NACK 1
* @note 检测从机是否应答
*************************************************/
static BOOL TSM12_I2C_SlaveAck(void){
TSM12_SDA_IPU();
TSM12_SCL_OUT_PP();
TSM12_SCL_OUT_1();
delay_us(5);
if(TSM12_SDA_READIN() == M_NACK)
{
TSM12_SCL_OUT_0();
return M_NACK;
}
TSM12_SCL_OUT_0();
return M_ACK;
}
3、读写函数
7位从机地址跟一个读写方向位
写入一字节数据
tips:
- 时序拉高至少4.7us。
- 数据位,最后一位为读写方向放在地址后(参考上图)。
/**********************************************************************
* @brief TSM12_I2C_WriteByte(u8 u8_data)
* TSM12写入8位数据
* @param
* @arg:u8 u8_data
* @return
* none
* @note R/W (O是Write,1是Read)高位先行,放在发送地址和读写位之后
**********************************************************************/
static void TSM12_I2C_WriteByte(u8 u8_data){
u8 i;
TSM12_SDA_OUT_PP(); //配置SDA推挽输出
TSM12_SCL_OUT_PP(); //配置SCL推挽输出
for(i=8;i>0;i--)
{
if((u8_data & 0x80) == 0x80)
{
TSM12_SDA_OUT_1();
}else{
TSM12_SDA_OUT_0();
}
u8_data <<= 1;
TSM12_SCL_OUT_1();
delay_us(5); //至少4.7us
TSM12_SCL_OUT_0();
delay_us(5);
}
}
读取一字节数据
tips:
- 注意拉高顺序,SCL高电平SDA数据才有效,因此读取的时候先拉高SCL较为稳妥。
/*****************************************************************
* @brief TSM12_I2C_ReadByte(void)
* TSM12读取一个8位数据
* @param
* @arg:
* @return
* u8_data,返回一个8位数据
* @note R/W (O是Write,1是Read)高位先行,放在发送地址和读写位之后
*****************************************************************/
static u8 TSM12_I2C_ReadByte(void){
u8 i;
u8 u8_data = 0x00;
TSM12_SDA_IPU(); //读数据主机上拉输入
TSM12_SCL_OUT_PP();
for(i=8;i>0;i--)
{
TSM12_SCL_OUT_1(); //SCL高电平数据才有效
delay_us(5);
u8_data <<= 1; //先左移,如果运算完左移最终会多左移一次
if(TSM12_SDA_READIN() == M_SET)
{
u8_data |= 0x01;
}
TSM12_SCL_OUT_0();
delay_us(5);
}
return u8_data;
}
从机地址与寄存器映射等宏定义
详细的寄存器控制位查数据手册
#define TSM12_ID_SEL_Address 0xD0 //接GND,如果接VDD则改为0xF0
#define TSM12_Sensitivity1 0x02 //通道1,2灵敏度
#define TSM12_Sensitivity2 0x03
#define TSM12_Sensitivity3 0x04
#define TSM12_Sensitivity4 0x05
#define TSM12_Sensitivity5 0x06
#define TSM12_Sensitivity6 0x07
#define TSM12_CTRL1 0x08
#define TSM12_CTRL2 0x09
#define TSM12_Ref_rst1 0x0A
#define TSM12_Ref_rst2 0x0B
#define TSM12_Ch_hold1 0x0C
#define TSM12_Ch_hold2 0x0D
#define TSM12_Cal_hold1 0x0E
#define TSM12_Cal_hold2 0x0F
#define TSM12_Output1 0x10
#define TSM12_Output2 0x11
#define TSM12_Output3 0x12
#define TSM12_Sens_Reset 0xBB //默认灵敏度
#define TSM12_Sens_User 0x33 //自定义灵敏度
向TSM12写入数据
tips:
- 阴影格子是主机发送从机的数据,白色格子是主机读取从机的数据。
- 数据格式:8位数据和一个应答信号组成。
- 数据写入顺序:先发送(从机地址+读写方向),然后是寄存器地址,再写入寄存器,直到停止信号。
/****************************************************************
* @brief TSM12_I2C_WriteData(u8 regAdd,u8 u8_data)
* TSM12写入数据
* @param
* @arg:u8 u8_data:要写入的数据
* @arg:u8 regAdd:寄存器地址
* @return BOOL
*
* @note 顺序:1)第一字节写入设备地址和R/W方向(1为Read)
* 2)第二字节写入要访问的寄存器内部地址
* 3)接下来就是写入寄存器,并跟随从机应答信号,直到停止信号
*************************************************************/
static BOOL TSM12_I2C_WriteData(u8 regAdd,u8 u8_data){
TSM12_I2C_Start();
TSM12_I2C_WriteByte(TSM12_ID_SEL_Address | 0x00); //设备地址和写方向
if(TSM12_I2C_SlaveAck() != M_ACK) //检测从机应答信号
{
return M_FALSE;
}
TSM12_I2C_WriteByte(regAdd); //寄存器内部地址
if(TSM12_I2C_SlaveAck() != M_ACK)
{
return M_FALSE;
}
TSM12_I2C_WriteByte(u8_data); //数据
if(TSM12_I2C_SlaveAck() != M_ACK)
{
return M_FALSE;
}
TSM12_I2C_Stop(); //停止信号
return M_TRUE;
}
从TSM12读取数据
tips:
- 读数据时要先写入对应寄存器地址,然后再读取数据。
- 读操作最后停止信号前要发送非应答信号。
- 注意前后两次写入地址的读写方向。
/*******************************************************
* @brief TSM12_I2C_ReadData(u8 regAdd)
* TSM12读取数据
* @param
* @arg:u8 regAdd:寄存器地址
* @return u8
*
* @note 顺序:1)写入设备地址
* 2)写入寄存器地址
* 3)开始读数据,先写入读方向设备地址
*********************************************************/
static u8 TSM12_I2C_ReadData(u8 regAdd){
u8 u8_data; //读取的数据
TSM12_I2C_Start();
TSM12_I2C_WriteByte(TSM12_ID_SEL_Address | 0x00); //设备地址和写方向
if(TSM12_I2C_SlaveAck() != M_ACK)
{
return M_FALSE;
}
TSM12_I2C_WriteByte(regAdd); //寄存器内部地址
if(TSM12_I2C_SlaveAck() != M_ACK)
{
return M_FALSE;
}
TSM12_I2C_Stop();
/* */
TSM12_I2C_Start();
TSM12_I2C_WriteByte(TSM12_ID_SEL_Address | 0x01); //设备地址和读方向
if(TSM12_I2C_SlaveAck() != M_ACK)
{
return M_FALSE;
}
u8_data = TSM12_I2C_ReadByte();
TSM12_I2C_MasterSendAck(M_NACK); //停止信号前发送非应答信号
TSM12_I2C_Stop();
return u8_data;
}
4、配置中断
配置引脚中断
static void TSM12_INT_IRQ_init(void){
TSM12_SDA_OUT_1();
TSM12_SCL_OUT_1();
TSM12_INT_IRQ_IPU();
TSM12_INT_IRQ_EXTI_FALLING_EN();
}
中断流程
- 结构体与变量声明(注意使用volatile)
/* 触摸按键状态结构体 */
typedef struct{
vBOOL TouchKey_FLAG_IsPress; //按键是否按下中断标志 volatile
}TouchKeyStatus_st;
static volatile TouchKeyStatus_st touchKeyStatusSt ={0,};
- 中断服务函数:如果触发中断,按下标志置1
/*******************************************
* @brief TSM12_TouchKey_ISR()
* TSM12触摸按键中断服务函数
* @param
* @arg:
* @return
* @note __STATIC_INLINE ; M_YES: 1 按下
*******************************************/
__STATIC_INLINE void TSM12_TouchKey_ISR(){
if(TSM12_INT_IRQ_READIN() == M_RESET) //中断低电平有效
{
if(touchKeyStatusStruct.TouchKey_FLAG_IsPress == 0)
{
touchKeyStatusStruct.TouchKey_FLAG_IsPress = M_YES;
}
}
}
- 中断回调函数
/********************************************************
* @brief HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
* GPIO中断的回调函数
* @param
* @arg:
* @return
* @note gpio库文件中弱定义
*********************************************************/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case TSM12_INT_IRQ_PIN: //PB15
TSM12_TouchKey_ISR();
break;
default:
break;
}
}
5、配置寄存器函数封装
上电初始化
- 初始化流程图(有些顺序打乱没影响)
- 初始化前等待函数(TSM12初始化时不能有按键动作,不然会一段时间失灵)
static void TSM12_WaitInit(void){
u16 timeOut = 300;
while(TSM12_ReadTouchKeyCh() != 0) //触摸键值不为0
{
delay_ms(10); //300*10ms
timeOut--;
if(timeOut == 0)
{break;}
}
}
- TSM12初始化函数
打开休眠功能,一段时间未按键会自动进入休眠模式,功耗在4-8uA
void TSM12_TouchKey_Init(void){
TSM12_WaitInit(); //等待按键释放
delay_ms(100);
TSM12_EN_OUT_0(); //片选
TSM12_I2C_WriteData(TSM12_CTRL2,0X0F); //软件复位和睡眠使能
// TSM12_I2C_WriteData(TSM12_CTRL2,0X07); //失能软件复位和使能睡眠
TSM12_I2C_WriteData(TSM12_Sensitivity1,TSM12_Sens_User); //通道1,2灵敏度
TSM12_I2C_WriteData(TSM12_Sensitivity2,TSM12_Sens_User); //通道3,4灵敏度
TSM12_I2C_WriteData(TSM12_Sensitivity3,TSM12_Sens_User); //通道5,6灵敏度
TSM12_I2C_WriteData(TSM12_Sensitivity4,TSM12_Sens_User); //通道7,8灵敏度
TSM12_I2C_WriteData(TSM12_Sensitivity5,TSM12_Sens_User); //通道9,10灵敏度
TSM12_I2C_WriteData(TSM12_Sensitivity6,TSM12_Sens_User); //通道11,12灵敏度
TSM12_I2C_WriteData(TSM12_CTRL2,0X03); //软件复位和睡眠失能
// TSM12_I2C_WriteData(TSM12_CTRL1,0X3A); //自动切换快慢模式/10s/高输出中断/响应时间4
TSM12_I2C_WriteData(TSM12_Ch_hold1,0x00); //打开1-8通道
TSM12_I2C_WriteData(TSM12_Ch_hold2,0x00); //打开9-12通道
TSM12_I2C_WriteData(TSM12_Ref_rst1,0xFF); //通道1-8参考复位
TSM12_I2C_WriteData(TSM12_Ref_rst2,0x0F); //通道9-12参考复位
TSM12_I2C_WriteData(TSM12_Ref_rst1,0x00); //失能通道1-8参考复位
TSM12_I2C_WriteData(TSM12_Ref_rst2,0x00); //失能通道9-12参考复位
// TSM12_I2C_WriteData(TSM12_Cal_hold1,0x00); //使能1-8通道感应矫正功能
// TSM12_I2C_WriteData(TSM12_Cal_hold2,0x00); //使能9-12通道感应矫正功能
TSM12_I2C_WriteData(TSM12_CTRL2,0X07); //失能软件复位和使能睡眠
TSM12_EN_OUT_1(); //结束TSM12发送数据
TSM12_INT_IRQ_init();
}
其他函数
以下函数建议连着电流表测试(不同应用程序不一样,仅给参考),要注意配置顺序,或者直接在上面函数使能就行
- 进入休眠函数
void TSM12_EnterSleep(void){
TSM12_EN_OUT_0();
delay_us(200);
TSM12_I2C_WriteData(TSM12_Ref_rst1,0x00); //失能通道1-8参考复位
TSM12_I2C_WriteData(TSM12_Ref_rst2,0x00); //失能通道9-12参考复位
TSM12_I2C_WriteData(TSM12_Ch_hold1,0x00); //打开1-8通道
TSM12_I2C_WriteData(TSM12_Ch_hold2,0x00); //打开9-12通道
TSM12_I2C_WriteData(TSM12_CTRL2,0x07); //软件复位不使能,睡眠使能
delay_us(200);
TSM12_EN_OUT_1();
TSM12_INT_IRQ_init(); //使能中断
}
- 退出休眠函数
void TSM12_ExitSleep(void){
TSM12_EN_OUT_0();
delay_us(200);
TSM12_I2C_WriteData(TSM12_CTRL2,0x03); //睡眠失能
delay_us(200);
TSM12_EN_OUT_1();
TSM12_INT_IRQ_init(); //使能中断
}
- 打开或关闭按键通道函数
void TSM12_ChannelHold(BOOL isEnable){
if(isEnable)
{
TSM12_I2C_WriteData(TSM12_Ch_hold1, 0x00); //打开1~8所有通道
TSM12_I2C_WriteData(TSM12_Ch_hold2, 0x00); //打开9~12所有通道
}else{
TSM12_I2C_WriteData(TSM12_Ch_hold1, 0xFF); //关闭1~8所有通道
TSM12_I2C_WriteData(TSM12_Ch_hold2, 0x0F); //关闭9~12所有通道
}
}
四、应用层
应用层根据自己的产品功能需求,以下函数供参考
读取按键值函数
/****************************************
* @brief TSM12_ReadTouchKeyCh(void)
* TSM12读取触摸按键值
* @param
* @return u8 按键值
*
* @note
*****************************************/
static u8 TSM12_ReadTouchKeyCh(void){
u8 touchKeyValue = 0; //触摸键值
u8 pBuffer[3]; //存储3通道8位键值数组
u8 *p = pBuffer;
TSM12_EN_OUT_0();
*p = TSM12_I2C_ReadData(TSM12_Output1); //读通道1-4的u8_data
*(p+1) = TSM12_I2C_ReadData(TSM12_Output2); //读通道5-8的u8_data
*(p+2) = TSM12_I2C_ReadData(TSM12_Output3); //读通道9-12的u8_data
TSM12_EN_OUT_1();
if(0 == touchKeyValue)
{
switch(*p) //OUTPUT1
{
case 0x03:
touchKeyValue = M_TOUCHKEY_CH_01;
break;
case 0x0C:
touchKeyValue = M_TOUCHKEY_CH_02;
break;
case 0x30:
touchKeyValue = M_TOUCHKEY_CH_03;
break;
case 0xC0:
touchKeyValue = M_TOUCHKEY_CH_04;
break;
default:
touchKeyValue = 0;
break;
}
}
if(0 == touchKeyValue)
{
switch(*(p+1)) //OUTPUT2
{
case 0x03:
touchKeyValue = M_TOUCHKEY_CH_05;
break;
case 0x0C:
touchKeyValue = M_TOUCHKEY_CH_06;
break;
case 0x30:
touchKeyValue = M_TOUCHKEY_CH_07;
break;
case 0xC0:
touchKeyValue = M_TOUCHKEY_CH_08;
break;
default:
touchKeyValue = 0;
break;
}
}
if(0 == touchKeyValue)
{
switch(*(p+2)) //OUTPUT3
{
case 0x03:
touchKeyValue = M_TOUCHKEY_CH_09;
break;
case 0x0C:
touchKeyValue = M_TOUCHKEY_CH_10;
break;
case 0x30:
touchKeyValue = M_TOUCHKEY_CH_11;
break;
case 0xC0:
touchKeyValue = M_TOUCHKEY_CH_12;
break;
default:
touchKeyValue = 0;
break;
}
}
return touchKeyValue;
}
读取键值优化函数
/*************************************
* @brief TSM12_TouchKey_Scan(void)
* 触摸按键扫描函数
* @param
* @return
* u8 keyValue:按下的按键值
* @note 消抖,扫描键盘有无按键
**************************************/
u8 TSM12_TouchKey_Scan(void){
#define KEY_JITTER_TIMES 2 //按键抖动次数
u8 keyValue = 0; //读取的按键值
static u8 nullKeyCounts = 0; //无效按键次数
static u8 prevKeyValue = 0; //前一次有效键值
u8 returnKeyValue = 0; //返回的键值
if(touchKeyStatusSt.TouchKey_FLAG_IsPress) //先读取键值,BOOL
{
touchKeyStatusSt.TouchKey_FLAG_IsPress = 0; //初始化按下标志位
keyValue = TSM12_ReadTouchKeyCh();
}
if(keyValue == 0) //无效键值
{
returnKeyValue = 0;
if(nullKeyCounts <= KEY_JITTER_TIMES)
{
nullKeyCounts++;
}
}
else if(keyValue != prevKeyValue) //有效键值且不同于上次按键
{
prevKeyValue = keyValue;
returnKeyValue = keyValue;
nullKeyCounts = 0; //重置抖动计数
}
else if(nullKeyCounts > KEY_JITTER_TIMES) //有效键值等于上次按键且超过抖动次数
{
prevKeyValue = keyValue;
returnKeyValue = keyValue;
nullKeyCounts = 0;
}
else{ //有效键值等于上次按键但未超过抖动次数
returnKeyValue = 0;
if(nullKeyCounts <= KEY_JITTER_TIMES)
{
nullKeyCounts++;
}
}
return returnKeyValue;
#undef KEY_JITTER_TIMES
}