總:STM32——學習總綱
一、什么是中斷
1.1 作用與意義
1.2 STM32 GPIO 外部中斷簡圖
二、NVIC
2.1 NVIC 基本概念
Nested vectored interrupt controller,嵌套向量中斷控制器,屬于內核(M3、M4、M7)
用不到很多的優先級,允許廠商裁剪?
內核中斷、外部中斷,都有其對應的中斷服務函數,是中斷的入口。被定義在中斷向量表中。
?
2.1.1 中斷向量表
STM32 是 32位單片機每一次取地址32位,也就是4byte。
main函數優先級最低。
STM32F1參考手冊:
2.2 NVIC 相關寄存器?
2.3 NVIC 工作原理
??
2.4 STM32 中斷優先級基本概念
?
2.5 STM32 中斷優先級分組
IPR寄存器只是用高四位 [7:4]分配優先級。x位表示 2^x 個優先級。
舉例:
2.6 STM32 NVIC使用
步驟 | 操作寄存器 | HAL庫 | |
1 | 設置中斷分組 | AIRCR[10:8] | HAL_NVIC_SetPriorityGrouping |
2 | 設置中斷優先級 | IPRx [7:4] | HAL_NVIC_SetPriority |
3 | 使能中斷 | ISERx | HAL_NVIC_EnableIRQ |
除了這三個HAL庫函數,還有其他不常用的NVIC函數。
一般在HAL_Init() 函數中設置分組2:NVIC_PRIORITYGROUP_2。
/*** @brief This function is used to initialize the HAL Library; it must be the first* instruction to be executed in the main program (before to call any other* HAL function), it performs the following:* Configure the Flash prefetch.* Configures the SysTick to generate an interrupt each 1 millisecond,* which is clocked by the HSI (at this stage, the clock is not yet* configured and thus the system is running from the internal HSI at 16 MHz).* Set NVIC Group Priority to 4.* Calls the HAL_MspInit() callback function defined in user file* "stm32f1xx_hal_msp.c" to do the global low level hardware initialization** @note SysTick is used as time base for the HAL_Delay() function, the application* need to ensure that the SysTick time base is always set to 1 millisecond* to have correct HAL operation.* @retval HAL status*/
HAL_StatusTypeDef HAL_Init(void)
{/* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \defined(STM32F102x6) || defined(STM32F102xB) || \defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \defined(STM32F105xC) || defined(STM32F107xC)/* Prefetch buffer is not available on value line devices */__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE *//* Set Interrupt Group Priority */HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */HAL_InitTick(TICK_INT_PRIORITY);/* Init the low level hardware */HAL_MspInit();/* Return function status */return HAL_OK;
}
/*** @brief Sets the priority grouping field (preemption priority and subpriority)* using the required unlock sequence.* @param PriorityGroup: The priority grouping bits length. * This parameter can be one of the following values:* @arg NVIC_PRIORITYGROUP_0: 0 bits for preemption priority* 4 bits for subpriority* @arg NVIC_PRIORITYGROUP_1: 1 bits for preemption priority* 3 bits for subpriority* @arg NVIC_PRIORITYGROUP_2: 2 bits for preemption priority* 2 bits for subpriority* @arg NVIC_PRIORITYGROUP_3: 3 bits for preemption priority* 1 bits for subpriority* @arg NVIC_PRIORITYGROUP_4: 4 bits for preemption priority* 0 bits for subpriority* @note When the NVIC_PriorityGroup_0 is selected, IRQ preemption is no more possible. * The pending IRQ priority will be managed only by the subpriority. * @retval None*/
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
/*** @brief Sets the priority of an interrupt.* @param IRQn: External interrupt number.* This parameter can be an enumerator of IRQn_Type enumeration* (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xx.h))* @param PreemptPriority: The preemption priority for the IRQn channel.* This parameter can be a value between 0 and 15* A lower priority value indicates a higher priority * @param SubPriority: the subpriority level for the IRQ channel.* This parameter can be a value between 0 and 15* A lower priority value indicates a higher priority. * @retval None*/
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
IRQn_Type:是中斷號枚舉類型,對于中斷向量表中斷位置(STM32f1參考手冊)。
PreemptPriority:搶占優先級
SubPriority:響應優先級
/*** @brief Enables a device specific interrupt in the NVIC interrupt controller.* @note To configure interrupts priority correctly, the NVIC_PriorityGroupConfig()* function should be called before. * @param IRQn External interrupt number.* This parameter can be an enumerator of IRQn_Type enumeration* (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xxx.h))* @retval None*/
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
三、EXTI
3.1 EXTI 基本概念
3.1.1 簡介
External interrupt/event Controller,外部中斷事件控制器
在H7中,稱為 Extended interrupt/event Controller,擴展中斷事件控制器。
包含20個產生 事件/中斷請求 的邊沿檢測器,即總共:20條 EXTI 線(F1)
?3.1.2 中斷和事件的理解:
中斷:要進入NVIC,有相應的中斷服務函數,需要CPU處理。
事件:不進入NVIC,僅用于內部硬件自動控制,如 :TIM、DMA、ADC。
中斷是計算機或單片機在執行程序時,因突發事件(如硬件信號、緊急任務等)暫停當前操作,轉去處理新事件,完成后自動返回原程序繼續執行的過程。
事件是 STM32 中實現硬件級快速響應的機制,適用于無需 CPU 干預的自動控制場景。
3.1.3 EXTI 支持的外部中斷/事件請求
互聯性的F1系列才包含以太網,所以非互聯性STM32只有19條 EXTI 線(F1),不包含EXTI線19,以太網喚醒事件。
EXTI線16~...是內部中斷/事件
學習中斷的主要任務是學習 EXTI線 0~15。
3.2 EXTI 主要特性
?掛起狀態位是針對 中斷 來說的,有一個寄存器進行選擇。
??
?3.3 EXTI 工作原理(F1)
主要使用 四個寄存器,一般不使用 軟件觸發。
STM31F1xxx參考手冊
?EXTI_RTSR 上升沿觸發選擇寄存器
(Rise Trigger Select Register)
EXTI_FTSR 下降沿觸發選擇寄存器
(Fall Trigger Select Register)
雙邊沿觸發設置
雙邊沿則控制對應位的Fall&Rise寄存器位都置1,允許EXTI輸入線x雙邊沿觸發。
EXTI_IMR? ? 中斷屏蔽寄存器
(Interrupt Mask Register)
EXTI_PR? ? ? 請求掛起寄存器
(Pending?Request Register)
一般使用寫入 ‘1’的方式清除這個寄存器,后邊一個辦法不常用。
3.4 F4/F7參考F1,H7 EXTI工作原理見B站視頻
四、EXTI 與 IO 的映射關系
4.1 AFIO 簡介(F1)
內容 | 寄存器 | 說明 | |
1 | 調試IO配置 | AFIO_MAPR[26:24] | 配置JTAG/SWD的開關狀態。可不關注 |
2 | 重映射配置 | AFIO_MAPR | 部分外設IO重映射配置,F1中并不是所有的IO都有重映射功能。可不關注 |
3 | 外部中斷配置* | AFIO_EXTICR 1~4 | 配置EXTI中斷線 0~15 對應到具體那個IO。重點 |
特別注意:
配置AFIO寄存器之前要使能AFIO時鐘,方法如下:
__HAL_RCC_AFIO_CLK_ENABLE();? 對應RCC_APB2ENR寄存器位0。參考手冊8.4.3、8.4.6
4.2 SYSCFG 簡介(F4/F7/H7 使用,見B站)
【【正點原子】手把手教你學STM32 HAL庫開發全集【真人出鏡】STM32入門教學視頻教程 單片機 嵌入式】第60講
4.3 EXTI 與 IO 對應關系*
4.3.1 理論對應關系
右列 0 稱為引腳號,中間A~K稱為 IO分組號。
EXTI0 表示對應 引腳號為0 的 IO。
也就是說,EXTI0 如果被控制對應 PA0,那么別的 Px0 的IO就不能與EXTI0 對應了。
EXTIx 輸入線只能一對一對應 IO。
參考手冊 9.2.5
4.3.2 AFIO寄存器配置關系(F1)
一個AFIO外部中斷配置寄存器可控制4條 EXTI線。
這樣的寄存器有四個,,總共可配置 16條 EXTI線對應 16個 IO。
五、如何使用中斷*
5.1 中斷一圖流
在本節中,主要學習GPIO外部中斷,外設中斷由外設本身配置,不由STM32寄存器配置。
5.2 STM32 EXTI的配置步驟(GPIO外部中斷)
步驟 | 作用 | HAL庫 | |
1 | 使能GPIO時鐘 | 使能GPIO時鐘 | |
2 | 設置GPIO輸入模式 | 上、下拉/浮空輸入 | HAL_GPIO_Init |
3 | 使能 AFIO(F1)/ SYSCFG(F4/F7/H7) | 設置AFIO/SYSCFG 時鐘開啟寄存器 | |
4 | 設置EXTI和IO對應關系 | 選擇 PAx~PKx 一對一對應EXTI的x線,寄存器:AFIO_EXTICR/SYSCFG_EXTICR | |
5 | 設置EXTI屏蔽,上、下沿 | 設置EXTI對應通道的屏蔽和上升沿、下降沿觸發,寄存器:EXTI_IMR、EXTI_RTSR、EXTI_FTSR | |
6 | 設置NVIC | 見 2.6 STM32 NVIC使用 | |
7 | 設計中斷服務函數 | 編寫對應中斷的中斷服務函數!需要清除中斷標志! |
5.3 SMT32 EXTI 的HAL庫設置步驟(GPIO外部中斷)
步驟 | HAL庫 | |
1 | ?使能GPIO時鐘 | __HAL_RCC_GPIOx_CLK_ENABLE |
2 | GPIO/AFIO(SYSCFG)/EXTI | HAL_GPIO_Init 一步到位配置 |
3 | 設置中斷分組 | HAL_NVIC_SetPriorityGrouping 此函數只需要配置一次 |
4 | 設置中斷優先級 | HAL_NVIC_SetPriority |
5 | 使能中斷 | HAL_NVIC_EnableIRQ |
6 | 設計中斷服務函數 | 編寫 EXTIx_IRQHandler,清中斷標志 |
STM32僅有7個外部中斷服務函數,在.s文件中斷向量表定義: EXTI 0~4:5條EXTI線單獨的中斷服務函數 EXTI 9_5:5~8EXTI線共用一個中斷服務函數 EXTI 15_10: 10~15EXTI線共用一個中斷服務函數 |
六、通用外設驅動模型
在此模型中,GPIO外部中斷只使用了1,4兩個步驟。
七、HAL庫中斷回調處理機制介紹
八、編程實戰
通過外部中斷控制一個燈亮滅
IO模式設置選擇
PA0 按下VCC3.3V 高電平,上升沿觸發,不按下需要一個下拉電阻,保持低電平,才有上升沿。
PE2.3.4 按下GND 低電平,下降沿觸發,不按下需要一個上拉電阻,保持高電平,才有下降沿。
工程:【免費】stm32F1nvicexperiment資源-CSDN下載
控制不同的優先級
exti.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"/*** @brief KEY0 外部中斷服務程序* @param 無* @retval 無*/
void KEY0_INT_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN); /* 調用中斷處理公用函數 清除KEY0所在中斷線 的中斷標志位 */__HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN); /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}/*** @brief KEY1 外部中斷服務程序* @param 無* @retval 無*/
void KEY1_INT_IRQHandler(void)
{ HAL_GPIO_EXTI_IRQHandler(KEY1_INT_GPIO_PIN); /* 調用中斷處理公用函數 清除KEY1所在中斷線 的中斷標志位,中斷下半部在HAL_GPIO_EXTI_Callback執行 */__HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN); /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}/*** @brief WK_UP 外部中斷服務程序* @param 無* @retval 無*/
void WKUP_INT_IRQHandler(void)
{ HAL_GPIO_EXTI_IRQHandler(WKUP_INT_GPIO_PIN); /* 調用中斷處理公用函數 清除KEY_UP所在中斷線 的中斷標志位,中斷下半部在HAL_GPIO_EXTI_Callback執行 */__HAL_GPIO_EXTI_CLEAR_IT(WKUP_INT_GPIO_PIN); /* HAL庫默認先清中斷再處理回調,退出時再清一次中斷,避免按鍵抖動誤觸發 */
}/*** @brief 中斷服務程序中需要做的事情在HAL庫中所有的外部中斷服務函數都會調用此函數* @param GPIO_Pin:中斷引腳號* @retval 無*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(20); /* 消抖 */switch(GPIO_Pin){case KEY0_INT_GPIO_PIN:if (KEY0 == 0){LED0_TOGGLE(); /* LED0 狀態取反 */ LED1_TOGGLE(); /* LED1 狀態取反 */ }break;case KEY1_INT_GPIO_PIN:if (KEY1 == 0){LED0_TOGGLE(); /* LED0 狀態取反 */ }break;case WKUP_INT_GPIO_PIN:if (WK_UP == 1){BEEP_TOGGLE(); /* 蜂鳴器狀態取反 */ }break;}
}/*** @brief 外部中斷初始化程序* @param 無* @retval 無*/
void extix_init(void)
{GPIO_InitTypeDef gpio_init_struct;KEY0_GPIO_CLK_ENABLE(); /* KEY0時鐘使能 */KEY1_GPIO_CLK_ENABLE(); /* KEY1時鐘使能 */WKUP_GPIO_CLK_ENABLE(); /* WKUP時鐘使能 */gpio_init_struct.Pin = KEY0_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下升沿觸發 */gpio_init_struct.Pull = GPIO_PULLUP;HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct); /* KEY0配置為下降沿觸發中斷 */gpio_init_struct.Pin = KEY1_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下升沿觸發 */gpio_init_struct.Pull = GPIO_PULLUP;HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &gpio_init_struct); /* KEY1配置為下降沿觸發中斷 */gpio_init_struct.Pin = WKUP_INT_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_IT_RISING; /* 上升沿觸發 */gpio_init_struct.Pull = GPIO_PULLDOWN;HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP配置為下降沿觸發中斷 */HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2); /* 搶占0,子優先級2 */HAL_NVIC_EnableIRQ(KEY0_INT_IRQn); /* 使能中斷線4 */HAL_NVIC_SetPriority(KEY1_INT_IRQn, 1, 2); /* 搶占1,子優先級2 */HAL_NVIC_EnableIRQ(KEY1_INT_IRQn); /* 使能中斷線3 */HAL_NVIC_SetPriority(WKUP_INT_IRQn, 2, 2); /* 搶占2,子優先級2 */HAL_NVIC_EnableIRQ(WKUP_INT_IRQn); /* 使能中斷線0 */
}
exti.h
#ifndef __EXTI_H
#define __EXTI_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 引腳 和 中斷編號 & 中斷服務函數 定義 */ #define KEY0_INT_GPIO_PORT GPIOE
#define KEY0_INT_GPIO_PIN GPIO_PIN_4
#define KEY0_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口時鐘使能 */
#define KEY0_INT_IRQn EXTI4_IRQn
#define KEY0_INT_IRQHandler EXTI4_IRQHandler#define KEY1_INT_GPIO_PORT GPIOE
#define KEY1_INT_GPIO_PIN GPIO_PIN_3
#define KEY1_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口時鐘使能 */
#define KEY1_INT_IRQn EXTI3_IRQn
#define KEY1_INT_IRQHandler EXTI3_IRQHandler#define WKUP_INT_GPIO_PORT GPIOA
#define WKUP_INT_GPIO_PIN GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口時鐘使能 */
#define WKUP_INT_IRQn EXTI0_IRQn
#define WKUP_INT_IRQHandler EXTI0_IRQHandler/******************************************************************************************/void extix_init(void); /* 外部中斷初始化 */#endif
main.c
#include "./stm32f1xx_it.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/EXTI/exti.h"int main(void)
{HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 設置時鐘, 72Mhz */delay_init(72); /* 延時初始化 */usart_init(115200); /* 串口初始化為115200 */led_init(); /* 初始化LED */beep_init(); /* 初始化蜂鳴器 */extix_init(); /* 初始化外部中斷輸入 */LED0(0); /* 先點亮紅燈 */while (1){printf("OK\r\n");delay_ms(1000);}
}