4. 软件架构设计:上位机烧录软件架构、通信协议选择与数据库设计
烧录产线的上位机软件,说白了就是整个产线的大脑。它得跟下位机(烧录器)通信,得管理烧录文件,还得记录每一颗芯片的命运。我见过不少产线,硬件堆得挺豪华,结果软件架构一塌糊涂,三天两头出问题。这一章,我就把这几年的经验掰开揉碎,跟你聊聊怎么搭一个靠谱的上位机软件。
4.1 上位机软件架构:别一上来就写代码
我个人习惯,先画架构图,再动手。一个典型的烧录上位机,我把它分成三层:
- UI 层:操作界面,给产线工人用的。按钮要大,状态要直观,别整那些花里胡哨的动画。
- 业务逻辑层:核心调度。比如什么时候开始烧录、烧录失败怎么处理、日志怎么记录。
- 驱动/通信层:跟硬件打交道。封装好 USB、以太网或串口的收发细节。
为什么要分层?你想想看,如果哪天老板说“咱们把串口换成以太网”,你只需要改最下面那层,上面的逻辑完全不用动。我在一个项目里吃过这个亏——所有通信代码跟业务逻辑揉在一起,改一个协议差点把整个软件重写。
核心原则:高内聚,低耦合。 每一层只干自己的事,别越界。
下面这张图,是我常用的架构模型:
4.2 通信协议选择:USB、以太网还是串口?
这个问题,几乎每个客户都会问我。我的回答永远是:看场景。没有最好的协议,只有最合适的。
| 特性 | USB | 以太网 | 串口 |
|---|---|---|---|
| 传输速度 | 高速(480 Mbps) | 百兆/千兆 | 低速(115200 bps 典型) |
| 传输距离 | 5 米以内 | 100 米以上 | 15 米左右 |
| 抗干扰能力 | 中等 | 强(屏蔽线) | 弱(易受干扰) |
| 多设备支持 | 需 Hub,有限 | 天然支持,可组网 | 需多串口卡 |
| 开发复杂度 | 中等(需驱动) | 低(Socket 编程) | 低(直接读写) |
| 典型场景 | 单工位、近距离 | 多工位、远距离 | 调试、低速设备 |
我的建议: 新项目优先考虑以太网。原因很简单——可扩展性好。今天你只有 4 个工位,明天扩到 40 个,换根交换机就行。USB 和串口扩起来就头疼了。
不过,如果你做的是便携式烧录器,或者现场只有 USB 口可用,那 USB 也是好选择。我记得有一次去客户现场,他们的产线工控机只有 USB 口,我临时把以太网方案改成 USB,折腾了两天。从那以后,我设计软件时都会把通信层抽象好,换协议就像换插件一样。
4.2.1 通信协议设计:别裸传数据
不管用哪种物理层,应用层协议都得自己定义。我见过最坑的做法——直接把二进制文件流发过去,连个包头都没有。一旦丢包,整个烧录就废了。
我常用的协议帧格式:
| 帧头 (2B) | 长度 (2B) | 命令 (1B) | 数据 (N B) | 校验 (2B) | 帧尾 (2B) |
| 0xAA 0x55 | 0x00 0x10 | 0x01 | ... | CRC16 | 0x0D 0x0A |
- 帧头/帧尾:用于同步,防止粘包。
- 长度:整个帧的长度,方便接收方解析。
- 命令:比如 0x01 表示开始烧录,0x02 表示校验。
- 校验:CRC16 或 CRC32,别用累加和,太容易冲突了。
我曾经踩过的坑: 有一版协议没加帧尾,结果下位机收到半个包就开始解析,直接烧进去一堆乱码。从那以后,我强制要求:帧头帧尾必须成对出现,缺一不可。
4.3 数据库设计:每一颗芯片都得有据可查
产线烧录,不是烧完就完事了。出了问题要追溯,客户要报告,质量部门要分析。没有数据库,这些事根本干不了。
我个人习惯用 SQLite,轻量、免安装、单文件。对于单机产线,完全够用。如果是多工位联网,可以考虑 MySQL 或 PostgreSQL。
4.3.1 核心表结构
我设计了三张核心表:
1. 烧录任务表 (burn_task)
CREATE TABLE burn_task (
task_id INTEGER PRIMARY KEY AUTOINCREMENT,
product_name TEXT NOT NULL, -- 产品型号
firmware_ver TEXT NOT NULL, -- 固件版本
firmware_hash TEXT NOT NULL, -- 固件哈希,防篡改
total_count INTEGER DEFAULT 0, -- 计划烧录数量
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
2. 烧录记录表 (burn_record)
CREATE TABLE burn_record (
record_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id INTEGER NOT NULL, -- 关联任务
chip_uid TEXT NOT NULL, -- 芯片唯一ID
result INTEGER NOT NULL, -- 0=失败, 1=成功
error_code INTEGER DEFAULT 0, -- 错误码
burn_time INTEGER, -- 烧录耗时(ms)
operator TEXT, -- 操作员
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (task_id) REFERENCES burn_task(task_id)
);
3. 错误码表 (error_code)
CREATE TABLE error_code (
code INTEGER PRIMARY KEY,
message TEXT NOT NULL -- 错误描述
);
关键点: chip_uid 字段一定要有。每一颗芯片都有唯一 ID(比如 STM32 的 UID),烧录时读出来存好。以后客户说“这批芯片有问题”,你查一下就知道是哪颗、什么时候烧的、谁操作的。
4.3.2 数据库设计避坑指南
- 别用自增 ID 做主键:对于烧录记录,建议用 chip_uid + task_id 做联合唯一索引,防止重复记录。
- 定期归档:产线一天可能产生几万条记录。我建议每个月归档一次,把旧数据导出到 CSV 或备份库,保持主库轻量。
- 加索引:在 created_at 和 chip_uid 上加索引,查询速度能快几十倍。别问我怎么知道的——有一次客户要查三个月前的记录,我等了五分钟才出结果,场面一度非常尴尬。
一个小技巧: 在烧录记录表里加一个 burn_time 字段,记录每次烧录的耗时。长期统计下来,你能发现哪些工位效率低,哪些芯片批次有问题。数据驱动优化,比拍脑袋靠谱多了。