100ask七天物联网训练营学习笔记 - 中断
由于这段时间工作比较忙,今天才回看了直播课程,继续记录一下学习内容。
1. 什么是中断
中断(Interrupt):打断当前CPU正在顺序执行的指令,转而去响应更紧急的指令或事件,紧急指令执行完成或事件响应结束后,回到打断前的位置继续顺序执行之前的指令。
我们通过上图可以 看出,一个完整的中断过程可以分为以下几步:
1)执行主程序时,中断发生
2)暂停主程序,并保存现场
3)跳转到中断程序
4)执行中断
5)返回主程序
6)恢复现场
7)继续执行主程序
通常,把CPU内部产生的紧急事件叫做异常,比如非法执行(除零)、地址访问越界等;把来自CPU内核外部的片上外设产生的紧急事件叫做中断,比如GPIO引脚电平变化、定时器溢出、串口完成收发等。异常和中断的效果基本一致,都是暂停当前任务,优先执行紧急事件,因此一般将中断和异常统称为中断。
2. 中断可以做什么
中断主要的任务是MCU内核发生异常时处理以及在deadline
到来之前完成外设产生的事件响应。
3. 中断优先级
优先级(Priority):当多个中断同时发生或正在处理中断时又有中断请求发生时,mcu内核该如何响应,这时就要靠中断请求的优先级来进行响应了。
在Cortex-M3内核中,有一个专门用来管理中断的模块,嵌套向量中断控制器(Nested Vectored Interrupt Controller)简称NVIC。NVIC负责统一管理所有异常和中断。
4. Cortex-M3
的中断及优先级
理论上Cortex-M3内核可以支持256
种异常和中断,其中编号1~15
是内核异常,16~256
是外部中断。
在Cortex-M3中,将优先级拆分为抢占优先级(Preempt Priority)和子优先级(Sub Priority),每个中断都拥有自己的两种优先级,高优先级的中断可以继续打断低优先级的中断,从而实现中断嵌套。
我们可以通过**应用中断和复位控制寄存器(Application Interrupt and Reset Control Register, AIRCR)**寄存器中的Bits[10:8](PRIGROUP)将优先级进行分组。分组决定每个可编程中断的PRI_n的Bits[7:0]的高低位分配,从而影响抢占优先级和子优先级的级数,关系如下图。
假如将优先级分组(PRIGROUP)设置为5,此时每个中断可设置抢占优先级Bits[7:6],范围为03,子优先级Bits[5:0],范围为063。
5. STM32F103的中断及优先级
从上一节我们得知,Cortex-M3设计了256
种中断,但实际以该内核投产的大多数芯片都用不了这么多的中断,比如STM32F103系列最多也就70
多种中断。
因为没有那么多的中断,所以STM32F103只使用了PRI_n的Bits[7:0]中的Bits[7:4]位来设置优先级,因此STM32F103优先级分组表如下:
也就是说在STM32F103中PRI_n的Bits[3:0]这4位硬件上没有被实现,所以STM32F103的优先级分组(PRIGROUP)只可以设置为3-7
。
这里需要注意的是,高抢占优先级(数字越小,优先级越高)的中断可以打断低抢占优先级的中断,从而实现中断的嵌套。子优先级可以在同时发生多个中断时,让CPU决定先响应哪个中断(数字越小,优先级越高)。
6. 中断示例
6.1 通过CubeMX创建工程
我们使用100ASK_STM32F103_MINI开发板进行实验,直接通过CubeMX创建项目,这样可以极大提高效率。
选择芯片stm32f103c8t6创建项目。
通过查阅原理图得知,用户按键连接在PA0,按键未按下为高电平,按下后为低电平,用户LED连接在PA1,低电平点亮。
第一步在CubeMX中设置PA0引脚为GPIO_EXTI0模式,PA1为GPIO_Output模式,而后进一步配置两个GPIO口的配置。
GPIO配置完成后,首先配置将优先级分组设置为2位抢占优先级2位子优先级(对应第5节图片的分组5),然后使能EXTI line0 interrupt中断,并将该中断抢占优先级设置为2,子优先级设置为0。
完成配置后生成项目。
6.2 代码分析
打开工程,我们在main
函数中的HAL_Init
函数里可以看到
// stm32f1xx_hal_cortex.h
#define NVIC_PRIORITYGROUP_0 0x00000007U /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1 0x00000006U /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2 0x00000005U /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3 0x00000004U /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4 0x00000003U /*!< 4 bits for pre-emption priority
0 bits for subpriority */
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);
}
// core_cm3.h
#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping
#define SCB_AIRCR_VECTKEY_Pos 16U
#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos)
#define SCB_AIRCR_PRIGROUP_Pos 8U
#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos)
__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
/* only values 0..7 are used */
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);
/* read old register configuration */
reg_value = SCB->AIRCR;
/* clear bits to change */
reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk));
/* Insert write key and priority group */
reg_value = (reg_value |
((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) );
SCB->AIRCR = reg_value;
}
// stm32f1xx_hal.c
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 */
// 这里默认将优先级分组设置为4位抢占优先级,0位子优先级,真正的配置在HAL_MspInit函数中
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* 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;
}
void HAL_MspInit(void)
{
/* USER CODE BEGIN MspInit 0 */
/* USER CODE END MspInit 0 */
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
// 这里才将优先级分组设置为2位抢占优先级,2位子优先级
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
/* System interrupt init*/
/** NOJTAG: JTAG-DP Disabled and SW-DP Enabled
*/
__HAL_AFIO_REMAP_SWJ_NOJTAG();
/* USER CODE BEGIN MspInit 1 */
/* USER CODE END MspInit 1 */
}
// main.h
#define KEY_Pin GPIO_PIN_0
#define KEY_GPIO_Port GPIOA
#define KEY_EXTI_IRQn EXTI0_IRQn
#define LED_Pin GPIO_PIN_1
#define LED_GPIO_Port GPIOA
// main.c
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : KEY_Pin */
GPIO_InitStruct.Pin = KEY_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LED_Pin */
GPIO_InitStruct.Pin = LED_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
// stm32f1xx_hal_cortex.c
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
{
uint32_t prioritygroup = 0x00U;
/* 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));
}
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
{
/* Check the parameters */
assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
/* Enable interrupt */
NVIC_EnableIRQ(IRQn);
}
// core_cm3.h
#define NVIC_SetPriority __NVIC_SetPriority
#define NVIC_EnableIRQ __NVIC_EnableIRQ
__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
else
{
SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
}
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
}
}
6.3 实验代码
// main.c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == KEY_Pin)
{
if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin))
{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
}
else
{
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
}
}
}
6.4 实验效果
这里的实现效果就是,按键按下LED点亮,松开按键LED熄灭。