cortex_m_vector_entry函数详解
2026/4/29大约 6 分钟初始化初始化中断
cortex_m_vector_entry函数详解
📚 本节导读
用途: 讲解OneOS中断分发函数cortex_m_vector_entry的完整工作原理
来源: drivers/boot/cotex-m/vector_table.c
一、函数概述
1.1 函数定位
cortex_m_vector_entry 是 OneOS 中断系统的核心分发函数,负责所有中断和异常的统一处理。
1.2 基本信息
| 属性 | 值 |
|---|---|
| 函数名 | cortex_m_vector_entry |
| 源文件 | drivers/boot/cotex-m/vector_table.c |
| 调用时机 | 任何中断或异常发生时 |
| 作用 | 统一处理所有中断,支持钩子机制 |
二、完整代码
2.1 函数定义
static void cortex_m_vector_entry(void)
{
vector_entry *vtable;
os_irq_hook hook;
#ifdef OS_USING_IRQ_MONITOR
cpu_usage_irq_monitor_start();
#endif
int irq = os_irq_num();
if (irq == 0)
{
irq = 1;
cortex_m_set_vector();
}
else
{
hook = (os_irq_hook) * (uint32_t *)RESERVED_RAM_VECTOR_HOOK;
hook(irq);
}
vtable = (vector_entry *)*(uint32_t *)RESERVED_RAM_VECTOR_ADDR;
vtable[irq]();
os_interrupt_stack_check();
#ifdef OS_USING_IRQ_MONITOR
cpu_usage_irq_monitor_stop();
#endif
}三、函数工作流程详解
3.1 中断监控开始(可选)
#ifdef OS_USING_IRQ_MONITOR
cpu_usage_irq_monitor_start();
#endif- 作用:如果启用了中断监控功能,记录中断开始时间
- 用途:用于统计 CPU 使用率和中断响应时间
STM32F103ZET6 实际配置
OS_USING_IRQ_MONITOR:未定义(在 oneos_config.h 中 "CPU monitor" 部分为空)
结论:条件不满足,这段代码不会执行。
3.2 获取当前中断号
int irq = os_irq_num();- 作用:调用
os_irq_num()从 IPSR 寄存器读取当前中断号 - 中断号说明:
- 0:复位
- 1-15:系统异常
- 16+:外部中断
3.3 特殊情况处理(irq == 0)
if (irq == 0)
{
irq = 1;
cortex_m_set_vector();
}详细说明:
- 当 irq == 0 时,表示是复位
- 将 irq 设置为 1(NMI),但实际上不执行 NMI 处理
- 重新调用
cortex_m_set_vector()进行向量表初始化 - 为什么这样设计:这是一个安全机制,确保在复位时向量表正确配置
3.4 调用中断钩子函数(irq != 0)
else
{
hook = (os_irq_hook) * (uint32_t *)RESERVED_RAM_VECTOR_HOOK;
hook(irq);
}第一步:读取钩子函数地址
hook = (os_irq_hook) * (uint32_t *)RESERVED_RAM_VECTOR_HOOK;RESERVED_RAM_VECTOR_HOOK:指向 RAM 保留区域,存储钩子函数地址(uint32_t *):将地址转换为uint32_t类型的指针*:解引用指针,读取该地址存储的值(os_irq_hook):将读取的值强制转换为函数指针类型
第二步:调用钩子函数
hook(irq);- 调用
cortex_m_irq_hook()函数 - 传入当前中断号
irq作为参数 cortex_m_irq_hook()会进一步调用所有注册的中断钩子
详细说明
关于中断钩子机制的完整解析,请参考:cortex_m_irq_hook函数详解
3.5 调用真正的中断处理函数
vtable = (vector_entry *)*(uint32_t *)RESERVED_RAM_VECTOR_ADDR;
vtable[irq]();详细说明:
- 从 RAM 保留区域读取原始向量表地址
vtable指向原始向量表(通常是__Vectors)- 根据中断号索引找到对应的处理函数
- 调用真正的硬件相关的中断处理函数
3.6 中断栈检查
os_interrupt_stack_check();- 作用:检查中断栈是否溢出
- 重要性:这是一个重要的安全检查,防止栈溢出导致系统崩溃
3.7 中断监控结束(可选)
#ifdef OS_USING_IRQ_MONITOR
cpu_usage_irq_monitor_stop();
#endif- 作用:如果启用了中断监控,记录中断结束时间
- 与开始时的
cpu_usage_irq_monitor_start()配对使用
STM32F103ZET6 实际配置
OS_USING_IRQ_MONITOR:未定义(在 oneos_config.h 中 "CPU monitor" 部分为空)
结论:条件不满足,这段代码不会执行。
四、🎯 钩子具体有什么用?(以 os_lpmgr_irq_entry 为例)
4.1 钩子的核心功能:低功耗管理(Tickless)
场景:设备进入睡眠模式时
- 设备空闲时,系统会进入低功耗睡眠模式以节省电能
- 睡眠前会设置一个定时器,在预期的下一个任务唤醒时间点唤醒
- 但实际可能因为其他中断(如 UART 收到数据、按键按下等)提前唤醒
4.2 os_lpmgr_irq_entry 的具体工作
static void os_lpmgr_irq_entry(int irq)
{
os_ubase_t level;
struct os_lpmgr_dev *lpmgr = gs_lpmgr;
os_spin_lock_irqsave(&gs_device_lock, &level);
if (gs_lp_sleep_state == LP_SLEEP_STATUS_ACTIVE)
{
if (gs_lp_sleep_nsec != 0)
{
if (irq != gs_lpce_irq)
{
gs_lp_sleep_nsec = os_clockevent_read(lpmgr->lpce);
os_clockevent_stop(lpmgr->lpce);
}
os_lpmgr_update(gs_lp_sleep_nsec);
}
gs_lp_sleep_state = LP_SLEEP_STATUS_UPDATED;
}
if (gs_lp_sleep_state != LP_SLEEP_STATUS_NONE)
{
/* period trig 1ms, avoid sleep forever */
os_clockevent_start_oneshot(lpmgr->lpce, max(1000000ULL, lpmgr->lpce->min_nsec));
}
os_spin_unlock_irqrestore(&gs_device_lock, &level);
}工作步骤:
- 判断系统是否正在睡眠状态
- 如果在睡眠,判断这个中断是不是我们设置的唤醒定时器中断
- 如果不是定时器中断:
- 读取实际睡眠了多长时间
- 停止原来的定时器
- 更新系统时间(弥补睡眠时间)
- 标记睡眠状态已更新
- 启动 1ms 的安全定时器(看门狗机制)
4.3 具体例子(实际场景)
场景:UART 收到数据提前唤醒
T0: 系统进入睡眠,设置 100ms 后唤醒(下一个任务)
↓
T15ms: UART 收到数据,产生中断(irq = 37)
↓
CPU 唤醒,调用 cortex_m_vector_entry()
↓
调用 os_lpmgr_irq_entry(37) ← 这就是钩子!
↓
检测:这不是我们设置的定时器中断
↓
读取时钟:实际只睡了 15ms
↓
更新系统时间:补上这 15ms
↓
停止原来的 100ms 定时器(不需要了)
↓
启动 1ms 安全定时器
↓
然后调用真正的 UART1_IRQHandler() 处理数据4.4 这个钩子解决的问题
- 时间准确性:记录实际睡眠时间,更新系统 tick
- 资源释放:提前被唤醒时停止不必要的定时器
- 安全保护:1ms 定时器防止系统死睡
- 电源优化:精确管理睡眠时间,最大化节能
五、🔧 钩子的其他可能用途
5.1 性能统计
static void irq_monitor_hook(int irq)
{
// 记录中断发生时间、统计中断频率
g_irq_stats[irq].count++;
g_irq_stats[irq].last_time = os_systick_get();
}
OS_IRQ_HOOK(irq_monitor_hook);5.2 调试追踪
static void irq_trace_hook(int irq)
{
// 记录所有中断,用于调试
os_kprintf("[IRQ] #%d occurred\n", irq);
}
OS_IRQ_HOOK(irq_trace_hook);5.3 安全监控
static void irq_safety_hook(int irq)
{
// 检查某些关键中断是否在安全时间窗口内
if (is_safety_critical(irq))
{
check_safety_constraints();
}
}
OS_IRQ_HOOK(irq_safety_hook);六、设计优势
6.1 统一入口管理
- 所有中断都经过这个函数,便于统一管理
- 可以在统一的地方添加钩子、栈检查、性能监控等功能
6.2 动态钩子支持
- 支持在运行时注册和修改中断钩子
- 多个钩子可以同时工作,互不干扰
6.3 灵活性
- 原始向量表地址保存在 RAM 中,支持动态修改
- 可以方便地替换原始向量表
6.4 安全性
- 包含中断栈检查等安全机制
- 保证系统稳定运行
6.5 可监控性
- 支持中断监控和 CPU 使用率统计
- 便于性能分析和调试
七、🎯 总结
7.1 核心流程
中断发生
↓
cortex_m_vector_entry() 被调用
↓
获取中断号
↓
调用所有注册的钩子(可选)
↓
从 RAM 读取原始向量表地址
↓
调用真正的中断处理函数
↓
中断栈检查
↓
中断返回7.2 关键要点
- cortex_m_vector_entry 是一个包装函数,不是真正的中断处理函数
- 钩子 是可选的扩展功能,用于执行通用操作
- vtableirq 才是调用真正的、与硬件相关的中断处理函数
- 这种设计兼顾了灵活性、兼容性和可扩展性