学习目标

  1. 了解NVIC中断控制器
  2. 理解优先级概念
  3. 理解优先级分组概念
  4. 掌握全局优先级分组配置方式
  5. 掌握具体中断优先级配置方式

    学习内容

    NVIC中断控制器

    NVIC(Nested Vectored Interrupt Controller)是一种嵌套式向量中断控制器,它是用于控制和管理嵌入式设备中的中断的硬件模块。NVIC可以自动地响应中断,并管理中断优先级、中断处理程序等,从而实现多个中断的快速、有序、有效地响应。
    NVIC是通过对中断向量表的修改来处理中断的。在NVIC中,中断向量表是一个数组,存储了所有中断处理函数的地址。当一个中断触发时,处理器会在中断向量表中查找对应中断号的处理函数地址,并跳转到该地址执行对应中断的处理。
    ARM系统的中断,内置的就是这种控制器。

    中断优先级

    中断优先级分为抢占优先级,响应优先级和自然优先级

抢占优先级

抢占优先级用于确定在多个中断请求同时发生时,哪个中断可以中断当前正在执行的中断。具有更高抢占优先级的中断请求可以抢占正在执行的较低抢占优先级的中断。这个机制允许高优先级的任务在需要时能够立即得到处理。

响应优先级

响应优先级用于确定中断请求被接收并开始执行的优先级。在多中断环境中,不同中断请求可能同时发生,系统需要根据响应优先级来决定哪个中断会首先得到处理。但响应优先级不会导致中断处理的打断,它仅在中断请求同时发生且抢占优先级相同时决定处理顺序。

自然优先级

自然优先级实际上是中断向量表的序号,序号越小优先级越高。

优先级的界定

无论是抢占优先级还是响应优先级,都是用数值表示的,数值越小,优先级越高。

优先级分组

优先级分组主要是说明抢占优先级和响应优先级的关系。
在ARM中,为了表示抢占优先级和响应优先级,仅用了4个Bit表示了优先级的等级。
173.png

优先级分组 抢占优先级 响应优先级
位数 范围 位数 范围
分组0 0bit NONE 4bit [0,15]
分组1 1bit [0,1] 3bit [0,7]
分组2 2bit [0,3] 2bit [0,3]
分组3 3bit [0,7] 1bit [0,1]
分组4 4bit [0,15] 0bit NONE

在代码中我们可以对全局进行中断优先级分组:

  1. nvic_priority_group_set(NVIC_PRIGROUP_PRE0_SUB4);
  2. nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
  3. nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
  4. nvic_priority_group_set(NVIC_PRIGROUP_PRE3_SUB1);
  5. nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);

对与具体的中断,我们可以进行配置抢占和响应优先级

  1. nvic_irq_enable(xxx_irqn, 抢占优先级, 响应优先级);

案例准备

  1. 创建多个外部中断
  2. 配置不同的中断优先级
  3. 中断中进行耗时操作(不对的行为,仅仅是用来观察执行结果)
  4. 比较中断执行

    外部中断级别逻辑

    1. static void EXTI0_config() {
    2. uint32_t extix = EXTI_0; // 哪个中断
    3. uint32_t extix_irq = EXTI0_IRQn;
    4. /********************* EXTI config *********************/
    5. // 时钟配置
    6. rcu_periph_clock_enable(RCU_SYSCFG);
    7. // 中断初始化
    8. exti_init(extix, EXTI_INTERRUPT, EXTI_TRIG_NONE);
    9. // 配置中断优先级
    10. nvic_irq_enable(extix_irq, 2, 1);
    11. // 使能中断
    12. exti_interrupt_enable(extix);
    13. // 清除中断标志位
    14. exti_interrupt_flag_clear(extix);
    15. }

    ```c void EXTI0_IRQHandler(void) { if(SET == exti_interrupt_flag_get(EXTI_0)) {

    1. uint32_t i;
    2. for(i = 0; i < 10; i++) {
    3. printf("exti 0 : %d \r\n", i);
    4. delay_1ms(1000);
    5. }

    } // 清除中断标志位 exti_interrupt_flag_clear(EXTI_0); }

  1. ```c
  2. exti_software_interrupt_enable(EXTI_0);

串口接收中断逻辑

  1. if(data[0] == 0x00) {
  2. printf("0\r\n");
  3. exti_software_interrupt_enable(EXTI_0);
  4. } else if(data[0] == 0x01) {
  5. printf("1\r\n");
  6. exti_software_interrupt_enable(EXTI_1);
  7. } else if(data[0] == 0x02) {
  8. printf("2\r\n");
  9. exti_software_interrupt_enable(EXTI_2);
  10. }

测试一

中断类型 全局优先级分组 抢占优先级 响应优先级
串口接收 分组2 0 0
外部中断0 1 0
外部中断1 2 0
外部中断2 3 0
  1. #define EXTI0_PRIORITY 1, 0
  2. #define EXTI1_PRIORITY 2, 0
  3. #define EXTI2_PRIORITY 3, 0

响应优先级相同,抢占优先级不同,测试不同的结果。

测试逻辑

通过串口,依次控制先执行 外部中断2,外部中断1,外部中断0,观察打印输出结果。

结论

  • 抢占优先级数值越小,优先级高
  • 抢占优先级高的中断,可以打断优先级低的,先执行,执行完成后,其他中断接着执行

007.png

测试二

中断类型 全局优先级分组 抢占优先级 响应优先级
串口接收 分组2 0 0
外部中断0 2 0
外部中断1 2 1
外部中断2 2 2
  1. #define EXTI0_PRIORITY 2, 0
  2. #define EXTI1_PRIORITY 2, 1
  3. #define EXTI2_PRIORITY 2, 2

响应优先级相同,抢占优先级不同,测试不同的结果。

测试逻辑

通过串口,依次控制先执行 外部中断2,外部中断1,外部中断0,观察打印输出结果。

结论

  • 响应优先级数值越小,优先级高
  • 响应优先级高的中断,在中断排队时,享有更靠前的优先级

008.png

测试三

中断类型 全局优先级分组 抢占优先级 响应优先级
串口接收 分组2 0 0
外部中断0 1 0
外部中断1 2 1
外部中断2 2 2
  1. #define EXTI0_PRIORITY 1, 0
  2. #define EXTI1_PRIORITY 2, 1
  3. #define EXTI2_PRIORITY 2, 2

响应优先级相同,抢占优先级不同,测试不同的结果。

测试逻辑

通过串口,依次控制先执行 外部中断2,外部中断1,外部中断0,观察打印输出结果。

结论

  • 中断优先级中,抢占优先级优于响应优先级

009.png

测试四

中断类型 全局优先级分组 抢占优先级 响应优先级
串口接收 分组2 0 0
外部中断0 2 0
外部中断1 2 0
外部中断2 2 0
  1. #define EXTI0_PRIORITY 2, 0
  2. #define EXTI1_PRIORITY 2, 0
  3. #define EXTI2_PRIORITY 2, 0

响应优先级相同,抢占优先级不同,测试不同的结果。

测试逻辑

通过串口,依次控制先执行 外部中断2,外部中断1,外部中断0,观察打印输出结果。

结论

  • 抢占优先级和响应优先级相同时,比较自然优先级

008.png

测试五

全局优先级分组和中断优先级冲突。

练习题

  1. 配置不同优先级,体会优先级顺序