ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

嵌入式开发中的Vibe Coding:AI生成代码的边界与工作流重构

嵌入式开发中的Vibe Coding:AI生成代码的边界与工作流重构 前几天我在工位调一个I2C触摸屏驱动改了快两天还是偶尔出现一次通信失败。后端组同事路过看了一眼说“哥这代码要不扔给AI试试”我当场有点无语。后来我真的扔给AI试了——结果不是它写不了而是它“能写”这件事本身逼着我重新想清楚嵌入式开发里到底什么才是人的核心价值。Vibe Coding这个概念是去年初开始在开发者圈子里热起来的。有人以为它是个新工具、新软件还要搜“vibe coding下载”其实它指的是一整套“让AI主导写代码、人来描述意图和审查结果”的工作方式。在Web、后台这类即时反馈很强的领域这套方式已经相当成熟了。但在嵌入式开发这边讨论一直两极分化一拨人说AI碰不了底层一拨人说全套交给AI省事到底。我的经验是两边都不太对。这篇文章我想基于自己这一年多在真实项目里用AI做嵌入式开发的经历把哪些场景好用、哪些千万别碰、以及工作流到底应该改成什么样一次讲清楚。1. Vibe Coding的“轻松感”和嵌入式开发的“硬约束”天生有摩擦先说清楚Vibe Coding到底是什么。这个说法流行开来后很多人把它误解成“随便写写提示词、躺着等AI出代码”。但真正用过之后你会发现它的核心不是“偷懒”而是一种工作重心的转移你负责描述系统、定义行为、判断边界代码生成这件事本身交给模型去完成。就像以前建筑师要亲自画每一张施工图现在建筑师只需要把设计意图讲清楚让制图员去画然后自己审核图纸有没有违反结构安全。这种模式在Web开发里特别顺因为反馈链路极短——AI生成一段代码刷新浏览器立刻就能看到结果错了马上改。但嵌入式开发完全是另一回事。代码只是“和物理世界对话”的中间层真正的难点在于代码之外的无数隐含约束时序、电平、中断优先级、电源状态、外设寄存器的上电默认值。你用AI生成一段看起来很合理的初始化函数编译能过但烧进板子里可能第一个while循环就卡死。这不是AI生成能力的问题而是嵌入式领域的“结果验证成本”实在太高了。所以嵌入式圈子里那两种极端声音我都不太认同。一种说“AI根本写不了嵌入式”这类人通常只试了一次AI给了一段有瑕疵的代码就直接给它判了死刑。另一种说“把整个固件需求丢给AI自动生成完事”这种更危险因为AI特别擅长一本正经地胡说八道——它会把时钟树配错会把GPIO的输出模式当成输入模式用甚至会在ISR里放一个delay()然后信心满满地告诉你这很稳妥。用过几次之后我的结论是AI可以承担“手”的角色但“脑”和“眼睛”还得是人。这也是“Vibe”这个词最准确的含义——你要对整个系统保持感知只是不再把精力花在逐行敲键盘上。2. 嵌入式真正的瓶颈不在“写不出代码”而在“定义边界和排除故障”在讨论怎么用AI之前得先把嵌入式开发这件事拆开看。我们每天的工作构成大致是这样的理清硬件行为读datasheet、看时序图、确认电平匹配定义软件架构模块怎么划分、接口怎么设计、错误怎么传播资源规划Flash够不够、RAM还有多少、CPU占用能不能压住调试和验证上板子跑、抓波形、看日志、定位那个“偶尔出现”的邪门bug你会发现真正吃掉项目时间的大头很少是“把代码从0敲到1000行”。而是“为什么波形不对”“为什么概率性通信失败”“为什么上电偶尔起不来”。这些问题里AI根本看不见硬件它只能看到代码层的表面逻辑。代码只是你把硬件约束翻译成处理器指令的一种形式。这里必须指出三个天然矛盾决定了Vibe Coding在嵌入式里不能照搬Web那套玩法。第一个矛盾是反馈滞后。Web改一行代码刷新就能看到结果嵌入式一个bug可能三四天才复现而且复现条件跟温度、电压、甚至周围的电磁环境都有关。AI在生成代码时完全感知不到这些它只会按照“大多数情况下最标准的写法”来输出。可嵌入式项目往往就死在这些“大多数情况”之外。第二个矛盾是资源约束。AI生成代码默认走“通用、稳妥、健壮”的路线典型表现是加一堆防御性判断、错误处理、日志输出。在拥有几个GB内存的服务器上这完全没问题但在一个Flash只有64KB、RAM只有8KB的单片机项目里AI生成的日志模块里一个sprintf就能把Flash吃穿再带上动态内存分配还会引入碎片问题。资源约束体现在每一个代码决策里这种“抠门式”编程思维恰恰是大模型最不擅长的。第三个矛盾是环境差异。交叉编译链版本、芯片厂商外设库的API演进、不同批次芯片的勘误表——这些东西在AI的训练数据里新旧混杂它很容易用老版本库的写法生成新工程代码。我就遇到过AI用STM32标准库语法去写HAL工程的情况编译报错不算最麻烦的麻烦的是那种“能编译、能运行、但行为和你预期不一样”的代码排查起来才真要命。所以纠结“AI能不能写嵌入式代码”其实是个伪命题。更准确的问题是在代码之外的那些判断性工作里AI能不能帮你减少重复劳动让你把时间省下来去盯真正要命的部分。想清楚这一点才能谈“哪些活交给AI是划算的”。3. 实测最好用的一类把嵌入式里的“翻译活”交给AI我自己这一年试下来AI在嵌入式开发里性价比最高的场景其实都不是“从0帮你写出一个固件”。而是一批可以被归为“翻译”的工作。3.1 场景一芯片手册里的寄存器配置翻译成C代码单片机开发有一个很重但没什么技术含量的事把datasheet里十几页的寄存器初始化说明改写成工程里的C代码。以前我得照着PDF一行一行看寄存器地址、位域含义、默认值再手工敲成宏定义和结构体。现在我的做法是直接把寄存器表格的关键信息整理成文本喂给AI让它生成结构体定义、初始化函数、读写接口并且明确要求在生成过程的最终代码里保留寄存器地址原样、注释每条配置的作用。这类任务AI完成得相当靠谱因为它有明确的参考源和目标格式而且错误容易暴露——寄存器地址错了编译能看出来位域含义理解错了也可以对照手册快速复查。AI做这个事和照着手册手抄在效果上没区别但速度快一个数量级我试过最顺利的一次是我把TouchGFX的底层接口定义丢给它让它生成一个移植层适配文件本来要干一下午的活在半小时内完成而且后续烧录验证确实没出问题。3.2 场景二临时脚本、上位机辅助工具、测试脚手架嵌入式开发里还有一大片“周边代码”解析串口日志的Python脚本、批量加校验和的烧录工具、从Excel器件表生成C枚举定义的小程序、给老模块补注释和头文件说明。这些代码和硬件没有直接耦合或者耦合很弱就算出错了也能很快发现并修正所以让AI来生成特别合适。举个例子我经常需要维护一个firmware_version.h每次发布要改版本号、编译时间、Git提交哈希。以前这事靠手工填老是忘。我用AI生成过一个几十行的Python脚本每次构建前自动读取最新的提交信息写入头文件。类似这种“一次性辅助代码”AI几乎是零门槛就能做对而且它做完后我再检查一遍也没什么成本。3.3 场景三算法移植前的框架搭建和模拟外设的测试桩还有一个容易被忽略的好用场景让AI先搭好一个传感器驱动的外围框架核心算法留给你自己填或者反过来算法你写好了让它生成配套的Mock层和单元测试。我来描述一下这个场景的实际操作模块接口我已经在文档里定义好了AIFill只是实现填充。比如我让它写一个BME280温湿度传感器驱动的框架要求内部按“复位、读取校准参数、配置模式、读取原始值、校准换算”的层次拆函数。AI生成的代码往往结构非常干净因为这种驱动在训练数据里实在太常见了。剩下的滤波、异常值剔除、动态校准逻辑再自己填。这么干的好处是AI不碰核心算法你对自己的业务逻辑保持完全控制同时那些“模板化”的传感器寄存器操作确实没必要人肉重敲。做这些的时候我慢慢总结出一个判断标准交给AI的任务必须是“错误能被快速暴露、结果可被对照验证、不涉及硬件时序”的那一类。满足这三点你就可以大胆用。反之就要慎重。4. 实测不能碰的一类启动代码、中断、时序和低功耗如果上一节是“哪里好使”这一节就得说“哪里千万别硬来”。我自己踩过坑也看过同事踩过更大的坑这些领域到现在我基本不指望AI能一步到位。4.1 启动代码和链接脚本出错成本最高AI偏偏最爱乱发挥链接脚本、启动文件、向量表、堆栈和堆的大小划分——这类文件的特点是“隐含约束极多而且错误不在编译期暴露”。Flash地址算错了、堆栈设太小、向量表偏移写错烧进去以后的表现就是“上电不断重启”或者“运行几分钟莫名其妙进hardfault”。最坑的是这种问题你用调试器查一开始根本不知道是链接脚本的问题会先去怀疑GPIO配置、怀疑外设初始化。我吃过一次亏。当时图省事让AI生成一个STM32H7的链接脚本它给出来的布局看起来井井有条但实际上把RAM区域的起始地址算偏了。烧进去以后板子疯狂重启我用调试器跟了半小时最后才灵光一闪打开.map文件看了下内存布局发现某个段被放进了不该放的地址。那次经历的教训是启动相关代码我从那以后全部走厂商官方模板或者手工核对每一处地址AI生成的只能当参考绝对不敢直接烧录。4.2 中断服务函数和DMA配置AI容易忽略“上下文的世界观”中断服务函数是嵌入式开发里最考验“时间敏感思维”的地方。要求在几微秒到几十微秒内完成关键处理必须考虑共享变量的原子性还要清事件标志。AI生成的ISR经常是什么风格在中断里调用带延时功能的函数或者调用了不可重入的库函数更常见的是漏掉事件标志的清除。我有一次让AI生成一个串口接收中断的处理逻辑它写得洋洋洒洒包括环形缓冲区的写入和回调通知逻辑看着完美。但真到板子上一旦收到大量数据就出现随机丢失。最后查出来它在ISR里读取数据时切换了一次缓冲区索引这个操作在单核裸机上本来没问题但因为它把另一个外设的中断优先级改错了导致两个中断之间发生了竞争。这类问题不是AI“写错”而是它根本不了解你的系统里有几个中断、各自的优先级和调用关系。DMA配置也是重灾区——内存对齐要求、缓冲区生命周期、传输完成中断里还能不能安全访问数据这些靠“通用代码”根本防不住。4.3 低功耗设计和复杂时序控制AI没有物理感知低功耗是嵌入式里最看“物理功底”的方向。它要求你知道哪个外设的唤醒时间有几百微秒、哪个GPIO在深度睡眠模式下必须保持特定电平、哪个电压域要先于另一个电压域关闭。AI生成这种代码多半是按“标准流程”给你一套看似完整的睡眠唤醒函数。但真跑起来电流测量数据会告诉你它完全想多了。再举一个时序控制的例子。我让AI写过一段等待传感器稳定后读取数据的代码它在其中加了一个delay_ms(500)。看起来没问题但在低功耗模式下我把主频从168MHz切到2MHz后这个delay_ms的实际耗时直接翻了几十倍整个系统的唤醒周期被拖得乱七八糟。这类问题的根源在于延时函数依赖时钟源而时钟源成了变量AI不可能感知到这些动态变化。所以涉及低功耗、时钟切换、外设间时序配合的地方我的态度很坚决AI可以提供片段参考但最终代码必须由人来逐行背过一遍并且要在目标板实测验证。我把这些总结成一张表方便大家对照领域主要风险原因建议启动代码、链接脚本启动即崩溃、内存布局错乱隐含约束多错误不易暴露使用官方模板手工核对地址中断服务函数竞争条件、标志未清、耗时失控AI不了解系统中断分布与优先级人写核心ISRAI只做外围辅助DMA配置缓冲区生命周期错乱、对齐问题涉及内存和硬件的动态配合对着手册逐项核验实测拷机低功耗/电源切换唤醒时间失控、外设状态丢失物理行为AI完全无感知电流实测为准AI仅做参考通信时序的微秒级控制波形不对、偶发超时反馈链路太长AI无手感用示波器/逻辑分析仪验证5. 重构后的嵌入式开发工作流契约先行、AI填充、人做验证用好AI不是简单地“问一句让它写”而是要把整个工作流重新组织一遍。我现在的做法可以归纳成一句话先写契约再让AI填充最后人做验证。5.1 从“写完再看”变成“先定接口再让AI实现”以前我试过直接给AI说“写一个Modbus RTU从站模块”结果它“自由发挥”了代码结构确实完整但接口命名风格跟我工程的现有规范完全对不上数据结构也没用我预期的内存池方案最后拆掉重来浪费的时间比手工写还多。后来我把工作流改成了这样先写设计文档模块职责、对外接口函数、数据结构定义、文件依赖关系、错误码定义把设计文档喂给AI明确要求“只能按文档的接口实现不允许改接口、不允许新增全局状态”AI生成的代码进入Review重点不是逐行看而是对照设计文档检查有没有“越界”行为硬件验证覆盖正常路径和异常路径改完之后效果完全不一样。还是那个Modbus RTU从站模块接口和寄存器地址表我花了一晚上定义清楚第二天上午把文档丢给AI下午就拿到了可以上板跑的主体代码剩下两个下午全部用来调通信时序和边界case。相比以前“通宵憋代码再花三天调兼容性”这个节奏健康太多了。5.2 硬性验证链编译、测试、上板、抓波形一步都不能省我给自己定了一条死规矩**任何AI生成或修改的代码都必须走完完整的验证链不能因为它“看起来没什么问题”就跳过某一步。**我的验证链是这样的编译阶段开启-Wall -Wextra -Werror跑静态分析工具确保零警告通过单元测试核心算法和协议解析用主机端测试框架跑一遍桩掉硬件层目标板测试烧进板子跑功能用例把正常流程、异常输入、反复上下电都测一遍信号级验证涉及时序的地方用示波器或者逻辑分析仪看实际波形对照设计文档确认特别提醒一点AI改了代码之后不要只测它声称“改完”的部分必须把关联模块的回归测试也跑一遍。我遇到过AI发现自己改的一段代码引入了一个小问题然后“自作主张”改了另一处模块来“补偿”结果新问题从概率性偶发变成必然触发测了半天才发现是它自以为的修复。5.3 给AI的结构化修改指令模板为了让AI少犯“越权”的错误我后来养成一个习惯给它下修改指令的时候必须包含几个关键信息。下面是我常用的一个模板字段内容修改目标哪个文件、哪个函数、具体要改什么行为约束条件不允许改动哪些文件/接口/全局变量不允许新增依赖预期影响预计会影响哪几个调用方是否需要同步修改验证要求改完必须保证编译通过、相关测试用例通过禁止事项不要顺手“优化”其他代码不要新增需求外功能这种写法看起来很死板但对AI非常有效。因为模型在面对模糊指令时会主动“补全”它认为合理的内容而嵌入式工程最怕的就是这种自作主张的补全。把约束写清楚实际上是在给AI划一道行为边界告诉它你的任务范围就到这里多一步都别做。6. 跟AI协作的实操注意事项上下文、越权与验证边界最后说几个在整个过程中积累的实操细节属于那种常规文档里不会写、但真能决定项目成败的小事。6.1 上下文管理寄存器映射表要喂到“饱”嵌入式项目的代码仓库往往是“好多文件大量宏定义层层封装”。AI的上下文窗口再大也有上限你不可能把一个完整工程全丢进去。我的做法是在仓库里维护一个AI_CONTEXT.md专门用来给AI提供关键上下文——芯片型号、编译链版本、外设库版本、全局内存约束、当前模块的接口定义、以及“不要把哪些文件改坏”的警告列表。每次让AI做改动的时候把这份文件连同具体的修改请求一起发过去。比如你想让AI改一个ADC驱动光说“把这ADC改成DMA模式”它大概率会犯错。你应该先给它看ADC寄存器映射表、当前DMA通道分配情况、中断优先级配置表再告诉它改动范围。上下文喂“饱”了AI生成代码的质量会提升一大截。这个习惯我养成了之后返工率低了很多。6.2 对AI的“越权行为”保持警惕它真的会自作主张这是我最想强调的一点。AI在生成代码时经常会在你要求的范围之外顺手“加一点私货”。你让它实现一个CRC校验它在main函数里给你加了个LED闪烁指示状态你让它写一个字符串解析函数它顺手把输入缓冲区的长度扩了一倍你让它“检查”一下某段代码它直接帮你把另一段函数的逻辑重构了。处理这个问题的唯一有效手段就是每次AI改动都用git diff逐块看并且要求AI在提交时分批、小粒度提交不要让改动糊成一坨。Review的时候重点不是看逻辑对不对而是先看“这行是不是我要的改动”之外有没有“额外送”的东西。坚持这样子做几次之后AI的“自觉性”也许会变得好一些——更可能的是你已经养成了逐行把关的习惯。6.3 “让AI解释代码”不是用来听讲而是用来抓漏洞很多教程会让你“让AI解释它生成的代码”说这样能帮助你理解。这个说法没错但我的用法不太一样。我让AI解释代码不是为了听一个流畅的故事而是要把它的解释和实际行为放在一起对照找出矛盾和疏漏。举一个真实的例子。我要求AI写一段SD卡初始化的代码它解释得头头是道什么CMD0、CMD8、ACMD41流程分毫不差。但烧到板子上就卡在初始化超时。查到最后是它漏掉了SPI模式下的IO复用切换——芯片手册明确写着要先把对应引脚从GPIO模式切换到SPI外设的复用模式否则片选信号根本出不来。这类错误靠AI的自我解释是发现不了的因为它解释的是“它以为它在做的事情”不是“硬件上实际发生的事情”。真正有效的验证方式还是把AI的代码放到目标板上用逻辑分析仪去对照手册上的时序要求一个时钟沿一个时钟沿地看。这一点也可以延伸一下Vibe Coding时代嵌入式工程师反而更不需要“代码讲解师”更需要“硬件侦探”。AI给了一个看似无懈可击的解释你手上唯一的破案工具是示波器、逻辑分析仪、电流表外加你对芯片手册的理解。这些工具和知识能力不会因为AI会写代码而贬值反而会变得更值钱。回到开头那个I2C驱动的问题。最后我没有真让AI直接给出“修复后的整段代码”而是把异常波形截图、I2C时序参数表和我的出错场景一起整理成上下文交给它让它帮我分析可能是哪几个原因。它给出了三个候选方向其中第二个——时钟延展导致从机卡住——正好对应我后来定位到的问题。代码层面的修复改动不大重要的是定位思路被快速聚焦了。这一年下来的一个体会也想分享给还处在观望阶段的同行Vibe Coding对嵌入式开发来说不是“有没有用”的问题而是“你怎么组织工作”的问题。代码本身的量产门槛在被AI拉低但代码之外的硬件感知、系统判断、边界定义和验证设计才是嵌入式工程师真正要守住、也值得花时间去积累的部分。最后分享一个我自己的工作流改变现在凡是让我写代码我都不再直接写第一版而是先在文档里把接口、约束、数据结构定义清楚然后把“写码”这段直接扔给AI自己转去做验证设计和用例规划。刚开始很不习惯总觉得不亲手敲几行代码就不踏实时间长了发现那些以前耗在“敲代码”上的时间省下来之后恰恰都变成了看波形、看datasheet和思考系统边界的时间。对一个嵌入式工程师来说这才是最值得投入的地方。
返回列表