☰
Vibe Coding与嵌入式开发:AI辅助的真相与实践指南
2026/10/1 15:00:39 网站建设 项目流程

别急着让AI接管你的单片机,先聊聊Vibe Coding和嵌入式开发的真相

最近圈子里“Vibe Coding”这个词热度高得吓人。特斯拉前AI负责人Andrej Karpathy提了一嘴“找个舒服的环境,听点音乐,像萨满跳大神一样跟LLM对话,让AI把代码写出来,但你自己完全不知道代码咋写的”,立刻引爆了社区。Web开发圈、脚本圈的朋友们玩得不亦乐乎,三个小时从零到一搭出一个小工具,这种体验确实很爽。

但作为干了十几年的嵌入式工程师,我看到这种“跟着感觉走、全权交给AI”的写代码方式,第一反应是:这玩意敢用在电机控制里吗?敢用在医疗设备里吗?敢用在微波成像板卡里吗?芯片寄存器写错一位,板子可能直接冒烟,这不是删库跑路能比的。

嵌入式开发和Vibe Coding之间,存在一个天然的“八字不合”。但这个“不合”不意味着我们要拒绝AI辅助,而是要用完全不同的姿势去对待它。这篇文章想聊聊我在实际项目中怎么用AI辅助嵌入式开发的,哪些环节真的能“Vibe”,哪些环节一点都不能“Vibe”,以及踩过的一堆坑。

不管你是刚入行的嵌入式新手,还是被老板逼着“用AI提效”的资深开发,这篇文章应该都能给你一点参考。

1. 拆解Vibe Coding:它到底是“神谕”还是“高级自动补全”

1.1 Vibe Coding的核心逻辑和适用边界

Vibe Coding说白了就是你把需求用自然语言描述给大模型,让它直接产出代码。你负责“意图”,它负责“实现”。典型的交互长这样:

用户:帮我写一个Python脚本,解析这个CSV文件,输出每列的平均值。 AI:好的,这里给你一个完整的脚本,包含错误处理和注释。

这种模式在最理想的情况下,确实让编程变成了“跟产品经理聊需求”——你只管说清楚要什么,实现细节交给别人。对于没有严格约束的领域,比如数据脚本、Web页面、前端交互、简单的后端API,这套玩法效率惊人。因为这类项目没有“硬件物理世界”的约束,错了顶多重跑一遍,编译器会帮你兜底,IDE会告诉你哪里类型错了。

但对于嵌入式开发,事情完全不一样。

1.2 为什么嵌入式开发对“不确定性”零容忍

我们随手列出嵌入式开发的几个硬核特征:

  • 资源受限:MCU可能只有2KB RAM,64KB Flash。AI生成的代码如果不考虑栈深度、堆碎片、代码段大小,很可能编译能过、一运行就死机。
  • 时序敏感:一个GPIO翻转要在精确的时间窗口里完成,多一条指令延迟,波形就偏了。AI生成的抽象代码层数太多,虚拟函数、回调嵌套,跑起来周期直接超限。
  • 并发与中断:很多Bug只在中断打断主程序的那一刻出现。AI很难从你喂给它的分散信息中理解“此处需要关中断”“此处需要volatile修饰”。
  • 硬件耦合:寄存器地址、位域定义、时钟树配置、外设工作模式,稍有偏差,芯片就罢工。AI记忆中的芯片型号资料通常是过时或不完整的,它非常擅长一本正经地生成错误的寄存器配置。
  • 交叉编译与调试成本高:嵌入式调试不像Web端改一行刷新浏览器就有结果。你得烧录、连接调试器、抓波形、看寄存器。每一步都可能花掉好几小时。如果AI生成的代码在目标板上出了问题,定位过程比对对碰糟心多了。

所以,Vibe Coding那种“我不知道代码怎么写的,但跑起来一切都对”的状态,在嵌入式领域基本是奢望。你不可能不知道代码怎么写的——因为你必须知道,否则连排查问题都无从下手。

但这一切不意味着我们没法用AI。只是要把它的定位从“代替你写代码”改成“帮你把已知的解决方案快速翻译成代码”。

2. 嵌入式开发里,哪些环节真的能“充当下手仔”

我拆解了自己最近几个项目,把日常工作按“适合AI辅助”和“不适合AI辅助”分了个类。

