第 1 章 背景:ECU 里为什么需要操作系统
一句话
一辆车里有几十到上百个 ECU(Electronic Control Unit,电子控制单元)。 本项目仿真的是其中最重要的一个:EMS 发动机管理 ECU——负责喷油、点火、采集传感器。
为什么不能像单片机那样写个 while(1) 就完事
因为任务有轻重缓急,而且要求确定性的时间:
- 爆震检测:必须在几毫秒内响应,晚了发动机就毁了
- 喷油点火计算:必须严格按 20ms 节奏来,抖动会导致抖动、熄火
- 故障诊断:慢一点无所谓,100ms 跑一次就行
如果写成一条大循环,一个慢任务就会把紧急任务拖死。 所以需要 OS 来做调度:谁优先级高,谁立刻抢 CPU。
AUTOSAR 是什么
一句话:汽车行业的软件标准。它规定了 OS 有哪些对象、哪些 API、哪些状态。 好处是:A 供应商写的软件能跑在 B 供应商的芯片上。
本项目用到的 AUTOSAR OS 对象只有 7 类,全部记住就够:
Task(任务) Resource(资源/锁) Event(事件)
Alarm(闹钟) Counter(计数器) ISR(中断)
Hook(钩子)

第 2 章 任务与优先级:抢占是第一课
一句话
AUTOSAR OS 用的是"固定优先级完全抢占":优先级数字越大越优先; 任何时候只要来了个更高优先级的任务,正在跑的低优先级任务立刻让出 CPU。
本项目的 7 个任务(背下这张表)
| 任务 | 优先级 | 类型 | WCET | 周期/触发 | 干什么 |
|---|---|---|---|---|---|
Task_Init |
9 | 基本 | 0.80ms | 上电一次 | 标定、RAM 自检 |
Task_Emerg |
7 | 基本 | 0.60ms | 爆震中断 | 紧急工况 |
Task_Sensor_10ms |
6 | 基本 | 0.50ms | 10ms | 采传感器 |
Task_Comm |
5 | 扩展 | 0.70ms | CAN 帧 | CAN 通信 |
Task_Ctrl_20ms |
4 | 基本 | 0.90ms | 20ms | 喷油点火闭环 |
Task_Diag_50ms |
3 | 扩展 | 0.40ms | 50ms | 诊断/故障码 |
Task_Bg_100ms |
1 | 扩展 | 1.20ms | 100ms | 后台存 NVM + 喂狗 |

任务的四种状态
ActivateTask
┌──────────────────────────────► ┐
│ │
SUSPENDED READY ──── 被调度器选中 ──► RUNNING
(挂起) (就绪) ◄── 被更高优先级抢占 ──┘
▲ │
│ │ WaitEvent(只有扩展任务)
└── 执行完毕 ─────────────────────┴──► WAITING (等待事件)
│ SetEvent
└──────► 回到 READY
第 3 章 时间是怎么流动的
一句话
仿真时间是浮点数毫秒。CPU 是独占资源,任务只有在真正占用 CPU 时才扣减自己的 "执行预算";预算扣完就代表这段代码跑完了。
三个核心概念
| 概念 | 含义 | 源码 |
|---|---|---|
now |
当前仿真时刻(ms) | os_kernel.py:226 |
budget_left |
该任务还剩多少 ms 没执行 | 扣减在 os_kernel.py:429 |
_events 事件堆 |
未来会发生什么事,按时间排序 | os_kernel.py:239,用 heapq |
第 4 章 Alarm 与 Counter:周期从哪来
一句话
任务的周期性不是靠 sleep 实现的,而是靠 Alarm(闹钟): 闹钟挂在 Counter(计数器)上,到点就 ActivateTask 或 SetEvent。
ISR_Systick(每1ms)──驱动──► SystemCounter ──挂载──► Alarm_10ms ──到点──► ActivateTask(Task_Sensor_10ms)
本项目的 2 个计数器、5 个闹钟
| 计数器 | 节拍 | 驱动源 |
|---|---|---|
SystemCounter |
1.0ms | ISR_Systick |
CrankCounter |
31.0ms | ISR_Crank(曲轴 180°,966rpm 时约 31ms) |

第 5章 问题总结
Q1:这个仿真能替代实板测试吗? 不能。它是简化模型(时间按 WCET 线性消耗),适合验证调度逻辑与配置约束, 不能替代 WCET 实测。真正的时序验证必须实板测量。
Q2:为什么 Python 和 JS 要各写一遍内核? 为了验证"逻辑本身是对的"而不是"某个语言碰巧跑通了"。 两边用完全相同的场景导出指标逐项对比,1336 项 0 差异。 这个方法真抓到过 bug:dyn_priority 初值写成 1,导致从不调用 GetResource 的任务被当成 P1 调度 —— 纯 Python 测试完全没发现。
Q3:同优先级为什么不切换? 省上下文切换开销。1ms 一次的节拍中断如果每次都换任务, 系统光切换就耗掉大量 CPU。这是 AUTOSAR 的明确约定。
Q4:PCP 会不会导致优先级反转消失但死锁? 不会。PCP 保证的是无死锁的阻塞(每个资源只有一个天花板,按序申请即可)。 真正的死锁要靠"按固定顺序申请资源"来避免,这属于应用层约定。
Q5:喂狗任务优先级最低,那不是很容易误复位吗? 这正是设计意图。看门狗不只防死循环,还防调度失衡。 如果低优先级任务长期饿死,说明系统设计有问题,复位反而是正确的保护。
Q6:我想给这个工具加一个任务,要改几处? 只改 oil_config.py 的 TASKS 一处。界面会自动多一张卡片、甘特图自动多一行。 这就是分层的好处。