完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
STM32 CubeMX学习:6. 按键的外部中断
1 基础知识 1.1 按键原理图 在探索者 STM32F4开发板上的按键 KEY0 连接在 PE4 上、KEY1 连接在 PE3 上、KEY2 连接在 PE2 上、KEY_UP连接在 PA0 上。如图所示: 在这块开发板上,KEY0、KEY1 和 KEY2 是低电平有效的,而 KEY_UP 是高电平有效的,并且外部都没有上下拉电阻,所以,我们需要在 STM32F4 内部设置上下拉。 1.2 按键软件消抖 考虑到真实的情况,由于按键的机械结构具有弹性,按下时开关不会立刻接通,断开时也不会立刻断开,这就导致按键的输入信号在按下和断开时都会存在抖动,如果不先将抖动问题进行处理,则读取的按键信号可能会出现错误。 为了消除这一问题,我们可以通过软件消抖或者硬件消抖两种方式来实现,我们在这里主要采用软件滤波的实现方法。软件滤波的思想其实非常简单,大家很容易就明白了。抖动的产生在按键按下和松开的两个边沿时刻,也叫下降沿(电平从高到低)和上升沿(电平从低到高)时刻,所以我们只需要在边沿时进行延时,等到按键输入已经稳定再进行信号读取即可,是不是很简单呢? 一般采用软件消抖时,会进行20ms的延时,示波器采集按键波形如图所示。 1.3 外部中断 单片机的外部中断通常是由GPIO的电平跳变引起的中断。在STM32中,每一个GPIO都可以作为外部中断的触发源,外部中断一共有16条线,对应着GPIO的0-15引脚,每一条外部中断都可以与任意一组的对应引脚相连,但不能重复使用。 例如,外部中断Line0可以和PA0,PB0,PC0等任意一条0号引脚相连,但如果已经和PA0相连,就不能同时和PB0,PC0其他引脚相连。大家在规划中断的时候要格外小心呦。 外部中断支持GPIO的三种电平跳变的模式,如下所示: 上升沿中断:当GPIO的电平从低电平跳变成高电平时,引发外部中断。 下降沿中断:当GPIO的电平从高电平跳变成低电平时,引发外部中断。 上升沿和下降沿中断:当GPIO的电平从低电平跳变成高电平和从高电平跳变成低电平时,都能引发外部中断。 2 程序的学习 2.1 按键的外部中断在CubeMX里的配置 STM32的GPIO提供外部中断功能,当GPIO检测到电压跳变时,就会发出中断触发信号给STM32,使程序进入外部中断服务函数。 (今天的程序,我们可以在LED灯的基础之上完成) 将PA0号引脚设置为按键的输入引脚,将其设置为外部中断模式。 接着点开GPIO标签页,对引脚进行如下设置,将GPIO模式设置为升降沿触发的外部中断,上下拉电阻设置为下拉电阻,最后设置用户标签为WK_UP。 在NVIC标签页下,可以看到外部中断已经开启。 点击“GENERATE CODE”生成代码 2.2 HAL_GPIO_ReadPin函数介绍 HAL库提供了读取引脚上的电平的函数HAL_GPIO_ReadPin。该函数说明如下: GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 此函数的返回参数为GPIO_PinState,如果是高电平则返回GPIO_PIN_SET(对应为1),如果是低电平则返回GPIO_PIN_RESET(对应为0); 该函数的作用在于返回引脚电平; 该函数具有两个参数: (1)GPIOx——对应GPIO总线,其中x可以是A…I。例如,PH10,则输入GPIOH (2)GPIO_Pin——对应引脚数。可以是0-15。例如,PH10,则输入GPIO_PIN_10 2.3 中断回调函数介绍 每当产生外部中断时,程序首先会进入外部中断服务函数。在stm32f4xx_it.c中,可以找到函数EXTI0_IRQHandler,它通过调用函数HAL_GPIO_EXTI_IRQHandler对中断类型进行判断,并对涉及中断的寄存器进行处理,在处理完成后,它将调用中断回调函数HAL_GPIO_EXTI_Callback,在中断回调函数中编写在此次中断中需要执行的功能。 2.4 程序中的前后台 在本次实验中,发现主循环和中断回调函数中都有代码。这是一个非常典型的以前后台模式组织的工程。但是,什么是前后台模式呢?我们可以想象一下一个餐厅的运作模式,餐厅往往分为前台的叫餐员和后台的大厨,前台只有在来了客人,或者后台做好了一道菜时才会工作,而后厨则一直在忙着做菜,只有前台来了新的单子或者已经有菜做好了才会停下一会手中的活。 在单片机中,中断就是前台,而循环就是后台,中断只在中断源产生时才会进行相应的处理,而循环则一直保持工作,只有被中断打断时才会暂停。前后台程序的异同可以参见下表: 编写前后台程序时,需要注意尽量避免在前台程序中执行过长或者过于耗时的代码,让前台程序能够尽快执行完毕,以保证其能够实时响应突发的事件,比较繁杂和耗时的任务一般放在后台程序中处理。 前后台模式可以帮助我们提高单片机的时间利用率,从而组织起比较复杂的工程。 2.5 程序流程 我们今天的程序中前后台任务各自承担的任务为: 前台程序——记录按键翻转的状态rising_falling_flag 后台程序——执行处理工作,根据记录的翻转状态进行按键状态的判断 在主循环中,首先通过边沿检测标志 rising_falling_flag 来判断按键是处于按下还是松开的边沿,如果是下降的边沿(rising_falling_flag == GPIO_PIN_RESET)则将LED灯熄灭,如果是如果是上升的边沿(rising_falling_flag == GPIO_PIN_SET)则将LED灯点亮。为了防止误触发,通过边沿检测的判断之后,程序还会再对电平进行一次读取,确认下降沿后跟随的是低电平或者上升沿后跟随的是高电平,如果不是则不切换LED状态。 在中断回调函数中,利用HAL_GPIO_ReadPin对rising_falling_flag进行赋值,从而判断触发中断的是上升沿还是下降沿。 使用exit_flag来实现主循环和中断回调函数之间的互斥,保证中断处理函数中的功能(判断上升/下降沿)只在主循环完成判断之后进行,或者主循环的判断只在中断处理函数运行(即检测到了一次上升沿或者下降沿)之后再进行。 最终main.c文件如下所示 /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * 《h2》《center》© Copyright (c) 2021 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 “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 */ /* 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 */ //backgroud program //后台程序 uint8_t exit_flag = 0; uint8_t rising_falling_flag; /** * @brief exit callback function * @param[in] GPIO_Pin:gpio pin * @retval none */ /** * @brief 外部中断回调 * @param[in] GPIO_Pin:引脚号 * @retval none */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == WK_UP_Pin) { if(exit_flag == 0) { exit_flag = 1; rising_falling_flag = HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin); } } } /* 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 */ HAL_GPIO_WritePin( LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET ); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //foreground program //前台程序 if(exit_flag == 1) { exit_flag = 2; if(rising_falling_flag == GPIO_PIN_RESET) { //debouce //消抖 HAL_Delay(20); if(HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin) == GPIO_PIN_RESET) { HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET); exit_flag = 0; } else { exit_flag = 0; } } else if(rising_falling_flag == GPIO_PIN_SET) { //debouce //消抖 HAL_Delay(20); if(HAL_GPIO_ReadPin(WK_UP_GPIO_Port, WK_UP_Pin) == GPIO_PIN_SET) { HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET); exit_flag = 0; } else { exit_flag = 0; } } } } /* 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_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the CPU, AHB and APB busses clocks */ 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 = 6; RCC_OscInitStruct.PLL.PLLN = 168; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB busses 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_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != 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 */ /* 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, tex: printf(“Wrong parameters value: file %s on line %drn”, file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 3 运行效果 一开始LED0为点亮状态,当按键按下时,红灯熄灭;当按键松开时,红灯点亮 代码我已经放到了我的GitHub仓库,如有需要可以下载使用: 总结 按键也是一种人机交互的操作,同时也可以设置成STM32的输入模式,可以用于设置某项功能的开启等作用。外部中断也是STM32中重要的中断类型,常常用于传感器的数据处理,例如陀螺仪,加速度计,磁力计的数据准备中断,通知stm32已经有新的传感器数据产生。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1802 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1629 浏览 1 评论
1096 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
735 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1685 浏览 2 评论
1944浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
746浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
580浏览 3评论
602浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
565浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 22:48 , Processed in 1.062611 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号