欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 手游 > 关于按键状态机解决Delay给程序带来的问题

关于按键状态机解决Delay给程序带来的问题

2024/10/24 10:19:20 来源:https://blog.csdn.net/2302_79504723/article/details/142672429  浏览:    关键词:关于按键状态机解决Delay给程序带来的问题

问题产生

我在学习中断的过程中,使用EXTI15外部中断,在其中加入HAL_Delay();就会发生报错

错误地方

其它地方配置

问题原因

在中断服务例程(ISR)中使用 HAL_Delay() 会导致问题的原因是:

  • 阻塞性

  • HAL_Delay() 是一个阻塞函数,它通过不断地轮询系统时钟来实现延迟。在 ISR 中调用此函数会阻塞中断处理,使得其他中断无法被及时响应,导致系统的实时性降低。

  • 中断禁止

  • 在某些微控制器中,进入中断处理程序时会自动禁止其他中断。这意味着在 HAL_Delay() 执行期间,其他中断将无法被触发,从而可能导致系统出现未定义行为或错失重要事件。

在嵌入式系统中,尤其是使用 STM32 等微控制器时,并不是说同一时间只能进行一个中断,而是中断是通过优先级和嵌套来管理的。

中断优先级

  • 优先级:每个中断可以被设置优先级。当多个中断同时发生时,只有优先级更高的中断会被处理。
  • 中断嵌套:如果启用了中断嵌套(通常在 NVIC 配置中设置),高优先级的中断可以打断低优先级的中断。

 

HAL_Delay() 不应该放在任何中断服务例程(ISR)中使用。原因如下:

  1. 阻塞性HAL_Delay() 是一个阻塞函数,会使当前执行的任务停止,直到延迟时间结束。这会导致该中断无法及时响应其他中断。

  2. 中断嵌套:在许多微控制器中,进入 ISR 时会禁用其他中断。如果在 ISR 中调用 HAL_Delay(),那么在此期间所有其他中断都会被屏蔽,可能导致系统的实时性下降和错失重要事件。

  3. 系统稳定性:长时间阻塞可能导致系统不稳定,甚至造成死锁或未定义行为。

解决方法

1.按键状态机模式

状态机是一个抽象概念,表示把一个过程抽象为若干个状态之间的切换,这些状态之间
存在一定的联系。状态机的设计主要包括4个要素:
1. 现态:是指当前所处的状态。
2. 条件:当一个条件满足,将会触发一个动作,或者执行一次状态的迁移。
3. 动作:表示条件满足后执行动作。动作执行完毕后,可以迁移到新的状态,也可以
             仍旧保持原状态。动作要素不是必需的,当条件满足后,也可以不执行任何
             动作,直接迁移到新状态。
4. 次态:表示条件满足后要迁往的新状态。

(1)定义枚举变量

  typedef enum{IDLE,//按键按下PRESSED,//按键确定状态RELEASED//按键释放}ButtonState;

(2)定义结构体

  typedef struct {ButtonState State;}KeyState;

(3) 初始化按键状态定义Key变量判断是否开启按键判断

  uint8_t Key=0;void KeyInit(KeyState*KeyKey){Key=1;KeyKey->State=IDLE;}

(4)按键三种状态判断

  void Key_State_Judge(uint8_t *Key ,KeyState*KeyKey){if(*Key==1){switch(KeyKey->State){case IDLE:if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==SET){KeyKey->State=IDLE;}else{KeyKey->State=PRESSED;}break;case PRESSED:if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==SET){KeyKey->State=IDLE;}else{KeyKey->State=RELEASED;HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);}break;case RELEASED:if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==SET){KeyKey->State=IDLE;}else{KeyKey->State=RELEASED;}break;}}}

 (5)总代码演示

/* 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"/* 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 *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(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();/* USER CODE BEGIN 2 */typedef enum{IDLE,//按键按下PRESSED,//按键确定状态RELEASED//按键释放}ButtonState;typedef struct {ButtonState State;}KeyState;uint8_t Key=0;void KeyInit(KeyState*KeyKey){Key=1;KeyKey->State=IDLE;}void Key_State_Judge(uint8_t *Key ,KeyState*KeyKey){if(*Key==1){switch(KeyKey->State){case IDLE:if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==SET){KeyKey->State=IDLE;}else{KeyKey->State=PRESSED;}break;case PRESSED:if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==SET){KeyKey->State=IDLE;}else{KeyKey->State=RELEASED;HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);}break;case RELEASED:if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)==SET){KeyKey->State=IDLE;}else{KeyKey->State=RELEASED;}break;}}}KeyState KeyKey;KeyInit(&KeyKey);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){Key_State_Judge(&Key,&KeyKey);/* 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};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;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_HSI;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_0) != HAL_OK){Error_Handler();}
}/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 *//* GPIO Ports Clock Enable */__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);/*Configure GPIO pin : PA1 */GPIO_InitStruct.Pin = GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/*Configure GPIO pin : PB1 */GPIO_InitStruct.Pin = GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}/* 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 */
 配置图片

2.Delay替换成TIM定时器中断

main.c

#include "stm32f10x.h"                  // Device header
#include "Key.h"
#include "OLED.h"
#include "Timer.h"
#include "LED.h"
uint8_t KeyNum;
int main(void)
{OLED_Init();		Timer_Init();Key_Init();LED_Init();while (1){KeyNum=Key_GetNum();if(KeyNum==1){LED1_Turn();}}
}

Key.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Timer.h"
uint16_t Num;void Delay_TIM()
{TIM_Cmd(TIM2, ENABLE);if(Num==20){Num=0;TIM_Cmd(TIM2, DISABLE);}
}/*** 函    数:按键初始化* 参    数:无* 返 回 值:无*/ 
void Key_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB1和PB11引脚初始化为上拉输入
}/*** 函    数:按键获取键码* 参    数:无* 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手*/
uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0;		//定义变量,默认键码值为0if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键1按下{Delay_TIM();while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);	//等待按键松手Delay_TIM();		//置键码为1KeyNum=1;}return KeyNum;			//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
void TIM2_IRQHandler(){if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){Num++;TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

Key.h

#ifndef __KEY_H
#define __KEY_Hvoid Key_Init(void);
uint8_t Key_GetNum(void);
void Delay_TIM();
#endif

Timer.h

#include "stm32f10x.h"                  // Device headervoid Timer_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);TIM_InternalClockConfig(TIM2);TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period=100-1;TIM_TimeBaseInitStruct.TIM_Prescaler=720-1;TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn ;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStruct);TIM_Cmd(TIM2, DISABLE);
}

Timer.c

#ifndef __Timer_H
#define __Timer_Hvoid Timer_Init(void);#endif

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com