1. NVIC中断控制器
NVIC(Nested Vectored Interrupt Controller)是一种嵌套式向量中断控制器,它是用于控制和管理嵌入式设备中的中断的硬件模块。NVIC可以自动地响应中断,并管理中断优先级、中断处理程序等,从而实现多个中断的快速、有序、有效地响应。
NVIC是通过对中断向量表的修改来处理中断的。在NVIC中,中断向量表是一个数组,存储了所有中断处理函数的地址。当一个中断触发时,处理器会在中断向量表中查找对应中断号的处理函数地址,并跳转到该地址执行对应中断的处理。
ARM系统的中断,内置的就是这种控制器。
2. 中断优先级
中断优先级分为抢占优先级,响应优先级和自然优先级
2.1 抢占优先级
抢占优先级用于确定在多个中断请求同时发生时,哪个中断可以中断当前正在执行的中断。具有更高抢占优先级的中断请求可以抢占 正在执行的较低抢占优先级的中断。这个机制允许高优先级的任务在需要时能够立即得到处理。
2.2 响应优先级
响应优先级用于确定中断请求被接收并开始执行的优先级。在多中断环境中,不同中断请求可能同时发生,系统需要根据响应优先级来决定哪个中断会首先得到处理。但响应优先级不会导致中断处理的打断,它仅在中断请求同时发生且抢占优先级相同时决定处理顺序。
2.3 自然优先级
自然优先级实际上是中断向量表的序号,序号越小优先级越高。
优先级的界定: 无论是抢占优先级还是响应优先级,都是用数值表示的,数值越小,优先级越高。
3. 优先级分组
优先级分组主要是说明抢占优先级和响应优先级的关系。
在ARM中,为了表示抢占优先级和响应优先级,仅用了4个Bit表示了优先级的等级。
|-------|------|----------|------|----------|
| 优先级分组 | 抢占优先级 || 响应优先级 ||
| 优先级分组 | 位数 | 范围 | 位数 | 范围 |
| 分组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 |
在代码中我们可以对全局进行中断优先级分组:
objectivec
nvic_priority_group_set(NVIC_PRIGROUP_PRE0_SUB4);
nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_priority_group_set(NVIC_PRIGROUP_PRE3_SUB1);
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
对与具体的中断,我们可以进行配置抢占和响应优先级
objectivec
nvic_irq_enable(xxx_irqn, 抢占优先级, 响应优先级);
4. 案例准备
- 创建多个外部中断
- 配置不同的中断优先级
- 中断中进行耗时操作(不对的行为,仅仅是用来观察执行结果)
- 比较中断执行
4.1 外部中断级别逻辑
4.1.1 中断初始化
objectivec
static void EXTI0_config() {
uint32_t extix = EXTI_0; // 哪个中断
uint32_t extix_irq = EXTI0_IRQn;
/********************* EXTI config *********************/
// 时钟配置
rcu_periph_clock_enable(RCU_SYSCFG);
// 中断初始化
exti_init(extix, EXTI_INTERRUPT, EXTI_TRIG_NONE);
// 配置中断优先级
nvic_irq_enable(extix_irq, 2, 1);
// 使能中断
exti_interrupt_enable(extix);
// 清除中断标志位
exti_interrupt_flag_clear(extix);
}
4.1.2 中断函数
objectivec
void EXTI0_IRQHandler(void)
{
if(SET == exti_interrupt_flag_get(EXTI_0)) {
uint32_t i;
for(i = 0; i < 10; i++) {
printf("exti 0 : %d \r\n", i);
delay_1ms(1000);
}
}
// 清除中断标志位
exti_interrupt_flag_clear(EXTI_0);
}
4.1.3 中断触发
objectivec
exti_software_interrupt_enable(EXTI_0);
4.2 串口接收中断逻辑
objectivec
if(data[0] == 0x00) {
printf("0\r\n");
exti_software_interrupt_enable(EXTI_0);
} else if(data[0] == 0x01) {
printf("1\r\n");
exti_software_interrupt_enable(EXTI_1);
} else if(data[0] == 0x02) {
printf("2\r\n");
exti_software_interrupt_enable(EXTI_2);
}
5. 测试一
|-------|---------|-------|-------|
| 中断类型 | 全局优先级分组 | 抢占优先级 | 响应优先级 |
| 串口接收 | 分组2 | 0 | 0 |
| 外部中断0 | 分组2 | 1 | 0 |
| 外部中断1 | 分组2 | 2 | 0 |
| 外部中断2 | 分组2 | 3 | 0 |
5.1 优先级配置
objectivec
#define EXTI0_PRIORITY 1, 0
#define EXTI1_PRIORITY 2, 0
#define EXTI2_PRIORITY 3, 0
响应优先级相同,抢占优先级不同,测试不同的结果。
5.2 测试逻辑
通过串口,依次控制先执行 外部中断2
,外部中断1
,外部中断0
,观察打印输出结果。
5.3 结论
- 抢占优先级数值越小,优先级高
- 抢占优先级高的中断,可以打断优先级低的,先执行,执行完成后,其他中断接着执行
6. 测试二
|-------|---------|-------|-------|
| 中断类型 | 全局优先级分组 | 抢占优先级 | 响应优先级 |
| 串口接收 | 分组2 | 0 | 0 |
| 外部中断0 | 分组2 | 2 | 0 |
| 外部中断1 | 分组2 | 2 | 1 |
| 外部中断2 | 分组2 | 2 | 2 |
6.1 优先级配置
objectivec
#define EXTI0_PRIORITY 2, 0
#define EXTI1_PRIORITY 2, 1
#define EXTI2_PRIORITY 2, 2
应优先级相同,抢占优先级不同,测试不同的结果。
6.2 测试逻辑
通过串口,依次控制先执行 外部中断2
,外部中断1
,外部中断0
,观察打印输出结果。
6.3 结论
- 响应优先级数值越小,优先级高
- 响应优先级高的中断,在中断排队时,享有更靠前的优先级
7. 测试三
|-------|---------|-------|-------|
| 中断类型 | 全局优先级分组 | 抢占优先级 | 响应优先级 |
| 串口接收 | 分组2 | 0 | 0 |
| 外部中断0 | 分组2 | 1 | 0 |
| 外部中断1 | 分组2 | 2 | 1 |
| 外部中断2 | 分组2 | 2 | 2 |
7.1 优先级控制
objectivec
#define EXTI0_PRIORITY 1, 0
#define EXTI1_PRIORITY 2, 1
#define EXTI2_PRIORITY 2, 2
响应优先级相同,抢占优先级不同,测试不同的结果。
7.2 测试逻辑
通过串口,依次控制先执行 外部中断2
,外部中断1
,外部中断0
,观察打印输出结果。
7.3 结论
- 中断优先级中,抢占优先级优于响应优先级
8. 测试四
|-------|---------|-------|-------|
| 中断类型 | 全局优先级分组 | 抢占优先级 | 响应优先级 |
| 串口接收 | 分组2 | 0 | 0 |
| 外部中断0 | 分组2 | 2 | 0 |
| 外部中断1 | 分组2 | 2 | 0 |
| 外部中断2 | 分组2 | 2 | 0 |
8.1 优先级配置
objectivec
#define EXTI0_PRIORITY 2, 0
#define EXTI1_PRIORITY 2, 0
#define EXTI2_PRIORITY 2, 0
响应优先级相同,抢占优先级不同,测试不同的结果。
8.2 测试逻辑
通过串口,依次控制先执行 外部中断2
,外部中断1
,外部中断0
,观察打印输出结果。
8.3 结论
- 抢占优先级和响应优先级相同时,比较自然优先级
9. 测试五
全局优先级分组和中断优先级冲突。