PPT 参考图 -- 中断系统与执行流程
graph TD
subgraph NORMAL
A["主程序<br/>Task A"] --> B["Task B"]
end
subgraph INTERRUPT
IRQ["中断请求<br/>(硬件事件)"] --> SAVE["保存现场<br/>(自动压栈)"]
SAVE --> SERVICE["中断服务函数<br/>ISR"]
SERVICE --> RESTORE["恢复现场<br/>(出栈)"]
RESTORE --> RETURN["返回主程序"]
end
NORMAL --> IRQ
RETURN --> NORMAL
| 术语 | 说明 |
|---|---|
| 中断 | CPU 暂停当前任务,处理紧急事件 |
| 中断优先级 | 决定多个中断同时触发时的响应顺序 |
| 中断嵌套 | 高优先级中断可以打断低优先级中断服务函数 |
| 向量表 | 存储所有中断函数入口地址的表(位于 0x08000000 起始) |
| 尾链(Tail Chaining) | Cortex-M3 优化:两个中断连续进出时省去多余出栈入栈 |
PPT 参考图 -- STM32中断与NVIC
| 特性 | STM32F103 |
|---|---|
| 可屏蔽中断通道 | 68 个 |
| 可编程优先级 | 16 级 (4bit) |
| 固定优先级 | 中断号越小优先级越高 |
| 系统异常 | 8 个 (Reset, NMI, HardFault, MemManage, BusFault, UsageFault, SVCall, PendSV, SysTick) |
| 外部中断 | EXTI0~EXTI15 (16 条线) |
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
| 分组宏 | 抢占位 | 子优先级位 | 抢占级数 | 子优先级数 |
|---|---|---|---|---|
NVIC_PriorityGroup_0 |
0 | 4 | 1 | 16 |
NVIC_PriorityGroup_1 |
1 | 3 | 2 | 8 |
NVIC_PriorityGroup_2 |
2 | 2 | 4 | 4 |
NVIC_PriorityGroup_3 |
3 | 1 | 8 | 2 |
NVIC_PriorityGroup_4 |
4 | 0 | 16 | 1 |
建议:全系统只用一种分组,在
main()入口配置一次。推荐NVIC_PriorityGroup_2。
PPT 参考图 -- EXTI简介与结构框图
graph TD
subgraph GPIO_PINS
PA0["PA0"]
PB0["PB0"]
PC0["PC0"]
PA1["PA1"]
PB14["PB14"]
PE0["PE0"]
end
subgraph AFIO
AFIO_MUX["AFIO 中断引脚选择"]
end
subgraph EXTI
EXTI0["EXTI0"]
EXTI1["EXTI1"]
EXTI14["EXTI14"]
end
subgraph NVIC
NVIC0["NVIC EXTI0_IRQn"]
NVIC1["NVIC EXTI1_IRQn"]
NVIC9_5["NVIC EXTI9_5_IRQn"]
NVIC15_10["NVIC EXTI15_10_IRQn"]
end
subgraph CORE
CPU["Cortex-M3"]
end
PA0 --> AFIO_MUX --> EXTI0 --> NVIC0 --> CPU
PB0 --> AFIO_MUX --> EXTI0
PC0 --> AFIO_MUX --> EXTI0
PA1 --> AFIO_MUX --> EXTI1 --> NVIC1 --> CPU
PB14 --> AFIO_MUX --> EXTI14 --> NVIC9_5 --> CPU
PE0 --> AFIO_MUX --> EXTI0
外部信号从 GPIO 引脚到 CPU 响应的完整信号链:
GPIO_EXTILineConfig 配置 AFIO_EXTICRx 寄存器的相应位段EXTI_RTSR(上升沿触发选择寄存器)和 EXTI_FTSR(下降沿触发选择寄存器)选择触发方式。边沿检测器在检测到配置的边沿时产生脉冲EXTI_PR(挂起寄存器)对应位置 1,此位保持 1 直到软件写入 1 清除(写 1 清零机制)中断响应全过程时钟周期:
| EXTI 线 | 中断号 | NVIC 通道 | IRQHandler |
|---|---|---|---|
| EXTI0 | 6 | EXTI0_IRQn |
EXTI0_IRQHandler |
| EXTI1 | 7 | EXTI1_IRQn |
EXTI1_IRQHandler |
| EXTI2 | 8 | EXTI2_IRQn |
EXTI2_IRQHandler |
| EXTI3 | 9 | EXTI3_IRQn |
EXTI3_IRQHandler |
| EXTI4 | 10 | EXTI4_IRQn |
EXTI4_IRQHandler |
| EXTI5~9 | 23 | EXTI9_5_IRQn |
EXTI9_5_IRQHandler |
| EXTI10~15 | 40 | EXTI15_10_IRQn |
EXTI15_10_IRQHandler |
EXTI5-9 共用中断函数
EXTI9_5_IRQHandler,需在函数内判断EXTI_GetITStatus(EXTI_Line5) ~ EXTI_GetITStatus(EXTI_Line9)。 EXTI10-15 共用EXTI15_10_IRQHandler,同理。
// 将 PB14 连接到 EXTI14
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
| EXTI_InitTypeDef 成员 | 取值 | 说明 |
|---|---|---|
EXTI_Line |
EXTI_Line0 ~ EXTI_Line15 |
中断线 |
EXTI_Mode |
EXTI_Mode_Interrupt / EXTI_Mode_Event |
中断模式 / 事件模式 |
EXTI_Trigger |
EXTI_Trigger_Rising / EXTI_Trigger_Falling / EXTI_Trigger_Rising_Falling |
触发方式 |
EXTI_LineCmd |
ENABLE / DISABLE |
使能 |
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
| NVIC_InitTypeDef 成员 | 取值 | 说明 |
|---|---|---|
NVIC_IRQChannel |
EXTI0_IRQn 等 |
中断通道 |
NVIC_IRQChannelPreemptionPriority |
0~15 (取决于分组) | 抢占优先级 |
NVIC_IRQChannelSubPriority |
0~15 (取决于分组) | 子优先级 |
NVIC_IRQChannelCmd |
ENABLE / DISABLE |
使能 |
| 函数 | 原型 | 说明 |
|---|---|---|
EXTI_GetITStatus |
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line) |
获取中断标志位 |
EXTI_ClearITPendingBit |
void EXTI_ClearITPendingBit(uint32_t EXTI_Line) |
清除中断标志位 |
void EXTIx_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_LineX) != RESET)
{
// 处理中断事件
EXTI_ClearITPendingBit(EXTI_LineX); // 清除标志位
}
}
void EXTI9_5_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) != RESET)
{
// PB14 中断处理
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
uint16_t SensorCount = 0;
void Sensor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 2. GPIO 配置 (上拉输入)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 3. AFIO 中断引脚选择
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
// 4. EXTI 配置 (下降沿触发)
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 5. NVIC 配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
uint16_t Sensor_Get(void)
{
return SensorCount;
}
// EXTI15_10 中断服务函数 (PB14 在 EXTI14 上)
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) != RESET)
{
SensorCount++;
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
int main(void)
{
Delay_Init();
OLED_Init();
Sensor_Init();
OLED_ShowString(1, 1, "Sensor Count:");
while (1)
{
OLED_ShowNum(2, 1, Sensor_Get(), 5);
Delay_ms(100);
}
}
PPT 参考图 -- 旋转编码器硬件
graph TD
subgraph ENCODER
A_PHASE["A 相 (PB12)"] -->|"EXTI12<br/>双边缘触发"| MCU
B_PHASE["B 相 (PB13)"] -->|"读取电平<br/>判断方向"| MCU
end
MCU --> COUNT["计数值<br/>+1 / -1"]
#include "stm32f10x.h"
int16_t EncoderCount = 0;
void Encoder_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// A相 PB12 — 上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// B相 PB13 — 同上
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// AFIO 映射
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource13);
// EXTI12 — 双边缘触发
EXTI_InitStructure.EXTI_Line = EXTI_Line12;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
int16_t Encoder_Get(void)
{
int16_t temp;
temp = EncoderCount;
EncoderCount = 0; // 读取后清零
return temp;
}
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line12) != RESET)
{
// 判断方向: 读取 B 相电平
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0)
EncoderCount++;
else
EncoderCount--;
EXTI_ClearITPendingBit(EXTI_Line12);
}
}
1. RCC_APB2PeriphClockCmd(GPIOx, ENABLE);
2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // AFIO 时钟
3. GPIO_Init() — 配置输入模式
4. GPIO_EXTILineConfig() — 选择引脚到 EXTI 线
5. EXTI_Init() — 配置触发方式
6. NVIC_PriorityGroupConfig() — 一次
7. NVIC_Init() — 配置中断优先级
8. 写 ISR 函数, 检查标志位并清除
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE),否则 GPIO_EXTILineConfig 无效EXTI_ClearITPendingBit 必须在 ISR 结束前调用,否则一直进中断NVIC_PriorityGroupConfig 行为未定义IPD + EXTI_Trigger_Rising;按键接 GND 用 IPU + EXTI_Trigger_Falling