掌握STM32单片机中断机制:解决程序异常的利器
发布时间: 2024-07-02 02:38:09 阅读量: 82 订阅数: 27
![stm32单片机代码](https://wiki.st.com/stm32mpu/nsfr_img_auth.php/2/25/STM32MP1IPsOverview.png)
# 1. STM32中断机制概述**
STM32微控制器提供了一个强大的中断机制,用于处理外部事件和内部请求。中断是一种硬件机制,当特定条件发生时,它会暂停当前正在执行的代码并跳转到一个称为中断服务函数(ISR)的特殊函数。ISR负责处理中断事件并采取适当的措施。
中断机制允许STM32对外部事件(如按钮按下或定时器超时)快速响应,而无需轮询。它还提供了对优先级中断的支持,允许系统在多个中断同时发生时确定哪个中断应优先处理。
# 2. 中断处理流程与中断向量表
### 2.1 中断处理流程
当一个中断事件发生时,STM32会执行以下中断处理流程:
1. **中断请求:** 外设或系统事件触发中断请求,向NVIC(嵌套向量中断控制器)发送中断请求信号。
2. **中断向量表查找:** NVIC根据中断请求信号在中断向量表中查找对应的中断向量地址。
3. **程序跳转:** CPU将程序计数器(PC)设置为中断向量地址,跳转到中断服务函数(ISR)的入口点。
4. **ISR执行:** ISR执行中断处理代码,处理中断事件。
5. **中断返回:** ISR执行完毕后,通过执行`RET`指令返回中断发生前的代码位置。
### 2.2 中断向量表
中断向量表是一个存储在固定内存地址处的特殊数据结构,它包含所有中断向量地址。每个中断向量地址指向一个ISR的入口点。
中断向量表的结构如下:
| 地址 | 中断向量 |
|---|---|
| 0x00000000 | 复位向量 |
| 0x00000004 | 非屏蔽中断向量 |
| 0x00000008 | 硬故障向量 |
| ... | ... |
| 0x000003FC | SysTick向量 |
**代码块:**
```c
// 中断向量表定义
__attribute__((section(".isr_vector")))
void (* const isr_vectors[])(void) = {
[0] = Reset_Handler, // 复位向量
[1] = NMI_Handler, // 非屏蔽中断向量
[2] = HardFault_Handler, // 硬故障向量
[3] = MemManage_Handler, // 内存管理错误向量
[4] = BusFault_Handler, // 总线故障向量
[5] = UsageFault_Handler, // 使用故障向量
[6] = SVC_Handler, // SVC调用向量
[7] = DebugMon_Handler, // 调试监视器向量
[8] = PendSV_Handler, // PendSV向量
[9] = SysTick_Handler, // SysTick向量
};
```
**逻辑分析:**
这段代码定义了STM32的中断向量表,它是一个数组,其中每个元素都是一个ISR的入口点地址。数组的索引对应于中断向量表中的向量号。
**表格:**
| 中断类型 | 中断向量号 | 中断向量地址 |
|---|---|---|
| 复位 | 0 | 0x00000000 |
| 非屏蔽中断 | 1 | 0x00000004 |
| 硬故障 | 2 | 0x00000008 |
| ... | ... | ... |
| SysTick | 9 | 0x000003FC |
**Mermaid流程图:**
```mermaid
graph LR
subgraph 中断处理流程
A[中断请求] --> B[中断向量表查找]
B --> C[程序跳转]
C --> D[ISR执行]
D --> E[中断返回]
end
```
# 3.1 中断优先级
中断优先级决定了当多个中断同时发生时,哪个中断会被优先处理。STM32芯片中,每个中断源都有一个优先级,优先级范围为0~255,数字越小,优先级越高。
**优先级分组和子优先级**
STM32的中断优先级分为两个部分:优先级分组和子优先级。优先级分组将中断源分为4个组,每个组有8个子优先级。优先级分组的取值范围为0~3,数字越大,组内中断的优先级越低。
**优先级设置**
中断优先级可以通过NVIC(嵌套向量中断控制器)寄存器进行设置。NVIC寄存器中包含了中断优先级分组和子优先级的设置位。
**代码示例**
```c
// 设置中断优先级分组为组1
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_1);
// 设置中断源IRQn的优先级为子优先级3
NVIC_SetPriority(IRQn, 3);
```
**逻辑分析**
* `NVIC_SetPriorityGrouping()`函数用于设置中断优先级分组。
* `NVIC_SetPriority()`函数用于设置指定中断源的优先级。
* 中断优先级分组和子优先级共同决定了中断的优先级。
## 3.2 中断嵌套
中断嵌套是指在处理一个中断时,又发生了另一个中断。STM32芯片支持中断嵌套,允许高优先级中断打断低优先级中断的执行。
**嵌套优先级**
中断嵌套时,高优先级中断会抢占低优先级中断的执行。抢占后的低优先级中断会进入一个嵌套栈,等待高优先级中断处理完成后再继续执行。
**嵌套栈**
STM32芯片中,每个中断源都有一个嵌套栈。嵌套栈用于保存被抢占中断的寄存器值和程序计数器值。
**嵌套中断处理流程**
当一个高优先级中断发生时,以下流程将发生:
1. 保存当前中断的寄存器值和程序计数器值到嵌套栈。
2. 执行高优先级中断服务函数。
3. 当高优先级中断处理完成后,从嵌套栈中恢复被抢占中断的寄存器值和程序计数器值。
4. 继续执行被抢占中断。
**代码示例**
```c
// 在高优先级中断服务函数中抢占低优先级中断
void HighPriorityISR()
{
// 保存低优先级中断的寄存器值和程序计数器值
NVIC_SaveContext();
// 执行高优先级中断服务函数
// 恢复低优先级中断的寄存器值和程序计数器值
NVIC_RestoreContext();
}
```
**逻辑分析**
* `NVIC_SaveContext()`函数用于保存当前中断的寄存器值和程序计数器值到嵌套栈。
* `NVIC_RestoreContext()`函数用于从嵌套栈中恢复被抢占中断的寄存器值和程序计数器值。
* 中断嵌套允许高优先级中断打断低优先级中断的执行,从而保证了系统的实时性。
# 4. 中断服务函数
### 4.1 中断服务函数的编写
中断服务函数(ISR)是响应特定中断请求而执行的代码段。每个中断源都有一个对应的 ISR,用于处理该中断事件。ISR 必须遵循以下规则:
- **名称约定:**ISR 的名称通常以 "NVIC_" 开头,后跟中断源名称。例如,用于处理外部中断 0 的 ISR 称为 "NVIC_EXTI0_IRQHandler"。
- **函数签名:**ISR 必须具有以下函数签名:
```cpp
void NVIC_EXTI0_IRQHandler(void);
```
- **函数体:**ISR 的函数体应包含处理中断事件所需的代码。通常包括以下步骤:
- 读取中断标志寄存器以确认中断源。
- 清除中断标志寄存器以复位中断请求。
- 执行中断处理逻辑。
- 返回中断处理程序。
### 4.2 中断服务函数的执行
当发生中断时,CPU 会根据中断向量表跳转到相应的 ISR。ISR 执行以下步骤:
1. **保存寄存器:**ISR 首先保存当前寄存器值,包括程序计数器 (PC)、堆栈指针 (SP) 和其他寄存器。
2. **处理中断:**ISR 读取中断标志寄存器以确定中断源,然后执行必要的处理逻辑。
3. **清除中断标志:**ISR 清除中断标志寄存器以复位中断请求。
4. **恢复寄存器:**ISR 恢复先前保存的寄存器值,包括 PC 和 SP。
5. **返回中断处理程序:**ISR 通过执行 "RET" 指令返回中断处理程序。
**代码块:**
```cpp
void NVIC_EXTI0_IRQHandler(void) {
// 读取中断标志寄存器
if (EXTI->PR & EXTI_PR_PR0) {
// 清除中断标志
EXTI->PR |= EXTI_PR_PR0;
// 执行中断处理逻辑
// ...
}
// 返回中断处理程序
__asm volatile("RET");
}
```
**逻辑分析:**
* ISR 读取中断标志寄存器 `EXTI->PR` 以检查外部中断 0 是否触发。
* 如果中断触发,ISR 清除中断标志寄存器 `EXTI->PR` 以复位中断请求。
* ISR 执行中断处理逻辑,例如读取输入引脚状态或设置输出引脚。
* ISR 通过执行 "RET" 指令返回中断处理程序。
# 5. 中断使能与禁止
### 5.1 中断使能
中断使能是指允许特定中断源产生中断请求。STM32 中断控制器提供了两种中断使能方式:全局中断使能和个别中断源使能。
**全局中断使能**
全局中断使能通过设置 NVIC_ISER 寄存器中的相应位来实现。NVIC_ISER 寄存器是一个 32 位寄存器,每一位对应一个中断源。要使能中断源 n,需要将 NVIC_ISER[n] 设置为 1。
```c
// 使能中断源 3
NVIC_ISER |= (1 << 3);
```
**个别中断源使能**
个别中断源使能通过设置中断源寄存器中的使能位来实现。每个中断源都有一个对应的寄存器,其中包含一个使能位。要使能中断源 n,需要将中断源寄存器中的使能位设置为 1。
```c
// 使能 UART1 中断
USART1->CR1 |= USART_CR1_RXNEIE;
```
### 5.2 中断禁止
中断禁止是指阻止特定中断源产生中断请求。STM32 中断控制器也提供了两种中断禁止方式:全局中断禁止和个别中断源禁止。
**全局中断禁止**
全局中断禁止通过设置 NVIC_ICER 寄存器中的相应位来实现。NVIC_ICER 寄存器是一个 32 位寄存器,每一位对应一个中断源。要禁止中断源 n,需要将 NVIC_ICER[n] 设置为 1。
```c
// 禁止中断源 3
NVIC_ICER |= (1 << 3);
```
**个别中断源禁止**
个别中断源禁止通过清除中断源寄存器中的使能位来实现。要禁止中断源 n,需要将中断源寄存器中的使能位设置为 0。
```c
// 禁止 UART1 中断
USART1->CR1 &= ~USART_CR1_RXNEIE;
```
### 中断使能与禁止的应用
中断使能与禁止在实际应用中非常重要。通过合理使用中断使能与禁止,可以优化系统的性能和功耗。
**优化性能**
在某些情况下,可以暂时禁止不必要的中断源,以提高系统性能。例如,在进行时间敏感的操作时,可以禁止低优先级中断源,以避免中断处理程序干扰操作。
**优化功耗**
在低功耗应用中,可以禁止不必要的中断源,以降低功耗。例如,在待机模式下,可以禁止所有非必要的中断源,以最大程度地降低功耗。
**避免中断冲突**
通过合理使用中断使能与禁止,可以避免中断冲突。中断冲突是指多个中断源同时请求中断时发生的现象。通过禁止低优先级中断源,可以防止高优先级中断源被低优先级中断源抢占。
# 6. 中断调试与故障处理**
中断调试和故障处理对于确保嵌入式系统可靠运行至关重要。本章将介绍中断调试方法和常见故障处理技术。
**6.1 中断调试方法**
* **使用调试器:**调试器可以提供中断触发、中断服务函数执行跟踪等功能,帮助快速定位中断问题。
* **查看中断向量表:**中断向量表可以显示中断源和对应的中断服务函数地址,有助于识别中断触发源。
* **检查中断优先级:**中断优先级配置不当会导致中断处理顺序混乱,使用调试器或查看中断控制器寄存器来检查优先级设置。
* **使用示波器:**示波器可以捕获中断信号,分析中断触发时间和中断处理过程,有助于诊断硬件问题。
**6.2 中断故障处理**
* **中断未触发:**检查中断源是否正确配置,中断引脚是否连接,中断控制器寄存器是否设置正确。
* **中断触发异常:**检查中断服务函数是否编写正确,是否存在死循环或无限递归,导致中断无法正常返回。
* **中断优先级错误:**确保中断优先级正确设置,避免高优先级中断被低优先级中断抢占。
* **中断嵌套错误:**检查中断嵌套配置,避免嵌套中断导致系统死锁。
* **中断使能错误:**确保中断已正确使能,检查中断控制器寄存器和中断服务函数中的使能设置。
0
0