> 技术文档 > STM32矩阵键盘扫描和按键长短按判定_stm32按键长按短按键值扫描

STM32矩阵键盘扫描和按键长短按判定_stm32按键长按短按键值扫描


矩阵键盘扫描

键值定义

对于4*4的矩阵,先定义一个只可读而内容不可修改的二维数组(这里可学习二位数组的定义及赋值方式):

const uint8_t KeyNumber[4][4]={{\'1\',\'2\',\'3\',\' \'}, {\'4\',\'5\',\'6\',\' \'}, {\'7\',\'8\',\'9\',\' \'}, {\'R\',\'0\',\'O\',\' \'}};

该二维数组的每一个元素和矩阵键盘的按键一一对应,如果想修改按键代表的值,只需对该数值中的元素进行修改。

键盘扫描

对键盘每一行依次置1,逐列读取输入引脚状态。当读取到有按键按下,则退出逐列扫描和逐行扫描

返回值对应于KeyNumber[4][4]数组中设定的值

注意:该程序中无消抖程序,是因为通过Systick每20 ms读取一次按键值,该时长大于按键振动的时间,即可实现消抖功能。常规使用延时函数消抖会造成程序阻塞卡顿,不是理想方法

uint8_t KeyScan_Tick(void){static uint8_t Flag=0;static uint8_t Col=0,Row=0;uint8_t NbOfKey=0;uint16_t GPIO_Pin_Out[4]={Out_Pin_0,Out_Pin_1,Out_Pin_2,Out_Pin_3};uint16_t GPIO_Pin_In [4]={In_Pin_0,In_Pin_1,In_Pin_2,In_Pin_3}; //逐行扫描for(Row=0;Row<4;Row++){ //逐行设置为高电平GPIO_WriteBit(Out_Port, GPIO_Pin_Out[Row], (BitAction) 1); //逐列扫描for(Col=0;Col<4;Col++){ //有按键按下if(GPIO_ReadInputDataBit(In_Port, GPIO_Pin_In[Col])==1){ //退出行扫描的标志位Flag=1; //按键按下退出列扫描break;}} //该行恢复至低电平GPIO_WriteBit(Out_Port, GPIO_Pin_Out[Row], (BitAction) 0);if(Flag==1){ //标志位清0Flag=0; //返回二维数组中对应的按键值NbOfKey=KeyNumber[Row][Col]; //退出行扫描break;}} //若无按键按下返回0return NbOfKey;}

Systick中断服务函数

配置SysTick的重装载值和时钟频率,当计数溢出后,代表1 ms时间到。

//配置SysTick重装载值为9000if(SysTick_Config(9000)){//如果SysTick配置失败则会卡死在这里while(1);}//将SysTick时钟频率设置为9MHzSysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

因SysTick为系统内核,因此无需额外配置NVIC初始化等程序,直接配置SysTick的中断服务函数即可。

void SysTick_Handler(void){static uint8_t circle=0;circle++;if(circle==20){circle=0;ReadPinFlag=1;}}

主函数

该程序实现的功能主要是对按键长按和短按的判定。当短按按键\'1\'时,每按一次使Number加1;长按时(按下时间大于600 ms),每过100 ms是Number加5,从而快速改变Number的值。当短按按键\'2\'时,每按一次使Number减1;长按时(按下时间大于600 ms),每过100 ms是Number减5,从而快速改变Number的值。

volatile uint8_t ReadPinFlag=0;int main(void){static uint8_t Last_KeyNum=0,Current_KeyNum=0;uint32_t Number=0;//配置SysTick重装载值为9000if(SysTick_Config(9000)){//如果SysTick配置失败则会卡死在这里while(1);}//将SysTick时钟频率设置为9MHzSysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);SCL_and_SDA_Init();KeyPinInit();OLED_Init();OLED_ShowString(0, 0, \"Number is:\", OLED_8X16);OLED_Update();while (1){ if(ReadPinFlag==1){static uint8_t PressTime=0;ReadPinFlag=0;Last_KeyNum=Current_KeyNum;//读取按键的键值//未按下返回值为0,按下返回值为\'0\'、\'1\'、\'2\'...Current_KeyNum=KeyScan_Tick();//上一次键值为0,当前键值为非0//代表按键刚被按下if((Last_KeyNum==0)&&(Current_KeyNum==\'1\')){Number++;OLED_ShowNum(0, 16, Number, 4, OLED_8X16);OLED_Update();}//上一次键值为非0,当前状态为非0//代表按键处于持续按下状态if((Last_KeyNum==\'1\')&&(Current_KeyNum==\'1\')){//每20ms,PressTime加1PressTime++;//长按判定时间设置为20*30 msif(PressTime>=30){if(PressTime==100)PressTime=30;//进入长按状态,每5*20 ms使Number加5if(PressTime%5==0)Number+=5;OLED_ShowNum(0, 16, Number, 4, OLED_8X16);OLED_Update();}}//上一次键值为0,当前键值为非0//代表按键刚被按下if((Last_KeyNum==0)&&(Current_KeyNum==\'2\')){Number--;OLED_ShowNum(0, 16, Number, 4, OLED_8X16);OLED_Update();}//上一次键值为非0,当前状态为非0//代表按键处于持续按下状态if((Last_KeyNum==\'2\')&&(Current_KeyNum==\'2\')){//每20ms,PressTime加1PressTime++;//长按判定时间设置为20*30 msif(PressTime>=30){if(PressTime==100)PressTime=30;//进入长按状态,每5*20 ms使Number加5if(PressTime%5==0)Number-=5;OLED_ShowNum(0, 16, Number, 4, OLED_8X16);OLED_Update();}}//上一次键值为非0,当前状态为0//代表按键被松开if((Last_KeyNum!=0)&&(Current_KeyNum==0)){//松开按键后将PressTime清零PressTime=0;}}}}