AI辅助嵌入式开发工作流 — 小白实操手册 v2.0
AI辅助嵌入式开发工作流 — 小白实操手册 v2.0
定位:用AI辅助开发,但不走弯路、不留技术债。
核心理念:AI是杠杆,你的工程思维才是地基。
作者:资深嵌入式工程师 + 上下文构建工程师
写给小白的一番话
你以为我要教你"用AI写代码"?不。
我要教你用AI构建一个能活很多年的嵌入式项目。
市面上的教程教的是:把需求丢给AI,复制代码,烧录,成功。——然后三个月后代码没法改、没法扩展、别人看不懂。这不是你的错,是那些教程的错。
我会做的:每教一个模块开发,就教你如何把这个模块的架构设计、接口约定、约束条件写成上下文,让AI在后续开发中始终理解"这个模块在哪里、边界是什么、为什么会这样设计"。这样当你需要改需求、加功能、修复Bug时,你的AI上下文就是一份活的项目蓝图,而不是每次新建Session从头解释。
现代LLM的上下文窗口已经可以到100万Token。但这不意味着你要把所有东西都塞进去——塞得多不如塞得对。
手册结构(建议学习顺序)
第一章:前置准备(工具+认知)
第二章:好上下文 vs 差上下文(具体例子)
第三章:架构设计优先(先画蓝图再动手)
第四章:模块化开发 + 上下文分层结构
第五章:避免上下文污染与项目README模板
第六章:提示词模板库
第七章:5个实战项目(手把手带做)
第八章:调试与排错
第九章:附录
第一章:前置准备
1.1 你的工具箱
电脑一台(Windows/Mac/Linux随意)
AI助手(主力推荐Claude,因为它的代码能力和上下文理解是目前最强的)
开发板:
- STM32F103C8T6最小系统板(约50元)+ ST-Link(约20元)
- 入门资料最全,社区最大
开发软件:
- Keil MDK(STM32开发,30天试用)
- VSCode + PlatformIO(ESP32或想更快入门)
- Arduino IDE(最简单,但不适合深学)
1.2 你必须建立的认知
认知1:AI是杠杆,不是替代者
AI能帮你写代码,但不能替你理解需求、设计架构、评估风险。一个嵌入式项目的质量,90%取决于你在AI介入前做了多少架构思考。
认知2:上下文是有结构的,不是越多越好
把一整本芯片手册丢给AI,不如告诉AI:当前在调哪个模块、这个模块的输入输出是什么、上次为什么这样设计。
认知3:项目要分Session管理,不是都在一个对话里
混乱的Session管理会导致:AI不知道你之前做了什么设计决策、重复解释项目背景浪费Token、上下文污染导致AI给出矛盾的代码。
第二章:好上下文 vs 差上下文
2.1 什么是"上下文"
上下文 = AI在当前对话中能看到的所有信息。包括:System Prompt、对话历史、你粘贴的代码片段、你描述的项目背景。
上下文构建:有意识地组织这些信息,让AI每次回复都基于准确、完整、无歧义的项目理解。
2.2 好上下文 vs 差上下文 — 两个真实对比
差上下文示例:
“我之前写了一个串口通信的代码,现在想让LED根据收到的命令闪烁,帮我改一下。”
AI不知道你用的是什么芯片、什么通信协议、LED在哪个引脚——这样的请求,AI只能猜测,大概率会出错。
好上下文示例:
【项目背景】
芯片:STM32F103C8T6
项目类型:工业传感器数据采集
当前阶段:已完成串口DMA接收,正在扩展LED状态指示功能
【已完成模块】
1. usart_dma.c/h - USART1 DMA接收,PB10=USART1_RX,波特率115200
2. ringbuffer.c/h - 环形缓冲区,深度256字节
3. 协议格式:自定义帧,帧头0xAA 0x55,数据长度+数据+CRC16
【本次需求】
新增led_cmd_handler.c/h:
- 收到字节0x01 → LED1亮(PA5输出高)
- 收到字节0x00 → LED1灭(PA5输出低)
- LED初始化已写在gpio_ctrl.c中,函数为LED1_Init()和LED1_SetState(uint8_t on)
【约束】
- 不能破坏现有DMA接收逻辑
- 新增代码必须放在单独的.c文件
- 遵循已有编码规范:匈牙利命名法,Tab缩进4字符
两者核心差距:好上下文包含了芯片型号、当前阶段、已完成模块的接口、具体引脚、协议格式——这些都是AI"猜不到"的信息。
第三章:架构设计优先
这是小白最容易跳过的步骤,也是未来技术债的根源。
3.1 什么是嵌入式软件架构
架构 = 这个项目分成哪几个模块,每个模块负责什么,模块之间怎么通信。
没有架构的项目:代码都堆在main.c里,GPIO配置分散在十几个地方,想改一个功能要改二十个文件。
有架构的项目:模块划分清晰,一个模块出问题不影响其他模块,每个模块有明确的输入输出接口,AI可以针对特定模块开发。
3.2 嵌入式分层架构(推荐给所有新手)
┌─────────────────────────────────────┐
│ 应用层(Application) │ ← main.c + 业务逻辑
├─────────────────────────────────────┤
│ 驱动抽象层(HAL Abstraction) │ ← 硬件无关的驱动接口
├─────────────────────────────────────┤
│ 硬件驱动层(Peripheral Drivers)│ ← GPIO/USART/DMA/TIM
├─────────────────────────────────────┤
│ 硬件层(Hardware) │ ← 芯片寄存器配置
└─────────────────────────────────────┘
应用层:写业务逻辑。比如"收到温度>30°C就亮红灯"。
驱动抽象层:定义统一的驱动接口,不直接操作硬件。比如TemperatureSensor_Init()、TemperatureSensor_Read()。
硬件驱动层:具体实现。比如基于DS18B20的实现。
这样做的好处:当你的硬件换了(比如从STM32换成ESP32),只需要重写驱动层,应用层代码不用动。
3.3 用AI辅助架构设计
我是一个嵌入式工程师,正在设计一个[项目名称]项目。
项目需求:[描述要实现的功能]
芯片型号:[芯片型号]
存储限制:Flash [X]KB,RAM [X]KB
请帮我完成以下架构设计:
1. 推荐分层架构方案
2. 推荐模块划分(每个模块的名字、职责、接口)
3. 模块之间的依赖关系
4. 建议先开发哪个模块
5. 给出每个模块的头文件接口声明(.h)
请用工程化的语言描述,不要只给概念。
3.4 如何让AI生成项目蓝图
核心理念:先有Prompt,再有蓝图。 不要让AI直接生成随意的架构文档——你要先通过Prompt框定需求,AI在约束下做设计。
第一步:给AI发这个Prompt
我是一个嵌入式软件工程师,需要你帮我为以下项目生成一份完整的架构设计文档(项目蓝图)。
## 项目基本信息
- 项目名称:[项目名称]
- 芯片型号:[型号,例如 STM32F103C8T6]
- 资源约束:Flash ≤ [X]KB,RAM ≤ [X]KB
- 开发板/外设:[具体型号或连接描述]
## 核心功能需求
1. [功能1]
2. [功能2]
3. [功能3]
## 已有约束
- 已有代码/模块:[描述已完成的模块]
- 编码规范:[命名风格,例如匈牙利命名法,Tab缩进4字符]
- 禁止事项:[例如禁止在中断里使用浮点运算]
## 你的任务
请生成一份完整的架构设计文档,必须包含以下章节:
1. 项目概述(一句话描述 + 资源约束确认)
2. 架构概览(分层架构说明)
3. 模块清单(每个模块:名称、职责、对外接口、依赖模块、状态=未开发)
4. 模块依赖关系(用文字描述)
5. 推荐开发顺序
6. 已知约束与风险
输出时注意:
- 每个模块的头文件接口用C语言声明(.h格式)
- 不要写任何.c实现代码,只需要接口声明
- 模块数量控制在5-8个
第二步:AI会生成这样的蓝图文档
# [项目名称] 架构设计文档
版本:v1.0 | 日期:[日期] | 状态:进行中
## 1. 项目概述
- 功能:[一句话描述]
- 目标芯片:[型号]
- 资源约束:Flash ≤ [X]KB, RAM ≤ [X]KB
## 2. 架构概览
[用文字描述分层结构和模块划分]
## 3. 模块清单
### 3.1 [模块A]
- 职责:...
- 对外接口:
- `void ModuleA_Init(void);`
- `uint8_t ModuleA_DoSomething(uint8_t input);`
- 依赖模块:ModuleB
- 状态:[未开发/开发中/已完成/已测试]
## 4. 模块依赖图
[文字描述依赖关系]
## 5. 开发顺序
1. ModuleA(无依赖,优先开发)
2. ModuleB(依赖ModuleA)
## 6. 已知约束
- [约束1]
- [约束2]
这份文档就是你的项目蓝图,也是你每次开新Session时的上下文起点。
第三步:生成后的处理
AI生成的蓝图通常还需要你做两件事:
1. 验证接口设计是否合理
重点检查:
- 模块之间的接口是否对等(A调用B,B是否也需要回调A?)
- 是否有循环依赖(A→B→C→A)
- 接口参数是否合理(数据结构有没有越界风险)
2. 根据你的实际情况手动调整
AI不知道你的芯片引脚占用、已有代码的具体接口,需要你手动修正。
3. 保存到项目目录
将最终版本保存为docs/architecture.md(或README.md),作为项目的基准蓝图。
第四章:模块化开发 + 上下文分层结构
核心理念:一个模块一个模块开发,每个模块都有一套完整的上下文。
4.1 模块开发7步法
第1步:把项目蓝图发给AI
第2步:告诉AI本次要开发的模块
第3步:让AI生成模块代码框架(.h + .c)
第4步:Review框架,确认接口设计合理
第5步:让AI逐个实现接口函数
第6步:编译测试
第7步:如果测试通过,更新项目蓝图(标记模块状态)
关键在Step 4:接口设计不合理时,不要急着让AI写代码。先让AI解释它为什么这样设计,你再判断是否需要调整。这是避免返工的最重要一步。
4.2 模块开发上下文模板
每次开发新模块时,给AI的上下文应该包含:
【项目蓝图摘要】
[粘贴项目架构文档中的相关部分]
【本次开发模块】
模块名:[模块名]
所在层级:[应用层/驱动抽象层/硬件驱动层]
对外接口:
- [接口1]
- [接口2]
【已有实现】
[列出已完成的相关模块和它们的接口]
【本次任务】
[描述要实现的功能]
【约束】
- [资源约束:例如中断里不能使用浮点运算]
- [接口约束:必须兼容已有XX接口]
- [编码规范:命名/缩进/注释风格]
【验收标准】
[用可测试的语言描述模块应该达到什么效果]
4.3 上下文分层结构 — 理论
推荐用三层结构组织项目上下文:
第一层:项目蓝图(Project Blueprint)
整个项目的架构概述,模块列表,模块间关系图。这部分内容固定,不需要每次Session重复输入。
第二层:模块上下文(Module Context)
当前正在开发的模块的详情:接口、约束、已知问题。每次只放1-2个模块。
第三层:即时任务(Task Context)
本次要解决的具体问题、错误信息、你尝试过的方法。只在当前Task的Session里使用。
4.4 三层结构的判断标准
这是最常见的困惑:“我的这条信息应该放蓝图、模块上下文、还是即时任务?”
判断方法:看这个信息"会不会变"
| 信息类型 | 放哪层 | 判断依据 |
|---|---|---|
| 模块清单、分层架构、模块依赖关系 | 项目蓝图 | 整个项目周期内基本不变 |
| 当前开发模块的接口、约束、硬件配置 | 模块上下文 | 在这个模块开发期间不变 |
| 本次要解决的具体Bug、错误信息、尝试过的方法 | 即时任务 | 每次任务都不同 |
常见错误:把即时任务的信息放进了蓝图
错误示例(把"这次要修DMA错位"写进了项目蓝图)→ 下次开新Session,蓝图里还带着旧Bug信息,AI会被误导。
正确做法:Bug信息只放在当前Session的即时任务里,修复完成后删除,蓝图保持干净。
4.5 Session管理核心原则
应该开新Session的场景:
- 切换到另一个模块开发
- Session里的上下文已经积累得很长,需要压缩
- 第二天继续开发
应该保持在同一个Session的场景:
- 同一个模块的迭代开发
- 正在调试一个Bug,需要看错误历史
- 还在处理同一个功能
开新Session前必做的总结Prompt:
请总结当前项目状态,包括:
1. 已完成模块列表 + 每个模块的对外接口
2. 正在进行的工作
3. 已知的未解决问题
4. 下一步计划
用结构化的格式输出,方便下次粘贴到新Session。
4.6 Token节省技巧
技巧1:删除无用的对话历史
在长Session中,定期让AI总结并删除旧的中间讨论:
从现在起,请假设我们之前的所有调试讨论已经完成。
基于以下项目状态继续工作:
[粘贴项目状态总结]
技巧2:不要重复粘贴相同代码
如果AI之前已经看过某段代码,你只需要说"参考之前在usart_dma.c第30-50行的实现"而不是把代码再贴一遍。
技巧3:明确任务边界
差:"帮我看看这个代码有什么问题"
好:"第15-30行的函数返回值有溢出风险,请检查并修复"
技巧4:用标记分隔新旧上下文
开新Session时,在粘贴项目总结后加一句:
以上是项目历史状态。现在我要开始一个新任务:
[新任务描述]
4.7 何时同上下文,何时开新Session
可以同上下文持续交互的场景
1. 同一模块的迭代开发
LED模块:从初始化 → 加熄灭函数 → 加呼吸灯效果,每次都在同一个Session里,AI知道LED模块的接口和已有的代码。
2. 调试Bug的过程
AI发现了竞态条件 → 你问怎么改 → 改了之后还是丢数据 → AI分析双缓冲方案。调试需要看历史,AI需要知道之前的修复尝试。
3. 需求渐进式明确
先说"我想做温度监测" → AI给出架构建议 → 加OLED显示 → 再加阈值报警。需求还没想清楚时,在同一Session逐步完善。
4. 架构Review和讨论
AI提出分层建议 → 你追问"不想改已有的usart模块怎么办" → AI给出最小侵入方案。讨论需要完整上下文。
5. 验证代码是否正确
AI测试了5种边界情况,发现2处问题 → 逐一修复 → AI逐一验证。AI需要记住它发现了什么问题,才能逐一确认修复结果。
必须开新Session的场景
1. 完全切换到新模块开发
之前在做usart_dma模块,现在要开始温度传感器模块 → 新开Session。
2. 上下文已经积累过长
判断标准:AI回复开始变慢,或开始"忘记"之前的一些细节。先在旧Session让AI做总结,开新Session粘贴总结继续。
3. 第二天继续开发
前一晚完成了LED模块和按键模块,第二天继续做串口通信。开新Session,粘贴项目状态总结。
4. 发现了之前的设计错误,需要重新审视
之前按照设计做了usart_dma和ringbuffer,现在发现缓冲区大小设计不合理。开新Session,直接说"之前的设计有问题"。
5. 换了AI模型
从Claude换到Kimi,或者换到DeepSeek → 新模型没有旧上下文。
6. 遇到了死胡同,需要换思路
尝试了3种方案都不行,陷入僵局 → 开新Session,跳出旧框架。
判断流程图
你现在有一个新任务/问题
│
├─ 是否需要理解之前Session的内容?
│ │
│ ├─ 否 → 开新Session
│ │
│ └─ 是 → 旧Session的上下文是否已经很长?
│ │
│ ├─ 否(<20轮)→ 继续用旧Session
│ │
│ └─ 是 → 先在旧Session里做总结
│ 开新Session粘贴总结
完整示例:旧Session总结 → 新Session接续
背景:你在做usart_dma模块,进行了2天的调试,现在需要开新Session继续。
Step 1 — 旧Session里,让AI总结项目状态:
请用以下格式总结当前项目状态:
【进行中】…
【未解决问题】…
【下一步】…
AI回复:
【项目名称】:工业传感器数据采集
【最后更新】:2026-03-28
【芯片】:STM32F103C8T6
## 已完成模块
| 模块名 | 层级 | 状态 | 关键接口 |
|--------|------|------|---------|
| gpio_ctrl | 硬件驱动层 | ✅完成 | `LED1_Init(); LED1_SetState(uint8_t on);` |
| usart.c/h | 硬件驱动层 | ✅完成 | `USART1_Init(void);` |
| ringbuffer.c/h | 驱动抽象层 | ✅完成 | `RB_Init(RingBuffer_t *rb); RB_Put(RingBuffer_t *rb, uint8_t byte);` |
## 进行中
正在调试usart_dma.c的接收中断,问题是DMA接收到的数据与串口助手发送的数据相比有错位。
## 未解决问题
1. DMA数据错位:发送0xAA 0x55 0x02 0x01 0x02,收到0xAA 0x55 0x02 0x00 0x02,怀疑是DMA半传输中断和主循环同时读缓冲区导致竞态条件
## 下一步
实现双缓冲方案解决竞态条件,然后继续做protocol.c的帧解析
Step 2 — 复制AI的回复,保存到项目目录session_summary_20260328.md
Step 3 — 开新Session,粘贴以下内容作为开头:
【项目名称】:工业传感器数据采集
【本次任务】:解决DMA接收数据错位问题,实现双缓冲方案
## 项目历史状态(2026-03-28旧Session结束时保存)
【已完成模块】
| 模块名 | 层级 | 状态 | 关键接口 |
|--------|------|------|---------|
| gpio_ctrl | 硬件驱动层 | ✅完成 | `LED1_Init(); LED1_SetState(uint8_t on);` |
| usart.c/h | 硬件驱动层 | ✅完成 | `USART1_Init(void);` |
| ringbuffer.c/h | 驱动抽象层 | ✅完成 | `RB_Init(); RB_Put(); RB_Get();` |
【进行中】
正在调试usart_dma.c的接收中断,DMA数据错位问题
【未解决问题】
1. DMA数据错位:发送0xAA 0x55 0x02 0x01 0x02,收到0xAA 0x55 0x02 0x00 0x02
2. 怀疑原因:半传输中断和主循环同时读缓冲区,竞态条件
【下一步】
实现双缓冲方案
## 本次任务详细说明
请帮我设计双缓冲方案,要求:
1. 禁止在中断和主循环同时操作同一缓冲区
2. 使用两个128字节缓冲区交替工作
3. 给出具体的.h/.c实现
效果:新Session的AI可以立即知道项目历史、当前问题、下一步,不需要重新解释项目背景。
4.8 用Git管理项目版本(选读)
如果你只用AI对话管理项目,Session结束后的代码历史也随之消失。Git可以把这部分记忆持久化。AI Session管理与Git版本管理是互补的:Session管理AI对话上下文,Git管理代码文件历史。
Git与本文工作流的对应关系
| 维度 | Session管理 | Git管理 |
|---|---|---|
| 管理的对象 | AI对话上下文 | 代码文件 |
| 时间范围 | 单次开发会话 | 项目全生命周期 |
| 记录的内容 | 决策、问题、上下文 | 代码变更、版本快照 |
| 用途 | AI回复质量 | 代码回溯、协作 |
最小Git入门(5个命令解决80%场景)
# 1. 初始化项目仓库
git init
# 2. 查看当前变更
git status
git diff
# 3. 提交变更(每次完成一个小功能就提交)
git add .
git commit -m "描述这次改了什么"
# 4. 查看提交历史(这就是你的开发日志)
git log --oneline
# 5. 创建实验分支(想尝试新方案时,不影响主线)
git checkout -b experiment/dma-fix
# 测试完成后合并回主线
git checkout main
git merge experiment/dma-fix
commit message写什么
| 场景 | commit message示例 |
|---|---|
| 完成一个模块 | feat: 完成gpio_ctrl模块 |
| 修复一个Bug | fix: 修复DMA接收数据错位问题 |
| 更新文档 | docs: 更新README模块状态 |
| 重构代码 | refactor: 简化protocol_parse状态机 |
commit message不要写:
- ❌
"update"—— 改了啥? - ❌
"fix bug"—— 哪个bug?
模块开发7步法 + Git版本控制
把Git commit融入模块开发流程,每一步都有对应的版本控制动作:
第1步:把项目蓝图发给AI
→ git commit: "docs: 初始项目蓝图"
第2步:告诉AI本次要开发的模块
→ git checkout -b feature/gpio_ctrl
第3步:让AI生成模块代码框架
→ git commit: "feat: gpio_ctrl框架生成"
第4步:Review框架
→ git diff 检查变更范围
第5步:让AI逐个实现接口函数
→ 每个小功能完成即commit
第6步:编译测试
→ git commit: "feat: gpio_ctrl实现完成,编译通过"
第7步:更新项目蓝图
→ git commit: "docs: 更新README模块状态",然后git merge回main
Session总结 → commit message的对应
Session总结是最适合写成commit message的内容,两者是天然对应的:
Session总结:
【已完成模块】gpio_ctrl ✅
【进行中】usart_dma 🔧
【未解决问题】DMA数据错位
【下一步】实现双缓冲
对应的commit message:
git commit -m "feat: 完成gpio_ctrl模块
- gpio_ctrl模块开发完成
- 实现LED_Init/LED_On/LED_Off/LED_Toggle接口
- 依赖:无
- 下一步:usart_dma模块开发"
好处:三个月后翻git log,比翻Session历史快得多。Git commit历史就是你的项目开发日志。
完整工作流(AI + Git双保险)
每次开发任务
│
├── Session管理(AI对话层面)
│ ├── 开新Session前:粘贴上次Session总结
│ └── 开发完成后:生成新的Session总结
│
└── Git管理(代码文件层面)
├── 每次小功能完成:git commit
├── Session结束:git commit -m "[Session总结]"
└── 新Session开始:git log --oneline 回顾历史
第五章:避免上下文污染与项目README模板
提示:把项目状态总结保存到项目目录的README.md里,每次开新Session时粘贴使用。格式见本章5.2节。
5.1 避免上下文污染
上下文污染:AI在之前的对话中形成了某种理解,这个理解是错的或者已经过时了,但它仍然在影响后续回复。
处理方法: 当你在新session发现AI对项目有误解时,明确纠正:
[旧session中的理解可能有误,请以下面的为准]
项目使用的是STM32F103,不是STM32F401;
LED在PA5,不是PC13;
[纠正更多错误认知]
5.2 项目README模板
每个项目根目录都应该有一个README.md:
# [项目名称]
## 概述
[一行话描述项目做什么]
## 芯片与资源
- 芯片:[型号]
- Flash/RAM:[X]KB/[X]KB
- 开发板:[板子型号]
## 架构
[简述分层架构和模块划分]
## 模块状态
| 模块 | 状态 | 备注 |
|------|------|------|
| gpio_ctrl | ✅完成 | |
| usart_dma | ✅完成 | |
| led_cmd_handler | 🔧开发中 | 正在调试命令解析 |
## 最新项目状态([日期])
[粘贴最近的AI总结]
## 开发日志
- 2026-03-28:完成LED命令处理模块单元测试
- 2026-03-27:完成串口DMA接收模块
## 快速开始
[描述如何编译、烧录、运行]
第六章:提示词模板库
模板A:项目架构设计请求
请为以下嵌入式项目设计架构:
【基本信息】
- 项目名称:[名称]
- 芯片型号:[型号]
- 资源约束:Flash ≤ [X]KB,RAM ≤ [X]KB
【核心功能需求】
1. [功能1]
2. [功能2]
3. [功能3]
【已有约束】
- 已有硬件:[描述已连接的硬件]
- 已有代码:[描述已有的模块和接口]
- 编码规范:[命名风格/缩进规范等]
请输出:
1. 推荐的分层架构方案(必须包含应用层+驱动层)
2. 模块划分(模块名 + 职责 + 接口列表)
3. 模块依赖关系描述
4. 开发顺序建议
5. 每个模块的头文件模板(.h,不实现.c)
模板B:模块开发请求
请实现以下嵌入式模块:
【项目背景】
[粘贴项目架构文档的相关部分]
【本模块信息】
- 模块名:[模块名]
- 所属层级:[应用层/驱动抽象层/硬件驱动层]
- 依赖模块:[依赖的模块列表]
【功能需求】
[详细描述要实现的功能]
【接口定义】
[描述本模块需要提供的接口]
【约束条件】
1. [约束1]
2. [约束2]
【编码规范】
- 命名:[命名规范]
- 缩进:[缩进规范]
- 注释:[注释要求]
请生成完整的.h和.c文件,并说明每个函数的作用。
模板C:调试求助
【项目阶段】:[新项目开发/调试中/维护中]
【芯片/开发板】:[型号]
【开发环境】:[KEIL/IAR/Arduino等,含版本号]
【问题描述】
[一句话描述问题,如"LED不亮但没有报错"]
【出现的现象】
- 预期:[应该发生什么]
- 实际:[发生了什么]
【错误信息】(如有)
[粘贴完整错误信息]
【相关代码】
[只粘贴和问题相关的代码片段,不要贴整个文件]
【已尝试的排查】(如有)
1. [排查1]:[结果]
2. [排查2]:[结果]
请帮我:
1. 分析根本原因
2. 给出修复方案
3. 如何验证修复
模板D:项目状态总结(用于Session交接)
【项目名称】:[项目名称]
【最后更新】:[日期]
【芯片】:[型号]
## 已完成模块
| 模块名 | 层级 | 状态 | 关键接口 |
|--------|------|------|---------|
| [模块A] | 驱动层 | ✅完成 | `void ModA_Init(void);` |
| [模块B] | 应用层 | ✅完成 | `uint8_t ModB_Run(uint8_t input);` |
## 进行中
[描述当前正在开发的内容]
## 未解决问题
1. [问题1]:怀疑原因[原因],已尝试[方法]
2. [问题2]:...
## 下一步
[描述接下来的开发计划]
## 最新代码片段参考
[如果有问题需要AI关注,粘贴关键代码]
模板E:代码Review请求
请审查以下嵌入式代码,重点关注:
1. 潜在Bug(内存泄漏、越界、竞态条件)
2. 嵌入式特定风险(中断安全、功耗、资源使用)
3. 可维护性(命名、注释、结构)
[粘贴代码]
请按以下格式输出:
【高风险】:[问题描述] → [建议修复]
【中风险】:...
【低风险/建议】:...
【总体评价】:对这个模块质量的评估
模板F:Session初始化(开新Session时用)
【项目名称】:[名称]
【本次任务】:[你要做什么]
## 项目历史状态(上次Session结束时保存)
[粘贴上次Session总结的项目状态]
## 本次任务详细说明
[描述你要做的具体任务]
第七章:5个实战项目
每个项目都包含:架构设计 → 模块上下文 → 具体实现 → 验收测试
项目1:LED流水灯(架构入门)
完整实现示例:下面演示一个项目如何从头到尾走完7步法,AI给出了完整可编译代码。
Step 1-2 — 你发出架构设计请求:
请为STM32F103C8T6的LED流水灯项目设计架构。硬件:PA0=红灯,PA1=黄灯,PA2=绿灯(低电平点亮,即输出0亮灯)。要求:分层架构、延时用定时器实现、灯灭时保持低功耗。请给出模块划分、接口设计、头文件声明。
Step 3 — AI生成的架构设计:
模块划分:gpio_hw / led_driver / timer / waterlight_app
头文件声明:
// gpio_hw.h
#ifndef GPIO_HW_H
#define GPIO_HW_H
#include <stdint.h>
void GPIO_HW_Init(void);
#endif
// led_driver.h
#ifndef LED_DRIVER_H
#define LED_DRIVER_H
typedef enum { LED_RED, LED_YELLOW, LED_GREEN } LED_ID_t;
void LED_Driver_Init(void);
void LED_On(LED_ID_t id);
void LED_Off(LED_ID_t id);
void LED_Toggle(LED_ID_t id);
#endif
// timer.h
#ifndef TIMER_H
#define TIMER_H
void Timer_Init(void);
void Timer_DelayMs(uint32_t ms);
#endif
// waterlight_app.h
#ifndef WATERLIGHT_APP_H
#define WATERLIGHT_APP_H
void WaterLight_Run(void);
#endif
Step 4 — Review后确认接口合理,进入实现。
Step 5 — AI生成的完整可编译代码:
// gpio_hw.c
#include "gpio_hw.h"
#include "stm32f1xx_hal.h"
void GPIO_HW_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2, GPIO_PIN_SET);
}
// led_driver.c
#include "led_driver.h"
#include "gpio_hw.h"
static const uint16_t led_pin[3] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2};
void LED_Driver_Init(void) { GPIO_HW_Init(); }
void LED_On(LED_ID_t id) { HAL_GPIO_WritePin(GPIOA, led_pin[id], GPIO_PIN_RESET); }
void LED_Off(LED_ID_t id) { HAL_GPIO_WritePin(GPIOA, led_pin[id], GPIO_PIN_SET); }
void LED_Toggle(LED_ID_t id) { HAL_GPIO_TogglePin(GPIOA, led_pin[id]); }
// timer.c
#include "timer.h"
#include "stm32f1xx_hal.h"
void Timer_Init(void) { HAL_InitTick(TICK_INT_PRIORITY); }
void Timer_DelayMs(uint32_t ms) { HAL_Delay(ms); }
// waterlight_app.c
#include "waterlight_app.h"
#include "led_driver.h"
#include "timer.h"
void WaterLight_Run(void) {
LED_ID_t sequence[3] = {LED_RED, LED_YELLOW, LED_GREEN};
while (1) {
for (int i = 0; i < 3; i++) {
LED_On(sequence[i]);
Timer_DelayMs(1000);
LED_Off(sequence[i]);
}
}
}
// main.c
#include "main.h"
int main(void) {
HAL_Init();
SystemClock_Config();
Timer_Init();
LED_Driver_Init();
WaterLight_Run();
}
Step 6 — 编译测试: KEIL MDK编译0 errors,0 warnings,烧录后三个灯按顺序各亮1秒。
Step 7 — 测试通过后,更新项目蓝图(README.md)
项目2:按键检测+状态机(模块化入门)
一句话:按键控制LED状态切换,短按亮/灭,长按进入闪烁模式
你发给AI的Prompt:
项目:按键控制LED状态切换。
硬件:PA5=LED(低电平点亮),PC13=按键(按下接地,低电平有效),上拉电阻。
状态机设计:
- STATE_IDLE:LED灭
- STATE_ON:LED亮
- STATE_BLINK:LED闪烁
- 按键短按(<500ms):切换STATE_IDLE ↔ STATE_ON
- 按键长按(≥500ms):切换到STATE_BLINK
要求:
- 状态机用switch-case实现
- 按键去抖用定时器,不用delay
- 每个状态对应一个处理函数
请设计模块划分和状态转移图,然后生成完整的.h/.c实现。
AI回复 — 头文件:
// key_driver.h
#ifndef KEY_DRIVER_H
#define KEY_DRIVER_H
#include <stdint.h>
typedef enum {
KEY_STATE_IDLE,
KEY_STATE_PRESSED,
KEY_STATE_LONG_PRESSED
} Key_State_t;
void KeyDriver_Init(void);
Key_State_t KeyDriver_Scan(void); // 返回按键状态
#endif
// led_fsm.h
#ifndef LED_FSM_H
#define LED_FSM_H
#include "key_driver.h"
typedef enum {
STATE_IDLE, // LED灭
STATE_ON, // LED亮
STATE_BLINK // LED闪烁
} App_State_t;
void LED_FSM_Init(void);
void LED_FSM_Run(Key_State_t key_event); // 根据按键事件驱动状态机
#endif
AI回复 — 核心状态机实现:
// led_fsm.c
#include "led_fsm.h"
#include "led_driver.h"
static App_State_t current_state = STATE_IDLE;
void LED_FSM_Init(void) {
current_state = STATE_IDLE;
LED_Off(LED_RED);
}
void LED_FSM_Run(Key_State_t key_event) {
App_State_t next_state = current_state;
switch (current_state) {
case STATE_IDLE:
if (key_event == KEY_STATE_PRESSED) {
next_state = STATE_ON;
}
break;
case STATE_ON:
if (key_event == KEY_STATE_PRESSED) {
next_state = STATE_IDLE;
} else if (key_event == KEY_STATE_LONG_PRESSED) {
next_state = STATE_BLINK;
}
break;
case STATE_BLINK:
if (key_event == KEY_STATE_PRESSED) {
next_state = STATE_IDLE;
}
break;
}
if (next_state != current_state) {
current_state = next_state;
switch (current_state) {
case STATE_IDLE: LED_Off(LED_RED); break;
case STATE_ON: LED_On(LED_RED); break;
case STATE_BLINK: /* 闪烁由定时器中断控制 */ break;
}
}
}
项目3:串口+DMA+环形缓冲区(工程化核心)
这是嵌入式最重要的基础模块,建议完整掌握。
你发给AI的Prompt:
项目:STM32F103 USART1 DMA接收 + 环形缓冲区
硬件:PA9=USART1_TX,PA10=USART1_RX,波特率115200,数据位8,停止位1,无校验
自定义协议帧格式:
[0xAA][0x55][数据长度][数据...][CRC16高8位][CRC16低8位]
要求:
- 驱动层:usart.c/h,封装USART初始化和DMA配置
- 环形缓冲:ringbuffer.c/h,深度128字节,线程安全
- 应用层:protocol.c/h,实现帧解析和CRC校验
请设计完整的模块架构和头文件,然后实现环形缓冲区和协议解析的核心代码。
AI回复 — 头文件:
// ringbuffer.h
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <stdint.h>
#include <stdbool.h>
typedef struct {
uint8_t *buffer;
uint16_t size;
uint16_t head; // 写指针
uint16_t tail; // 读指针
} RingBuffer_t;
void RB_Init(RingBuffer_t *rb, uint8_t *buf, uint16_t size);
bool RB_Put(RingBuffer_t *rb, uint8_t byte);
bool RB_Get(RingBuffer_t *rb, uint8_t *byte);
uint16_t RB_Available(RingBuffer_t *rb);
void RB_Clear(RingBuffer_t *rb);
#endif
// protocol.h
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <stdint.h>
#include <stdbool.h>
#define FRAME_HEADER_0 0xAA
#define FRAME_HEADER_1 0x55
typedef struct {
uint8_t data_len;
uint8_t data[128];
uint16_t crc;
} Frame_t;
bool Protocol_Parse(RingBuffer_t *rb, Frame_t *out_frame);
uint16_t Protocol_CalcCRC16(const uint8_t *data, uint16_t len);
bool Protocol_Validate(const Frame_t *frame);
#endif
AI回复 — 环形缓冲区核心实现:
// ringbuffer.c
#include "ringbuffer.h"
#include <string.h>
void RB_Init(RingBuffer_t *rb, uint8_t *buf, uint16_t size) {
rb->buffer = buf;
rb->size = size;
rb->head = 0;
rb->tail = 0;
}
bool RB_Put(RingBuffer_t *rb, uint8_t byte) {
uint16_t next_head = (rb->head + 1) % rb->size;
if (next_head == rb->tail) {
return false; // 缓冲区满
}
rb->buffer[rb->head] = byte;
rb->head = next_head;
return true;
}
bool RB_Get(RingBuffer_t *rb, uint8_t *byte) {
if (rb->tail == rb->head) {
return false; // 缓冲区空
}
*byte = rb->buffer[rb->tail];
rb->tail = (rb->tail + 1) % rb->size;
return true;
}
uint16_t RB_Available(RingBuffer_t *rb) {
if (rb->head >= rb->tail) {
return rb->head - rb->tail;
} else {
return rb->size - rb->tail + rb->head;
}
}
void RB_Clear(RingBuffer_t *rb) {
rb->head = 0;
rb->tail = 0;
}
AI回复 — 协议解析核心实现:
// protocol.c
#include "protocol.h"
uint16_t Protocol_CalcCRC16(const uint8_t *data, uint16_t len) {
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
bool Protocol_Parse(RingBuffer_t *rb, Frame_t *out_frame) {
uint8_t byte;
static uint8_t state = 0;
static uint8_t expected_len = 0;
static uint8_t data_idx = 0;
while (RB_Available(rb) > 0) {
RB_Get(rb, &byte);
switch (state) {
case 0:
if (byte == FRAME_HEADER_0) state = 1;
break;
case 1:
if (byte == FRAME_HEADER_1) state = 2;
else state = 0;
break;
case 2:
expected_len = byte;
if (expected_len > 128) { state = 0; break; }
data_idx = 0;
state = 3;
break;
case 3:
out_frame->data[data_idx++] = byte;
if (data_idx >= expected_len) state = 4;
break;
case 4:
out_frame->crc = (uint16_t)byte << 8;
state = 5;
break;
case 5:
out_frame->crc |= byte;
out_frame->data_len = expected_len;
state = 0;
return Protocol_Validate(out_frame);
}
}
return false;
}
bool Protocol_Validate(const Frame_t *frame) {
uint16_t calc_crc = Protocol_CalcCRC16(frame->data, frame->data_len);
return (calc_crc == frame->crc);
}
项目4:温湿度采集系统(多模块协同)
一句话:每2秒采集一次DHT11温湿度,通过串口打印,超温报警
你发给AI的Prompt:
项目:温湿度监测+报警系统
芯片:STM32F103C8T6
硬件:
- PB12=DHT11数据引脚(单总线)
- PA9=USART1_TX(连接电脑,波特率9600)
- PA5=报警LED(低电平点亮)
要求:
- 分层架构:sensor层(驱动)/data层(处理)/app层(业务)
- 温湿度数据结构:typedef struct { uint8_t temp; uint8_t hum; } DHT11_Data_t;
- 串口打印格式:"Temp: XX°C, Hum: XX%"
- 报警:温度>35°C时LED快闪(200ms间隔)
请给出完整模块架构、每个模块的头文件,以及sensor和data层的核心实现。
AI回复 — 模块架构:
模块划分:
- sensor_dht11(硬件驱动层):DHT11单总线读取原始数据
- sensor_manager(驱动抽象层):定时采集、过滤、屏蔽硬件差异
- uart_print(硬件驱动层):串口打印格式化字符串
- alarm(应用层):温度判断、报警逻辑
- app(应用层):主循环,协调各模块
AI回复 — 头文件:
// sensor_dht11.h
#ifndef SENSOR_DHT11_H
#define SENSOR_DHT11_H
#include <stdint.h>
typedef struct {
uint8_t temp;
uint8_t hum;
} DHT11_Data_t;
bool DHT11_Read(DHT11_Data_t *data);
#endif
// sensor_manager.h
#ifndef SENSOR_MANAGER_H
#define SENSOR_MANAGER_H
#include "sensor_dht11.h"
void SensorMgr_Init(void);
void SensorMgr_Task(void); // 每2秒调用一次
DHT11_Data_t SensorMgr_GetData(void);
#endif
// alarm.h
#ifndef ALARM_H
#define ALARM_H
#include <stdbool.h>
#include "sensor_dht11.h"
void Alarm_Init(void);
void Alarm_SetThreshold(uint8_t temp_threshold);
void Alarm_Update(const DHT11_Data_t *data); // 每秒调用
#endif
AI回复 — sensor_manager核心实现:
// sensor_manager.c
#include "sensor_manager.h"
#include "uart_print.h"
#include "alarm.h"
static DHT11_Data_t current_data = {0, 0};
void SensorMgr_Init(void) {
// 硬件初始化已在main中完成
}
void SensorMgr_Task(void) {
DHT11_Data_t raw;
if (DHT11_Read(&raw)) {
current_data.temp = raw.temp;
current_data.hum = raw.hum;
char buf[32];
snprintf(buf, sizeof(buf), "Temp: %d°C, Hum: %d%%\r\n",
raw.temp, raw.hum);
UART_Print(buf);
Alarm_Update(¤t_data);
}
}
DHT11_Data_t SensorMgr_GetData(void) {
return current_data;
}
项目5:OLED菜单系统(综合应用)
一句话:用OLED屏幕显示可交互菜单,用按键控制上下选择和确认
你发给AI的Prompt:
项目:嵌入式菜单系统
芯片:STM32F103C8T6
硬件:
- PB6=I2C1_SCL,PB7=I2C1_SDA(OLED 0.96寸,SSD1306驱动,I2C地址0x3C)
- PC13=按键K1(上下选择),PA5=按键K2(确认)
功能:
- 屏幕128×64像素,字符模式8行×16列
- 菜单:显示温度/显示时间/设置阈值/关于
要求:
- 菜单模块与应用逻辑分离
- oled.c/h:支持清屏、显示字符、显示数字
- menu.c/h:结构体数组定义菜单项,最多8个菜单项
- key.c/h:短按消抖300ms,长按1s检测
请设计完整的分层架构,给出头文件和menu模块的核心实现。
AI回复 — 头文件:
// oled.h
#ifndef OLED_H
#define OLED_H
#include <stdint.h>
#include <stdbool.h>
#define OLED_ADDR 0x3C
void OLED_Init(void);
void OLED_Clear(void);
void OLED_SetPos(uint8_t x, uint8_t y); // x=列(0-127), y=页(0-7)
void OLED_Puts(uint8_t x, uint8_t y, const char *str);
void OLED_PrintNum(uint8_t x, uint8_t y, int32_t num);
#endif
// menu.h
#ifndef MENU_H
#define MENU_H
#include <stdint.h>
#include <stdbool.h>
typedef void (*MenuAction_t)(void);
typedef struct {
const char *title;
MenuAction_t action;
} MenuItem_t;
typedef struct {
const char *name;
MenuItem_t *items;
uint8_t item_count;
int8_t selected; // 当前选中项,-1表示无
} Menu_t;
void Menu_Init(Menu_t *menu);
void Menu_Up(Menu_t *menu);
void Menu_Down(Menu_t *menu);
void Menu_Confirm(Menu_t *menu);
void Menu_Render(const Menu_t *menu);
#endif
AI回复 — menu核心实现:
// menu.c
#include "menu.h"
#include "oled.h"
void Menu_Init(Menu_t *menu) {
menu->selected = 0;
}
void Menu_Up(Menu_t *menu) {
if (menu->selected > 0) {
menu->selected--;
}
}
void Menu_Down(Menu_t *menu) {
if (menu->selected < (int8_t)(menu->item_count - 1)) {
menu->selected++;
}
}
void Menu_Confirm(Menu_t *menu) {
if (menu->selected >= 0 && menu->selected < (int8_t)menu->item_count) {
MenuItem_t *item = &menu->items[menu->selected];
if (item->action != 0) {
item->action();
}
}
}
void Menu_Render(const Menu_t *menu) {
OLED_Clear();
for (uint8_t i = 0; i < menu->item_count; i++) {
if (i == (uint8_t)menu->selected) {
OLED_Puts(0, i, "> ");
} else {
OLED_Puts(0, i, " ");
}
OLED_Puts(16, i, menu->items[i].title);
}
}
第八章:调试与排错
本章展示如何用AI辅助调试的完整对话过程,而不只是展示调试的技术知识。
8.1 用AI做代码Review
请审查以下嵌入式代码,重点关注:
1. 潜在Bug(如越界,空指针、竞态条件)
2. 编码规范问题
3. 可优化的地方(如性能、安全性、可维护性)
每条问题请给出严重程度(高/中/低)和修复建议。
[粘贴代码]
8.2 常见错误分类与解决
Flash相关错误
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
| “No Algorithm found for: 0x08000000” | Flash算法未配置 | KEIL → Debug → Settings → Flash Download → Add “STM32F10x Med-density Flash” |
| “Error: L6406E: no space” | 代码超出Flash容量 | 优化代码/换更大芯片/启用代码压缩 |
串口相关错误
| 现象 | 原因 | 排查方法 |
|---|---|---|
| 输出乱码 | 波特率不匹配 | 确认代码和串口助手都用9600或115200 |
| 乱码+随机字符 | 晶振配置错误 | 确认HSE_VALUE=8MHz或外部晶振值正确配置 |
| 丢字符 | 缓冲区溢出 | 检查DMA接收是否及时处理数据 |
DMA相关错误
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 数据错位 | DMA和CPU同时操作缓冲区 | 加临界区保护(__disable_irq/__enable_irq) |
| 中断不触发 | 中断优先级配置错误 | 检查NVIC配置,确保DMA中断优先级高于其他实时中断 |
8.3 AI辅助调试 — 完整对话示例
场景:DMA接收数据错位,展示如何用AI一步步定位问题和修复。
你(第一句话):
【项目阶段】:调试中
【芯片/开发板】:STM32F103C8T6 + ST-Link
【开发环境】:Keil MDK 5.38
【问题描述】
串口DMA接收数据错位
【出现的现象】
- 预期:发送 "AA 55 02 01 02",收到的数据应完全一致
- 实际:每次收到 "AA 55 02 00 02",第4字节(0x01)变成了0x00
【错误信息】
编译无报错,只有运行结果错误
【相关代码】(usart_dma.c 核心部分)
void DMA1_Channel5_IRQHandler(void) {
if (DMA_GetITStatus(DMA1_IT_HT5)) {
DMA_ClearITPendingBit(DMA1_IT_HT5);
data_half_ready = 1;
}
if (DMA_GetITStatus(DMA1_IT_TC5)) {
DMA_ClearITPendingBit(DMA1_IT_TC5);
data_full_ready = 1;
}
}
// 主循环
while (1) {
if (data_half_ready) {
data_half_ready = 0;
ProcessData(0, 128);
}
if (data_full_ready) {
data_full_ready = 0;
ProcessData(128, 256);
}
}
【已尝试的排查】
1. 换了3根USB线,结果一样 → 排除硬件连接问题
2. 用示波器量TX引脚,发送数据正确 → 排除发送端问题
3. 不用DMA,直接阻塞接收,数据正确 → 确定是DMA相关问题
请帮我:
1. 分析根本原因
2. 给出修复方案
3. 如何验证修复
AI回复(第一轮分析):
根本原因分析:竞态条件(Race Condition)
你的代码存在一个经典的DMA双缓冲竞态问题:DMA半传输中断(HT)触发后、数据被主循环处理前,DMA已经开始了第二轮接收,覆盖了缓冲区的前半部分。
修复方案:采用双缓冲 + 临界区保护
方案A(推荐):只用TC中断,不用HT中断。串口数据量小的情况下,半满中断反而增加复杂度。
方案B:如果需要及时处理,用双缓冲。两个128字节缓冲区交替,DMA写buf0时主循环读buf1,反之亦然。
你(追问方案B):
方案B的详细实现,给我.h和.c文件,包括双缓冲和临界区保护的具体代码。
AI回复(修复代码):
// usart_dma.h
#ifndef USART_DMA_H
#define USART_DMA_H
#include <stdint.h>
#include <stdbool.h>
#define DMA_BUF_SIZE 128
typedef struct {
uint8_t buf0[DMA_BUF_SIZE];
uint8_t buf1[DMA_BUF_SIZE];
volatile uint8_t active_buf; // 0=buf0被DMA写, 1=buf1被DMA写
volatile bool data_ready;
uint8_t ready_buf;
} DMA_Context_t;
extern DMA_Context_t dma_ctx;
void USART_DMA_Init(void);
uint8_t* DMA_GetReadyBuf(void);
void DMA_Swap(void);
#endif
建议:对于普通串口收包,只用TC中断是更实用的方案:
- 缓冲区设256字节或更大
- 只用传输完成中断(TC),不用半传输中断(HT)
- 主循环检测data_ready标志,一次性处理完整帧
这样完全避免竞态条件,实现最简单。
你(采纳简化方案):
好,我用只用TC中断的简化方案。帮我写一个更简单的实现。
AI回复(简化方案):
// usart_dma.h
#ifndef USART_DMA_H
#define USART_DMA_H
#include <stdint.h>
#include <stdbool.h>
#define DMA_BUF_SIZE 256
extern volatile bool dma_transfer_complete;
void USART_DMA_Init(void);
bool DMA_IsDataReady(void);
void DMA_ClearFlag(void);
#endif
// usart_dma.c
#include "usart_dma.h"
volatile bool dma_transfer_complete = false;
uint8_t dma_rx_buf[DMA_BUF_SIZE];
void USART_DMA_Init(void) {
dma_transfer_complete = false;
__HAL_DMA_DISABLE(&hdma_usart1_rx);
hdma_usart1_rx.Init.Mode = DMA_NORMAL; // 非循环模式,收满256字节停下来
__HAL_DMA_ENABLE(&hdma_usart1_rx);
HAL_UART_Receive_DMA(&huart1, dma_rx_buf, DMA_BUF_SIZE);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
dma_transfer_complete = true;
}
bool DMA_IsDataReady(void) {
return dma_transfer_complete;
}
void DMA_ClearFlag(void) {
dma_transfer_complete = false;
HAL_UART_Receive_DMA(&huart1, dma_rx_buf, DMA_BUF_SIZE);
}
你(验证结果):
发5帧数据测试,全部正确。问题解决了。
8.4 本案例的教训
- 调试要带上下文:发了完整的环境+现象+代码+排查过程,AI才能准确定位
- 追问要具体:问"方案B的详细实现"比问"怎么修"更容易得到可用的代码
- 采纳简化方案:不是每个场景都需要复杂方案,单TC中断就解决了问题
- 验证要量化:发5帧全部正确 → 比"感觉好了"更有说服力
第九章:附录:工具链与资源
| 工具 | 用途 | 获取 |
|---|---|---|
| Keil MDK | STM32开发 | www.keil.com(30天试用) |
| STM32CubeMX | 代码生成/引脚配置 | www.st.com/stm32cubemx(免费) |
| PlatformIO | ESP32/多平台开发 | VSCode插件市场(免费) |
| SSCOM | 串口调试助手 | 百度搜索下载 |
| STM32CubeProgrammer | 独立烧录工具 | www.st.com(免费) |
| Git | 版本管理 | git-scm.com(免费) |
本手册由资深嵌入式工程师编写,结合上下文构建工程最佳实践。强调架构优先、模块化开发、可持续的项目上下文管理。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)