基于STM32的TTS语音合成(SNR9816)
·
若该文为原创文章,转载请注明原文出处。
手上有一个SNR9816模块,测试一下TTS。
在测试过程中,发现使用STM32的很少,所以就自己写驱动了。
一、硬件介绍
1、模块

2、原理图

3、引脚说明

这里的RX和PA9相连接。2脚悬空,波特率115200bps
二、测试
1、协议说明
协议帧格式
| 字节索引 | 字段名称 | 长度(字节) | 取值/说明 |
|---|---|---|---|
| 0 | 起始标识 | 1 | 固定为 0xFD,表示命令帧开始 |
| 1-2 | 数据长度 | 2 | 高字节在前(网络字节序),值为 文本数据长度 + 2(即命令字节+参数字节) |
| 3 | 命令字 | 1 | 固定为 0x01,表示播放文本 |
| 4 | 参数 | 1 | 本例中固定为 0x01(可能代表音量或其他设置,具体含义取决于设备) |
| 5 ~ 末尾 | 文本数据 | N | GB2312 编码的文本内容(N 为文本字节数) |
帧长度计算
整个帧的总字节数 = 1(起始标识) + 2(长度字段) + 1(命令) + 1(参数) + N(文本数据) = N + 5。
2、数据封装输出测试
#include <stdio.h>
#include <string.h>
/**
* 模拟发送 TTS 消息,构建数据帧并打印其十六进制内容。
* 注意:此函数要求输入的字符串已为 GB2312 编码(字节流)。
* 若实际输入为 UTF-8,需先调用编码转换函数(如 iconv 或自定义转换表)。
*/
void sendTTSMessage(const char *gb2312_str) {
unsigned int len = strlen(gb2312_str); // 获取 GB2312 字符串字节数
unsigned char buffer[len + 6]; // 预留帧头 + 命令 + 参数 + 数据空间
// 填充帧头
buffer[0] = 0xFD; // 命令帧起始标识
buffer[1] = (len + 2) >> 8; // 长度高字节(数据长度 + 命令字节 + 参数字节)
buffer[2] = (len + 2) & 0xFF; // 长度低字节
buffer[3] = 0x01; // 命令:播放文本
buffer[4] = 0x01; // 参数:音量等(示例中固定为 0x01)
// 复制 GB2312 数据
for (unsigned int i = 0; i < len; i++) {
buffer[i + 5] = (unsigned char)gb2312_str[i];
}
// 打印构建好的数据帧(十六进制)
printf("TTS 数据帧 (%d 字节): ", len + 5);
for (unsigned int i = 0; i < len + 5; i++) {
printf("%02X ", buffer[i]);
}
printf("\n");
// 模拟发送成功消息
printf("TTS 消息发送成功\n");
}
int main(int argc, char *argv[]) {
// 测试用例:若未提供命令行参数,使用预定义的 GB2312 字符串 "你好"
// "你好" 的 GB2312 编码为:C4 E3 BA C3
const char *test_message = "\xC4\xE3\xBA\xC3";
if (argc > 1) {
// 假设用户通过命令行直接输入了 GB2312 编码的字符串(需确保终端/控制台编码一致)
// 注意:在大多数环境中直接输入汉字会得到 UTF-8 编码,因此本程序仍按 GB2312 处理,可能出错。
// 建议测试时直接修改 test_message 或从文件读取 GB2312 字节。
test_message = argv[1];
}
printf("输入字符串 (视为 GB2312 字节): ");
for (size_t i = 0; i < strlen(test_message); i++) {
printf("%02X ", (unsigned char)test_message[i]);
}
printf("\n");
sendTTSMessage(test_message);
return 0;
}
把程序放到C语言在线编译器上运行,会打印输出数据。

把数据,通过串口发送给模块,就可以听到TTS语音了。
FD 00 06 01 01 C4 E3 BA C3
三、STM32代码
1、usart.h
#ifndef __USART1_H
#define __USART1_H
#include "stm32f10x.h"
#include <stdio.h>
void USART1_Config(void);
int fputc(int ch, FILE *f);
void USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...);
void UART1SendByte(unsigned char SendData);
#endif /* __USART1_H */
2、usart.c
#include "usart.h"
#include <stdarg.h>
void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* 使能 USART1 时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
/* USART1 使用IO端口配置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA
/* USART1 工作模式配置 */
USART_InitStructure.USART_BaudRate = 115200; //波特率设置:9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位数设置:8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位设置:1位
USART_InitStructure.USART_Parity = USART_Parity_No ; //是否奇偶校验:无
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制模式设置:没有使能
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//接收与发送都使能
USART_Init(USART1, &USART_InitStructure); //初始化USART1
USART_Cmd(USART1, ENABLE);// USART1使能
}
/* 描述 :重定向c库函数printf到USART1*/
int fputc(int ch, FILE *f)
{
/* 将Printf内容发往串口 */
USART_SendData(USART1, (unsigned char) ch);
while (!(USART1->SR & USART_FLAG_TXE));
return (ch);
}
/*发送一个字节数据*/
void UART1SendByte(unsigned char SendData)
{
USART_SendData(USART1,SendData);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
/*
************************************************************
* 函数名称: Usart_SendString
*
* 函数功能: 串口数据发送
*
* 入口参数: USARTx:串口组
* str:要发送的数据
* len:数据长度
*
* 返回参数: 无
*
* 说明:
************************************************************
*/
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
{
unsigned short count = 0;
for(; count < len; count++)
{
USART_SendData(USARTx, *str++); //发送数据
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成
delay_us(100);
}
}
3、main.c
#include "delay.h"
#include "sys.h"
#include "OLED.h"
#include "usart.h"
//C库
#include <string.h>
#include "stdio.h"
#include "math.h"
// 测试用例:若未提供命令行参数,使用预定义的 GB2312 字符串 "布防成功"
// "布防成功" 的 GB2312 编码为:B2BC B7C0 B3C9 B9A6
const char *test_message = "\xB2\xBC\xB7\xC0\xB3\xC9\xB9\xA6 ";
/**
* 模拟发送 TTS 消息,构建数据帧并打印其十六进制内容。
* 注意:此函数要求输入的字符串已为 GB2312 编码(字节流)。
* 若实际输入为 UTF-8,需先调用编码转换函数(如 iconv 或自定义转换表)。
*/
void sendTTSMessage(const char *gb2312_str) {
unsigned int len = strlen(gb2312_str); // 获取 GB2312 字符串字节数
unsigned char buffer[len + 6]; // 预留帧头 + 命令 + 参数 + 数据空间
// 填充帧头
buffer[0] = 0xFD; // 命令帧起始标识
buffer[1] = (len + 2) >> 8; // 长度高字节(数据长度 + 命令字节 + 参数字节)
buffer[2] = (len + 2) & 0xFF; // 长度低字节
buffer[3] = 0x01; // 命令:播放文本
buffer[4] = 0x01; // 参数:音量等(示例中固定为 0x01)
// 复制 GB2312 数据
for (unsigned int i = 0; i < len; i++) {
buffer[i + 5] = (unsigned char)gb2312_str[i];
}
Usart_SendString(USART1, buffer, len+5);
}
int main(void)
{
unsigned char *dataPtr = NULL;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断控制器分组设置
delay_init();
Usart1_Init(115200);
delay_ms(200);
sendTTSMessage(test_message);
while(1)
{
}
}
测试正常
ASCII/GBK/GB2312中文汉字区位码,内码,编码在线查询软件

如有侵权,或需要完整代码,请及时联系博主。
更多推荐

所有评论(0)