工作类型适合度原因
寄存器初始化代码生成高只要给出芯片手册相关页,AI能生成结构清晰的Init代码
通信协议解析(I2C/SPI/Modbus/CAN)中高协议格式清晰,AI擅长按字节解析和组装
CMake/Makefile编写高语法固定,模式化程度高
单元测试夹具与Mock代码高模式化极强,且不跑在目标硬件上
上位机Qt界面骨架高UI布局、信号槽连接方式很模式化
中断处理函数低时序敏感,行为与硬件紧密耦合
低功耗状态机低每个分支出于硬件实测,不能用“常识”推断
内存池/无锁队列低并发Bug极其隐蔽,AI无法帮你验证
设备驱动核心逻辑中可以生成骨架,但必须逐行审查

这个表格是我多年实践下来一个粗略划分,具体到你自己的项目,边界可能会漂移,但大方向不变:凡是“文档明确、规则固定、模板化强”的内容,AI都能做得不错;凡是“依赖运行时行为、硬件特性、时序约束”的内容,AI只能提供参考,不能直接信任。

2.1 举一个典型的“高适配”例子:I2C传感器驱动骨架

假设我们要在STM32上驱动一个温湿度传感器SHT30。标准流程是:写I2C读函数,发送软复位命令,读取状态,然后读温湿度数据,最后转换为物理量。

如果把这个需求直接甩给AI,它大概率会给你一份看起来非常完整的代码:

