【蓝桥杯】STM32三行按键详解 长按 短按
用的是国信长天的嵌入式方向的开发板,使用的芯片是STM32F103RBT6,基于stm32f1的固件库编程。
当初写下这篇博客的本意也是让自己的知识更加巩固,希望大家看完后能有所收获。话不多说,直接上代码!
程序思路:
程序每1ms进入一次滴答定时器中断函数,函数内的key_count为按键计数变量,当key_count = 20时,即20ms时,令blKEY_Flag = 1;
当blKEY_Flag = 1时,key_proc函数中的uckey_times变量开始计数,当计数值大于40时,为长按。当计数值大于0小于20时,为短按.
具体代码如下:
按键宏定义
unsigned char ucTrg = 0;
unsigned char ucCont = 0;
unsigned char uckey_times = 0;
unsigned char uckey_num = 0;
_Bool blKey_Flag = 0;
#define KEY1 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)
#define KEY2 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)
#define KEY3 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)
#define KEY4 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2)
#define KEYPORT KEY1 | (KEY2 << 1) | (KEY3 << 2) | (KEY4 << 3) | 0xf0
按键扫描函数
//按键扫描
void key_scan(void)
{
unsigned char ucRead = 0;
ucRead = (KEYPORT) ^ 0xff;
ucTrg = ucRead & (ucRead ^ ucCont);
ucCont = ucRead;
}
按键扫描函数解析
理解不了三行按键代码的同学,可以像如下代码一样,将具体值代入变量中,再去一步一步的往下推,一两遍过后,相信你一定可以理解的!
//未按下:
KEYPORT = 0xff;
ucRead = (KEYPORT) ^ 0xff = 0xff ^ 0xff = 0x00;
ucTrg = ucRead & (ucRead ^ ucCount) = 0x00 & (0x00 ^ 0x00) = 0x00;
ucCount = ucRead = 0x00;
//第一次按下KEY1:
KEYPORT = 0xfe;
ucRead = (KEYPORT) ^ 0xff = 0xfe ^ 0xff = 0x01;
ucTrg = ucRead & (ucRead ^ ucCount) = 0x01 & (0x01 ^ 0x00) = 0x01 & 0x01 = 0x01;
ucCount = ucRead = 0x01;
//持续按下KEY1:
KEYPORT = 0xfe;
ucRead = (KEYPORT) ^ 0xff = 0xfe ^ 0xff = 0x01;
ucTrg = ucRead & (ucRead ^ ucCount) = 0x01 & (0x01 ^ 0x01) = 0x00;
ucCount = ucRead = 0x01;
按键短按
这个函数只有短按的功能,对于一般的题目该函数已经足够了,如果题目要求要用到按键长按,那么请直接跳到后面的长按。
//短按
void key_func(void)
{
if (blKey_Flag)
{
blKey_Flag = 0; //在滴答定时器中计时,每20ms使 blKey_Flag=1
key_scan();
switch (ucTrg)
{
case 0x01: led_control(0x01);
break;
case 0x02: led_control(0x04);
break;
case 0x04: led_control(0x10);
break;
case 0x08: led_control(0x40);
break;
}
}
}
按键短按+长按函数
该函数同时具有短按和长按的功能,uckey_times是判断长短按的关键,uckey_times > 40是长按,uckey_times < 20是短按。
//短按+长按
void key_proc(void)
{
if (blKey_Flag)
{
blKey_Flag = 0;
key_scan();
/*长按部分*/
switch (ucCont)
{
case 0x01: uckey_times++, uckey_num = 1;
if (uckey_times > 40) led_control(0x55);
break;
case 0x02: uckey_times++, uckey_num = 2;
if (uckey_times > 40) led_control(0xaa);
break;
case 0x04: uckey_times++, uckey_num = 3;
if (uckey_times > 40) led_control(0x55);
break;
case 0x08: uckey_times++, uckey_num = 4;
if (uckey_times > 40) led_control(0xaa);
break;
}
/*短按部分*/
if (ucTrg == 0x00 && ucCont == 0x00)
{
if (uckey_times > 0 && uckey_times < 20)
{
switch (uckey_num)
{
case 1: led_control(0x01);
break;
case 2: led_control(0x04);
break;
case 3: led_control(0x10);
break;
case 4: led_control(0x40);
break;
}
}
uckey_times = 0;
}
}
}
systick.c
可以看到我是通过轮询的思想来写的代码,通过中断函数的计时变量来控制多久执行一次我的按键程序,该思想也可以用于ADC,LCD显示,USART打印等…
unsigned char key_count = 0;
void SysTick_Handler(void)
{
TimingDelay_Decrement();
// lcd_count++;
// if (lcd_count > 200) lcd_count = 0,LCD_Flag = 1;
//
key_count++;
if (key_count > 20) key_count = 0,blKEY_Flag = 1;
// ADC_count++;
// if (ADC_count > 800) ADC_count = 0,blADC_Flag = 1;
//
// ucUSART_Times++;
// if (ucUSART_Times > 50) ucUSART_Times = 0,RxCounter = 0,memset(RxBuffer2,0,3);
}
第一次写博客,如有不对的地方,欢迎大家在评论区指正。