目录
1.简介
这里单片机型号是正点原子F103ZET6精英板,keil5的版本是V5.14,cubeMX的版本是6.6.1
对于STM32的外部中断,中断请求源是外部的IO信号,而产生中断的方法就是通过外部IO口的电平变化,但是值得注意的是,STM32的每个IO口都可以作为外部中断的输入口。STM32F103 的中断控制器支持 19 个外部中断/事件请求,每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置,STM32F103 的 19 个外部中断为:
EXTI 线 0~15:对应外部 IO 口的输入中断。
EXTI 线 16:连接到 PVD 输出。
EXTI 线 17:连接到 RTC 闹钟事件。
EXTI 线 18:连接到 USB 唤醒事件。
EXTI 线 19:连接到以太网唤醒事件。
这是GPIO和中断线的映射关系,可以看出每一个GPIO组的编号相同的IO口被分配在同一根中断线上,但是中断线每次只能连接一个IO口,所以我们要根据要求来判断中断线具体在哪一个IO口上。
硬件的使用不过多介绍,这里我们使用的是中断来检测按键, KEY0控制 DS0,按一次亮,再按一次灭;KEY1 控制 DS1,效果同上;WK_UP 则控制蜂鸣器。
引脚对应关系:
PB5-DS0 (低电平亮)
PE5-DS1 (低电平亮)
PB8-BEEP (高电平发声)
PE4-KEY0 (低电平有效)
PE3-KEY1 (低电平有效)
PA0-WK_UP (高电平有效)
2.配置STM32cubeMX
1.新建文件,芯片选型
选好之后点击Start Project,创建工程模板
2.sys设置和RCC设置
3.配置GPIO输出
注意:都要设置成推挽输出模式
4.配置中断输入并开启中断
注意:KEY0和KEY1设置成下降沿触发,上拉电阻,WK_UP设置成上升沿触发,下拉电阻
5.配置时钟
如图,配置为最大的72MHz
6.设置项目名称以及代码生成设置
勾上这个选项代码更加的简洁分明 ,然后生成代码
3.Keil5里面打开工程并且编写程序
keil5打开的时候如图:
中断触发后在HAL里调用顺序是这样子的:
EXTI?_?_IRQHandler --> HAL_GPIO_EXTI_IRQHandler --> HAL_GPIO_EXTI_Callback
基本上每一个外设都有一个中断公共处理程序(
HAL_GPIO_EXTI_IRQHandler)
,我们需要在中断服务程序中调用它,这个中断公共处理程序会根据外设寄存器中的各个标志位分析产生中断的原因,然后进一步调用中断回调函数(HAL_GPIO_EXTI_Callback
)
由上图,我们在stm32f1xx_hal.c这个里面通过Ctrl+F搜索到相关函数进一步查看中断的触发程序
代码如下:
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)//这个宏函数可以获取相应IO口的某个状态,所以就可以判断是不是GPIO_PIN引发的中断
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);//清除中断标志位
HAL_GPIO_EXTI_Callback(GPIO_Pin);//调用中断回调函数
}
}
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
这里会发现回调函数前面有一个_weak的修饰,这里表示回调函数是一个弱函数,通俗表达就是系统定义了这样一个替补函数,如果用户不去定义这个函数的话,这个函数就会被编译;但是如果用户自己定义了同名,同返回值,同参数表的函数,就可以覆盖这个_weak函数,这样就可以实现函数在HAL库中先定义,并且用户还可以重写它。
这里再总结一下HAL库对于中断的完整处理过程:
1.中断请求源(如GPIO)请求中断
2.中断被触发,跳转到中断服务程序
3.中断服务程序调用中断公共处理函数
4.中断公共处理函数分析处理中断
5.处理结果就是调用公共中断回调函数1,2,3.....
所以在每一次外部中断被触发,并且经过确认之后,就会调用回调函数,所以我们一般在回调函数里面编程。
我们在main.c
文件的最下方定义自己的回调函数来完成在中断中想实现的操作:
/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==GPIO_PIN_4 )
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}
if(GPIO_Pin==GPIO_PIN_3 )
{
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
}
if(GPIO_Pin==GPIO_PIN_0 )
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
}
/* USER CODE END 0 */
这一段代码相对来说很好理解,不过多解释。
利用串口下载软件Flymcu烧录到开发板上,按下复位键,刚开始是所有的灯都亮了,蜂鸣器也在发声,按下那三个按键,这样就实现了上面所说的功能:
KEY0按一次红灯亮,再按一次灭;
KEY1 控制 绿灯,效果同上;
WK_UP 则控制蜂鸣器,也是一下响,一下停。
附上main.c代码:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2022 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 "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 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==GPIO_PIN_4 )
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}
if(GPIO_Pin==GPIO_PIN_3 )
{
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
}
if(GPIO_Pin==GPIO_PIN_0 )
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
}
/* 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 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* 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_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
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_DIV2;
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 */