这半年我开始认真尝试用AI辅助写STM32项目,说实话,效果比预想中好,但踩过的坑也不少。如果你以为AI编码就是“把需求扔进去、代码自己吐出来”,那大概率会在调试阶段被狠狠教育。真正能提效的,是把AI嵌进一套明确的人机协作流程里,让它在擅长的地方干活,在容易出错的地方给人让位。
这篇就记录一下我目前跑通的一套流程:从需求拆解、提示词准备,到代码生成、编译排错,再到硬件联调、归档复盘,整个过程怎么分工、怎么提问、怎么验收,每一步我都把实际用过的套路和翻车教训写出来。适合刚接触AI编程的嵌入式开发者,也适合已经把AI当工具但觉得效率不稳的老手。
1. 先想明白:AI在STM32开发里能做多少事
1.1 AI真正擅长的几类嵌入式活
很多人对AI写代码的第一印象是“生成点灯、串口打印这种Demo级代码”,但实际用下来,AI的价值远不止于此。我在项目里用得最顺手的,其实是那些“模式化但细节繁琐”的任务。
举个例子:写一个完整的UART空闲中断接收解析,带CRC校验、分包处理、超时重传。这种代码逻辑不复杂,但很容易漏掉状态标志位的清理和缓冲区边界的判断。把需求描述清楚丢给AI,它给你生成一个结构完整的接收状态机,剩下的人工审查只聚焦在两三个关键点上,效率比从零写快太多。
AI真正擅长的,我归纳下来是这四类:
- 模板化外设初始化,比如GPIO复用配置、定时器参数计算、SPI/I2C时序初始化这类“照着数据手册填寄存器”的工作。
- 业务逻辑骨架设计,比如状态机、命令解析表、环形缓冲区、任务调度框架,AI对设计模式的掌握比大多数初学者系统。
- 代码解释与注释补全,把一段你接手的老代码丢给它,让AI逐行注释、画出调用关系,能节省大量读代码的时间。
- 报错信息的翻译与修复建议,编译器输出的问题,AI基本都能解析出方向,而且能直接给出修改后的代码片段。
这些任务有个共同特点:它们需要的是“从文字描述到代码实现”的转译能力,而嵌入式里属于这类的工作并不少。
1.2 更要命的是AI会一本正经地瞎编
分享一个真实翻车案例。我让AI生成一个STM32F407的定时器中断初始化,它给出的代码用了__HAL_TIM_ENABLE_IT(&htim, TIM_IT_UPDATE),这个是对的。但紧接着又在回调函数里写了htim.Instance->SR &= ~TIM_SR_UIF,直接在HAL库版本里混入了寄存器操作。
问题不是这条语句本身错,而是它不符合项目规范。项目里统一用HAL库,直接操作寄存器会让后续维护的人很困惑,而且不同HAL库版本下,这样操作还可能出现中断标志没清干净导致死循环的情况。
更要命的场景是“缝合”。有一次让它生成STM32H750的PWM输出初始化,它给出的代码里混了标准外设库的TIM_Cmd()和HAL库的HAL_TIM_PWM_Start(),两个库的函数同时出现,编译直接报错。这种错误,有经验的人一眼就能看出来,但如果你盲目信任AI的输出,把它直接粘贴进工程,排查起来会非常消耗时间。
AI生成的代码像是一个“见过很多开源项目但经验混乱的实习生”写出来的:单看局部很合理,整体风格和硬件细节却可能出问题。所以核心结论是:AI编码的产出物是“建议”,不是“交付物”,审查环节绝对省不掉。
1.3 人机分工才是流程设计的核心
用了一阵子之后,我最大的体会是:AI协同开发最忌讳的模式,是“人写一段,AI补一段”,这会导致代码风格分裂、上下文断裂、审查成本翻倍。真正顺手的做法是按任务类型划分:判断和决策归人,转译和实现归AI。
我当前用在STM32项目里的分工逻辑是这样一个原则:
- 人来做的部分:需求定义、架构设计、芯片选型、硬件调试、安全边界、代码审查。
- AI来做的部分:按清晰接口生成实现代码、解释晦涩文档、处理编译错误、补充测试脚本、整理项目注释。
这个分工的含义是:AI介入的粒度不是“文件级”,而是“模块级的实现细节”。你先把架构定好,接口定义清楚,再让AI去填充具体实现,最后人类来验收。这套思路贯穿后面整个流程。
2. 开干之前:把项目上下文喂给AI
2.1 一份“芯片身份证”,比长篇需求描述管用
很多人让AI写代码的时候,习惯直接说“帮我写个STM32的SPI读取传感器”,然后AI就默认用最常见的外设配置生成代码。问题在于,STM32这个家族跨度极大,F1、F4、H7的HAL库代码差异明显,而且同一个系列里不同子型号的外设资源也不同。
我现在会在每个项目的开始阶段,准备一份固定的“芯片上下文”文本,每次和AI对话前先贴给它。内容大致是这样:
目标芯片:STM32F407ZGT6,LQFP144封装 存储资源:Flash 1024KB,SRAM 192KB HAL库版本:STM32F4xx_HAL_Driver V1.27 开发工具:STM32CubeMX生成初始化代码 + Keil MDK编译 编码规范:使用HAL库,不使用寄存器直接操作,保持代码风格与CubeMX生成区域一致 关键外设:SPI1(连接温度传感器),USART2(日志输出),TIM3(1ms时基) 时钟树:HSE=25MHz,PLL倍频到168MHz主频,APB1=42MHz,APB2=84MHz为什么要给这么细?因为AI对“STM32”这个词的理解太宽泛了。你告诉它HSE是25MHz,它在生成RCC配置相关的辅助函数时就不会瞎猜;你告诉它HAL库版本,它就不会把F4的HAL库写成标准外设库的格式。这个“芯片身份证”的成本是一次性的,但之后每次对话都能复用。
2.2 一套可复用的提示词骨架,让AI稳定输出
除了芯片信息,我还固定了一套提示词模板,让AI输出的代码格式和范围都保持可控。模板大概是这样的:
你是嵌入式软件开发专家,熟悉STM32的HAL库和参考手册。现在请帮我完成以下任务。 【任务描述】 (在这里描述具体需求) 【约束条件】 1. 仅输出C语言的.c和.h文件内容,不需要解释,不需要额外的文字说明。 2. 不要修改CubeMX自动生成区域内的任何代码,包括main函数体里的初始化调用。 3. 不使用while(1)空循环,如果需要延时使用HAL_Delay或定时器。 4. 函数必须有详细的注释,写明入参、返回值、调用时机。 5. 如果存在处理出错的情况,必须有错误码返回,不要静默失败。 【输出格式】 先输出头文件,再输出源文件,文件之间用分隔线分开。这套模板的关键点在于“约束条件”。AI默认倾向于输出一段完整且自解释的代码,但嵌入式项目里,完整自解释往往意味着它想替你决定架构。加了约束之后,它就知道自己只是个实现者,不该动架构层面的东西。
我踩过一次坑:没加“不要修改CubeMX生成区域”这条约束,AI在修改某个函数时顺手把MX_GPIO_Init()里的引脚配置顺序也给调整了,理由是“逻辑更清晰”。结果CubeMX重新生成代码后,我的改动全被覆盖,还差点引入时钟冲突。从那以后,这条约束写进了所有提示词。
2.3 把AI生成的代码当新人代码来审查
我始终觉得,AI协同开发能不能提效,关键不在生成环节,而在审查环节。你要把AI当成一个“执行力很强但经验不足的初级工程师”,生成代码必须走一遍Code Review。
我的审查清单固定在项目里,每次AI返回代码后按清单核一遍:
- 函数名与项目命名规范是否一致。
- 是否存在HAL库函数与寄存器混用的情况。
- 缓冲区操作有没有越界风险,尤其是环形缓冲区的读写指针。
- 中断回调里是否有耗时操作,比如在中断里做打印或延时。
- 错误处理是否完整,失败路径是否会留下未初始化资源。
- 是否有未使用的变量、未初始化的指针。
- 与CubeMX生成区的边界是否清晰,改动是否只发生在USER CODE区域。
这个清单看起来简单,但实际执行起来非常有效。我统计过几次项目里AI生成代码的修改率,初版生成直接能用的情况大概只有三成,更多时候需要修改20%到40%的细节。但即便如此,效率还是比自己从零写要快。因为AI给出的整体框架和逻辑顺序,通常已经覆盖了大部分边界情况,人的工作量集中在关键判断点上。
3. 全流程实操:从一个传感器驱动看协同开发
3.1 先用自然语言把需求拆成AI能吃的粒度
这个部分我会用一个实际的模拟项目来走完整流程:项目名称定为“某环境数据采集模块”,需求是:STM32通过SPI读取一个外置温度传感器芯片的数据,每隔1秒采集一次,通过串口以可读格式上报。
第一步不是写代码,而是拆需求。我把整个项目拆成了六个小块,每个块单独跟AI交互:
- CubeMX工程初始化,包括时钟树、SPI1、USART2、定时器配置——这一步我不用AI,直接用CubeMX图形界面生成。
- SPI读写底层封装,包括片选控制、读写时序的基本函数。
- 温度传感器的寄存器定义与指令封装,包括配置寄存器、读取温度转换结果的命令。
- 周期性采集逻辑,基于TIM3时基中断或者主循环轮询。
- 串口上报格式,把裸数据转换成人类可读的字符串。
- 错误处理逻辑,比如SPI通信失败时的重试策略。
为什么要这么拆?因为AI在生成代码时,上下文窗口是有限的。你让它一口气完成整个系统,它可能生成得挺热闹,但每部分的深度都不够。拆成一个一个的小任务,每个任务限定了输入和输出,AI反而能给出更扎实的实现。
3.2 让AI生成驱动代码的正确姿势:给时序让他翻译
第二个块是SPI读写封装。我的提示词大概是这样:
芯片上下文:见前文描述。 任务:实现SPI1外设对传感器芯片的读写函数。 传感器侧通信规则:SPI模式3,时钟频率1MHz,片选低有效,先写一个字节的命令,再读两个字节的数据,MSB先行。 要求:提供两个函数: uint8_t sensor_read_reg(uint8_t reg_addr, uint8_t *data, uint16_t len); uint8_t sensor_write_reg(uint8_t reg_addr, uint8_t data); 实现时使用HAL_SPI_TransmitReceive,注意片选的时序控制,整个传输过程中片选保持低电平。这个提示词里最关键的是“传感器侧通信规则”那段。很多人不会写驱动,不是不会用HAL库,而是看不懂数据手册里的时序图。AI的优势恰恰在于,你能把时序规则用中文描述给它,它能帮你翻译成代码。这相当于把“读图写代码”的活外包了。
生成的结果我需要做两个检查:第一个是CS片选操作的时序位置,确认是在传输前拉低、传输后拉高;第二个是收发缓冲区的长度匹配,HAL_SPI_TransmitReceive需要一个大小一致的收发缓冲区,如果发送和接收长度不一致,是典型的出bug点。
3.3 业务逻辑交给AI搭骨架,自己填硬件的“手感”
SPI底层封装做好之后,接下来是业务逻辑层。我让AI实现一个采集状态机,状态包括初始化、空闲、触发采集、等待转换完成、读取结果、上报数据、错误处理。
任务:实现一个温度采集状态机,状态转移条件如下: - 初始化完成后进入空闲态。 - 收到软件触发标志后进入采集态,先向传感器发送启动转换命令。 - 等待至少100毫秒后读取转换结果。 - 如果读取成功,进入上报态,通过串口输出格式化数据,随后回到空闲态。 - 如果读取失败,进入错误态,连续失败3次后复位传感器,重新初始化。 状态机运行在主循环中,每轮只执行一个状态,不要在状态函数内部使用阻塞延时超过100毫秒的代码。AI生成的状态机骨架通常很规范,transfer条件写得也完整。但这里有个关键点,需要人来补:传感器“等待转换完成”的实际时机。数据手册上写着典型转换时间是90毫秒,但你不知道实际硬件上电后要多久才能稳定工作。这种靠感觉调的经验参数,就得自己在调试中微调。
所以业务逻辑我的分工方式是:AI搭框架,我填“硬件手感”。AI知道状态机怎么写,但不知道你的传感器板卡上的上电时序、去耦电容大小、电平转换芯片的延迟这些物理因素。这些数据只能靠示波器和实际测试获得,然后以常量的形式嵌进AI生成的骨架里。
3.4 把编译器报错原样丢给AI,但别让它一次改十处
开发过程中最常见也最耗时的环节,就是编译报错。用AI处理这个环节,我有一套自己的固定流程。
编译报错出现后,我把编译器的输出信息完整复制下来,连同对应的源代码文件一起丢给AI,让它先解释错误原因,再给出修复方案。
以下是编译输出: (一大段编译器输出,包含错误文件和行号) 请: 1. 逐条解释每个报错的原因,区分“语法错误”“类型不匹配”“未声明标识符”这几类。 2. 对每一类问题给出修复后的代码片段。 3. 如果有多个错误之间存在因果关系(比如第一批错误解决了,第二批会消失),请明确指出。 4. 不要一次性修改所有能改的地方,只处理当前这几条报错。这里有个重要经验:不要让它一口气把所有报错都改掉。编译器报错经常是“一个错引发连锁反应”的状态,改了根本原因,后面的错误就自动消失了。AI如果把所有报错都当成独立问题来处理,往往会把代码改得面目全非。
我自己遇到过一次典型场景:一个未声明的结构体类型导致了十几个错误,AI看到错误列表后试图把所有错误都消除,结果引入了更多问题。把它约束成“先解释、再修复当前这几条”之后,它只改了根因,后面十几个错误全部自然消失。
4. 硬件调试阶段:这里别指望AI
4.1 波形和电平的问题,AI是真的看不懂
涉到硬件的调试,AI的局限性就非常明显了。最典型的例子:SPI通信,代码逻辑完全正确,但读取回来的数据永远是0xFF或者0x00。你让AI分析,它只能从代码层面检查时序、检查器件的寄存器配置,但实际原因往往是芯片没有正常上电、片选引脚虚焊、MISO和MOSI接反、传感器供电电压不足。这些问题,AI看不到也猜不到。
我现在的习惯是:现象描述和代码没问题的情况下,第一时间拿起逻辑分析仪抓波形。确认CS、SCK、MOSI、MISO这几根线上的电平变化是否符合预期。AI能做的是帮你写注释和排查脚本,但上示波器和分析仪看波形的动作,必须人来做,因为这个判断依赖感官和现场信息。
4.2 用AI做调试辅助工具,效率提升反而更明显
虽然AI不能直接帮你调硬件,但它在“围绕调试做辅助工具”这件事上特别有用。我会让AI写一些Python脚本,用来分析和可视化来自串口或逻辑分析仪的数据。
比如有一次,我采集到的传感器数据看起来有周期性的突变,怀疑是电源干扰。我让AI写一个Python脚本,读取串口记录的CSV文件,自动计算相邻数据的差值和方差,找出一段数据里异常跳变点。AI很快就给出了一个带matplotlib绘图和数据统计的分析脚本,生成的图表让我快速定位到干扰集中在几个特定的采样点,进而发现是板卡上开关电源的开关频率影响。
辅助调试的AI应用还能包括:自动解析内存Dump、生成协议分析工具、把二进制日志转成可读字符串、统计串口日志里的错误码频次。这些任务本质上是“从数据处理需求到实现脚本”的转译,AI非常擅长。
4.3 调试通过后,让AI补齐文档是性价比最高的收尾
硬件调通了,代码能跑了,大部分人会直接进入下一个功能,但我会在这个节点让AI做一次“文档补全”。
具体做法是:把所有源码文件整合成一个完整的项目代码发给AI,让它生成一份项目说明文档。内容包括:编译环境、依赖库、硬件连接表(引脚分配)、代码模块结构、关键函数说明、已知问题和限制。
为什么这个环节值得做?因为嵌入式项目最大的隐性成本是“接手别人的代码”。你三个月后回来看自己写的驱动,可能也需要重新梳理思路。让AI基于实际代码生成文档,比让它基于“记忆里的项目”写文档,准确性高得多。
另外我会让AI把所有函数头的注释补齐,把那些“这个延时为什么是200ms”的魔法数注释清楚。这个过程相当于让AI对代码做一次全面审计。如果AI在生成文档时发现某个函数逻辑上不完整,往往说明代码里还有隐藏问题。
5. 实测中的翻车实录与排查思路
5.1 乱改CubeMX生成区,下一轮重新生成全部消失
这是我最开始用AI时踩过的第一个大坑。当时我让AI优化MX_GPIO_Init()的代码顺序,它认为先配置某个引脚能够减少电平跳变。我偷懒没有审查,直接编译下载了。当时看起来没问题,但是后来我用CubeMX调整了一个外设配置,重新生成代码,旧的初始化函数全被覆盖成CubeMX默认版本,我“优化过”的内容全部消失,而且因为CubeMX恢复时引脚配置顺序错乱,导致上电瞬间一个继电器误动了一下。
现在我的规则是:CubeMX生成区内的代码,AI一句话都不能碰。需要修改的初始化配置,回CubeMX里改;需要新增的逻辑,写到USER CODE区域或者新建独立文件里。
5.2 串口乱码排查:AI帮你算出波特率,但算不出晶振
有一个很经典的翻车案例,让我印象深刻。让AI生成串口初始化代码,它算出来的波特率寄存器值是按8MHz外部晶振计算的,但我的板子上实际用的是25MHz晶振。结果串口打印出来的字符全是乱码。
更要命的是,AI在“帮忙修复”的时候,直接把HSE_VALUE宏从25MHz改成了8MHz,理由是“这样计算就和代码一致了”。这在编译层面完全能通过,但整个系统的Systick延时、所有HAL_Delay的时间基数全部因此改变,原本点灯频率1秒一次,变成了2.6秒一次。
这条经验给我一个教训:AI能帮你计算波特率寄存器值,但它不知道你板上实际焊接的晶振频率。硬件相关的基础参数(晶振频率、分频系数、电压范围),必须在项目上下文中明确写死,而且要经过实测验证。
5.3 “缝合怪”代码:F4的HAL库配F1的标准外设库
前面提到过,AI生成代码时存在“缝合”多个库风格的问题。H7上用F1的库,或者HAL和标准库混用,这种错误在AI输出中并不少见。
我现在的应对办法是“基于报错反向追踪”。如果AI生成代码编译时出现“undefined symbol IM_TimeBaseInit”,十有八九是它引用了标准外设库的函数,而工程里只链接了HAL库。这时候不要急着让AI“把函数改成HAL版本”,而是先问AI“为什么你会用到这个函数,你参考的是哪个库的示例代码”,让它自己暴露思路,再统一纠正。
还有一种更隐蔽的情况:AI用了LL_TIM_Init,而这个项目的库版本是旧的,根本不存在这个函数。这类跨库调用问题,平时编译就能发现,但在条件编译的代码里,可能只在特定宏定义下才会暴露,排查起来更耗时。所以审查时我会专门注意“条件编译中的库函数调用”。
5.4 建立自己的“提示词档案库”,比调AI更值钱
用久了你会发现,抛开固定的几个常用提示词外,许多任务虽然每回问法不同,但底层的约束和结构是相通的。比如“生成SPI读取传感器寄存器代码”和“生成I2C读取温湿度代码”本质上都是“总线读写+器件时序”,只是接口不同。
我现在维护了一个本地提示词档案库,按用途分类保存:
- 芯片上下文模板:不同芯片、不同HAL库版本各存一份。
- 驱动开发模板:总线读写类、传感器类、执行器类。
- 状态机实现模板:事件驱动型、时基驱动型。
- 调试辅助脚本模板:数据解析、绘图、日志分析。
- 错误排查模板:编译错误、运行时错误、硬件异常。
每条记录包括:最终生效的提示词、AI给出的初版代码、我做了哪些修改、最终合并进工程的版本、踩过什么坑。这个档案库的价值会随着时间积累越来越大。后来自AI编码工具升级迭代,很多基础模板还能复用。
我把每次AI对话产生的有效代码也做了版本管理,用Git单独开了一个分支,只存AI原始输出,不跟工程主分支混在一起。这样即使AI给的代码在工程里试用失败,也能随时回看原始的生成结果,对比找到问题所在。
最后说点实际体会
这套流程跑了一两个完整小项目之后,我最大的变化是:写代码的时间变少了,看数据手册和抓波形的时间变多了。以前写驱动,一半时间花在敲代码上,现在这个环节能让AI代劳,但前提是我必须更清楚自己想要的接口和时序是什么样的。换句话说,AI没有减少我对硬件的理解需求,反而把“理解硬件”这件事变成了核心能力,因为如果你不懂,你连给AI写的提示词都是错的,审查也没法审到点子上。
如果你准备开始尝试AI协同开发STM32,建议别上来就搞复杂项目。找一个自己熟悉的最小外设,比如点灯或者串口打印,跑一遍完整的流程,把芯片上下文模板、提示词模板、审查清单先固定下来。流程跑顺之后,再逐步扩大AI的参与范围,你会发现嵌入式软件开发这件事,确实能有一种新的干法。