【蓝桥杯嵌入式】4_key:单击+长按+双击

1、电路图

将4个按键的引脚设置为input,并将初始状态设置为Pull-up(上拉输入)

为解决按键抖动的问题,我们使用定时器中断进行消抖

打开 TIM 3时钟并设置参数,中断间隔10ms,当计数达到10000时溢出。80M/80/10000=100,1/100=0.01s=10ms

1、Prescaler (PSC - 16 bits value): 预分频器,用于分频器计数器时钟。设置为 80-1 表示时钟频率将被分频 80。

2、Counter Mode: 计数器模式,这里设置为 "Up",意味着计数器将从 0 开始向上计数,直到达到自动重装载寄存器的值。

3、Dithering: 抖动功能,用于减少电磁干扰(EMI)。这里设置为 "Disable",表示禁用抖动。

4、Counter Period (AutoReload Register ...): 计数器周期,即自动重装载寄存器的值。设置为 10000-1 表示计数器将从 0 计数到 9999,然后重置为 0。

5、Internal Clock Division (CKD): 内部时钟分频,这里设置为 "No Division",表示内部时钟没有被进一步分频。

6、auto-reload preload: 自动重装载预加载,这里设置为 "Disable",表示在更新事件后立即加载新的周期值,而不是在下一个更新事件之前。

7、Master/Slave Mode (MSM bit): 主/从模式,这里设置为 "Disable",表示定时器工作在独立模式,不作为从属定时器。

8、Trigger Event Selection TRGO: 触发事件选择,这里设置为 "Reset (UG bit from TIMx_EGR)",意味着当更新事件(Update Event)发生时,TRGO(触发输出)将被重置。

打开定时器中断

2、代码

2.1 单击+长按

(1)internet.c

cs 复制代码
#include "interrupt.h"

struct keys key[4]={0};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)
	{
		 key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		 key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		 key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		 key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(uchar i=0;i<4;i++)	   //遍历4个按键
		{
			//break跳出switch循环后再进行4次for循环,循环结束后在10ms后中断再进入循环。消抖用的一般都是延时,但该程序已将定时器配置为10m
			switch(key[i].judge_sta)
			{
				case 0:
				{
					if(key[i].key_sta==0) //如果key[i].key_sta==0,按键按下,但不能肯定,进入第二步
					{
						key[i].judge_sta=1;
						key[i].key_time=0;
					}
					break;				
				}
				case 1:
				{
					if(key[i].key_sta==0) 	 //如果10ms后判断还是0,则确认是按下
					{
						key[i].judge_sta=2;  //进入第三步判断松手
					}
					else
					{
						key[i].judge_sta=0;
					}
					break;					
				}
				case 2:
				{
					if(key[i].key_sta==1) 	 //如果是1,则说明按键被松开
					{
						key[i].judge_sta=0;  //回到初始状态
						if(key[i].key_time<=70)
						{
							key[i].single_flag=1;//标志位置1
						}
					}
					else
					{
						key[i].key_time++;
						if(key[i].key_time>70) //按键按下超过700ms则判断为长按,因为10ms执行一次,所以设置为70
						{
							key[i].long_flag=1;
						}
					}					
				}
				break;
			}
		}
	}
}

(2)internet.h

cs 复制代码
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_

#include "main.h"
#include "stdbool.h"

struct keys
{
	uchar judge_sta;
	bool key_sta;
	bool single_flag;
	bool long_flag;
	uint key_time;
};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif

(3)main.c

cs 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "interrupt.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
void key_pro(void);
extern struct keys key[4];

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* 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_TIM3_Init();
  /* USER CODE BEGIN 2 */
    HAL_TIM_Base_Start_IT(&htim3);
    LCD_Init();//LCD ij ʼ    
	
    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  key_pro();	  
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* 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 */
void key_pro(void)
{
	if(key[0].single_flag==1)
	{
		LCD_DisplayStringLine(Line0, (uchar *)"single_flag     ");
		key[0].single_flag=0;
	}
	if(key[0].long_flag==1)
	{
		LCD_DisplayStringLine(Line0, (uchar *)"long_flag       ");
		key[0].long_flag=0;
	}
	if(key[0].double_flag==1)
	{
		LCD_DisplayStringLine(Line0, (uchar *)"double_flag       ");
		key[0].double_flag=0;
	}
}
/* 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 */

2.2 单击+长按+双击

