摘要
本文记录了一次完整的 AI 辅助嵌入式工程移植实践:将亿佰特 E22-433M30S(SX1268,433MHz LoRa) 评估板的 STM32 HAL 裸机演示固件(Keil MDK 工程),移植到 Zephyr RTOS 4.4。整个过程由 AI 代理 主导完成——包括代码分析、方案设计、代码移植、编译调试,直到 J-Link 烧录和上板验证通过。 本文重点讲方法论:AI 是怎么一步步做的、哪些环节最关键、人机如何分工,以及相比原裸机工程, Zephyr 版的优缺点。
1. 任务背景
原工程:STM32F103C8T6 + SX1268 LoRa 模组 + SSD1306 OLED(u8g2 图形库)+ 三按键菜单 (MultMenu 自研框架)+ PWM 蜂鸣器 + USB CDC。裸机无 RTOS,Keil/ARMCC5 构建, 代码含 GBK 编码中文注释、CubeMX 生成代码与手写代码混合。
目标:在 D:\luglZephyrproject(Zephyr 4.4 工作区)下创建一个功能等价的 Zephyr 应用。
约束:不改原工程、不改 Zephyr 内核;界面和操作习惯尽量与原 demo 一致。
这个任务的难点不在于"写代码",而在于两端的上下文都很大:原工程几十个源文件、 Zephyr 工作区几千个驱动/binding/示例。人来做需要数天调研;AI 来做,关键在于 先侦察、再决策、后动手。
2. 方法论:AI 移植的四阶段流程
侦察(Explore) → 决策(Plan) → 实施(Implement) → 验证(Verify)
2.1 侦察:并行子代理探索,只带结论回来
面对两个陌生代码库,AI 没有直接埋头读代码,而是并行派出两个只读探索子代理:
子代理 A:解剖原工程。回答一组具体问题:main 初始化顺序、LoRa 参数默认值与配置序列、 菜单框架 API、按键扫描机制、u8g2 移植层做法、蜂鸣器 PWM 参数、引脚表、第三方库版本等。 产出一份带文件路径和行号的结构化报告。
子代理 B:摸清 Zephyr 侧的能力。回答:SX1268 是否被 Zephyr 4.4 支持(哪个后端、 binding 有哪些属性)、LoRa 子系统 API 长什么样、有没有现成示例、STM32F103 用哪块板 (时钟/USB/flash 情况)、SSD1306/按键/PWM 在 Zephyr 的惯例写法、工作区怎么构建。
要点:探索代理返回的是"结论 + 关键代码摘录",而不是几千行源码 dump。 这既节省了主对话的上下文,也让后续设计建立在已核实的事实上(例如"TIM2_CH2 可以 重映射到 PB3"是 grep 过 pinctrl dtsi 才写进方案的,不是猜的)。
2.2 决策:先问对人,再写方案
侦察完成后,AI 识别出 4 个会实质改变架构的岔路口,没有擅自决定,而是让人类选择:
| LoRa 实现 | Zephyr LoRa 子系统 vs 移植 Semtech 裸驱动 | Zephyr 子系统(代码少、跟内核走;代价是自定义同步字 0x14 用不了,新旧固件不互通) |
| OLED 界面 | 完整移植 u8g2+MultMenu vs 用 CFB 重写简化菜单 | 完整移植(保真优先) |
| USB CDC | 保留 vs 砍掉省 flash | 保留 |
| 小游戏 | 移植 vs 不移植 | 不移植(原工程里就是死代码) |
然后 AI 把方案写成一份可检查的计划文档:目录结构、overlay 每个节点的写法、 每个移植层文件的职责、原接口到新 API 的映射表、构建命令、风险清单(含退路)。 人类批准后才动手。
要点:方案里的每一条都锚定在侦察阶段验证过的事实上;每个风险都附了退路 (例如"蜂鸣器 remap 若不生效,退到 PA1 飞线或 GPIO 软件 PWM")。
2.3 实施:coder 子代理一次性交付,构建闭环
实施交给一个 coder 子代理,交接材料是:批准的计划 + 侦察报告的浓缩版 + 明确的验收标准 (west build 零 error)。coder 完成了:
工程骨架(CMakeLists / prj.conf / overlay / 构建脚本)
第三方代码拷贝(u8g2 裁剪版、MultMenu,剔除游戏)
5 个平台移植层文件(u8g2 I2C 回调、按键、蜂鸣器、LED、LoRa 应用层)
MultMenu 最小改动适配(HAL_GetTick→k_uptime_get_32 等符号映射)
自己跑构建、自己修编译错误,直到通过,并报告 ROM/RAM 实测占用
coder 还主动解决了计划外的问题:板级 dts 默认开启的 usart2/usart3/i2c1/spi2 与本工程 引脚冲突需要关闭;SRAM 只有 20KB,需要逐项压缩栈和 USB 缓冲。
2.4 验证:AI 复验构建 + 烧录,人类做硬件裁判
AI 亲自复跑构建和 rom_report/ram_report,不轻信子代理的报告;
用 J-Link 烧录时遇到真实问题:Zephyr 默认传 STM32F103C8(64KB)给 J-Link, 86KB 固件被拒 → AI 诊断后改用 STM32F103CB(128KB 兼容型号)完成烧录;
上板功能验证由人类完成:Rx 接收测试通过(菜单可收包、显示 RSSI 和丢包统计)。
分工原则:AI 负责一切可在工具链内闭环的验证(编译、静态资源占用、烧录校验), 人类负责物理世界的最终裁判(屏幕亮不亮、能不能收到包)。
3. 移植过程实录:关键映射
3.1 运行时结构
裸机: main while(1) { Menu_Task(); } + SysTick 1ms 中断扫键 + EXTI3 中断内直接 SPI 读包
↓↓↓
Zephyr:菜单线程(key_scan_tick + Menu_Task + k_msleep(2))
+ LoRa 驱动 IRQ → 系统工作队列回调(只做 memcpy + 置标志)
原工程在最高优先级中断里做整套 SPI 读包,是裸机的典型写法;Zephyr 版把这项工作 下沉到驱动和工作队列,应用层完全没有中断上下文代码。
3.2 硬件描述:从 C 代码到 Devicetree
原工程散落在 gpio.c/spi.c/main.h 里的引脚定义,在 Zephyr 版收敛为一个 overlay 文件 (boards/stm32_min_dev_stm32f103xb.overlay):
lora_sx1268: sx1268@0 {
compatible = "semtech,sx1268";
reset-gpios = <&gpiob 0 GPIO_ACTIVE_LOW>;
busy-gpios = <&gpiob 1 GPIO_ACTIVE_HIGH>;
dio1-gpios = <&gpioa 3 GPIO_ACTIVE_HIGH>;
tx-enable-gpios = <&gpiob 12 GPIO_ACTIVE_HIGH>; /* 射频开关 */
rx-enable-gpios = <&gpiob 13 GPIO_ACTIVE_HIGH>;
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_3V3>; /* TCXO 供电 */
tcxo-power-startup-delay-ms = <5>;
force-ldro;
};
原工程 e22_demo_init() 里 20 余行逐条下发的寄存器配置(TCXO、DCDC、射频开关、 复位序列),全部由内核驱动根据这份描述自动完成。
3.3 应用层:接口不变,实现换芯
e22_demo.c 对外接口(e22_demo_init/menu_config/transmit/receive/check_rx_done) 与原工程完全一致,菜单代码零感知;内部从 20+ 个 sx126x_* 寄存器调用换成 5 个 Zephyr API(lora_config/lora_send/lora_recv_async 等)。发射功率换算 (30→12dBm 等)等应用层知识原样保留。
3.4 界面:最大程度复用
u8g2(裁剪版 43 个源文件)和 MultMenu 菜单框架整体拷贝、近乎零改动—— 只重写了 u8g2 的 I2C 字节回调(约 100 行)和按键/延时等胶水层。 菜单树、PID 光标动画、Tx/Rx 测试状态机、丢包统计全部原样工作。
4. 成果
| 构建 | west build 零 error |
| FLASH 占用 | 86656 B / 128 KB(66%) |
| SRAM 占用 | 20200 B / 20 KB(98.6%,余量约 263 B) |
| 烧录 | J-Link 校验通过(87040 B) |
| 上板验证 | Rx 接收测试通过(OLED 菜单操作、收包、RSSI/丢包显示正常) |
| 新增手写代码 | 约 1000 行(6 个移植层文件 + overlay + 配置),复用代码约 3000 行(u8g2 + MultMenu) |
5. 相比裸机工程的优点
硬件与代码解耦。引脚、时钟、外设全在 devicetree 里,换板子/换引脚只改 overlay, 不动 C 代码。原工程换个 SPI 口要改好几处 HAL 调用。
驱动生态现成。SX1268、USB CDC、PWM、I2C 都是内核维护的驱动,省了 数千行 HAL 移植层代码(原工程 e22_hal.c、u8g2_hal.c 里直接操作寄存器的部分)。
并发模型更安全。中断里不再做 SPI 事务;接收回调在工作队列跑, 天然避免了原工程"ISR 里长事务阻塞系统"的隐患。
可维护性与可移植性。同一套应用代码理论上可以编译到任何 Zephyr 支持的 MCU 上(换板 = 换 overlay),裸机工程则与 STM32F1 深度绑定。
构建现代化。命令行 west/CMake/Ninja + GCC,可进 CI;不再依赖 Keil GUI 和付费的 ARMCC5。
运行时基础设施。k_timer、工作队列、sys_reboot、printk 控制台等都是现成组件, 蜂鸣器提示音从"阻塞 50ms"变成非阻塞也就一行改动。
6. 相比裸机工程的缺点
资源开销大。裸机工程 flash 占用估计不到 40KB;Zephyr 版 86KB,SRAM 更是 吃到 98.6%(只剩 263 字节)。USB CDC 一项就占约 7.5KB RAM——在 20KB 小内存 芯片上非常奢侈。裸机对资源的控制力是 RTOS 给不了的。
底层控制力下降。Zephyr LoRa 驱动只支持标准同步字(0x1424/0x3444), 原工程的自定义同步字 0x14 表达不了 → 新旧固件不互通。想要完全一致就得 放弃子系统、回去移植裸驱动。
实时行为不再直白。裸机里"中断来了立刻跑我的代码";Zephyr 里要经过 ISR→驱动→工作队列→回调,时序链条变长,调试需要理解驱动内部。
学习/概念成本。devicetree、binding、Kconfig、pinctrl 一套概念体系, 对裸机工程师有门槛(本次实践中由 AI 承担了这个成本)。
小内存芯片适配需要精打细算。为了塞进 20KB SRAM,裁了主栈、ISR 栈、 USB FIFO、缓冲池——这些调节需要经验,且余量小带来栈溢出风险。
验证闭环变长。裸机改一行 Keil 里点编译;Zephyr 全量构建要几分钟, 且驱动行为依赖内核版本(本次就遇到板名 revision、USB 新旧栈等 4.4 特有的坑)。
7. 经验总结:AI 做移植,人该做什么
AI 擅长的(本次全部委托给 AI):
大海捞针式的代码库侦察(几千个文件里定位 binding、pinctrl、示例)
机械但繁琐的映射工作(HAL 调用 → Zephyr API、引脚表 → overlay)
编译-修错闭环(coder 子代理自行迭代到零 error)
资源占用分析和裁剪建议
人类不可替代的(本次由人完成):
架构取舍拍板:保真 vs 简洁、要不要 USB、接不接受新旧固件不互通—— 这些是产品决策,不是技术问题
硬件验证:屏幕亮不亮、蜂鸣器响不响、能不能收到包,只有人能看
需求边界:明确"不移植游戏"这类范围约束,避免 AI 过度交付
最有价值的工作方式:不是"让 AI 自由发挥",而是 侦察 → 人类决策 → AI 实施 → AI 工具链内验证 → 人类物理世界验收 的流水线。 本次从任务发起到上板验证通过,人类的实际参与只有:回答 4 个选择题、 确认方案、插 J-Link、按了几下按键看 OLED——其余全部由 AI 完成。
我要赚赏金
