在开发板中我们经常需要使用到按键,当按下不同按键时,需要得到不同的结果;按键的状态又分为短按、长按、单击、双击,在这里我只设计了短按、长按、不按的状态。
其中最重要的就是使用到了Switch case 语句,多分支选择,不同case对应不同按键状态
首先定义一下按键的.h文件定义结构体:
KEY.H
#ifndef _KEY_H_
#define _KEY_H_
#include "main.h"
typedef struct{
bool gpio;
uint8_t state;
char kind;
uint16_t count;
}KEY;
extern KEY key[4];
void key_init(void);
void key_interrupt(void);
void key_while(void);
#endif
定义了一个KEY结构体,结构体成员gpio用来存放四个按键的电平状态;state用来存放四个按键的按下或未按下的状态,用来状态机选择case;kind是字符类型,用来标识四个按键的类型‘n’表示未按下,‘s’表示短按,‘l’表示长按;count用于判断长按还是短按的时间计数。
KEY.c
#include "main.h"
#include "Display.h"
KEY key[4];
void key_init(void)//按键的结构体的初始化
{
key[0].gpio=1,key[0].state=0,key[0].kind='n',key[0].count=0;
key[1].gpio=1,key[1].state=0,key[1].kind='n',key[1].count=0;
key[2].gpio=1,key[2].state=0,key[2].kind='n',key[2].count=0;
key[3].gpio=1,key[3].state=0,key[3].kind='n',key[3].count=0;
}//key[i].gpio = 1表示按键未按下的正常状态
/*在key.h中定义了一个结构体,包含gpio用来判断引脚电平、state用来状态机选择case
kind用来判断按键长按还是短按、count用来判断长按的时间计数
*/
void key_interrupt(void)
{
key[0].gpio=HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
key[1].gpio=HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
key[2].gpio=HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
key[3].gpio=HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
for(uint8_t i=0;i<4;++i)
switch(key[i].state){
case 0:
if(key[i].gpio==0) key[i].state=1;
else key[i].state=0;
break;
case 1:
if(key[i].gpio==1){ //松手
if(key[i].count<100) key[i].kind='s';
else if(key[i].count>=100) key[i].kind='l';
key[i].state=0;
key[i].count=0;
}
else key[i].count++;
break;
default: break;
}
}
/* 开启一个通用定时器TIM4用于进行定时计数,预分频系数设定为80-1,AutoReload Register(ARR)
自动重装载系数为10000-1;定时器时钟频率为80MHZ,通过计算定时时间为0.01s即10ms,即每10ms进入一次TIM回调函数运行一次key_interrupt(),count计数值大于100即判定为长按,那么100*0.01s = 1s;长按超过1s就是长按。
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
key_interrupt();
}
}
/*
按键状态循环函数,用于接收按键按下的标识状态后做出后续的动作,key_while()放在主循环中
*/
void key_while(void)
{
if(key[0].kind=='s')
{
//当按键1短按时,相对应的代码操作是什么
key[0].kind='n';
}
if(key[1].kind=='s')
{
//当按键2短按时,相对应的代码操作是什么
key[1].kind='n';
}
if(key[2].kind=='s')
{
//当按键3短按时,相对应的代码操作是什么
key[2].kind='n';
}
if(key[3].kind=='s')
{
//当按键4短按时,相对应的代码操作是什么
key[3].kind='n';
}
if(key[2].kind=='l')
{
//当按键3长按时,相对应的代码操作是什么
key[2].kind='n';
}
//后续的操作类似
}
其中的TIM4除了可以用于计时判断按键长按还是短按外,还有一个重要的用途是用于按键的消抖处理,计时10ms正好用来按键按下和按后的消抖处理,保证按键不会因为弹片的抖动而影响按键状态的判定
主函数main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim2)
{
}
if(htim == &htim3)
{
}
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_TIM17_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim4);
HAL_TIM_Base_Start_IT(&htim17);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
LED_Init();
LED_Ctrl(led|=0x01); //点亮led1
key_init();
LCD_Init();
LCD_Clear(Black);
LCD_SetTextColor(White);
LCD_SetBackColor(Black);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
key_while();//把按键循环检测放在主循环中不断循环检测按键状态
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/