bool sht30_read_data(float *temp, float *humi) { uint8_t cmd[2] = {0x2C, 0x06}; uint8_t buff[6]; // 发送测量命令 HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd, 2, 100); HAL_Delay(20); // 读取数据 HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR, buff, 6, 100); // 解析温度 *temp = -45.0f + 175.0f * ((buff[0] << 8) | buff[1]) / 65535.0f; // 解析湿度 *humi = 100.0f * ((buff[3] << 8) | buff[4]) / 65535.0f; return true; }

这段代码表面上没毛病。但在真实项目里,我至少会追问这样几个点:

  • 如果I2C总线上没有设备响应,HAL_I2C_Master_Transmit返回超时怎么办?AI漏掉了错误处理。
  • 在读取之前需要等待芯片的“数据就绪”状态吗?SHT30在重复读取时可能返回上一次的数据,AI不知道这一点。
  • 如果使用SHT40(兄弟型号),命令字变成了0xFD,地址也变了。AI很可能把型号搞混。

所以我的用法是:让AI生成骨架,然后我自己对照数据手册逐行检查,补上错误处理、超时机制、重试逻辑和芯片特有行为。这样效率确实高,但我得完全掌握这段代码的每一个字。

2.2 高适配的第二好例子:写CMakeLists.txt

嵌入式项目现在越来越多人用CMake。我可以让AI“告诉我,为一个Cortex-M4芯片,想用arm-none-eabi-gcc编译,链接脚本是stm32f4.ld,如何写一个CMakeLists.txt?”。这类问题模式极度固定,AI给出的结果是直接可用的,而且比我自己敲快得多。

到这里你就明白了,Vibe Coding在嵌入式里的台词应该是:“AI,帮我生成一个靠谱的草稿”,而不是“AI,帮我把活干了”。

3. 实操过程:让AI帮我写一个嵌入式Linux下I2C驱动

这部分我想拿最近做的一个具体案例来聊聊——在一块ARM Linux板卡上写一个触摸屏触摸控制器(比如GT911)的I2C驱动。这个任务混合了“内核框架”、“设备树”、“时序”和“配置寄存器”,对AI来说挑战不小,但干得好能省一大半时间。

先描述一下任务背景:板子是某ARM Cortex-A7核心板,Kernel 4.19,设备树使用I2C总线2,触摸控制器挂在地址0x5D。我要实现的功能是:探测触摸屏,读取触摸点坐标,通过输入子系统上报触摸事件。

工作流程如下。

3.1 前半段:用AI搭出驱动框架

我把这段描述直接丢给大模型,要求它生成一个Linux i2c client driver的C文件。它很快就生成了结构完整的内核模块:

#include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/delay.h> #define GT911_ADDR 0x5D #define GT911_POINT_REG 0x814E // ... 省略具体实现

这个骨架真的省了我很多事。像struct i2c_driver、struct input_dev分配、input_set_abs_params这些模板化代码,我自己敲至少要十分钟,AI几秒钟就出来了。

但我做了以下几件事:

  1. 检查了它是否包含必要的头文件。AI有时候会漏掉linux/of.h(设备树API),编译直接报错。
  2. 确认了输入子系统的初始化顺序。AI生成的结构是:先注册i2c设备,再在probe里分配input_dev,这没问题。但要注意如果触摸控制器在probe时没准备好,需要设置异步探测,AI不知道你板子的上电时序。
  3. 检查了错误路径。AI经常在check_err之后漏了goto清理,导致probe失败时泄漏资源。
  4. 跟真实的GT911芯片手册做了对照。比如GT911的配置寄存器地址、重新映射命令0x8040/0x8041、低功耗命令0x804D等等。AI偶尔会把这些地址搞混。

3.2 后半段:自己动手补全细节

在AI生成的代码基础上,我手动补上了下面这些关键逻辑:

static irqreturn_t gt911_irq_handler(int irq, void *data) { struct gt911_data *ts = data; uint8_t buf[8]; int ret; if (gpio_get_value(ts->int_gpio) == 0) { ret = i2c_master_recv(ts->client, buf, sizeof(buf)); if (ret < 8) { dev_err(&ts->client->dev, "short read\n"); return IRQ_HANDLED; } input_report_abs(ts->input, ABS_X, (buf[1] << 8) | buf[2]); input_report_abs(ts->input, ABS_Y, (buf[3] << 8) | buf[4]); input_sync(ts->input); } return IRQ_HANDLED; }

你要问这里为什么用gpio_get_value先判断一下中断线的电平?因为在边沿触发模式下,中断可能会在总线回复不到位时产生虚假触发。这个细节AI不会知道,数据手册里也不一定有,是我在真实板子上调试时发现的问题。把中断触发方式从边沿改成电平触发,或者软件先读取GPIO电平再决定是否处理,是经验决定的东西。

3.3 编译和实测:AI在交叉编译方面给出的帮助有限

其实这一步,AI能帮上的忙少很多。写好了代码后,要复用具体的交叉编译工具链和内核源码树。你得自己搞清楚:

  • 内核源码在哪,ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-怎么设置。
  • 该内核版本是否已经把触摸屏驱动编译成模块,还是要编进内核。
  • make menuconfig里怎么把触摸驱动设为M。

即便你让AI帮你写一个构建命令,它能给出的也无非是网上的标准命令。真正重要的是你那个板子的编译环境、内核版本对应的构建系统长什么样,这些信息AI不一定知道。你最好自己敲过一遍,搞清楚每个环节,再考虑“自动化”。

把驱动编译出来后,我用scp传到板子上,insmod加载,然后用devmem直接往I2C寄存器写测试数据包来验证驱动解析逻辑是否正确。这一步妥妥要靠人工,AI无法替你验证总线波形和GPIO电平。

4. 实战进阶:微波成像板卡项目里的“AI能写与不能写”

热词列表里提到了“微波成像嵌入式”,这个我碰巧参与过一个类似的项目。那是一个用于无损检测的小型微波成像装置,核心是一块高速ADC采样的FPGA + ARM嵌入式板,需要把采集到的原始数据处理后,在Linux+Qt5上位机上实时显示二维图像。

这个项目的复杂度集中在:数据量大(每帧数十MB)、要实时处理、且前端采集硬件时序要求极高。

4.1 Qt5上位机部分:可以多让AI放手

如果用温湿度驱动算“轻度信赖”,那Qt5上位机的部分,我比较愿意让AI大胆发挥。因为Qt的UI开发高度模式化,信号槽连接方式、控件布局、样式表,AI都能做得不错。

我的具体操作是,给AI扔一个需求:“写一个Qt5程序,接收串口数据,每帧数据头是0xAA 0x55 + 两个字节长度,然后数据体是16位整数的像素矩阵,解析出来用QImage显示在QLabel上,并支持鼠标点击选择显示坐标值。”

AI能很快给我一份像模像样的代码:

void SerialReader::processFrame(const QByteArray &frame) { QDataStream stream(frame.left(frame.size()-2)); stream.setByteOrder(QDataStream::LittleEndian); quint16 width, height; stream >> width >> height; QImage image(width, height, QImage::Format_Grayscale16); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { quint16 value; stream >> value; image.setPixel(x, y, (value >> 8) + ((value & 0xFF) << 8)); } } ui->label->setPixmap(QPixmap::fromImage(image)); }

但让我特别惊讶的是,它第一次生成的代码里用了QDataStream::LittleEndian,而我的设备实际输出的是大端。修改这个Bug倒不难,但需要注意:一旦把上位机代码交给你不熟悉的AI,这类“隐式协议约定”非常容易被它忽略掉。

所以对于Qt部分,我的体会是:让AI搭骨架、写UI、画表格都行;但涉及协议解析、字节序转换、性能优化、跨线程信号槽安全,必须由人亲自把关。

4.2 FPGA/ADC采集部分:AI完全放弃治疗

到了这个层面,AI基本就歇菜了。不是说生成不了代码,而是FPGA的Verilog代码里充满了时钟域交叉、FIFO深度设计、时序约束、流水线延迟匹配这些物理概念。AI生成的RTL代码可能在仿真里跑得通,但一旦综合到具体FPGA型号、真实ADC芯片上,时序要多拉跨有多拉跨。

