引子
什么是中断
举个生活中的小栗子吧,我正在编写这个文档,突然门铃响了,我去开下门,原来是快递,签收完快递后,又回来接着写。
上面的例子中,
1. 我就是CPU
2. 编写文档,是主运行程序
3. 门铃响了,是中断信号
4. 查看到是快递,是查询中断号
5. 签收快递,是中断处理程序
6. 签收完快递后继续工作,是中断返回
即,中断就是由于某些事件打断CPU主运行程序运行,并处理该事件,处理完后继续运行主程序的过程。
为什么需要中断
同样用上面的例子,把中断去掉,即上述步骤中第3步去掉,即,我需要编写文档的同时,需要过段时间就去门口看看是否有快递到了,可见这样的过程非常浪费我的时间,效率也非常的低。
中断的目的是提高CPU的利用率。因此也可以理解为什么中断越短越好。
什么是中断优先级
还用上面的例子,如果门铃响的同事,厨房煤气烧的热水也开了,此时就需要优先级了,例如优先把煤气关掉。
因此,当多个中断同时触发时,优先级可以告诉CPU该优先处理哪个。
什么是中断嵌套
上面的例子,如果我在签收快递的过程中,水开了,我先去关了煤气,在回来继续签收快递,这就是中断嵌套,在中断中处理优先级更高的中断。
什么是入栈和出栈
还是上面的例子,当我收快递前,我先找本子记录下文档写到哪里了,然后在去收快递,收完后,我从本子里记录的位置重新工作。
其中,本子就是栈,记录到本子的过程就是入栈,从本子中读出的过程就是出栈。
可见,目的只有一个,为了恢复现场,防止收完快递后忘记自己写到哪里了。
栈是一中内存的管理方式,具有先进后出,后进现场的特点。
Cortex-M3中断管理
Cortex-M3设计了一个非常优秀的中断系统,让系统异常(可以看做是特殊的中断)和中断的处理非常的及时和方便,即NVIC(Nested Vectored Interrupt Controller)。
在Cortex-M3的相关资料中,异常和中断都是分开说明的,此处我把他们合并在一起说明,都当做中断来看,原因是他们的特性实在太相似了。
向量中断支持
- M3把各种中断都映射不同的地址上,当中断发生时,M3会根据中断号从表中查找到中断入口函数的地址,然后跳转过去并执行。
- 映射在哪个地址上是可以配置的,因此我们在修改程序的起始地址时,必须把中断向量重新映射,例如boot+app开发时。
- 如下表,1-15号用于系统异常,16号以上用于外部中断。
可嵌套中断支持
前面了解到,中断嵌套与优先级密闭不可分。
如上表优先级一列,在M3中优先级的数值越小,优先级越高。其中,复位,NMI和硬件fault的优先级是固定,且高于其他中断。
理论上,M3支持3个固定最高优先级+256级可编程优先级,同时支持128级抢占。M3毕竟是由于嵌入式系统,芯片厂家在实现时都会进行精简,比理论值要小。因此,请忘记上面的数字,以芯片实际情况为准。
如下图,M3为了管理优先级,
1. 通过寄存器AIRCR为优先级分组,通过分组我们可以知道哪些bit代表抢占优先级,哪些bit代表亚优先级,如图中1->2->4。
2. 中断优先级寄存器阵列的每个寄存器都是8位的,映射到优先级分组后,如图中3->4,经过此映射,M3就知道每个中断的抢占优先级和亚优先级分别是多少了。
3. 当多个中断同时发生时,M3首先选择抢占优先级最高的中断执行,当抢占优先级相同的中断同时触发时,M3优先选亚优先级最高的执行。
4. 到此,我们也明白了为什么理论上优先级有256级,抢占优先级只有128个了。
动态优先级调整支持
可以在程序运行过程中更改某中断的优先级。
中断可屏蔽
中断延迟大大缩短
1. 向量化的设计,省去软件判断中断来源。
2. M3自动压栈和出栈R0-R3,R12,LR,PSR,PC寄存器,注意R4-R11需要手工入栈。
3. 优先级的有效合理分配,可以使需要的中断立马及时响应。
4. 咬尾中断和晚到中断机制,保证高优先级中断的实时响应。
5. 永不屏蔽的NMI(不可屏蔽中断),可以使系统第一时间做出响应,除非CPU挂了。
中断
注释:
如果理解了入栈、出栈流程和优先级,那么咬尾中断和晚到中断机制就好理解了。
咬尾中断:出栈时的优化,不出栈直接运行更高优先级中断,高优先级处理完毕后,一次一起出栈,省掉一次出栈过程。
晚到中断机制:入栈时的优化,入栈初期,更高优先级中断产生,先入栈更高优先级,提高高优先响应速度。
代码配置
下面代码和上面的描述一一对应,不难理解。
static VOID UART1_NvicConfiguration(VOID)
{
NVIC_InitPara NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQ = USART1_IRQn; /* 要配置的中断号 */
NVIC_InitStructure.NVIC_IRQPreemptPriority = 0; /* 抢占优先级 */
NVIC_InitStructure.NVIC_IRQSubPriority = 0; /* 亚优先级 */
NVIC_InitStructure.NVIC_IRQEnable = ENABLE; /* 使能控制 */
NVIC_Init(&NVIC_InitStructure);
}
代码路径
https://github.com/YaFood/GD32F103/tree/master/TestUART