Files
STM32F4-Base/docs/文档规范.md
T
wandering1 504db26698
CI / L0 静态门禁 (pre-commit) (push) Failing after 2m1s
CI / L0.5 主机侧单测 (MSVC) (push) Successful in 21s
docs(内存): 阶段子项改层级编号 4.阶段.项,消除与顶层章节同号撞面
根因:`RAM使用分析与优化方案.md` 顶层有 §1 背景 / §2 现状测量(2.1~2.6),而 §4
「方案」下"阶段 N"的子项也用裸号 0.x / 1.x / 2.x——阶段 2 的 2.1/2.2/2.3 与顶层
2.x **完全同号**,阶段 1 的 1.x 又长得像"第 1 章第 x 节"。`doc-links` 对本文自
引用按设计直接放过,于是门禁和人一起看错:AGENTS.md 把"栈余量台账"写成 §1.4,
而 §1.4 其实是阶段 1 的 lftpd 静态化项(上一笔提交的信息里我把它误判成断链,
已 amend 更正)。

改法:阶段子项一律改层级编号,阶段号不变、只加 `4.` 前缀 —— 0.x→4.0.x、
1.x→4.1.x、2.x→4.2.x。共改 14 个标题;文内自引用与实施序列表(S4~S9 那套)、
暂缓清单、§5 预期效果列里的裸号一并补成 §4.x.y。

外部引用同步 15 处:AGENTS.md、lftpd/README ×2、菜单页面设计、RAM预算台账 ×2、
项目架构总览 ×3、FTP_Test_Guide ×3、boot_log_data/README、部署环境要求、审查
报告、ram_baseline.py ×3。最有说服力的两处歧义:审查报告与 RAM预算台账都写
"缩堆见 …§2.2"——既可读成顶层 §2.2(静态大项排行)也可读成阶段 2 第 2 项,
现均为 §4.2.2(阶段 2 第 2 项"缩堆")。

另修 4 处把 §1.4 当"栈台账"的错指:采样有效性校验设计说明 :441/:593/:650 改
指 §2.3(任务栈配置与实测峰值)与 §4.0.6(静态最坏链与判定);id_aliases.json
 的 note 改指 §4.0.6 并标注"原 §1.4,2026-10-22 层级编号"。

冻结与示例:其它文档**变更记录行**里的旧编号按项目纪律不改写(项目架构总览
:367、RAM预算台账 :98),改在被引文档 §4 开头留一张换算表 + 约定说明;把旧号
当语法示例写的 7 处一并换掉(文档规范 ×3、check_doc_links ×3、rename_ref ×1),
否则示例本身指向已改名的锚点。主文档版本行 bump 到 v1.3 / 2026-10-22。

门禁:check_doc_links.py --all --sections-strict → 146 个文档 0 问题(章节引用
2513 处、机器可判定 1491);check_ids.py --strict 增量拦截通过;mem-numbers、
ram_baseline --gate(SRAM1 57.4% / CCM 87.9%)、task-table 全绿;本轮未改任何
.c/.h。16 个文件字节卫生自查 BOM/CR/U+FFFD/行尾空格全清。已提交本地,未推送。
2026-10-22 15:38:14 +08:00

22 KiB
Raw Blame History

文档规范

适用对象:本仓库 docs/ 下的全部 Markdown 文档,以及根目录 README.md、AGENTS.md。 强制程度:§1~§6 为强制(pre-commit 机械校验,违反即拦截提交);§7~§8 为约定(评审时人工把关)。 相关文档:README.md(文档索引)、代码规范/pre-commit使用说明.md(钩子清单)、架构设计/图形绘制流程.md(配图流程)。


1. 目录与命名

1.1 目录

规则 说明
目录名一律不含空格 空格迫使脚本/链接/CI 处处加引号,且 URL 编码形态(UI%20设计)极易被漏改(D-6,2026-09-25 起强制;全库已无含空格目录)
按领域归入子目录 顶层不放散落 .md;新领域先建无空格子目录,再在 README.md 登记
现有目录 架构设计/ 子系统设计/ 算法/ 网络/ 测试说明/ 代码规范/ 内存管理/ 问题记录/ 环境与部署/ UI设计/ 硬件平台/ 芯片手册/ images/