打个比方,Vibe Coding在这种场合就相当于让一个“学了很多理论知识的学生”去徒手焊接一块高密度BGA板——他可能说得头头是道,但你绝对不敢让他上手焊。有些东西,AI没办法替代经验。

5. 常见问题与排查技巧:AI辅助嵌入式开发的坑

这部分是我最想写的,因为网上那些“AI编程爽翻天”的帖子不会告诉你这些。我把我和朋友项目里遇到的典型问题整理一下,帮你提前踩坑。

5.1 AI生成的“万能”代码反而变成隐患

AI有一个坏习惯:非常喜欢把代码结构弄得“过度工程化”。在嵌入式里,抽象层越多,就越容易出错。我见过AI生成的一段读取电池电量的代码,给它加了一个“Strategy Pattern”,搞了一堆接口类和虚函数。在桌面上这可能很酷,但在一个只有64KB Flash的小单片机里,这种行为只会让代码变得臃肿、难调试。

排查技巧:拿到AI代码后,先做一个减法。把所有不必要的抽象、动态内存分配、标准库依赖删掉,用最朴素的方式重新组织。除非你真的很需要一个接口,否则别用虚函数。MCU项目里,简单的switch往往比一堆函数指针更可靠。

5.2 错误处理经常被AI吞掉

AI生成代码时默认“所有运行都是顺利的”。我让AI写一个读取SPI Flash ID的驱动,生成的代码没有检查HAL_SPI_TransmitReceive的返回值,也没有对超时时间做判断。如果芯片没焊好、线断了、供电不稳,函数就会卡死在while循环里,整板死机。

排查技巧:审查AI代码时,把每一个API调用都圈出来,问自己三个问题:出错返回什么?调用方收到返回了没有?如果出错,应该做什么?缺失的部分逐一手动补上。另一个技巧是给AI提示“请在每个关键点加上错误处理”,它往往会给你补得比较齐全。

5.3 并发和中断的“想当然”

AI特别容易忽略MCU里的重入问题。它生成的一个处理UART数据的函数里,使用了全局缓冲区,但函数本身不具备原子性。当UART中断来了,正好也访问这个缓冲区时,就会发生数据竞争。这在PC上可能只是小概率崩溃,在嵌入式里会表现为偶发的“死机”“乱码”,极其难排查。

排查技巧:凡是全局变量,注明是否被中断访问。如果在中断里访问了,要么加volatile,要么在操作前关中断。AI生成的代码一般不会主动处理这一层,你需要自己补。

5.4 芯片型号、寄存器资料过时

大模型的知识截止日期在那里摆着,你让它写一个最新款的瑞萨RA8D1的启动代码,它极有可能沿用老的RA6M3寄存器,甚至把ARM Cortex-M85内核的指令集搞拧。

排查技巧:不要迷信AI给出的寄存器地址。任何一个芯片的外设寄存器地址,都必须打开Datasheet核对一遍。我自己习惯的做法是:把芯片手册的寄存器章节摘录文本直接贴到对话里,让AI基于这个文本来生成代码。这样它不容易胡编。如果手册是PDF,有些文本提取不是很好,那你就得自己动手多敲几行核心寄存器初始化。

5.5 浮点运算的陷阱

带FPU的MCU越来越多,但AI在生成代码时往往完全不关心是硬浮点还是软浮点。比如它在某个STM32F4项目里用了double类型做运算,导致编译出的代码体积爆炸或性能下降。此时你改一下编译器配置,或者把类型改成float,游戏体验会好很多。

排查技巧:在AI生成的代码里搜索double,一个一个转成float(如果你的芯片FPU是单精度的话)。另外,如果代码用了math.h里的函数,要确认目标芯片上有没有硬件实现,没有的话会隐式链接软浮点库,性能会差很多。

5.6 过度信任导致的“撞墙”现象

Vibe Coding有一种心理效应:因为代码是AI生成的,你会下意识地觉得“它比我懂”,所以跳过代码审查。这在嵌入式里是致命的。我见过同事用AI生成的控制PWM的代码,编译没有报错,但他忘了设置GPIO_PIN_AF配置,电机一动不动。他查了半天硬件,最后发现是初始化代码里少了三行。这类问题,AI没法自己发现,因为它不知道你的硬件连接。

排查技巧:对于AI生成的代码,一定要像对待同事写的代码那样做Code Review。可以拿代码去问AI:“这段代码是否完整配置了PWM输出引脚?”让它自己借助知识反思一下,往往能发现一些问题。但最终,还是要靠你在硬件上实测。

