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

news/2025/2/8 19:12:48 标签: 嵌入式硬件, 蓝桥杯, stm32

全部代码网盘自取

链接:https://pan.baidu.com/s/1PX2NCQxnADxYBQx5CsOgPA?pwd=3ii2 
提取码:3ii2

1、电路图

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

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

打开TIM3时钟并设置参数,中断间隔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

#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

#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

/* 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

#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

#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

/* 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 */


http://www.niftyadmin.cn/n/5845223.html

相关文章

springboot项目的单元测试

文章目录 依赖编写单测代码一些注意点 依赖 依赖包含了 JUnit、Mockito、Spring Test 等常用的测试工具 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><…

安卓/ios脚本开发按键精灵经验小分享

1. 程序的切换 我们经常碰到这样的需求&#xff1a;打开最近的应用列表&#xff0c;选取我们想要的程序。但是每个手机为了自己的风格&#xff0c;样式都有区别&#xff0c;甚至连列表的滑动方向都不一样&#xff0c;我们很难通过模拟操作来识别点击&#xff0c;那么我们做的只…

Java 线程池:7参数配置、4拒绝策略与执行流程详解

1. 为什么需要线程池&#xff1f; 在 Java 并发编程中&#xff0c;线程的创建和销毁是一项昂贵的操作。频繁地创建和销毁线程会带来较高的系统开销&#xff0c;甚至可能因线程数过多而导致 OOM&#xff08;OutOfMemoryError&#xff09; 或 CPU 过载。 线程池&#xff08;Thre…

【Linux网络编程】之守护进程

【Linux网络编程】之守护进程 进程组进程组的概念组长进程 会话会话的概念会话ID 控制终端控制终端的概念控制终端的作用会话、终端、bash三者的关系 前台进程与后台进程概念特点查看当前终端的后台进程前台进程与后台进程的切换 进程组 进程组的概念 当我们使用以下命令查与…

C#冒泡排序,选择排序

冒泡排序 冒泡排序是一种简单的排序算法&#xff0c;它重复地遍历要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就把它们交换过来。这个过程一直进行&#xff0c;直到没有需要交换的元素为止&#xff0c;这时数列就排序完成了。这种算法的名字来源…

kafka消费端之消费者协调器和组协调器

文章目录 概述回顾历史老版本获取消费者变更老版本存在的问题 消费者协调器和组协调器新版如何解决老版本问题再均衡过程**第一阶段CFIND COORDINATOR****第二阶段&#xff08;JOINGROUP&#xff09;**选举消费组的lcader选举分区分配策略 第三阶段&#xff08;SYNC GROUP&…

Rust 核心语法总结

一、Rust 核心语法总结 1. 基础语法 变量绑定 let x = 5; // 不可变绑定 let mut y = 10; // 可变绑定数据类型 标量类型:i32, u32, f64, bool, char复合类型:元组 (i32, f64)、数组 [i32; 5]字符串:String(堆分配)、&str(切片)所有权系统 所有权规则…

Day54:eval()函数

在 Python 中,eval() 函数是一个内置函数,用于执行一个字符串表达式,并返回该表达式的计算结果。这个函数将字符串当作 Python 表达式来执行,它可以接受一个字符串作为输入,然后求值并返回结果。 今天我们将学习如何使用 eval() 函数,包括它的基本用法、常见应用以及潜在…