(1)internet.c

cs 复制代码
#include "interrupt.h"

struct keys key[4]={0};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM3)
	{
		 key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		 key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		 key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		 key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(int i=0;i<4;i++)	   //遍历4个按键
		{
			switch(key[i].judge_sta)
			{
				case 0:					//按下检测
				{
					if(key[i].key_sta==0) 
					{
						key[i].judge_sta=1;
						key[i].key_time=0;
					}
				}
				break;
				case 1:					//消抖检测
				{
					if(key[i].key_sta==0) 	 //如果按键按下
					{
						key[i].judge_sta=2; 				
					}
					else
					{
						key[i].judge_sta=0;
					}
				}
				break;
				case 2:					//按键操作检测
				{
					if(key[i].key_sta==1 && key[i].key_time<70)  //如果按键松手,并且没有长按
					{
						if(key[i].double_click_en==0)				//按键第一次按下
						{
							key[i].double_click_en=1;				
							key[i].double_click_time=0;			//松手时间清零,准备计时
						}
						else									//click_en=1,按键第二次按下
						{
							key[i].double_flag=1;				
							key[i].double_click_en=0;				//click_en清零
						}
						key[i].judge_sta=0;
					}
					else if(key[i].key_sta==1 && key[i].key_time>=70)//如果按键松手,并且有长按,不执行操作
					{
						key[i].judge_sta=0;
					}
					else											//如果按键没有松手,判断是否为长按
					{
						if(key[i].key_time>=70)//按下700ms为长按
						{
							key[i].long_flag=1;
						}
						key[i].key_time++;	 			        
					}
				}
				break;
			}
			if(key[i].double_click_en==1)     //按键第一次按下后
			{
				key[i].double_click_time++;
				if(key[i].double_click_time>=30)//双击时间间隔为300ms
				{
					key[i].single_flag=1;
					key[i].double_click_en=0;
				}
			}
		}
	}
}

(2)internet.h

cs 复制代码
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_

#include "main.h"
#include "stdbool.h"

struct keys
{
    uchar judge_sta;
    bool key_sta;
    bool single_flag;
    bool double_flag;
    bool long_flag;
    uint key_time;
uint double_click_time;         
uint double_click_en;      	
};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif

(3)main.c

cs 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "interrupt.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
void key_pro(void);

extern struct keys key[4];

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* 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_TIM3_Init();
  /* USER CODE BEGIN 2 */
    HAL_TIM_Base_Start_IT(&htim3);
    LCD_Init();//LCD ij ʼ    
	
    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  key_pro();	  
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* 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 */
void key_pro(void)
{
	if(key[0].single_flag==1)
	{
		LCD_DisplayStringLine(Line0, (uchar *)"single_flag     ");
		key[0].single_flag=0;
	}
	if(key[0].long_flag==1)
	{
		LCD_DisplayStringLine(Line0, (uchar *)"long_flag       ");
		key[0].long_flag=0;
	}
	if(key[0].double_flag==1)
	{
		LCD_DisplayStringLine(Line0, (uchar *)"double_flag       ");
		key[0].double_flag=0;
	}
}
/* 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 */
相关推荐
膝盖中箭-_-#4 小时前
【STM32系列】利用MATLAB配合ARM-DSP库设计IIR数字滤波器(保姆级教程)
stm32·matlab·数字滤波器·iir
比特在路上4 小时前
蓝桥杯之c++入门(六)【string(practice)】
c++·职场和发展·蓝桥杯
hurrycry_小亦4 小时前
洛谷题目: P8774 [蓝桥杯 2022 省 A] 爬树的甲壳虫 题解 (本题较简)
算法·职场和发展·蓝桥杯
程序员JerrySUN5 小时前
AIoT 未来趋势:机遇与挑战并存
linux·嵌入式硬件·docker·容器·eureka
阿昊真人5 小时前
HAL库 Systick定时器 基于STM32F103EZT6 野火霸道,可做参考
stm32·单片机·嵌入式硬件
sakabu12 小时前
STM32HAL库RTC时钟
stm32·单片机·实时音视频
电子绿洲14 小时前
售后板子HDMI无输出分析
单片机·嵌入式硬件·智能硬件
LK_0714 小时前
【蓝桥杯—单片机】第十届省赛真题代码题解题笔记 | 省赛 | 真题 | 代码题 | 刷题 | 笔记
笔记·单片机·蓝桥杯