6. 深度思考:嵌入式工程师如何正确地“Vibe Coding”

写了很多实际案例,最后想聊聊更深层的想法。

6.1 把AI当作“最强实习生”而不是“资深专家”

实习生给你写代码,你会怎么做?你大概率会让他写一个具体的小模块,然后你去review、提修改意见,反复几次后才可能合入。面对AI,就应该是这个态度。

在嵌入式开发中,AI的定位是一个非常聪明、但不懂业务、不懂硬件、不懂你的项目历史和上下文约束的实习生。它写出来的东西,可以作为第一版草稿,帮你省去从空白文件开始敲击键盘的时间和精力。但代码是否可用,完全取决于你这位“资深工程师”的把握。

6.2 “能手工写出关键代码”这条底线不能丢

现在我有一个很深的感触:当你在Vibe Coding里待久了,会慢慢丧失“从零构建”的能力。嵌入式开发的特殊性在于,很多知识是“默会知识”,只有在手工写代码、查手册、调波形时才会获得。如果所有代码都由AI代劳,你只是一个“代码搬运工”,一旦AI遇到训练数据里没有的场景,你就卡壳了。

所以我建议,每个嵌入式工程师都应该保持一个习惯:每周至少手写一次“关键路径”代码。比如一个定时中断的初始化、一个DMA传输的配置、一段状态机。哪怕你知道AI能写,也要自己动手写一遍,然后跟AI的版本对比一下。这种刻意练习能确保你的硬件直觉一直在线。

6.3 建立自己的“Vibe提示词库”

如果你已经在用AI辅助开发,我强烈建议花点时间整理自己的提示词库。比如我常用的几个:

  • “你是资深嵌入式Linux驱动工程师。请根据以下数据手册内容,生成设备驱动初始化代码。要求:使用内核常见API,注意错误处理,返回-errno,代码风格遵循内核规范。”
  • “忽略你已有的STM32知识。以下是我这个项目的寄存器信息,请严格基于这些信息编写初始化函数:...”
  • “请把以下这段C代码转换为等价的Rust/或使用更安全的写法,但不能增加运行开销。”
  • “下面是我写的驱动probe函数,请审查并发风险、资源泄露、错误路径。”

这些提示词有个共同点:给AI限定范围,告诉它“不要幻想”,严格基于你喂进去的资料去工作。这样可以显著降低它“自由发挥”的概率。

6.4 坚持“验证优先”的开发流程

Vibe Coding很容易让你陷入“生成代码-复制粘贴-编译报错-再生成”的循环里。这种迭代方式在嵌入式里是噩梦,因为编译过不等于运行对。

我现在的流程是:

  1. 文字描述需求和约束。
  2. 让AI生成代码骨架。
  3. 把代码放到编辑器里,逐行审阅。
  4. 补全错误处理和极简抽象。
  5. 针对逻辑疑点,用单元测试在PC端跑纯逻辑验证(比如协议解析部分)。
  6. 交叉编译并部署到板卡上。
  7. 用逻辑分析仪、示波器或调试器验证硬件时序。
  8. 记录问题,回到第2步修正。

每一步都稳扎稳打,虽然比“纯Vibe”慢,但比“纯手写”快得多。我测算过,带AI介入后,我写一个新的I2C驱动大概能节省40%的时间,但调试时间并没有因为AI而减少多少。所以关键是保留充足的调试余量。

7. 一些补充:嵌入式AI辅助开发的未来想象

写到最后,还是想说点轻松的。虽然现在AI在嵌入式领域还远谈不上“接管”,但趋势已经很明显:

  • 芯片厂商已经在推AI辅助的寄存器配置工具,比如一把抓出初始化代码。
  • 调试器工具开始集成AI日志分析,帮你从一串串寄存器输出中定位问题。
  • RTOS厂商也在探索用AI生成任务划分逻辑。

但这些工具再怎么演进,有一点不会变:嵌入式开发的本质是与物理世界打交道,任何代码都必须接受真实硅片的审判。你不能让AI帮你“感觉”电机在转,也不能让AI帮你“感觉”触摸屏的响应。只有示波器上的波形和电工胶布粘住的传感器能告诉你真相。

所以,我的态度是:Vibe Coding很好,但请把它用在正确的场合。在嵌入式里,它是一位得力助手,不是一艘自动驾驶的船。驾驶员永远是你。

如果你也想试着用AI辅助你的嵌入式项目,我最后再分享一个我个人的小技巧:把AI的回复当成另一个工程师提交的pr,而不是“标准答案”。带着挑毛病的心态去读它的代码,你的项目质量反而会让你惊喜。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询