1.1b 代码目录的 README.md

每个自建代码目录(非第三方库、非 HAL/CMSIS 生成)必须有一份 README.md, 说明该目录的职责、文件清单与关键约束,让新接手者不必读遍代码即可建立认知。

规则 说明
覆盖范围 App/(含 log/ task/ util/)、Drivers/BSP/(含各芯片子目录)、Src/、Inc/、scripts/、tools/(含各子工具)、test/、MDK-ARM/、simulator/(含 sim/ tests/)、docs/
排除 第三方库与生成代码:Drivers/CMSIS/、Drivers/STM32F4xx_HAL_Driver/、Middlewares/Third_Party/、Lib/(FatFs/dhara/lvgl)、simulator/SDL2/、构建产物目录
必备内容 ① 一句话定位 + 是否自研 ② 文件/子目录清单表(每个一句话作用)③ 关键约束或陷阱(改前须知)④ 指向 docs/ 权威文档的链接
命名 一律 README.md(大写),放该目录根部
链接 目录间互链用同级相对路径(如 ../NET/README.md);指向 docs/ 用仓库根相对路径
门禁 README 内所有链接受 doc-links 钩子检查;新增/改名目录或文件后须同步更新对应 README

目的:目录即文档的入口。根 README.md 给全局地图,各目录 README.md 给局部细节, 二者构成"地图 + 地标"的导航体系(避免所有信息堆在单一 README)。

1.2 文件名

