100ask七天物联网训练营学习笔记 - 中断

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)简称NVICNVIC负责统一管理所有异常和中断。

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模式,PA1GPIO_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熄灭。

请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值