## 中断
### 简介
- 中断(Interrupt)是一种事件响应机制,可以使单片机在处理其他任务的同时及时响应并处理紧急事件,提高系统的实时性能和可靠性。
- 中断是一种异步事件响应方式,当指定的事件发生时,中断处理器会立即暂停当前程序执行,转而执行中断服务程序,处理完中断事件后再返回原来的程序执行流程。
### 组成部分
- 在单片机中,中断由中断源、中断控制器和中断服务程序组成。
#### 中断源
- 产生中断事件的硬件设备或软件程序,如定时器溢出、外部输入信号、串口接收数据等。
#### 中断控制器
- 负责检测中断源产生的中断请求,并向CPU发送中断信号,在CPU完成当前指令后跳转到相应的中断服务程序。
在32位单片机中,中断控制器通常由NVIC(Nested Vector Interrupt Controller)实现。NVIC支持多达240个中断源,可以灵活配置优先级、屏蔽中断等设置。
下面是一个在STM32F4单片机上使用NVIC实现中断初始化的示例代码:
```c
// 初始化中断
void init_interrupt(void) {
// 使能IRQ中断
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_4);
// 初始化中断优先级
NVIC_Init(&NVIC_InitStructure);
}
```
#### 中断服务程序
- 是一个用于处理特定中断事件的程序。
- 包括清除中断标志位、保存现场、执行特定操作、恢复现场等步骤,最后通过返回指令返回到原来的任务流程。
在32位单片机中,中断服务程序通常由ISR(Interrupt Service Routine)实现。ISR是一种特殊的函数,具有固定的格式和命名规则,以便编译器正确地生成中断向量表和中断服务程序入口地址。例如,对于外部中断EXTI1的中断服务程序,其函数定义应该如下所示:
```c
// EXTI1中断服务程序
void EXTI1_IRQHandler(void) {
// 处理中断事件
// ...
// 清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line1);
}
```
### 响应过程
- 中断响应过程主要包括中断源产生中断请求、中断控制器检测到中断请求、CPU暂停当前任务跳转到中断服务程序、中断服务程序处理中断事件、CPU返回原任务继续执行等几个步骤。
以下是中断响应过程的详细说明:
1. 中断源产生中断请求
当某个硬件设备或软件程序产生了一个需要CPU立即处理的事件时,会向中断控制器发送中断请求信号。例如,当定时器溢出时,定时器会向中断控制器发送一个定时器中断请求。
2. 中断控制器检测到中断请求
中断控制器不断检测是否有中断请求发生,当检测到中断请求时,会向CPU发送中断信号,通知CPU有中断事件需要处理。
在32位单片机中,中断控制器通常由NVIC实现。NVIC会根据中断优先级和屏蔽设置等信息来判断是否响应中断请求,并向CPU发送相应的中断信号。
3. CPU暂停当前任务,跳转到中断服务程序
当CPU收到中断信号后,会暂停正在执行的任务,并将当前指令的下一条指令地址(PC)保存到堆栈中,以便在中断服务程序执行完后返回原来的任务流程。然后,CPU会从中断向量表中读取相应中断源对应的中断服务程序入口地址,并跳转到该地址开始执行中断服务程序。
4. 中断服务程序处理中断事件
中断服务程序是用于处理特定中断事件的程序,它包括清除中断标志位、保存现场、执行特定操作、恢复现场等步骤,最后通过返回指令返回到原来的任务流程。
在中断服务程序中,需要清除中断标志位以便下一次中断响应,例如,对于外部中断EXTI1的中断服务程序,可以使用以下代码清除中断标志位:
```c
// 清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line1);
```
5. CPU返回原任务继续执行
当中断服务程序执行完毕后,CPU会从堆栈中读取之前保存的PC值,恢复执行原来的任务流程,即返回到之前暂停的任务处,继续执行原来的指令。
### 中断向量表
- 中断向量表(Interrupt Vector Table)是一种特殊的数据结构,用于存储所有中断服务程序的入口地址。
- 在单片机启动时,中断向量表会被加载到固定的内存位置,并在中断响应时被CPU使用。
- 它通常是一个连续的存储区域,每个中断源都占据一个固定的位置。
- 当中断控制器检测到中断请求时,会将相应的中断编号作为索引,从中断向量表中读取对应的中断服务程序入口地址,并跳转到该地址开始执行中断服务程序。
以下是在STM32F4单片机上定义中断向量表的示例代码:
```c
// 中断向量表
void (* const InterruptVectorTable[]) (void) __attribute__ ((section(".isr_vector"))) = {
(void (*)(void)) &_estack, // 栈指针
Reset_Handler, // 复位中断
NMI_Handler, // 非屏蔽中断
HardFault_Handler, // 硬件故障中断
MemManage_Handler, // 存储器管理中断
BusFault_Handler, // 总线错误中断
UsageFault_Handler, // 用法错误中断
0, // 保留
0, // 保留
0, // 保留
0, // 保留
SVC_Handler, // 系统调用中断
DebugMon_Handler, // 调试监视中断
0, // 保留
PendSV_Handler, // 挂起中断
SysTick_Handler, // 系统定时器中断
// 外部中断0~15的中断服务程序
EXTI0_IRQHandler,
EXTI1_IRQHandler,
EXTI2_IRQHandler,
EXTI3_IRQHandler,
EXTI4_IRQHandler,
EXTI9_5_IRQHandler,
EXTI15_10_IRQHandler,
// 更多的外部中断服务程序
};
```
### 注意事项
- 在使用中断时,需要注意一些问题,如中断的优先级、中断嵌套、中断延迟等,以确保系统的稳定性和可靠性。
#### 中断优先级
- 中断优先级可以决定哪个中断优先处理。
- 在NVIC中,中断优先级分为4位或3位,越高的值表示越低的优先级。
- 在设计中,需要根据具体应用场景来设置合理的中断优先级,以避免因低优先级中断被屏蔽而导致紧急事件无法及时处理的情况。
以下是在STM32F4单片机上设置中断优先级的示例代码:
```c
// 设置某个中断的优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; // 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; // 子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
```
#### 中断嵌套
- 中断响应时,如果又有新的中断请求产生,就会发生中断嵌套。
- 在处理中断嵌套时,需要注意保存现场和恢复现场的顺序,以免出现逆序恢复导致系统错误的情况。
以下是在STM32F4单片机上处理中断嵌套的示例代码:
```c
// 外部中断0服务程序
void EXTI0_IRQHandler(void) {
// 保存现场
NVIC_SystemHandlerPendingBitConfig(SYS_TICK, DISABLE); // 禁用SysTick中断
__disable_irq(); // 禁用所有中断
// 处理中断
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) {
EXTI_ClearITPendingBit(EXTI_Line0);
// 触发外部中断1
EXTI_GenerateSWInterrupt(EXTI_Line1);
}
// 恢复现场
__enable_irq(); // 使能所有中断
NVIC_SystemHandlerPendingBitConfig(SYS_TICK, ENABLE); // 使能SysTick中断
}
```
#### 中断延迟
- 当中断服务程序执行时间过长或存在阻塞操作时,会导致其他中断响应受到影响,甚至出现错误。
- 在设计中,需要尽量避免中断延迟,并采取一些手段优化中断服务程序的执行效率,以确保系统实时性。
以下是降低一些常见中断延迟的方法:
- 将中断服务程序的执行时间尽量缩短;
- 禁止在中断服务程序中使用延时函数、锁存器等方式;
- 对于需要较长时间处理的中断事件,可以通过将其放在主循环中异步处理的方式来避免中断延迟。
#### 响应优先级和先占优先级
在中断处理中,常常涉及到两个概念:响应优先级和先占优先级。
响应优先级是指中断请求发生时,CPU按照一定的优先级顺序来处理多个中断请求。具有更高响应优先级的中断会在低优先级中断之前得到处理。
先占优先级是指在某个中断服务程序执行期间,不允许其他优先级低于其自身的中断干扰它的执行。如果此时发生了优先级更高的中断请求,则需要等待当前中断服务程序执行完成后再进行处理,这就是优先级抢占。
在STM32F4单片机中,中断响应优先级分为抢占优先级和响应优先级,其中抢占优先级用于决定同优先级中断之间的抢占关系,而响应优先级则用于决定不同优先级中断之间的响应顺序。
以下是在STM32F4单片机中设置中断优先级的示例代码:
```c
// 设置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; // 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; // 响应优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
```
在该示例中,抢占优先级为1,响应优先级为1,表示该中断服务程序的优先级比所有响应优先级为2及以下的中断都要高。
总之,在设计中断处理过程时,需要合理设置中断响应和抢占优先级,以满足系统实时性和稳定性的要求。
### 注意事项:
#### 中断开关
在使用中断前,需要先打开中断总开关,使得单片机可以响应中断请求。在STM32F4单片机中,可以通过以下代码打开中断总开关:
```c
// 打开中断总开关
__enable_irq();
```
在使用完中断后,需要关闭中断总开关,以避免产生其他不必要的中断响应。在STM32F4单片机中,可以通过以下代码关闭中断总开关:
```c
// 关闭中断总开关
__disable_irq();
```
#### 中断优先级
在使用中断时,需要根据实际需求设置中断优先级,以确保不同优先级中断的响应顺序和抢占关系符合设计要求。如果中断优先级设置不当,可能会导致系统性能下降或出现严重错误。
#### 中断嵌套
当多个中断同时发生时,可能会出现中断嵌套的情况,这会影响中断处理效率和正确性。因此,在编写中断服务程序时,需要考虑中断嵌套的情况,并采取相应的措施避免嵌套带来的问题。
#### 中断延迟
当中断服务程序执行时间过长或存在阻塞操作时,会导致其他中断响应受到影响,甚至出现错误。因此,在设计中,需要尽量避免中断延迟,并采取一些手段优化中断服务程序的执行效率,以确保系统实时性。
#### 中断与优先级反转
中断与优先级反转是一种由于中断优先级问题导致的系统问题,这可能会使得高优先级的任务被低优先级的中断事件阻塞。在设计中需要避免出现中断与优先级反转的情况,并采取一些手段解决该问题。
总之,在使用中断时,需要全面考虑系统的实时性、可靠性和稳定性,以确保中断处理能够达到预期的效果。
|