
上一篇完成了工程骨架、时钟配置、串口初始化和LED点灯算是把AI协同开发的“地基”打好了。今天这篇是这个项目的下半场目标很明确把温湿度传感器、OLED屏幕、按键交互全部跑通最终让板子上电就能显示实时数据并且能用按键切换显示模式和校准温湿度。整个过程中我会保留与AI实际对话的提示词和AI给出的代码片段也会把我踩过、改过的坑全部标记出来。如果你也是一个嵌入式工程师正在尝试把AI编程真正落到自己的项目里这篇文章应该能帮你省掉不少试错时间。1. 第二阶段目标与整体规划1.1 第二阶段要解决的核心问题项目做到第二个阶段很多人在前一个阶段就已经“死”在了一半AI生成了一个能编译的工程但烧到板子上什么反应都没有。这个问题我也遇到过而且不止一次。所以第二阶段我没有急着让AI“赶紧把功能写出来”而是先把任务拆成了四个核心问题传感器数据怎么准确读出来、显示驱动怎么稳定跑起来、按键交互怎么设计才不容易误触、以及主逻辑怎么把所有东西串成一个可维护的状态机。DHT22看起来简单但它用的是单总线协议时序要求非常苛刻MCU主频、编译器优化等级、延时函数实现方式都会影响读取结果。OLED用的是I2C接口AI生成代码时最容易犯的错误是搞错设备地址和寄存器配置或者直接拿别人移植好的库改两行就用一旦I2C时序不合规屏幕只会花屏或黑屏。按键部分看似简单但消抖和长按、短按、双击的区分恰恰是新手最容易踩坑的地方。1.2 为什么选择这套功能组合选DHT22加OLED加按键是为了让AI协同开发的“测试闭环”完整起来。传感器负责数据的输入OLED负责数据输出按键负责干预系统行为这三者组合之后程序结构里就会出现ADC采集DHT22用单总线不需要ADC但可以用定时器捕获、I2C通信、GPIO外部中断、状态机、延时管理这些嵌入式开发的典型要素。更重要的是这些模块之间需要协同工作AI生成的代码如果只是“能编译”而“不能运行”在联调时一定会暴露问题。我建议所有做AI协同开发的新手都按这个思路走不要一上来就让AI生成一个完整的大系统而是把系统拆成一个一个“能被硬件验证的小模块”。每个模块AI生成代码后先在开发板上单独测试确认没问题再组合到一起。这个习惯能帮你定位90%以上的问题。2. 用AI跑通传感器驱动从提示词到时序代码2.1 编写高质量AI提示词的套路很多人抱怨AI生成的嵌入式代码不能用但仔细看他输入的提示词往往只有一句话“用STM32读取DHT22”。这句话信息量太少了AI只能按照最通用的模板生成大概率基于Arduino或某种特定平台跟你的芯片型号、引脚分配、时钟树结构全不匹配。我把这段时间摸索出的提示词写法总结成一个公式硬件平台 外设接口 引脚 数据传输协议 延时方式 错误处理 注释风格。以DHT22驱动为例我实际使用的提示词是这样的用中文描述避免歧义请基于STM32F103C8T6使用HAL库生成读取DHT22温湿度传感器的C语言驱动。DHT22数据引脚连接到PA0配置为开漏输出、输入上拉。读取时序要求起始信号保持低电平至少1ms然后释放等待DHT22拉低响应信号。微秒级延时使用SysTick定时器实现的delay_us函数不要使用HAL_Delay因为它的精度不够。需要实现DHT22_Init和DHT22_ReadData两个函数返回值枚举表示读取成功或超时失败。注释中说明每段代码对应的数据手册时序要求。这样详细的描述AI生成的代码基本能直接编译。但“能编译”离“能跑”还有很远请看下面这段AI生成的时序关键代码。2.2 DHT22时序驱动的生成与人工修正AI第一次给我的核心读取代码如下我做了精简uint8_t DHT22_ReadData(float *temperature, float *humidity) { uint8_t data[5] {0}; uint8_t count 0; // 主机拉低起始信号 HAL_GPIO_WritePin(DHT22_GPIO_Port, DHT22_Pin, GPIO_PIN_RESET); delay_us(1000); HAL_GPIO_WritePin(DHT22_GPIO_Port, DHT22_Pin, GPIO_PIN_SET); delay_us(30); // 设置为输入模式等待从机响应 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DHT22_Pin; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(DHT22_GPIO_Port, GPIO_InitStruct); // 等待低电平响应 uint32_t timeout 10000; while (HAL_GPIO_ReadPin(DHT22_GPIO_Port, DHT22_Pin) GPIO_PIN_SET) { if (--timeout 0) return DHT22_TIMEOUT; } // ... 后续读取40位数据 }这段代码第一次读完数据全是0xFF偶尔漂出几个错误值。我排查后发现三个问题。第一开漏输出的初始化不彻底。AI只设置了输出模式却没有在初始化时把GPIO配置为开漏。DHT22的单总线协议要求总线空闲时为高电平主机拉低时是真正的“输出低”释放后依赖上拉电阻拉高。如果用推挽输出释放时输出高电平和DHT22的低电平响应会产生竞争时序全乱。正确做法是在GPIO初始化时把DHT22引脚配置为开漏带上拉读取前不需要反复切换输入输出模式只要把输出寄存器写1引脚就是高阻输入状态。第二起始信号后的延时不对。DHT22手册要求起始信号低电平时间大于1ms之后主机释放总线等待20~40us再从机响应。AI生成的代码在拉低后delay_us(1000)没问题但释放后只delay_us(30)就立刻读引脚这个30us太短实际应该保持至少30us但在一些主频高、编译器优化大的板子上30us不稳。我实际测试改成了50us并且用逻辑分析仪核对过读取成功率大幅提升。第三读取40位数据的边沿判断方法有待优化。AI用了两个循环分别测量低电平和高电平时间但没加超时保护一旦DHT22没有应答程序会卡死在while里。这个在真实硬件上非常要命因为单片机跑飞了只能看门狗复位。我在每个while循环里都加了超时计数超时后立刻返回错误这样即使传感器坏掉系统也不会死机。2.3 定时器与GPIO操作审查要点在过DHT22驱动的过程中我跟AI反复交互了四五轮最终总结出审查GPIO操作的三条铁律看初始化结构体Mode是GPIO_MODE_OUTPUT_OD还是GPIO_MODE_OUTPUT_PPPull是上拉还是下拉Speed设多少对单总线来说开漏上拉是标准答案推挽必出问题。看延时函数实现AI生成的delay_us是不是空循环如果直接用nop凑数在开优化后可能整个循环被优化掉。必须用SysTick或者定时器确保延时是实际物理时间。看GPIO读写位置DHT22读取时有没有对同一引脚先写1再读取如果输出寄存器还保持着低电平引脚读回来永远是0。这个坑特别隐蔽我一度以为传感器坏了换个新的还是这样最后发现是释放时没把输出寄存器置高。我自己后来把读取DHT22的代码整理成了一个稳定版本核心逻辑就是用GPIO的输出寄存器写1来“释放总线”不切换模式避免库函数带来的额外延迟。AI生成的代码可以作为起点但你必须有足够的能力看出哪些地方需要改这就是“AI协同”而不是“AI代工”的本质。3. AI辅助OLED显示与按键逻辑别急着复制粘贴3.1 I2C驱动生成后必须检查的内容OLED屏我选的是常见的0.96寸I2C接口SSD1306方案这个模块网上资料很多AI训练数据里也有大量相关代码。但正因为资料多AI很可能把不同驱动库混在一起生成结果就是编译通过但屏幕黑屏。拿到AI生成的I2C驱动后我检查了三件事。第一I2C设备地址。SSD1306的地址由硬件引脚决定常见是0x3C或0x3D我用的模块用0x3C。AI有时会生成扫描地址或直接写0x78那是8位地址带写位非常容易混淆。确认方式很简单看厂家原理图用示波器抓I2C起始后的设备地址字节也行。第二初始化和清屏指令是否完整。SSD1306需要一连串初始化命令包括显示关闭、时钟分频、多路复用比例、偏移地址、起始行、分段重映射、COM引脚配置、对比度、预充电、VCOMH、显示开启等。AI生成的初始化函数经常偷工减料把几个关键命令合并造成屏幕对比度异常或者显示偏移。最稳妥的方法是把SSD1306数据手册里的初始化命令列表抄给AI让它生成带命令注释的代码。第三I2C通信的速率与时序。STM32F103的I2C外设硬件设计有瑕疵如果直接用HAL库默认配置可能遇到总线锁死。我宁可让AI生成“软件模拟I2C”也就是用两个GPIO手动翻转时钟和数据线。虽然代码看起来Low一点但稳定性和移植性都很好尤其对新手来说软件I2C可以精确控制时序出问题了也容易排查。3.2 按键消抖与状态机设计按键部分我用了一个独立按键连接PB1引脚低电平有效。AI第一次给的代码是if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) GPIO_PIN_RESET) { mode; }这代码在真实硬件上几乎必然出现按键抖动一次按压可能触发多次mode切换一天下来显示模式乱跳。我让AI加上消抖逻辑后它给了一个基于HAL_Delay的解法检测到按下后延时20ms再次确认电平。这个思路是对的但HAL_Delay会阻塞主循环在传感器采集周期内可能导致DHT22时序超时。最终我改成了前后沿检测加状态机整体放在一个10ms定时器中断里处理。Event状态机的核心很简单把按键事件分为“无事件”“单击”“长按”系统根据事件切换显示模式。实际效果是单击可以在“温度-湿度-舒适度指数”三种显示模式间切换长按后进入温湿度校准模式。这种交互逻辑如果让AI一次性生成它很容易写出大量if嵌套看得人头大。我用的提示词是请用一个枚举类型的按键事件和状态机模式避免阻塞式延时。10ms定时器中断扫描按键连续检测到10次稳定低电平视为按下释放后触发事件持续按住超过1000ms视为长按。请给出状态转移表对应的switch-case代码。AI生成的代码结构基本可用但我发现它把“按下”和“释放”事件都放在同一个中断服务函数里容易发生重复触发。我修改为只记录扫描结果主循环里做边缘检测彻底释放了中断函数的压力。3.3 主程序框架的AI生成与组合当各个模块都单独验证通过后我让AI生成一个main.c的主循环框架。AI给出的典型结构是初始化外设、OLED清屏、while(1)里读传感器、显示、读按键、延时100ms。这个结构看似没问题但延时100ms会导致按键响应迟钝而且DHT22两次读取间隔要求至少1s如果100ms轮询一次频繁读取会让传感器数据不稳定。我重新设计了一个非阻塞主循环用一个全局结构体保存传感器数据定时器中断每1s触发一次读取OLED每200ms刷新一次显示按键事件由10ms定时器扫描产生主循环只负责根据当前模式把传感器数据格式化到显示缓冲区。AI在这个框架里帮我生成字符串格式化代码和显示页面切换逻辑我负责保证各模块之间的资源不冲突。这样分工下来AI承担了它擅长的“把想法翻译成代码”的部分我承担了硬件时序和系统调度的部分。主循环简化成了类似下面这样while (1) { process_button_event(g_button_event); Dashboard_Update(g_sensor_data, g_display_mode); OLED_Refresh(); }所有耗时操作都放到了中断和定时器回调里主循环只做事件分发。这种模式看起来简单但对新手来说理解“为什么不能在主循环里加HAL_Delay”才是关键。4. 联调现场那些AI和硬件一起“坑”我的瞬间4.1 时序不对、引脚复用的排查过程联调第一天DHT22数据一直在乱跳OLED偶尔正常显示偶尔花屏。我本来以为是传感器坏了换了一个还是一样。后来用逻辑分析仪抓DHT22的波形发现主机起始信号低电平持续了40us就从高变低而程序里明明写了delay_us(1000)。问题出在delay_us函数的实现上AI生成的是纯软件空循环延时编译器在O2优化下把循环体里的变量优化掉了根本起不到延时的作用。这个坑让我意识到AI生成的代码必须在目标编译优化等级下做测试。后来我把SysTick的延时函数改成基于全局计数器的方式并且在延时前后用逻辑分析仪验证过才算彻底解决。如果你没有逻辑分析仪也可以用示波器或者最简单的“串口打印时间戳”方式来验证延时函数是否准确。4.2 中断与服务函数冲突的典型问题我还遇到一个极具代表性的问题DHT22读取函数放在定时器中断回调里执行读取过程中突然来了一个按键中断导致I2C通信被打断OLED花屏。这是因为两个中断的优先级没配置好。DHT22的时序读取要求整个读取过程不能被打断否则40位数据里就会混入毛刺。AI生成代码时往往默认所有中断优先级相同甚至根本不开中断。我的处理方式是把SysTick优先级调到最高按键外部中断优先级设为次高DHT22读取过程中使用临界区保护也就是暂时关闭全局中断读取结束后再恢复。这里用到的库函数是__disable_irq()和__enable_irq()虽然粗暴但在时序敏感的场景下很有效。4.3 用串口日志快速定位问题联调过程中我始终保持串口日志输出每个关键节点都打印状态。比如DHT22读取成功输出一次“T:25.6 H:60.3”读取失败输出“DHT timeout,count3”。吃过大亏之后我才理解为什么很多资深工程师宁愿多花时间搭日志系统也要让每一行关键代码“开口说话”。AI生成的代码默认不包含日志因为它不知道你的调试口配置和打印环境。我用一个简单的方法在main函数里初始化串口后重定义fputc到串口然后就能直接用printf。注意在STM32F103上用HAL库的标准做法需要勾选MicroLIB选项否则printf的浮点支持会占用大量Flash。这个细节我栽过一次分享出来大家少走点弯路。5. 代码审查与质量落地5.1 让AI帮你做代码走查当主程序能跑通后我没有着急收工而是把整个工程的核心文件复制给AI让它以“嵌入式资深工程师”的身份做代码走查。AI给了几条很有价值的建议把DHT22的延时等待改成宏定义方便调整、把显示模式枚举和字符串映射表合并成一个结构体、把I2C起始停止函数中的字节序写清楚。但也给过错误建议比如“把delay_us用__NOP()优化”这在F103上根本不可靠。所以我的经验是AI走查只能当“第二双眼睛”不能完全信任。它找出的显性问题未初始化的变量、遗漏的返回值检查、数组越界很准但涉及硬件层面的问题依然要靠你自己对照数据手册验证。AI看到的只有代码看不到硬件波形这就是它最大的盲区。5.2 单元测试在嵌入式里怎么做说到单元测试很多人觉得嵌入式搞不了。其实可以只是复杂度比纯软件高。我没有引入复杂的测试框架而是把硬件相关操作做了抽象层比如I2C读写函数封装成接口在PC上用模拟IC实现同样的接口跑一些纯逻辑测试。板子上的状态机、显示模式切换、数据处理这些跟硬件耦合不深的部分都能在本地测试环境里用gcc编译一遍跑通逻辑后再交叉编译到板子上。AI在这个过程中帮了大忙它帮我生成了CMake测试目录、mock了HAL函数、生成了断言用例。实际测试发现显示模式切换在边界条件下会越界比如mode从2切换到0之后又切换到2字符串映射数组溢出。这个bug在MCU上通过反复按键可能一天才能发现在PC上的单元测试里一秒钟就复现了。5.3 固件烧录后的最终验证所有代码合并后我在开发板上做了三天连续运行测试。目的是看稳定性和数据漂移。测试期间我把DHT22放在一个纸箱里模拟不同温度环境OLED屏幕每2秒刷新一次按键每天随机按压几百次。最终测试结果连续运行72小时不死机温度读数误差在±0.5度内湿度误差在±3%内按键短按长按响应准确率100%。烧录工具我用的ST-LINK加STM32CubeProgrammer配置好复位模式和烧录地址后一键下载。这里提醒一句AI生成的代码很少包含芯片Flash读保护设置如果你的量产板需要防读保护可以在CubeMX配置里把RDP级别选上。这跟别人用“嵌入式软件反编译”来逆向学习是两码事正规途径是读官方的技术手册和合法的开源工程。6. 关于“AI下的嵌入式软件怎么学”的一点个人思考6.1 AI没有改变工程师的底层能力要求跟AI合作这个项目之后我最大的感受是AI把“写代码”的成本拉低了但把“判断代码对不对”的门槛抬高了。以前写一个DHT22驱动你要么看手册自己写要么抄别人的改写错了马上能感觉到现在AI几秒钟给你几十行代码看起来毫无破绽但里面隐藏的时序错误要等到硬件跑起来才暴露。所以“AI下的嵌入式软件怎么学”这个问题我的答案是算法和协议基础不能丢数据手册必须会看示波器和逻辑分析仪必须会用。AI可以帮你处理重复性的编码工作但它不能替你做硬件信号完整性分析也不能替你在电路板上飞线测量。越是底层的东西越值得你花时间亲手搞明白。6.2 嵌入式软件反编译与学习的边界网上偶尔能看到“嵌入式软件反编译”相关的讨论有人想通过反编译别人的固件来学习驱动怎么写。以我个人的经验这条路不仅效率低而且非常容易踩到版权和合规红线。正规的嵌入式学习路径应该是读芯片厂商的参考手册、白皮书以及那些协议和数据手册还有很多优质的开源项目可以参考比如STM32的官方例程、GitHub上知名嵌入式库这些内容已经完全足够构建你的知识体系。固件反编译得到的是一堆机器码和反汇编对新手来说根本找不到关键逻辑反而浪费时间。AI协同开发也是如此你让AI生成代码本质是让它在公开数据集里检索最接近你需求的模式而不是凭空发明硬件时序。所以你的提示词越接近“数据手册的语言”AI生成的东西越可靠。我用AI写了几个月代码发现真正提高效率的不是让它“直接给我完整项目”而是把项目拆成一个个小问题每个问题让AI给出候选方案然后用硬件去验证。这个过程练出来的是工程师的判断力这种能力AI暂时还替代不了。经过这个第二阶段的实战我手里这套工程已经从“能点灯”进化到“能做完整的温湿度监测产品原型”。如果你也在尝试用AI开发嵌入式项目我的建议是先选一个简单明确的硬件目标把提示词写细致把每个模块分开验证最后合并时尤其注意中断和延时。别指望AI一次到位它更像是那个效率极高但偶尔犯糊涂的结对同事——你要做的不是抱怨而是在它犯错之前设置好测试关卡在它犯错之后看懂哪里错了、为什么错。这样合作过一两个完整项目之后你会发现自己独立写代码的能力反而更强了。