STM32+WT2003Hx MP3语音芯片UART控制完整工程

科技时尚 2026-07-22 chy123 3900

方案背景

智能家电、充电桩、医疗设备的语音提示功能,已经成为产品体验的一项标配。过去工程师习惯用OTP语音芯片,但OTP容量固定,换音必须要换芯片重新烧录,虽然otp语音芯片便宜,但是产品迭代的成本不低。WT2003Hx系列MP3语音芯片内置32位120MHz处理器,支持MP3和WAV解码,通过标准UART串口就能播放海量音频,芯片还集成0.5W D类功放与16位DAC,一颗芯片就能独立完成语音播报。本文以STM32为主控,手把手搭建一套可直接移植的UART控制工程

硬件连接

WT2003Hx的串口是3.3V TTL电平,和STM32的USART外设直接对接即可,无需电平转换。以SOP16封装为例,关键引脚如下:

• 芯片TX(16脚)连接STM32的RX引脚

• 芯片RX(6脚)连接STM32的TX引脚

• 芯片BUSY(15脚)连接STM32任意GPIO输入,用来检测播放状态

电源VCC接2.6V到5.0V,建议和STM32共用3.3V或独立5V,电源纹波控制在5%以内

• 喇叭接PWM+(13脚)与PWM-(14脚),推荐8欧0.5W喇叭(内置0.5W D类功放,工作在SPK输出模式)

• 芯片与STM32两侧GND必须共地

如果你的STM32是5V系统,芯片RX端需要串接限流电阻或加电平转换,避免3.3V接收端被5V直驱损坏。BUSY是输出脚,平时为低电平,播放时拉高,非常适合用中断或轮询判断一曲是否播完。

通信协议

WT2003Hx所有控制都走同一套UART帧。默认波特率9600,数据位8、停止位1、无校验。整条指令帧格式如下:

7E 长度 命令码 参数 校验和 EF

长度字段等于命令码、参数、校验和这三段加起来的字节数,再加上长度字段自身那1个字节,也就是参数长度加3。校验和等于长度、命令码、全部参数三者累加之后取低8位。

举一个规格书里的真实例子,播放片内Flash中WCZY1文件包索引1的音频,完整帧是:

7E 0A 95 57 43 5A 59 31 00 01 1E EF

其中57 43 5A 59 31是WCZY1这五个字符的ASCII码,00 01是索引地址,1E是校验和。我们来验算:0A加95加57加43加5A加59加31加00加01等于21E,低字节正好是1E,和帧里一致。芯片收到指令后会回一帧,返回码00表示执行成功。

完整工程代码

下面是基于STM32 HAL库的发送封装,核心是自动算长度与校验和,业务层只管传命令码和参数。

