自动化检测工装(Cortex-M4 + SPI 小屏 + FreeRTOS)
做这套工装的目的很具体:让产线上一块被检测的控制板,从“接线、上电、凭经验看现象”变成 “接上、按键、看屏幕上的结论”。工装通过 RS485 上的总线主站与被检测板交互, 按十一步固定工序依次完成初始化、连接、设地址、电压校准、温度校准、保存参数、重启、 采集校准、均衡检测,最后汇总结果;同时用本地 ADC 判断若干检测点的电压是否在允许区间内。 功能是实现了的,而且已经跟着两个硬件版本改过一轮;但我在这套东西上犯的一个错误, 比所有功能加起来都更值得写下来:最初有几项检测只有流程和显示,没有真正的阈值判定。
一、工装是做什么的:给产线一套“照着做就不会漏”的检测流程
先说清楚被检测的对象:一块需要出厂校准与功能确认的控制板。它出厂前必须完成几件事—— 设置通信地址、校准电压采集通道、校准温度采集通道、把参数存进自己的存储器、 重启确认参数还在、触发一次采集确认结果合理、确认均衡回路能按要求动作。 这些事分开看都不难,但要在产线上每天重复很多遍、且一遍都不能漏, 靠人记流程是不可靠的。
所以我理解的“检测工装”,本质上是三件事的组合:
- 一把会判定的尺子:它不只是加电、不只是显示读数,而是拿着允许区间去比对, 给出“这一项通过 / 不通过”;
- 一份不会漏的流程:顺序固定、每步都有明确出口, 操作者只需要按提示往下走;
- 一个说人话的界面:屏幕上直接写“哪一步、测到了什么、结论如何”, 而不是让操作者去对照表格判断。
硬件上,工装是一块带小屏的控制板:一颗 Cortex-M4 主控跑 FreeRTOS, 一路 RS485 作为总线主站与被检测板通信,本地 ADC 负责测几个检测点的电压, 一块 SPI 小屏负责显示,几个按键负责选择与参数设置。
一个值得先说的复用痕迹:它其实是个“主站”
这套工装的通信底层是从同一系列的板卡工程里复用过来的, 复用得很彻底——连函数名里都还留着“从站”的字样, 但工装在这条总线上实际扮演的是主机:由它发起请求,被检测板应答。 我一开始被这个名字带偏过,按“从站”的思路去读接收流程,读得很别扭。 这件事本身不严重,但它是一个很典型的复用代价: 代码的名字描述的是它的出身,不是它现在的角色。 我在《接手陌生代码的前 30 分钟》里 把这套“先确认谁主谁从、再读收发逻辑”的做法写成了检查项。
二、十一步检测工序
整套检测被拆成十一步,顺序是固定的。每一步都对应一个“完成事件”, 并且共用同一个错误出口——任何一步失败,都跳到统一的错误处理, 把失败原因显示出来并停止推进。下面这张表是我认为这套工装里最有价值的部分: 每一步到底在验证什么。
| # | 工序 | 这一步在做什么 | 在验证什么 |
|---|---|---|---|
| 1 | 检测程序初始化 | 装载当前选中的板型配置、清空上一次的结果、初始化本地采集与显示 | 工装自身可用:配置能装进来、屏幕能显示、本地采集通道能读到数 |
| 2 | 被检测板初始化 | 通过总线让被检测板进入可检测状态,并确认它应答 | 被检测板的通信口已经工作、当前地址可达 |
| 3 | 串口连接 | 连续多次通信成功才算“连上”,而不是发一条收到回复就算 | 链路质量:线缆、终端匹配、波特率与帧格式是否真的稳定 |
| 4 | 设置地址 | 下发目标通信地址,然后读回确认 | 参数写入通路可用,且新地址已经生效 |
| 5 | 电压校准 | 触发被检测板的电压校准动作,回读校准结果 | 被检测板的电压采集通道正常,校准系数写进去了 |
| 6 | 温度校准 | 触发被检测板的温度通道校准动作,回读结果 | 温度采集通道与温度校准系数正常(这一路和电压路是独立的两条链) |
| 7 | 保存参数 | 下发保存命令,把前面几步写进去的参数落到被检测板的存储器里 | 被检测板的参数保存通路可用 |
| 8 | 设备重启 | 下发重启命令,然后等待它重新上线并再次通信成功 | 参数在掉电重启后还能被正确加载——这一步才是“保存成功”的真正判据 |
| 9 | 采集校准 | 触发一次采集动作,读回采集结果 | 校准之后的采集链路给出的数值是合理的(重启后再测,测的是落盘后的参数) |
| 10 | 均衡检测 | 触发均衡动作,读回动作状态 | 均衡回路能按要求动作,且状态回读与动作一致 |
| 11 | 检测退出 | 汇总所有检测点的结果,给出总判定 | 前 10 步的判据是否全部满足;任何一项没通过,总判定就是不通过 |
为什么顺序不能随意改
这套顺序里有三处是“必须这么排”的,不是随便定的:
- 两次校准必须在保存之前。校准系数是要被保存的内容之一, 先保存再校准,等于保存了一份旧系数。
- 重启必须在保存之后。参数写进存储器 ≠ 参数能被正确加载。 只有真的重启一次、再读回来一致,才能证明“存进去的确实是我写的”。 这一步是我认为最容易被省掉、又最不该省掉的一步。
- 采集校准必须在重启之后。因为这一步要验证的是 “重启之后加载进来的参数能给出合理读数”,放在重启前测的是运行内存里的值, 证明不了落盘的那份。
另外,第 3 步“连续通信成功才算连上”也是踩出来的: 最早的写法是发一条能收到回复就认为连上了,结果在接触不良的工装线上, 偶尔第一条恰好成功、后面全程失败,屏幕却显示“连接正常”。 改成连续成功若干次之后,接触不良会被拦在第 3 步, 而不是散在后面每一步里表现成莫名其妙的校准失败。
三、把“检测逻辑”变成“配置数据”
如果每一步的判据都硬写在流程代码里,那么“换一个板型”就等于“改一遍流程代码”, 改十几次以后没人敢动这段代码。我的做法是把检测过程拆成 三种可以描述、可以填表的对象,流程代码只负责推进, 判据全部从配置里取。
| 描述结构 | 描述的是什么 | 包含哪些字段(讲契约,不给数值) | 用在什么场合 |
|---|---|---|---|
| 测试点 | “在本地测一个电压,判断它是否落在允许区间内” | 名称、ADC 通道、允许最小值、允许最大值、实测值、通过标志 | 由工装自己用 ADC 测的那些检测点 |
| 带响应的命令 | “发一条指令,等一个响应,然后测若干测试点” | 名称、要发送的指令、等待响应的时长、关联的测试点集合及数量 | 被测板被触发某个动作后,需要同时量几个点的电压 |
| 读寄存器校验 | “读一个寄存器,判断返回值是否落在允许区间内” | 名称、寄存器地址、返回值允许最小值、返回值允许最大值 | 纯通信类判据:校准结果、状态字、版本号等 |
这套拆法的好处很直接:新增一项检测不用改流程代码, 只需要在配置里加一条;屏幕上“哪一项没通过”也能直接显示配置里的名称, 不用再写一份对照表。判据和流程分离之后, 同一套流程可以服务多个板型(下一节展开)。
/* 通用骨架(按个人理解重写的示意,不含任何具体判据与数值,也不来自任何第三方工程) */
if (value >= expect_min && value <= expect_max) {
mark_pass(item); /* 落在允许区间:这一项通过 */
} else {
mark_fail(item); /* 落在区间外:记录实测值与区间,供屏幕显示 */
}
/* 我曾经漏掉的一种情况:集合本身是空的 —— 一个点都没测,
汇总时"没有任何一项失败",于是被当成了通过。这条必须在流程里显式拦住。 */
if (item_count == 0) {
mark_config_error(item); /* 空集合不是"通过",是"配置不完整" */
}最后那段注释里写的,是我在这套工装上栽的那个跟头,细节留在 第九节展开。这里先记住一句话: “没有任何一项失败”和“全部通过”是两件事, 中间差着一个“到底有没有测”。这句话我用了很久才真正理解。
四、四种板型靠配置区分,而不是靠四份代码
被检测板有四种:两个规格 × 两种结构形式(普通结构与加固结构)。 它们的检测工序完全一样,差别只在判据与检测点: 加固结构的板子多了几个要测的点,两个规格的电压允许区间不同, 连按键映射都可能因为结构件不同而变。
如果按“一种板型一份代码”来做,就是四份几乎一样的流程代码, 任何一处流程改动都要改四遍,而且一定会有一份忘了改。我的做法是 只有一份流程代码,四份配置数据: 一个分派函数根据当前选择(或根据被检测板回读的硬件版本)把对应的配置指针 交给同一个检测任务,检测任务本身完全不知道自己在测哪一种板子。
| 差异维度 | 四套配置是否相同 | 说明 |
|---|---|---|
| 检测工序与顺序 | 完全相同 | 十一步流程是所有板型共用的,这也是“一套代码”能成立的前提 |
| 检测点清单 | 不同 | 加固结构多了若干检测点,配置里的测试点数量与通道都不一样 |
| 电压允许区间 | 不同 | 两个规格的电压等级不同,同一个物理点的允许上下限也不同 |
| 通信指令与寄存器校验 | 大部分相同,少部分不同 | 共用的协议框架一致,个别指令与校验区间随规格变化 |
| 屏幕上的型号显示 | 不同 | 只是给人看的提示,不作为任何判据的来源 |
五、小屏显示:一块 SPI 屏的驱动栈与中文字库
工装的人机界面就是一块 SPI 小屏加几个按键。屏的驱动芯片是一颗常见的 TFT 驱动 IC,SPI 加 DMA 搬运数据,初始化序列(帧率、电源、Gamma 等)按数据手册给的顺序下发。 这部分没有太多可发挥的余地,我按三层来组织:
- 最底层是“写命令 / 写数据”:只负责把字节按 SPI 时序送出去, 并处理片选与数据/命令的选择线;
- 中间层是“画东西”:区域填充、画矩形、画点。 这一层需要设置显示窗口,然后把像素数据连续送出去;
- 上层是“显示内容”:显示字符串、显示整数、显示小数、显示中文、显示图片。 字符串与数字都要先把内容转成字模或字符点阵,再调用中间层。
横竖屏与中文字库
横竖屏切换我是用一个编译期开关做的:切换时把宽和高的定义互换, 并且改写“设置显示窗口”时的坐标换算,其它层完全不用改。 这么做的前提是所有绘制都只通过中间层—— 如果有哪一处直接算了行列地址,切屏时就会出错。这类“看起来只是改个宏”的改动, 实际上是在检验分层有没有真正落地。
中文字库和图片都是 const 数组,直接占 Flash。
这里有一个很现实的取舍:放整套字库太占空间,
只放用到的字又会在改文案时缺字。
我这一版是“只取用到的字”,所以每次改界面文案都要重新取一遍字模。
如果界面文案会频繁变,更好的做法是放一份常用字库,
把界面文案限制在那份字库覆盖的范围内。
显示与检测必须解耦
刷屏是慢操作。我最早把“上一步、测一步、刷一次屏”写在一起, 后来发现这个耦合有两个坏处:一是刷屏占用的时间被算进了检测节拍, 二是任何一次显示异常都会影响检测流程。 现在的做法是检测逻辑只更新“当前步骤 + 各项结果”这份状态, 显示任务按自己的节拍去读这份状态并刷新。 两边通过一份共享状态交互,谁都不等谁。
六、参数设置界面:编辑态 + 导航态 + 逐位编辑
工装上有一组参数需要在现场改:通信地址、连续检测的等待时间、硬件版本选择、 是否自动分配地址。改参数的界面如果用“一个参数一个页面”来堆, 很快就变成一团乱麻。我用的是“一个设置页 + 两种模式”的结构。
| 按键角色 | 导航态下的作用 | 编辑态下的作用 |
|---|---|---|
| 上一个 / 下一个 | 在当前页的若干项之间移动光标 | 移动当前正在编辑的那一位(数位) |
| 加 / 减 | 无作用(避免误改) | 把光标所在的那一位加一或减一,并按位进位/借位 |
| 确认 | 进入编辑态,把当前项的生效值读进编辑缓冲 | 把编辑缓冲写回参数,回到导航态 |
| 取消 / 返回 | 退出设置页 | 丢弃编辑缓冲,回到导航态(生效值不变) |
这里有两个我认为值得单独说的设计:
- 逐位编辑,而不是整体加减。一个数值参数按十进制位拆开, 光标在哪一位就只改哪一位。这样做的好处是:不需要长按连加(现场按键手感差、容易加过头), 而且每一位的取值范围天然只有 0~9, 上限检查只需要在最高位上做一次,比“整体加减再判断越界”简单得多。
- 编辑缓冲与生效值分离。进入某一项时先把当前生效值读进一个编辑缓冲, 所有按键都只改缓冲,确认时才写回。 好处是取消操作不需要“回滚”——因为生效值从头到尾就没被动过。 这也是我不在编辑过程中直接写存储器的原因:既避免误按键改掉参数, 也避免频繁擦写。
/* 通用状态机骨架(按个人理解重写,不含任何业务语义,也不来自任何第三方工程)
用函数指针表把"当前在第几项"分派到各自的处理函数,避免一长串 if/else */
typedef enum { ST_NAV, ST_EDIT, ST_SAVE, ST_EXIT } ui_state_t;
typedef ui_state_t (*state_handler_t)(void);
static const state_handler_t handlers[] = {
handle_nav, handle_edit, handle_save, handle_exit
};
ui_state_t st = ST_NAV;
while (running) {
if (key_event_pending()) {
st = handlers[st](); /* 每个处理函数返回"下一个状态" */
}
refresh_display(st); /* 显示与按键处理分开,互不阻塞 */
}这个结构还有一个意外的好处:每一项参数自己的显示方式与取值范围, 都写在它自己的处理函数里,新增一项参数就是新增一对 “进入时读初值 / 确认时写回”的函数,不用去动主流程。 这套写法后来我在别的地方也复用了几次,包括一个更简单的按键菜单。
七、两个硬件版本之间改了什么
这套工装跟着硬件改过一轮。我把两次提交之间的改动整理成一张表—— 这张表本身就是我认为产线工装最需要的东西: 硬件改版之后,固件这边到底哪些部分必须跟着改。
| 改动项 | 我理解的动因 | 连带影响到哪些模块 |
|---|---|---|
| 新增 LED 控制 | 让操作者在工位上不看屏幕也能知道“正在测 / 通过 / 不通过” | 显示层与检测流程之间多了一路状态输出 |
| 新增检测电压开关 | 新硬件把检测电压做成了可控开关,而不是常通 | 检测点的加电时机、检测顺序、以及“什么时候允许测量”的判断 |
| 修改按键映射逻辑 | 结构件与接口板变了,按键扫描的行列对应关系随之改变 | 按键扫描层;如果映射写死在扫描代码里,就必须一起改 |
| 修改点位检测逻辑 | 检测电压可控之后,检测点的数量与顺序都需要重新安排 | 配置里的测试点清单、以及每个点测量前的准备动作 |
| 修改均衡检测逻辑 | 均衡回路的动作时序随新硬件调整 | 带响应的命令配置、状态回读与判据 |
这五项是一起改的,原因并不神秘:它们在同一条链上。 检测电压从“常通”变成“可控”,就意味着“什么时候加电”变成了流程的一部分; 流程一变,检测点的顺序就得跟着调;顺序一调,按键操作习惯也跟着变。 硬件改版最怕的不是改动本身,而是改动的波及范围没有被说清楚—— 所以我现在会强制自己做这张表,把“改了什么、为什么改、影响到哪里”写下来, 而不是只留一条提交信息。
检测电压做成可控开关,好在哪里
这一点值得单独写。检测电压常通的时候,工装一上电, 检测电压就一直加在被检测板的那些检测点上。带来两个问题: 一是操作者还没把板子放稳,电压已经加上去了,接触瞬间可能产生电弧或误动作; 二是被检测板在某些状态下不应该承受这个电压,常通等于逼着它一直承受。 改成可控开关之后,电压只在需要测量的那一刻加上去, 测完就撤掉。这既是安全上的改善,也让“测量”这个动作有了明确的边界: 能测到值,说明这一路的开关、通路、采样全都正常。
首版打样与固件上传的节奏
这个工程第一个版本提交时,提交信息里写的是“新版本已打样, 等待后续上传”——也就是说,硬件先小批量打样,固件等硬件回来再补。 这种节奏有它的道理:硬件的问题必须靠实物暴露,等固件全部写完再打样, 一旦硬件要改,固件里跟硬件相关的部分(按键映射、点位顺序)都要返工。 代价也很实在:那段时间仓库里的固件版本与手上的实物版本是对不上的, 必须靠提交信息把“现在仓库里这份对应哪个实物”讲清楚。 我现在对这条的理解是:版本对应关系是这个阶段最重要的信息, 比代码本身还重要。
八、问题与解决:现象、原因、办法
这个工程没有留下设计文档,问题记录主要来自提交信息和我自己复盘时的观察。 下面每一条都是“现象 → 原因 → 办法”的结构, 前几条的改动理由来自提交信息本身,后面几条是我照着流程复现时遇到的。
| 现象 | 原因 | 办法 |
|---|---|---|
| 通信底层的名字与工装实际扮演的角色相反,读收发逻辑时被绕住 | 底层是从同系列板卡工程复用的,工装在这条总线上其实是主机 | 读代码前先确认“谁发起、谁应答”,再按角色去找收发路径;把这一点写进接手检查项 |
| 硬件改版后按键全部错位,按“加”变成了“返回” | 按键扫描的行列映射写在扫描代码里,硬件一改就整体失效 | 把映射抽成一张表,扫描代码只负责“哪个键被按下”,映射表负责“它是什么意思” |
| 检测电压常通,板子还没放稳电压就加上去了 | 早期硬件没有检测电压的开关控制 | 新硬件把检测电压做成可控开关,只在测量前的那一瞬间加电 |
| 检测点顺序写死在流程里,新硬件多两个点就要改流程 | 判据与流程耦合在一起 | 把检测点做成配置数据,流程只负责按配置遍历(见第三节) |
| 屏幕显示的型号是对的,判据却是另一套,整批判定失真 | “当前板型”只体现在显示字符串上,没有成为独立的状态 | 板型做成枚举状态,显示、配置选择、判据来源都从它取值 |
| 偶尔出现“连接正常”但后续每一步都失败 | 连接判定只要求“发一条、收一条”,接触不良时恰好蒙对第一条 | 改成连续多次通信成功才认为连上,把链路问题拦在第一步 |
| 刷屏时检测流程明显变慢,偶发一次显示异常还会影响判定 | 把“测一步、刷一次屏”写在同一条执行路径上 | 检测逻辑只更新状态,显示任务按自己的节拍读状态刷新,两边解耦 |
| 设备重启之后,参数没有被正确加载 | 早期流程把“保存参数”当成最后一步,没有重启复验 | 把“重启 + 重新上线 + 回读”插进流程,作为保存是否成功的判据 |
这八条里,前七条我有明确的处理,第八条是这套流程里最容易被省掉、 却最不该省的一步。而比这八条都更重要的是下一节要写的那件事: 有几步检测看起来在测,实际上根本没有判据。
九、这个项目最大的教训:工装里“看起来测过了”的假测试
这是我在这个项目里犯的最严重的错误,也是我决定把这篇记录写出来的主要原因。
现象是这样的:流程走完了,屏幕上每一项后面都显示“通过”, 操作者按下确认,板子被判定为合格。但当我为了排查另一个问题、 特意拿一块已知有问题的板子去跑的时候,它依然显示通过。 换句话说,其中若干项检测只有流程与显示,没有真正的阈值判定—— 无论被检测板返回什么,甚至不返回,这一步都会走到“通过”。
它是怎么变成这样的
复盘下来,原因不是“忘了写”,而是几种很自然会发生的写法:
- 只发送、不校验。这一步的动作是“下发一条命令”, 至于对方回了什么、回得对不对,没有写进判据。流程推进的条件是“命令发出去了”。
- 只判断“有没有回帧”,不判断“值对不对”。 收到了一个格式正确的响应,就认为成功;响应里的数值从来没有和允许区间比过。
- 配置里给了这一项,但绑定的测试点集合是空的。 遍历一个空集合,结果是“一项都没失败”, 而汇总逻辑写的是“没有失败项就是通过”——于是空集合天然等于通过。 这一种最阴,因为它连“忘了写判定”的痕迹都没有。
- 失败路径从来没有被触发过。错误出口写在那里, 但从来没有任何一次运行真的走进去过,所以也没人知道它能不能正常工作。
- 显示层与判定层耦合。屏幕上那个“OK”是流程推进画上去的, 不是判据算出来的。看屏幕的人无从分辨这两者的区别。
为什么这件事在产线上特别危险
工装的唯一作用就是“拦住不合格品”。假测试相当于把不合格品放到了下一道工序, 而且它比“没有工装”更糟:没有工装的时候,人至少还知道自己没测; 有了一个显示“通过”的工装,所有人都会认为测过了。 问题会在更贵的地方暴露——在整机装配后、在老化房里、到最终使用者手里。 每一处的返工代价都比在工位上高一个量级。
怎么避免:最低要求是“能构造出必然失败的情形”
我后来给自己定了一条最低要求,也是我认为判断一套工装是否可信的唯一标准: 对每一个判定项,都必须能构造出一个让它必然失败的输入, 并且亲眼确认工装会报失败。做不到这一点的判定项, 一律视为“没有判定”,而不是“判定通过”。
| 检查项 | 我踩过的反例 | 怎么验证它真的在判定 |
|---|---|---|
| 每个判定项都有判据来源 | 只发了命令,没有任何区间或期望值 | 把这一项的判据字段清空,看它是否报“配置不完整”;如果照样通过,说明判据没接上 |
| 空集合必须被显式拦住 | 测试点数量为 0,“没有失败项”被当成通过 | 故意把某一项的测试点数量改成 0,确认工装报的是“配置错误”而不是“通过” |
| 结果必须三态,而不是两态 | 只有“通过 / 不通过”,没执行的项被默认算通过 | 人为跳过一步,确认汇总显示的是“未执行”且总判定不为通过 |
| 判据要能被看见 | 屏幕上只显示“OK”,看不出它在和什么比 | 把“实测值 + 允许区间”一起显示出来,“没有判据”就会一眼暴露 |
| 失败路径必须被走过 | 错误出口存在,但从没触发过,不知道它会不会用 | 用已知坏板、断线、故意写错的期望区间三种方式,各触发一次失败 |
| 用已知坏板做终检 | 只用好板跑过流程,看到“全部通过”就以为流程对了 | 每次改判据后,先用“已知坏板”跑一遍;它必须被拦下来 |
这套“先构造必然失败的情形、再谈通过”的思路, 我是从可靠性测试那轮实验里学来的, 具体怎么设计用例写在《可靠性测试:我在这块板子上做过的八项实验》里; 而“把产测流程做进固件”这件事本身的取舍—— 好处是产线不会漏、代价是固件变得更复杂、更容易把调试代码带进发布版—— 我在《从项目到量产:固件里该留什么、该去掉什么》里单独整理过。
十、局限与还没做完的部分
这套工装能跑完流程、能给出判定,也已经跟着两个硬件版本改过一轮。 但下面这些我都清楚是欠着的:
| 问题 | 现状 | 我打算怎么改 |
|---|---|---|
| 三态结果还没有在全流程落地 | 我在新的判据里加了“未执行”这一态,但汇总与显示还偏向两态 | 把三态作为汇总的唯一输入类型,让“未执行”在屏幕与结果记录里都明确可见 |
| 判据版本与被检测板版本没有互相确认 | 工装知道自己用的是哪一份配置,但不会校验被检测板的软硬件版本是否匹配 | 把“配置版本 ↔ 被测板版本”做成一张对照表,不匹配就拒绝开始检测 |
| 判据改动只能靠手动回归 | 每次改配置都要人工把四种板型各跑一遍 | 先做一台“模拟被检测板”的替身(用另一块板或一个脚本假装应答),把回归变成可重复的动作 |
| 工装自身不能升级 | 工装没有 IAP,改判据就要拆机重新烧写 | 把配置数据外置(从文件或上位机下发),并考虑给工装本身做一次可回滚的升级 |
| 结果没有导出 | 检测结果只留在屏幕上和板子的参数区里,没有可存档的记录 | 加一路结果输出(串口或存储),让每块板的检测记录能被追溯 |
| 字库只放了用到的字 | 界面文案一改就要重新取字模,改文案的成本很高 | 换成一份覆盖常用字的字库,并接受它占用的那部分 Flash |
| 运行时任务划分没有整理成文档 | 业务任务是在哪一层创建的,我整理时没有逐项确认下来 | 把任务、优先级、栈大小、节拍来源整理成一张表,作为后续改动的基线 |
| 假测试的检查没有变成强制流程 | “构造必然失败的情形”目前还靠我自己记得做 | 写成一张上线前检查表,改判据之后必须逐条打勾,才算这次改动做完 |
最后一条是我最想先做的。前面那些都是技术上的欠缺, 而这一条是流程上的欠缺——假测试之所以能存在那么久, 不是因为我不会写判定,而是因为没有一个动作逼着我去验证判定真的生效。 把这件事变成一张必须打勾的表,比再写多少功能都重要。
参考资料与说明
- Cortex-M4 主控芯片参考手册(SPI、DMA、ADC、通用定时器章节), 用于确认外设初始化流程与 DMA 搬运配置。
- SPI TFT 驱动芯片数据手册(初始化序列、帧率与电源设置、Gamma 曲线、RGB565 像素格式章节), 用于确认屏的初始化顺序与像素数据格式。
- FreeRTOS 官方公开文档(任务、队列、软件定时器与节拍章节), 用于确认任务划分与延时调用的基本约定。
- Modbus 应用协议规范(Modbus Application Protocol Specification), 用于确认功能码与异常码的定义。
- TIA/EIA-485 串行通信标准相关公开资料,用于确认半双工总线的方向控制要求。
- GNU GPL v3 许可证文本与官方常见问题,用于确认许可证义务与再分发条件。
- 关于本工程:该工程根目录带有 GNU GPL v3 许可证。 出于对这份许可证的尊重,本文只描述架构、流程与数据组织方式, 不展示该工程的任何代码;文中的代码片段都是为说明结构而按个人理解 另行重写的通用骨架,与该工程代码无关。
- 本文不列出任何产品特定参数:寄存器地址、响应等待时间、电压允许上下限、 板型型号字符串、按键编号、屏分辨率与字库取模参数均已省略,只保留流程与结构。
- 文中涉及的芯片型号、驱动 IC 与工具名称仅用于说明技术方案,与相关厂商无隶属或授权关系。
- 出于对个人项目实现细节的保护,本文只公开方法与思路; 涉及核心实现的部分以步骤与字段说明代替代码,示例代码为按个人理解重写的通用片段。