STM32——KEY按键
一、基础工程
1. 查看引脚
B1~B4 对应引脚 为 PB0,PB1,PB2,PA0。按键按下后,接入低电平;抬起后为高电平。
2. CubeMX配置
4个对应引脚配置成 GPIO_Input。
3. 所用HAL库函数
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
GPIO_PinState bitstatus;
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
if ((GPIOx->IDR & GPIO_Pin) != 0x00U)
{
bitstatus = GPIO_PIN_SET;
}
else
{
bitstatus = GPIO_PIN_RESET;
}
return bitstatus;
}
功能:读取引脚电平状态
参数说明:
GPIOx:端口号 GPIOA,GPIOB,GPIOC
GPIO_Pin:引脚号 GPIO_PIN_0,GPIO_PIN_1,GPIO_PIN_2 ...
返回值:
GPIO_PinState PinState:引脚的状态 GPIO_PIN_SET , GPIO_PIN_RESET
4. 编写Key_Scan()函数
uint8_t Key_Scan(void)
{
uint8_t key_val=0;
//PB0
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=1;
}
//PB1
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
{
key_val=2;
}
//PB2
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
{
key_val=3;
}
//PA0
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=4;
}
return key_val;
}
5. 简单的例子
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
key_val=Key_Scan();
if(key_val==1)
{
ucled ^= 0x01;
}
Led_Disp(ucled);
}
二、按键代码模板
void Key_Proc(void)
{
key_val = Key_Scan();
key_down = key_val & (key_val ^ key_old);
key_up = ~key_val & (key_val ^ key_old);
key_old = key_val;
if(key_down==1)//1~4
{
//按键1按下,执行相应操作
}
}
- key_val 读取按键值
- key_down 按键下降沿检测,只在按键按下瞬间为按键值,其他时刻全为0
- key_up 按键上升沿检测,只在按键抬起瞬间为按键值,其他时刻全为0
- key_old 保存上一次检测按键值
案例1:key1控制LED1开和关
注意点,书写规范。
- 声明都在 *.h 文件中
- Led_Disp() 和 KeyScan() 都涉及到 GPIO,那么就在 gpio.c 进行定义。
//gpio.h
/* USER CODE BEGIN Prototypes */
void Led_Disp(uint8_t ucled);
uint8_t Key_Scan(void);
/* USER CODE END Prototypes */
//gpio.c
/* USER CODE BEGIN 2 */
void Led_Disp(uint8_t ucled)
{
HAL_GPIO_WritePin(GPIOC,0xFF<<8,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); // PD2
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,ucled<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
uint8_t Key_Scan(void)
{
uint8_t key_val=0;
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=1;
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
{
key_val=2;
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
{
key_val=3;
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=4;
}
return key_val;
}
/* USER CODE END 2 */
//main.h
/* USER CODE BEGIN EFP */
void Led_Proc(void);
void Key_Proc(void);
/* USER CODE END EFP */
//main.c
/* USER CODE BEGIN PV */
uint8_t ucled=0x01;
uint8_t key_val,key_up,key_down,key_old;
/* USER CODE END PV */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Led_Proc();
Key_Proc();
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void Led_Proc(void){
Led_Disp(ucled);
}
void Key_Proc(void){
key_val = Key_Scan();
key_down = key_val & (key_val ^ key_old);
key_up = ~key_val & (key_val ^ key_old);
key_old = key_val;
if(key_down == 1){
ucled ^= 0x01;
}
}
/* USER CODE END 4 */
案例2:按键按下LED亮,松开LED灭
void Key_Proc(void){
key_val=Key_Scan();
key_down = key_val & (key_val^key_old);
key_up = ~key_val & (key_val^key_old);
key_old=key_val;
if(key_down==3)
{
ucled=0x01;//下降沿,按下LED亮
}
if(key_up==3)
{
ucled=0x00;//上升沿,松开LED灭
}
}
三、长按 短按
按键按下后开始计时,如果没有在规定时间内抬起,就是长按,否则就是短按。
- 短按,LED1亮,LED2不亮
- 长按,LED1灭,LED2亮
void Key_Proc(void){
key_val=Key_Scan();
key_down = key_val & (key_val^key_old);
key_up = ~key_val & (key_val^key_old);
key_old=key_val;
if (key_down){
uskey = 0; //按下就从0开始计时
}
if (uskey < 1000){
if(key_up == 1){ //1s 内抬起,短按
ucled = 0x01;
}
if(key_up == 2){
// 按键2短按操作,自行填写
}
}else{
if(key_val == 1){ //1s 后未抬起,长按
ucled = 0x02;
}
if(key_up == 2){
//按键2长按操作
}
}
}
这里的 uskey,出门右转 STM32点亮LED的系统定时器。
四、单击 双击
当按键抬起时,开始计时。
双击:当在规定时间内再次按下该按键;
单击:超时没有按键按下。
案例
双击B1,LED2亮,LED1灭
单击B1,LED1亮,LED2灭
void Key_Proc(void){
key_val=Key_Scan();
key_down = key_val & (key_val^key_old);
key_up = ~key_val & (key_val^key_old);
key_old=key_val;
//抬手
//第一次单击并抬手,要执行下面操作的 flag=0操作
//第二次单击并抬手,要执行下面操作的 flag=1操作
if(key_up){
key_temp = key_up; //记录此时的动作
//flag初始值为0
if(key_flag==0){ //开始计时
uskey = 0;
key_flag = 1;
}else{ //双击结束
key_flag = 0;
}
}
if(key_flag==1){
if(uskey<300){ //没超时
if(key_down==1 && key_temp==1){ //双击B1,点亮灯2
ucled = 0x02;
}
if(key_down==2 && key_temp==2){
//双击B2
}
}
else{
if(key_temp==1){ //超时
ucled = 0x01; //单击B1,亮灯1
}
if(key_temp==2){
//单击B2
}
key_flag=0;
}
}
}
当按键抬起后,key_up会从按键值变0,需要有个临时变量 key_temp 保存按键值。那么双击可以定义为 key_temp=1 && key_down==n。
单击 双击,需要考虑下面3个问题:
- 上电后第一次按下
- 双击结束后的下一次
- 单击结束,超时后的第一次