语音模块与主控 MCU 的串口对接是离线语音方案里最常见也最容易踩坑的环节:命令帧定义随意、播报请求和识别结果混在一条链路里、音量记忆没想清楚,联调时全冒出来。本篇把多个真实项目里验证过的串口协议设计要点汇总成一篇,供画板写码前对照。
一、先想清楚"谁主导":三种典型角色分配
- 语音模块当主控:模块识别语音后通过 IO 或串口直接控制外设,MCU 只负责执行,适合功能简单的小家电;
- MCU 当主控:语音模块只做识别和播报,收到语音后上报结果码,由 MCU 决定执行动作并下发播报指令,适合有状态机、有通信需求的智能设备;
- 双向对等:模块上报识别结果,MCU 主动下发状态查询与播报,适合状态复杂、需要双向反馈的产品。
角色分配决定协议结构,动手前先定,联调到一半改角色等于重写。
二、命令帧设计:版本号、校验和、结束符一个都不能省
真实项目里反复出现的教训:
- 帧头帧尾用固定字节,中间载荷长度字段显式给出,接收端按长度截包,不要依赖超时切包;
- 加校验和或 CRC。串口在电机、继电器等噪声环境下工作,无校验的协议会把干扰字节当命令执行,这类"偶发乱动作"极难排查;
- 每帧带版本或命令类别字段,固件升级后新旧命令不兼容时可以引导分支处理;
- 命令码表一次性定稿并写进协议文档,双方按同一份表开发,"你发 0x03 我以为是暂停"的来回在联调现场每天都能见到。
三、识别结果上报:带状态,不只带命令码
MCU 主控方案里,模块上报建议包含:识别到的词条 ID、识别置信度或原始文本(若芯片支持)、当前唤醒状态。带唤醒状态这一条经常被忽略------MCU 不知道模块在待机还是唤醒,就会出现"发了播报指令但模块没反应,MCU 以为模块死了"的误判。配合前述唤醒状态门控的做法,MCU 侧可以先查状态再发指令。
四、播报请求:处理"正在播报"的冲突
MCU 连续下发播报指令时,模块正在播上一条,新指令怎么办?协议里要约定:
- 打断式:新播报打断旧播报,适合告警类;
- 排队式:新指令进队列依次播,适合流程引导;
- 丢弃式:播报中忽略新指令,MCU 收到忙状态后重试。
三种模式选哪种写进协议,模块和 MCU 都按约定实现。没约定的联调现场,表现为"有时播有时不播",双方各说各话。
五、音量与状态记忆:开机默认值显式化
音量调节是高频功能。要点:开机音量默认值在固件里显式设定,并明确是否记忆上次音量;若记忆,断电重启后的行为要在测试用例里覆盖。多个项目出现过"客户调到最小音量后断电,重启以为模块坏了"的工单,根因就是默认值与记忆行为没定义清楚。
六、联调自检清单
- 双方持有同一份命令码表和协议文档;
- 逻辑分析仪抓一遍完整交互:上电握手、唤醒、识别、上报、播报、待机;
- 异常用例过一遍:连发播报指令、播报中发新指令、待机态发指令、断电重启;
- 噪声环境测试:电机运行中连续交互 30 分钟,确认无误码误动作;
- 固件升级前后各跑一遍全量用例,确认协议兼容。
串口协议不复杂,复杂的是没写下来的约定。把上面六条过一遍,联调周期能缩短一大半。