规则 说明
不含空格 同目录规则
主题词 + 类型后缀 xxx设计说明.md / xxx_Test_Guide.md / xxx_Trap_Records.md / xxx使用说明.md
中英混排 中文主题词直接写(如 系统墙钟时间维护说明.md);含专有名词/缩写可用下划线连接(如 BSD_Socket_API_使用指南.md)
只用 ASCII 与中文 不含 ()【】〔〕 等全角括号、不含 &、?、*、<、>、`
版本号写法 版本后缀用下划线连接,不用全角括号:xxx_V1.0.md(✅)/ xxx(V1.0).md(❌)

例外:docs/芯片手册/ 为外部芯片数据手册转录,文件名沿用上游原名(含空格等),不受本节约束。


2. 编码与换行

项 要求
编码 UTF-8;.md 文档不带 BOM
换行 与文件现状保持一致;新建文档用 LF
行尾空白 禁止(pre-commit trailing-whitespace 自动清理)
文件尾 必须以单个换行结束(end-of-file-fixer)
乱码 禁止出现 U+FFFD(替换字符,字节序列 EF BF BD)

⚠️ 本仓库 BOM 铁律(见 AGENTS.md「铁律:源码编辑与编码」):C 源码中 Drivers/BSP/UI/*、App/task/ui_task.c、App/cfg/cfg_file.c、Lib/lv_conf.h 等带 BOM,被工具改写后 BOM 会静默丢失 → ARMCC 改按 CP936 解码 → 中文字面量编译失败。 文档(.md)不受此限,一律用无 BOM UTF-8。

行尾策略(2026-10-09 起写死进 .gitattributes,本节是权威口径):* text=auto eol=lf(仓库 blob 与工作区统一 LF)+ *.bat/*.cmd 强制 CRLF(cmd.exe 对 LF-only 的 goto/标签不可靠)+ PNG/TTF/DLL/LIB 等显式 binary + *.jpg 走 LFS 且 -text(该条必须排在 * 之后才能覆盖)。不要再依赖 core.autocrlf,也不要把工作区文件"改回 CRLF"——CubeMX 重生成写的就是 LF;此前每次重生成后 40+ 个第三方文件在 git status 里显示"已修改",根因就是行尾由每台机器各自的 autocrlf 决定。用脚本批量改写文档时仍须按原样回写换行符,否则会产生整文件行尾漂移的巨型 diff。


3. 链接规范

3.1 文档间互链:仓库根相对路径

文档之间引用一律使用仓库根相对路径(形如 `docs/xxx/yyy.md` 或 [文本](docs/xxx/yyy.md)),不使用 ../ 相对链接。

理由:根相对路径对文档所在层级不敏感,迁移子目录时零改动;../ 形态在目录调整时会成片失效。

3.2 图片链接:文档所在目录相对

图片是例外——用相对当前文档的路径:

文档位置 图片写法
docs/xxx.md(docs 根下) ![标题](images/RemoDispBus帧格式.svg)
docs/子目录/xxx.md ![标题](../images/RemoDispBus帧格式.svg)

⚠️ 踩坑记录:2026-09-25 目录重组时,5 篇文档从 docs/ 迁入子目录,images/xxx.svg 未同步改为 ../images/xxx.svg → 7 处真实断链(由 D-7 门禁 check_doc_links.py 揪出)。迁移含图文档时务必检查此项。

3.3 引用尚未创建 / 已删除的文件

场景 写法 示例
计划中、尚未创建 反引号内加 [计划] 前缀 `[计划] scripts/gen_task_table.py`
已删除 写明「已删除」等状态词 删除 \tools/ui_mock/render_mockup.py`(已删除)`
引用报错样例 / 日志片段(含路径) 该行加 [示例] 标记 | **典型报错** | \[示例] docs/a.md:1 [markdown 链接] x.svg` |`

为什么:check_doc_links.py 会豁免这些标记;否则门禁会把"诚实标注了未创建"的引用误判为断链。

3.4 链接写法示例须放行内代码

讲「怎么写链接」的语法示例必须包在反引号里(行内代码 span):

  • ✅ 正确:写 `![标题](相对路径.svg)` 的形式
  • ❌ 错误:写 ![标题](相对路径.svg) 的形式(会被门禁当作真实链接去校验存在性)

check_doc_links.py 扫描前会剥离行内代码 span,故写法示例不会误报;但反引号内的仓库根相对路径(docs/xxx.md)仍会被校验——那是本工程的引用约定形态。

3.5 引用章节:紧邻写法 + 别名表,让门禁能校验

引用某文档的某小节,写成 `docs/xxx/yyy.md` §N.M(文档名与 § 紧邻,中间只允许空白/反引号/括号)。

写法 门禁
`docs/子系统设计/看门狗设计说明.md` §10.4 ✅ 硬门禁(失效拦提交)
`看门狗设计说明.md` §10.4(裸文件名,全库唯一) ✅ 硬门禁
看门狗设计说明 §10.4(省略 .md,文件名全库唯一) ✅ 硬门禁
链路延迟 §8.3(文档名前缀,全库唯一) ✅ 硬门禁
「内存方案 §2.1」——已登记别名 ✅ 硬门禁
「报告 §4.2」——昵称歧义、未登记 [示例] ⚠️ 判不了 → 进 --section-todo 清单
本文档内「见 §3.1」 ✅ 硬门禁(本文档必须有该编号;确实指别篇却省了文档名的,机器判不了 → 跳过并进待办)

两档严重性:目标明确(路径 / 裸名 / 前缀 / 别名 / 同句 §A / §B 链)→ 错误;按自引用解析 → 默认告警,但 pre-commit 钩子带 --sections-strict,两档都拦提交(2026-10-11 起:全库告警已清零,清零之后就该守住)。手工跑想只看告警:去掉该 flag。

别名表 scripts/section_aliases.json:昵称 → 文档路径。登记一次,正文一个字不用改,引用就纳入硬门禁。只登记无歧义的昵称(「报告」命中多篇 → 不登记,逼着写清楚)。

覆盖率必须看着:每次跑 doc-links 都打印一行

章节引用:共 1576 处,机器可判定 843 处(53%);跳过 → 昵称式 68 / 变更记录 419 / 外部手册 187 / …

比例只许涨不许跌。清 backlog:

& '.venv\Scripts\python.exe' scripts/check_doc_links.py --all --section-todo

它列出全部机器判不了的 §(能唯一猜到的还给「建议改写:docs/… §N」),改一处涨一处;猜不到的就是必须写清楚文档名的那些。

口径怎么调出来的:全库 1578 处 §。宽松版报警 173 处(几乎全是昵称误判)→ 加文档名解析 111 → 加紧邻 + § 链 + 前缀匹配 + 别名表 + 「父级编号不存在就不是自引用」判别 → 可判定比例 7% → 58%(914 处),--section-todo 待办 83 → 0;清完 backlog 后两档都拦提交。变更记录小节(422 处)与外部手册编号(218 处)刻意不校验:前者是「当时那个编号」的历史指称,后者归厂商文档。

把待办清零靠的三类动作(按性价比排序;引号内是改前写法 [示例]):① 手册引用写全名——「手册 §9.2.1」一律改成 docs/芯片手册/CH395F.md §9.2.1(CH395F / GD5F / SD2506 / RM0090 摘录里都有编号标题,改完立刻纳入校验);② 昵称写全或登记别名——「架构 §5.5」→ UI_设计架构.md §5.5、「报告 §6.5」→ 全路径、「V0.0 报告 §4.1」→ V0.0 §4.1(别名表登记 V0.0);③ 路径补 docs/ 前缀——子系统设计/xxx.md 这种「从 docs/ 起算」的写法机器解析不到。

两条写作铁律(机器只看同一行):§ 必须与文档名紧邻(换行/跨句就判不了);「见/按/该 + 昵称」这类含糊指代,宁可多打 30 个字符写全路径。

首轮清账揪出的真断链(41 处,已修)——[示例] 下列编号是修复前的错值,故意原样保留作反例:链路延迟测试说明.md 10 处引 §8.1,而该文 §8 下只有 8.2/8.3/8.4(§8.1 早被删);GD5F2GQ5UE_Test_Guide.md 引 §4.8(BBT 实为 §3);自动化测试流程.md 引 ST-LINK_CLI_复位使用说明.md §10(该文只到 §7);模拟器差距清单.md 引 §219(实为 §4.1);实机测试实施指南.md 引 §4.4 白名单(白名单在 看门狗设计说明.md §4.3);V0.1_网表详细分析报告.md 表格里 12 处裸 §3.4/§5.3 其实指 V0.0 报告(补 V0.0 前缀 + 登记别名)。教训:编号会随重构漂移,只有机器校验能发现。

3.6 改名 / 挪目录:用 scripts/rename_ref.py,不要手工扫

固定动作(改名、挪目录、合并文档一律走这条,别靠 git grep 手改):

# 1) 预览:看它打算改哪些文件的哪些行(默认 dry-run,不落盘)
& '.venv\Scripts\python.exe' scripts/rename_ref.py docs/旧/名.md docs/新/名.md

# 2) 确认无误后落盘:git mv + 改写全库引用 + 自动跑 check_doc_links --all 复核
& '.venv\Scripts\python.exe' scripts/rename_ref.py docs/旧/名.md docs/新/名.md --apply

# 整目录搬迁(末尾带 / = 挪进该目录并保留文件名;旧/新都是目录 = 前缀改写)
& '.venv\Scripts\python.exe' scripts/rename_ref.py docs/旧目录/ docs/新目录/ --apply

它覆盖的引用形态:反引号根相对引用、Markdown 链接(根相对与文档相对都按原风格改写)、图片链接的 ../ 层级换算(§3.2 那条实证坑由它兜住)、#include "x.h" 裸文件名、MDK-ARM/*.uvprojx 的 <FilePath>、simulator/CMakeLists.txt 的 ${PROJECT_ROOT}/… 与 ${VAR}/…、以及 skill / 脚本 / 配置里的路径串。

它管不到的三件事(脚本会在结尾提示):

  1. 昵称式引用(「内存方案 §2.1」)与正文里的裸文件名散文 → 落盘后 git grep -n '<旧文件名>' 扫一遍;
  2. 生成物(docs/images/引用关系图.*)不改写 → 重跑 & 'tools\refgraph\gen_refgraph.bat';
  3. 改的是 .c/.h 且新增了目录 → 还要把目录加进 uvprojx 的 <IncludePath>(→ new-source-file skill),随后 @build 验证。

4. 门禁:文档-代码一致性(D-7)

三个层次,全部由 pre-commit 机械校验:

层次 钩子 检查内容 触发条件
存在性 doc-links docs/**/*.md 里的链接、反引号路径是否指向真实文件;§N.M 章节引用是否指向真实小节(§3.5) 改动任意 .md
数值 task-table README.md、AGENTS.md 的 FreeRTOS 任务表是否与 Src/freertos.c 一致 改动 README.md / AGENTS.md / Src/freertos.c
体积 mem-numbers 内存/Flash 数字是否与最新 .map 一致(受管区 + 陈旧散句数字) 改动内存文档 / App·Src·Inc 源码 / 脚本
体积 check-added-large-files 阻止 >1MB 大文件入库 任意文件

4.1 本地手动运行

# 全库文档链接存在性
& '.venv\Scripts\python.exe' scripts/check_doc_links.py --all

# 改名 / 挪目录(先预览再落盘,见 §3.6)
& '.venv\Scripts\python.exe' scripts/rename_ref.py docs/旧/名.md docs/新/名.md
& '.venv\Scripts\python.exe' scripts/rename_ref.py docs/旧/名.md docs/新/名.md --apply

# 任务表一致性(只检查)
& '.venv\Scripts\python.exe' scripts/gen_task_table.py --check

# 任务表不一致时:自动重写受管表格区
& '.venv\Scripts\python.exe' scripts/gen_task_table.py --write

# 内存数字(受管快照 + 陈旧散句数字扫描)
& '.venv\Scripts\python.exe' scripts/check_mem_numbers.py --scan
& '.venv\Scripts\python.exe' scripts/check_mem_numbers.py --write   # 同步受管快照

# 只看期望内容,不写文件
& '.venv\Scripts\python.exe' scripts/gen_task_table.py --diff
& '.venv\Scripts\python.exe' scripts/check_mem_numbers.py --diff

# 生成完整内存台账(人工分析用)
& '.venv\Scripts\python.exe' scripts/ram_report.py -o docs/内存管理/data/ram_report_<日期>.md

4.2 受管区域(锚点)

文档里凡随代码变化的具体数值,都必须放进受管区,由脚本生成、禁止手改:

任务表(README.md / AGENTS.md)——表格外的说明文字原样保留:

<!-- TASK_TABLE:BEGIN generated by scripts/gen_task_table.py -->
| Task | 栈 (words) | 优先级 |
|---|---|---|
| ... |
<!-- TASK_TABLE:END -->

内存/Flash 快照(docs/内存管理/RAM使用分析与优化方案.md §2.1):

<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
| 区域 | 用量 | 容量 | 占比 |
|---|---|---|---|
| ... |
<!-- MEM_SNAPSHOT:END -->

改栈流程:先改 Src/freertos.c 的 osThreadAttr_t → 跑 gen_task_table.py --write → 提交。 改内存流程:先改代码 + @build 出 .map → 跑 check_mem_numbers.py --write → 提交。 钩子报不一致时不要手改受管区——跑 --write 让脚本重算。

陈旧数字扫描(mem-numbers --scan)

除受管区外,脚本还会扫正文里的陈旧区域数字:从 docs/内存管理/data/ram_report_*.md 历史快照提取「曾经是区域用量、现已变化」的值(如旧 RW_IRAM1 82,048), 若正文仍以陈述现状的口吻引用即告警。

允许保留旧数字的情况(脚本按行内关键词自动放行):该行含 「历史 / 旧 / 曾 / 此前 / 快照 / data/ / 修复前 / 优化前 / 迁入前 / 实施时 / 当时」等标记。 讲任务栈 / 内存池 / 峰值水位等非区域用量的行也自动跳过(那些数字语义不同)。

设计取舍:朴素地"扫所有 4~7 位数字"实测产生 45 处误报(栈配置 4096、堆配置 49,152、 峰值 1184 全是合法数字),门禁会被无视。改为"只报历史快照里出现过、当前已变的区域值"后 误报 0、真问题全中。宁可窄而准,不可宽而吵。

4.3 豁免机制

check_doc_links.py 的豁免(不算失效,不报错):

  • `[计划] ` 前缀标记
  • 该行含 [示例] 标记(引用报错样例 / 日志片段等含路径的引文)
  • 引用附近 ±40 字符内出现「未创建 / 尚未创建 / 已删除 / 待排期」等状态词
  • 含通配符 *、尖括号 <模块> 等占位符
  • 模板变量样式(NN_ / XX_ / 界面名 / 模块名 / ${...} / %s 等)
  • 行内代码 span 里的链接写法
  • 外部 URL(http/https/mailto)与纯锚点(#...)
  • docs/芯片手册/ 等外部资料目录

章节引用(D 类)另有的跳过:昵称式引用(「内存方案 §2.1」)、非紧邻写法、变更记录小节内的历史指称、外部手册编号(RM0090 等)、目标文档本身不用数字编号标题。

⚠️ 状态词按就近(±40 字符)判定,不是整行——否则文件名本身含「不存在」「删除」等字样时会误豁免同行的其它真实引用(2026-09-25 实测修正)。


5. 文档结构

5.1 必备要素

要素 要求
一级标题 文档首行为 # 标题,标题即主题,不写"文档""说明"等冗余后缀
开篇引言 可选 > 引用块,写明适用对象 / 阅读前提 / 相关文档
章节编号 多章节文档用 ## 1. ### 1.1 编号,便于交叉引用(§3.2)
变更记录 设计类、流程类文档建议末尾保留「变更记录」表(日期 | 变更 | 说明)

5.2 新增文档检查清单

新增一篇文档时逐项确认:

  • 放在正确的子目录(目录名无空格)
  • 文件名符合命名约定(无空格 / 主题词+类型后缀)
  • UTF-8 无 BOM、LF 换行
  • 互链用仓库根相对路径;图片用当前目录相对路径
  • 尚未创建/已删除的引用加 [计划] 或状态词
  • 已在 README.md 对应章节登记一行(否则视为未完成)
  • 若涉及任务表/栈,跑 gen_task_table.py --check
  • pre-commit run --files <新文档> 通过

6. 与代码文档的边界

内容 归属
项目级铁律、构建/烧录/测试命令、平台约束 AGENTS.md(AI 协作上下文,精简优先)
开发者入门、架构速览、目录导航 README.md
详细设计、专项说明、测试指南 docs/ 下对应子目录
跨文档待办登记 docs/任务规划/README.md(统一登记簿)
评审报告与专项审计 docs/问题记录/

避免重复:同一事实只在一处详述,其它位置用链接指向。典型如 FreeRTOS 任务表——Src/freertos.c 是唯一事实源,README.md/AGENTS.md 的表由脚本生成,不在别处再抄一份。


7. 写作约定

项 约定
语气 陈述事实,不用"我们认为""大家注意"等口语
标点 中文正文用全角标点;代码、路径、命令、文件名用行内代码 `...`
术语 首次出现给全称 + 缩写(如"配置引擎(cfg_file)"),此后统一用同一术语
表格 对比、清单、参数优先用表格而非长段落
命令 用 powershell 代码块,含 & '.venv\Scripts\python.exe' 等本仓库统一入口
图 拓扑类用 Mermaid 代码块;技术配图走 SVG 生成器(见 架构设计/图形绘制流程.md)
状态标记 ✅ 已完成 / ⚠️ 注意事项 / ❌ 禁止 / 📌 要点(克制使用,不滥用 emoji)

8. 常见错误速查

现象 原因 修复
doc-links 报失效链接 文件被移动/删除后引用未同步 改引用路径,或加 [计划] / 状态词
doc-links 报语法示例行 链接示例没包反引号 把示例包进 `
task-table 报不一致 freertos.c 改栈后文档未同步 跑 gen_task_table.py --write
提交 diff 出现整文件行尾变化 脚本改写时强制了 LF/CRLF 改写须按原换行符回写(见 §2)
图片不显示 文档迁入子目录后未改 ../images/ 补 ../(见 §3.2)
引用"计划中"文件被拦 没加 [计划] 前缀 加前缀

变更记录

日期 变更 说明
2026-09-25 建立本文档 正式确立目录/命名/编码/链接/门禁/结构与写作约定;承接 D-6(去空格)与 D-7(一致性门禁),配套交付 scripts/check_doc_links.py、scripts/gen_task_table.py 两个门禁脚本与 doc-links/task-table 两个 pre-commit 钩子
2026-09-25 文件名风格统一(D-6 剩余) 代码规范/嵌入式C语言代码规范(V1.0).md → 嵌入式C语言代码规范_V1.0.md(全角括号 → 下划线),同步 5 个文档 + 1 个脚本共 9 处引用;§1.2 明确禁用全角括号与版本号写法;docs/芯片手册/ 作为外部资料例外
2026-09-26 体积档门禁落地(D-7 第三档) 交付 scripts/check_mem_numbers.py + pre-commit mem-numbers 钩子;§4.2 增「受管区域(锚点)」与「陈旧数字扫描」两节,记录误报取舍