一、基础工程

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个问题:

    1. 上电后第一次按下
    2. 双击结束后的下一次
    3. 单击结束,超时后的第一次

    作者:年轮不改

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32——KEY按键

    发表回复