STM32 智能垃圾桶项目笔记(五):语音合成模块(SYN6288)配置与语音播报实现
本系列笔记是笔者学习 B 站 up 主 “技术探索者” STM32 系列视频所作的记录,不理解的地方推荐观看视频~
目录
- 一、前言
- 二、语音合成模块(SYN6288)核心内容
- 2.1 模块特性与接线说明
- 2.2 通信协议:帧格式与命令逻辑
- 2.3 CubeMX 配置(串口 2 9600 波特率)
- 三、代码实现(驱动 + 主程序)
- 3.1 驱动文件(driver_SYN6288.c)
- 3.2 主程序与中断回调函数
- 四、关键疑问解答:为何串口 2 无需手动启动接收?
- 五、下一篇预告:智能垃圾桶功能整合
- 六、总结
一、前言
大家好,我是 Hello_Embed。上一篇我们完成了舵机(SG90)的控制,实现了 “定时转动” 功能。本次笔记聚焦智能垃圾桶的 “交互体验升级”—— 通过 SYN6288 语音合成模块 实现 “语音播报” 功能,比如开盖时播放 “欢迎使用”、关盖时播放 “谢谢使用”。
需要说明的是:SYN6288 仅负责 “文字转语音”(TTS),不涉及语音识别功能(语音识别后续若有需求再补充)。本次将从模块特性、通信协议、配置到代码实现,完整覆盖 “中文语音播报” 的核心流程。
二、语音合成模块(SYN6288)核心内容
2.1 模块特性与接线说明
2.1.1 核心特性
SYN6288 是一款低成本、高音质的语音合成模块,核心优势如下:
- 功能:支持中文 / 英文文字转语音,可设置背景音乐、语速、音量;
- 通信:基于串口(UART)通信,配置简单,仅需 4 根线;
- 编码:支持 GB2312、GBK、BIG5、UNICODE 等编码(中文需用 GB2312 或 GBK);
- 数据长度:单次最大支持 206 字节数据发送(满足日常播报需求);
- 波特率:默认 9600(可通过命令修改,与蓝牙、超声波的串口波特率区分)。
2.1.2 接线图与原则
模块接线图如下:
接线原则(串口交叉连接,5V 供电):
SYN6288 引脚 | 功能 | 连接对象(STM32) | 说明 |
---|---|---|---|
VCC | 电源正极 | 5V 引脚 | 模块仅支持 5V 供电,勿接 3.3V |
GND | 电源负极 | GND 引脚 | 必须与 STM32 共地,避免干扰 |
RXD | 模块接收端 | 串口 2 TX(PA2) | 模块接收 STM32 发送的指令 |
TXD | 模块发送端 | 串口 2 RX(PA3) | 模块向 STM32 反馈状态(可选) |
2.2 通信协议:帧格式与命令逻辑
SYN6288 采用 “帧结构” 通信,所有指令(配置、语音播报)需按固定格式发送,核心帧格式如下:帧头(1字节) + 数据区长度(2字节) + 数据区(≤203字节)
各部分详细说明:
- 帧头:固定为
0xFD
(模块仅识别此帧头的指令,避免误触发); - 数据区长度:2 字节,高字节在前、低字节在后,代表 “数据区的总字节数”;
- 数据区:包含 4 部分,总长度需与 “数据区长度” 匹配:
- 命令字(1 字节):如
0x01
代表 “语音合成”、0x00
代表 “参数配置”; - 命令参数(1 字节):如设置编码格式、背景音乐开关;
- 待发送文本(≤200 字节):需按指定编码(如 GB2312)转换为字节数组;
- 异或校验(1 字节):对 “帧头 + 数据区长度 + 数据区前 n-1 字节” 进行异或运算,确保数据传输无误。
- 命令字(1 字节):如
示例:“宇音天下” 语音播报帧
以发送 8 字节 “宇音天下”(GB2312 编码)为例,帧结构拆解:
- 帧头:
0xFD
; - 数据区长度:
0x00 0x0B
(十进制 11,代表数据区共 11 字节); - 数据区:
0x01
(命令字:语音合成) +0x01
(命令参数:无背景音乐) +“宇音天下”8字节编码
+异或校验值
; - 最终整帧数据符合 “帧头 + 长度 + 数据区” 的格式,模块接收后会解析并播报。
波特率与编码配置
- 默认波特率:9600(无需修改,与手机蓝牙调试 APP 兼容性好);
- 中文编码:必须使用 GB2312 或 GBK(否则会出现乱码 / 无语音),本次选择 GB2312(收录 6000+ 常用汉字,满足需求)。
2.3 CubeMX 配置(串口 2 9600 波特率)
使用 串口 2 与 SYN6288 通信(避免与串口 1/3 冲突),配置步骤如下:
- 进入
Connectivity → USART2
,模式选择Asynchronous
(异步通信); - 基本参数配置:
- 波特率:
9600
(与模块默认波特率一致); - 数据位:
8
,停止位:1
,校验位:None
(标准串口参数);
- 波特率:
- 使能中断:勾选
NVIC Settings → USART2 global interrupt
(接收模块反馈信息); - 引脚:串口 2 默认引脚为 PA2(TX)、PA3(RX),无需手动修改;
- 生成工程:确认基础配置不变,点击
Generate Code
,编译无错误即可。
三、代码实现(驱动 + 主程序,仅加注释)
核心需求:① 封装驱动函数(构造语音帧、发送配置命令);② 主程序实现 “编码初始化 + 中文语音播报”;③ 中断回调处理串口 2 接收。
3.1 驱动文件(driver_SYN6288.c)
#include "driver_SYN6288.h"
#include <string.h> // 用于 memcpy 函数(复制数组)// 声明串口2 句柄(CubeMX 自动生成在 usart.c 中,需确保包含 usart.h)
extern UART_HandleTypeDef huart2;/*** @brief 串口2 数据发送函数(SYN6288 专用)* @param buf:待发送的字节数组* @param len:待发送的字节数* @retval 无* @note 调用 HAL_UART_Transmit 阻塞发送,超时时间 1000ms*/
void SendStr5(unsigned char* buf, unsigned int len)
{// 向串口2 发送数据,超时 1000ms(确保模块有足够时间接收)HAL_UART_Transmit(&huart2, buf, len, 1000);
}/*** @brief 构造 SYN6288 语音合成帧并发送* @param Music:背景音乐选择(0=无音乐,1~15=不同背景音乐)* @param HZdata:待播报的文本字节数组(需按 GB2312 编码)* @retval 无* @note 帧结构:帧头(0xFD) + 数据区长度 + 命令字(0x01) + 命令参数 + 文本 + 异或校验*/
void SYN_FrameInfo(uint8_t Music, uint8_t *HZdata)
{unsigned char Frame_Info[50];// 存放整帧数据(最大支持 50 字节,满足短文本播报)unsigned char HZ_Lenth;// 待播报文本的字节长度unsigned char ecc = 0;// 异或校验值(初始化为 0)unsigned int i = 0;// 计算待播报文本的长度(HZdata 为 GB2312 编码的字节数组,strlen 统计字节数)HZ_Lenth = strlen((char*)HZdata);// -------------------------- 1. 构造帧头与数据区长度 --------------------------Frame_Info[0] = 0xFD; // 帧头:固定为 0xFDFrame_Info[1] = 0x00; // 数据区长度高字节:当前文本短,高字节为 0Frame_Info[2] = HZ_Lenth + 3; // 数据区长度低字节:文本长度 + 3(命令字1 + 命令参数1 + 校验1)// -------------------------- 2. 构造命令字与命令参数 --------------------------Frame_Info[3] = 0x01; // 命令字:0x01 代表“语音合成”Frame_Info[4] = 0x01 | Music << 4; // 命令参数:低4位 0x01(正常语速),高4位 Music(背景音乐)// -------------------------- 3. 计算异或校验 --------------------------// 第一步:对帧头+数据区长度+命令字+命令参数(前5字节)进行异或for(i = 0; i < 5; i++){ecc = ecc ^ (Frame_Info[i]); // 异或运算:相同为 0,不同为 1}// 第二步:对待播报文本的每个字节进行异或(确保文本数据无传输错误)for(i = 0; i < HZ_Lenth; i++){ecc = ecc ^ (HZdata[i]);}// -------------------------- 4. 组装整帧数据并发送 --------------------------memcpy(&Frame_Info[5], HZdata, HZ_Lenth); // 将文本字节数组复制到帧的第5位开始Frame_Info[5 + HZ_Lenth] = ecc; // 帧的最后一位:异或校验值// 发送整帧数据:长度 = 前5字节(帧头+长度+命令) + 文本长度 + 1字节校验SendStr5(Frame_Info, 5 + HZ_Lenth + 1);
}/*** @brief SYN6288 模块参数配置函数(发送配置命令)* @param Info_data:配置命令字节数组(需按模块帧格式构造)* @retval 无* @note 用于发送编码设置、波特率修改等配置指令*/
void YS_SYN_Set(uint8_t *Info_data)
{uint8_t Com_Len;// 计算配置命令的总长度(Info_data 为完整的配置帧)Com_Len = strlen((char*)Info_data);// 发送配置命令到串口2SendStr5(Info_data, Com_Len);
}
3.2 主程序与中断回调函数
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "driver_SYN6288.h"// 全局变量:语音模块(串口2)接收缓冲区(存储模块反馈的状态信息)
uint8_t SYS6288Rx = 0;
// 全局变量:蓝牙模块(串口3)接收缓冲区(上一篇定义,此处复用)
uint8_t Rx_dat = 0;/*** @brief 串口接收中断回调函数(处理串口3 蓝牙、串口2 语音的接收)* @param huart:触发中断的串口句柄* @retval 无* @note 1. 串口3(蓝牙):接收指令控制 PA7 LED 亮灭;* 2. 串口2(语音):接收模块反馈,重启接收中断以持续接收*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{// 若中断来自串口3(蓝牙模块)if(huart->Instance == USART3){// 接收到 0xA1(十六进制):点亮 PA7 LEDif(Rx_dat == 0xa1){HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);// PA7 拉低,LED 亮}// 接收到 0xA2(十六进制):熄灭 PA7 LEDelse if(Rx_dat == 0xa2){HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); // PA7 拉高,LED 灭}// 重启串口3 接收中断(确保下次能继续接收蓝牙指令)HAL_UART_Receive_IT(&huart3, &Rx_dat, 1);}// 若中断来自串口2(语音模块)else if(huart->Instance == USART2){// 重启串口2 接收中断(接收模块反馈的状态,如“播报完成”信号)HAL_UART_Receive_IT(&huart2, &SYS6288Rx, 1);}
}int main(void)
{// 1. 初始化 HAL 库、外设(CubeMX 自动生成:串口2/3、GPIO 等)HAL_Init();MX_GPIO_Init();MX_USART2_UART_Init();MX_USART3_UART_Init();// -------------------------- 2. 初始化 SYN6288 语音模块 --------------------------// 配置命令:设置模块为 GB2312 编码(确保中文播报正常)// 帧结构解析:0xFD(帧头) + 0x00 0x08(数据区长度8字节) + 0x01(命令字:参数配置) + 0x00(命令类型:编码设置) + 0x01(编码:GB2312) + 0x00 0x00 0x00(保留) + 0x08(异或校验值)uint8_t initCmd[] = {0xFD, 0x00, 0x08, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08};YS_SYN_Set(initCmd); // 发送编码配置命令HAL_Delay(500); // 延时 500ms,等待模块初始化完成(避免后续指令发送失败)// -------------------------- 3. 播报中文语音:“你好嵌入式” --------------------------// helloStr:“你好嵌入式”的 GB2312 编码字节数组(0x00 为结束符)// 编码对应:0xC4E3=你,0xBAC3=好,0xC7B0=嵌,0xC8EB=入,0xCABD=式uint8_t helloStr[] = {0xC4, 0xE3, 0xBA, 0xC3, 0xC7, 0xB0, 0xC8, 0xEB, 0xCA, 0xBD, 0x00}; SYN_FrameInfo(0, (char*)helloStr); // 发送语音帧:0=无背景音乐,helloStr=待播报文本// 4. 主循环(仅延时,无其他逻辑,专注测试语音播报)while (1){HAL_Delay(100); // 延时 100ms,避免主循环空跑占用资源}
}
四、关键疑问解答:为何串口 2 无需手动启动接收?
上一篇中,蓝牙模块(串口 3)需在 main
中调用 HAL_UART_Receive_IT(&huart3, &Rx_dat, 1)
启动接收,而语音模块(串口 2)无需手动启动,核心原因如下:
- 串口 3(蓝牙):模块上电后无主动信号输出,需手动调用
HAL_UART_Receive_IT
开启中断,才能检测后续的蓝牙指令; - 串口 2(语音):在调用
YS_SYN_Set(initCmd)
发送 “编码配置命令” 时,模块接收指令后会主动向 STM32 发送反馈信号(如 “配置成功” 信号),此反馈信号会触发串口 2 的接收中断,进入HAL_UART_RxCpltCallback
; - 在回调函数中,已添加
HAL_UART_Receive_IT(&huart2, &SYS6288Rx, 1)
重启接收中断,因此后续模块的反馈信号能被持续接收,无需在main
中手动启动。
五、下一篇预告:智能垃圾桶功能整合
至此,智能垃圾桶的四大核心模块(超声波测距、蓝牙通信、舵机控制、语音播报)已全部实现。下一篇将进行 功能整合,实现完整的 “智能开盖” 逻辑。
项目定位为 “练手级”,无需复杂结构,后续可根据需求用 3D 打印制作垃圾桶外壳,进一步完善硬件。
六、总结
本次笔记围绕 SYN6288 语音模块展开,核心收获包括:
- 掌握模块的串口接线与通信协议(帧头 0xFD + 数据区长度 + 数据区 + 校验);
- 理解中文语音播报的关键:需使用 GB2312/GBK 编码,并用
SYN_FrameInfo
构造语音帧; - 区分不同串口的中断启动逻辑,解决 “为何串口 2 无需手动启动接收” 的疑问。
下一篇将是本项目的 “收尾篇”,通过整合四大模块,实现 “感知 - 决策 - 执行 - 交互” 的完整智能垃圾桶功能。请关注 Hello_Embed,见证项目最终落地!