#include main.h extern UART_HandleTypeDef huart2; // 连接WT2003Hx的串口 // 计算校验和并发送完整指令帧 void WT2003_SendCmd(uint8_t cmd, uint8_t *param, uint8_t param_len) { uint8_t frame[32]; uint8_t len = param_len + 3; // 长度字段自身 + 命令码 + 参数 + 校验和 uint8_t checksum = len + cmd; // 长度字节参与累加 uint8_t i; frame[0] = 0x7E; frame[1] = len; frame[2] = cmd; for (i = 0; i < param_len; i++) { frame[3 + i] = param[i]; checksum += param[i]; } frame[3 + param_len] = checksum & 0xFF; frame[4 + param_len] = 0xEF; HAL_UART_Transmit(&huart2, frame, 5 + param_len, 100); } // 播放片内Flash文件包中指定索引的音频 // pkg_name: 5字节文件包名ASCII index: 2字节索引地址 void WT2003_PlayPackageIndex(uint8_t *pkg_name, uint16_t index) { uint8_t param[7]; param[0] = pkg_name[0]; param[1] = pkg_name[1]; param[2] = pkg_name[2]; param[3] = pkg_name[3]; param[4] = pkg_name[4]; param[5] = (uint8_t)(index >> 8); param[6] = (uint8_t)(index & 0xFF); WT2003_SendCmd(0x95, param, 7); } // 停止播放 void WT2003_Stop(void) { WT2003_SendCmd(0xAB, NULL, 0); } // 设置音量 0到31级,掉电记忆 void WT2003_SetVolume(uint8_t vol) { if (vol > 31) vol = 31; WT2003_SendCmd(0xAE, &vol, 1); } // 切换音频输出 0为SPK 1为DAC,切换后等待再发其他指令 void WT2003_SwitchOutput(uint8_t mode) { WT2003_SendCmd(0xB6, &mode, 1); HAL_Delay(200); } // 插播Flash中指定索引音频,可在背景音中插入紧急语音 void WT2003_InsertPlay(uint16_t index) { uint8_t param[3] = {0x00, (uint8_t)(index >> 8), (uint8_t)(index & 0xFF)}; WT2003_SendCmd(0xB1, param, 3); } // BUSY高电平表示正在播放 uint8_t WT2003_IsBusy(void) { return HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin); } ``` 在主函数里先延时1秒等待芯片挂载文件系统,再发播放指令: ```c int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); HAL_Delay(1000); // 等待WT2003Hx上电挂载文件系统 uint8_t pkg[5] = {'W', 'C', 'Z', 'Y', '1'}; // 文件包名WCZY1 WT2003_PlayPackageIndex(pkg, 0x0001); // 播放索引1 while (1) { if (WT2003_IsBusy() == 0) // 播放结束,可做后续业务逻辑 { // 例如触发下一曲或切换状态 } HAL_Delay(50); } } ```

在主函数里先延时1秒等待芯片挂载文件系统,再发播放指令:

int main(void){ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); HAL_Delay(1000); // 等待WT2003Hx上电挂载文件系统 uint8_t pkg[5] = {'W', 'C', 'Z', 'Y', '1'}; // 文件包名WCZY1 WT2003_PlayPackageIndex(pkg, 0x0001); // 播放索引1 while (1) { if (WT2003_IsBusy() == 0) // 播放结束,可做后续业务逻辑 { // 例如触发下一曲或切换状态 } HAL_Delay(50); }}

常用指令速查

掌握下面几条,大部分播报场景都能覆盖:

• 播放文件包索引(95):参数为5字节包名加2字节索引

• 停止(AB):无参数

• 音量(AE):1字节0到31,掉电记忆

• 输出切换(B6):00切SPK,01切DAC,切换后等200毫秒再发下一条

• 插播(B1):3字节,标示字加2字节索引,可在背景音中插入紧急语音

• 查询状态(C2):读取当前是播放、暂停还是停止

开发注意事项

第一,上电初始化要等。WT2003Hx走PC模式,需要挂载文件系统,上电后建议等500毫秒到1秒再发码,否则芯片不响应。

第二,BUSY信号对同步很有用。平时低电平,播放时拉高,用中断或轮询都能判断一曲是否播完,避免重复发码。

第三,USB换音后必须拔线。通过USB把电脑上的音频拷进盘符后,一定要先断开USB线再发串口指令,否则发码无响应。

第四,外挂Flash认品牌。规格书建议用唯创自家Flash,第三方Flash可能出现杂音、爆音甚至无法播放。

第五,发码要有间隔。规格书要求每串命令之间留出200到300毫秒,发完可通过返回码确认执行结果;切换输出这类指令后同样建议等200毫秒再发下一条。

结语

WT2003Hx加STM32的组合,把语音播报的硬件成本和开发门槛都降了下来。一块SOP16小芯片,UART几行代码就能驱动,PC模式还能像U盘一样随时换音。如果你正在做家电、充电桩、门锁或者工业设备,这套工程可以直接套用,先把协议帧和校验和对通,再叠加业务指令就能快速量产。