没志青年
发布于 2025-07-27 / 29 阅读
0

32单片机 -中断管理

中断和事件的区别:

  • 中断是需要CPU参与的,执行中断服务程序。通过自定义中断服务程序,可用来实现特定的功能,通常需要手动清除标志位。

  • 事件(标志位)完全是由硬件自动处理的,不需要CPU参与。事件用来在外设之间通知,比如定时器信号触发ADC采样转换,会自动清除标志位。

STM32 单核心,每个中断源对应着一个中断号。

分为内核中断和外部中断:

  • 内核中断:不可屏蔽中断,无法通过软件进行控制

  • 外部中断:可屏蔽中断,可以通过软件进行控制

中断

NVIC 管理器

嵌套向量中断控制器

NVIC 是 CPU的一个外设,。

作用:

  • 动态优先级配置:

  • 中断使能:

    • 总中断

    • 单独中断:

  • 中断向量表自动跳转:

  • 中断状态管理:

只需要关心前两个就行了,我们需要配置的。

STM32、GD32、ESP32 的芯片设计者,为每个外设分配了一个唯一的中断号。

中断号是让 NVIC 知道是哪个中断源,通过软件设置 NVIC,可以决定中断的优先级、是否开启中断。

中断优先级

F0 系列的 NVIC 只有 2 位,可配置0~3,不区分抢占和响应优先级,没有分组的概念。

对于 Cortex-M 内核,数字越小,优先级越高。

NVIC_InitTypeDef nvic_struct;
nvic_struct.NVIC_IRQChannel = EXTI2_3_IRQn;
nvic_struct.NVIC_IRQChannelPriority = 0x00;
nvic_struct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_struct);

F1标准库:

优先级分组

分组

抢占优先级位数

子优先级位数

描述

0

0 bit

4 bits

无抢占优先级,只有子优先级

1

1 bit

3 bits

2级抢占,8级子优先级

2

2 bits

2 bits

4级抢占,4级子优先级

3

3 bits

1 bit

8级抢占,2级子优先级

4

4 bits

0 bit

16级抢占,无子优先级

默认的分组不清楚,但是,开发中必须显式的设置优先级分组,最常用的就是分组2。

F0 标准库:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

中断开关

全局中断开

__enable_irq();
__set_PRIMASK(0);

__disable_irq();
__set_PRIMASK(1);

stm32_hal_cortex.c 是对 core_cm3 的进一步封装

外部中断

引脚与外部中断线的映射

外部中断配置

F0 标准库:

void EXTI2_Config(void)
{
  EXTI_InitTypeDef   exti_struct = {0};
  NVIC_InitTypeDef   nvic_struct = {0};

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource2);

  exti_struct.EXTI_Line = EXTI_Line2;
  exti_struct.EXTI_Mode = EXTI_Mode_Interrupt;
  exti_struct.EXTI_Trigger = EXTI_Trigger_Rising;
  exti_struct.EXTI_LineCmd = ENABLE;
  EXTI_Init(&exti_struct);

  nvic_struct.NVIC_IRQChannel = EXTI2_3_IRQn;
  nvic_struct.NVIC_IRQChannelPriority = 0x00;
  nvic_struct.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&nvic_struct);
}


void EXTI2_3_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line2) != RESET)
    {    
      // 处理逻辑
      EXTI_ClearITPendingBit(EXTI_Line2);
    }
}


//F0 标准库没有专门的反初始化函数,下面这个函数最后两步没测试过
void EXTI2_Deinit(void)
{
  EXTI_InitTypeDef   exti_struct = {0};
  NVIC_InitTypeDef   nvic_struct = {0};

  // 禁用 EXTI2 外部中断
  exti_struct.EXTI_Line = EXTI_Line2;
  exti_struct.EXTI_Mode = EXTI_Mode_Interrupt;
  exti_struct.EXTI_Trigger = EXTI_Trigger_Rising;
  exti_struct.EXTI_LineCmd = DISABLE;
  EXTI_Init(&exti_struct);

  // 禁用 EXTI2_3 中断,如何只 禁用2的,保留3的?
  nvic_struct.NVIC_IRQChannel = EXTI2_3_IRQn;
  nvic_struct.NVIC_IRQChannelPriority = 0x00;
  nvic_struct.NVIC_IRQChannelCmd = DISABLE;
  NVIC_Init(&nvic_struct);

  // 清除 EXTI2 的挂起标志
  EXTI->PR = EXTI_Line2;

  // 恢复 SYSCFG 映射为默认
  SYSCFG->EXTICR[0] &= ~(SYSCFG_EXTICR1_EXTI2); // EXTI2 映射到 PA2(默认)
}

系统异常向量中断

NMI_Handler:

处理关键硬件异常(如时钟失效)

HardFault_Handler:

  • 访问非法地址:数组越界、野指针、访问未映射寄存器

  • 硬件错误,CPU 无法处理

SVC_Handler 和 PendSV_Handler 在 RTOS 中用到,裸机用不到。

SysTick_Handler:

  • HAL 库中作为时基

  • RTOS 作为时基

  • 标准库好像不用这个??不确定

必须声明这5个异常处理函数,否则编译器/链接器会报错。