stm32G431总共有111个中断源,所以有时难免有两个或者两个以上的中断一起来临,或者正在处理一个中断服务函数时突然又有一个中断来临,所以微控制器都有一个处理中断的机制。stm32系列芯片用到的机制是:NVIC。
NVIC:嵌套向量中断控制器(Nested Vectored Interrupt Controller),STM32的中有一个强大而方便的NVIC,它是属于CM4内核的器件。NVIC 控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对CM4内核里面的NVIC进行裁剪,把不需要的部分去掉,所以STM32的NVIC是CM4的NVIC的一个子集。
NVIC寄存器定义在core_cm4.h文件中,CM4内核支持256个中断,其中包含了16个系统异常和240个外部中断,并且具有256级的可编程中断设置。但 STM32并没有使用CM4内核的全部东西,而是只用了它的一部分,stm32G431芯片有111个中断,包括9个内核中断和102个可屏蔽中断,具有16级可编程的中断优先级,我们常用的就是这102个可屏蔽中断。
NVIC中断系统
Cortex-M4优先级设定
- 支持3个固定的高优先级和多达256级的可编程优先级,支持128级抢占。
- 每个中断的优先级由一个8位的寄存器来设定,分为高低两个位段。高位段表示抢占优先级,低位段表示响应(子)优先级。 CM4允许最少使用位数为3个位,即至少要支持8级优先级。
- 优先级以MSB对齐,简化程序的跨器件移植
STM32中有两个优先级的概念:抢占式优先级 和 响应优先级(子优先级),每个中断源都需要被指定这两种优先级。
- 具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套在低抢占式优先级的中断中。
- 当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。
- 如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个。
- 如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断向量表中的排位顺序决定先处理哪一个。
EXTI外部中断/时间控制器
STM32G431每个引脚都可以设置为外部线中断输入,实际上,stm32芯片集成了一个外部中断/事件控制器(EXTI),由23个能产生事件/中断请求的边沿检测器组成,每个输入线可以独立地配置输入类型(事件或中断)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。
23个中断/事件请求包括:
- IO可以做为EXTI线(0..15)【常用】
- EXTI 线 16 连接到 PVD(可编程电压监测器,用于掉电检测) 输出
- EXTI 线 17 连接到 RTC 闹钟事件
- EXTI 线 18 连接到 USB OTG FS 唤醒事件
- EXTI 线 19 连接到以太网唤醒事件
- EXTI 线 20 连接到 USB OTG HS(在 FS 中配置)唤醒事件
- EXTI 线 21 连接到 RTC 入侵和时间戳事件
- EXTI 线 22 连接到 RTC 唤醒事件
- 使用外部线中断需要开启AFIO中对应的中断功能。
然后我们常用的是I/O作为外部中断输入,每个引脚都可以设为外部线中断输入。
然后就看看程序代码了
中断向量地址,startup_stm32g431xx.s文件和手册中的对比一下,也就可以发现是差不多了。
NVIC的相关函数
- 设置中断优先级组
一般是nvic_prioritygroup2,所以以后要配置,即2位抢占式优先级 和2位响应优先级
再看看设置中断优先级组底层函数
- 设置中断优先级组
/**
* @brief Set the priority grouping field (pre-emption 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 bit for pre-emption priority,
* 4 bits for subpriority
* @arg NVIC_PRIORITYGROUP_1: 1 bit for pre-emption priority,
* 3 bits for subpriority
* @arg NVIC_PRIORITYGROUP_2: 2 bits for pre-emption priority,
* 2 bits for subpriority
* @arg NVIC_PRIORITYGROUP_3: 3 bits for pre-emption priority,
* 1 bit for subpriority
* @arg NVIC_PRIORITYGROUP_4: 4 bits for pre-emption priority,
* 0 bit for subpriority
* @note When the NVIC_PriorityGroup_0 is selected, IRQ pre-emption is no more possible.
* The pending IRQ priority will be managed only by the subpriority.
* @retval None
*/
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
NVIC_SetPriorityGrouping(PriorityGroup);
}
- 设置中断优先级
/**
* @brief Set 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 (stm32g4xxxx.h))
* @param PreemptPriority: The pre-emption 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)
{
uint32_t prioritygroup;
/* Check the parameters */
assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
prioritygroup = NVIC_GetPriorityGrouping();
NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority));
}
HAL_NVIC_SetPriority函数用于设置一个中断的优先级,它有三个形参,第一个为IRQn_Type类型参数,指定中断源。 第二个和第三个形参分别设定中断的抢占式优先级和响应优先级,这个的设置要与中断组配合使用。
HAL_NVIC_SetPriority函数实际是调用定义在core_cm4.h文件的NVIC_SetPriority函数实现功能的,该函数通过设置SCB_SHP寄存器或者NVIC_IPRx寄存器实现功能。
- 使能中断
按住ctrl+f,可以查找一下
/**
* @brief Enable 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 (stm32g4xxxx.h))
* @retval None
*/
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
{
/* Check the parameters */
assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
/* Enable interrupt */
NVIC_EnableIRQ(IRQn);
}
HAL_NVIC_EnableIRQ函数用于在NVIC控制器中使能指定中断,它有一个形参,是IRQn_Type类型参数。 HAL_NVIC_EnableIRQ函数实际是通过调用定义在core_cm4.h文件中NVIC_EnableIRQ函数实现功能,NVIC_EnableIRQ函数设置了NVIC_ISER寄存器内容。
- 不允许中断
/**
* @brief Disable a device specific interrupt in the NVIC interrupt controller.
* @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 (stm32g4xxxx.h))
* @retval None
*/
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn)
{
/* Check the parameters */
assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
/* Disable interrupt */
NVIC_DisableIRQ(IRQn);
}
HAL_NVIC_DisableIRQ函数是在NVIC控制器中禁用指定中断,用法与HAL_NVIC_EnableIRQ函数相同。最终通过设置NVIC_ICER寄存器值实现功能
- NVIC中断系统函数
/**
* @brief Initiate a system reset request to reset the MCU.
* @retval None
*/
void HAL_NVIC_SystemReset(void)
{
/* System Reset */
NVIC_SystemReset();
}
HAL_NVIC_SystemReset函数用于初始化一个MCU复位要求,它设计调用NVIC_SystemReset函数实现功能。
了解了这些应该就可以写代码了(这里我们用按键来作为外部中断的触发条件)
所以B1和B4只能选一个来作为外部中断触发条件。
首先配置stm32cubemx
这里配置触发模式
使能中断
设置两位抢占式优先级(但我感觉这里不配置也行。。。)
然后在keil里面找到虚函数,重写虚函数。
和中断相关的函数一般在这个.c文件里
然后往里去找
找到之后在main函数中重写
main.c
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_0) // B4
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);
}
if(GPIO_Pin == GPIO_PIN_1) //B2
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_9);
}
if(GPIO_Pin == GPIO_PIN_2) //B3
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_10);
}
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
/* USER CODE END 0 */
个人拙见:去keil里多看看一些中断相关的函数,看着看着就顺眼了。