SYN6288指南

01 什么是SYN6288

SYN6288 是一款高性价比的中文语音合成芯片,能够通过异步串口(UART)接收文本数据并实现高效的文本到语音(TTS)转换。它支持多种编码格式,包括 GB2312、GBK、BIG5 和 Unicode,能够自然流畅地合成中文、英文字母及数字混合的文本内容,每次语音合成的最大文本长度可达 200 字节。芯片提供丰富的控制功能,如语音合成、停止、暂停、继续合成以及波特率调整等,广泛适用于需要语音播报的嵌入式系统和智能设备开发场景。

简单来说,SYN6288模块是一个文字转语音功能的模块,能够为单片机项目赋予“说话”的能力。
在这里插入图片描述

02 如何控制SYN6288

主控制器和SYN6288语音合成芯片之间通过UART接口连接。控制器可以通过这个通讯接口以命令帧的形式向SYN6288语音合成芯片发送控制命令和文本。SYN6288语音合成芯片接收到文本后,将其合成为语音信号输出。随后,输出的语音信号经过功率放大器放大,最后连接到喇叭进行播放。这个过程使得单片机项目能够实现语音播报的功能。

在这里插入图片描述

2.1 SYN6288命令帧格式

帧结构帧头 (1 字节)数据区长度 (2 字节)命令字 (1 字节)命令参数 (1 字节)待发送文本 (小于等于 200 字节)异或校验 (1 字节)
数据0xFD0xXX 0xXX0xXX0xXX0xXX ...0xXX
说明定义为十六进制 0xFD高字节在前,低字节在后长度必须和前面的“数据区长度”一致

注意:数据区(含命令字,命令参数,待发送文本,异或校验)的实际长度必须与帧头后定义的数据区长度严格一致,否则芯片会报接收失败。

控制命令表

在这里插入图片描述

例子

在这里插入图片描述

03-1 使用STM32发送一个语音

3.1 串口初始化代码

波特率 9600 是 SYN6288 默认设置。若要改波特率每次SYN6288系统重置后都要发送更改波特率的命令帧。

/**
  * 函    数:串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void USART2_Init(void)
{
    // 开启 GPIOA 和 USART2 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    // 配置 TX(PA2)为复用推挽输出,RX(PA3)为上拉输入
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // TX
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // RX
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置 USART2
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600; // 波特率为 9600
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 开启发送和接收
    USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验
    USART_InitStructure.USART_StopBits = USART_StopBits_1; // 1 位停止位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8 位字长
    USART_Init(USART2, &USART_InitStructure);

    // 使能 USART2
    USART_Cmd(USART2, ENABLE);
}
/**
  * 函    数:串口发送一个字符串
  * 参    数:String 要发送字符串的首地址,长度
  * 返 回 值:无
  */
void USART2_SendString(u8 *DAT, u8 len)
{
	u8 i;
	for(i = 0; i < len; i++)
	{
		while((USART2->SR & 0X40) == 0);
		USART2->DR = (*DAT++);
	}
}

3.2 向SYN6288发送数据帧代码

//Music:选择背景音乐。0:无背景音乐,1~15:选择背景音乐
//HZdata字符串内输入空格可以使语音合成模块在文字之间停顿(相当于休止符)
void SYN_FrameInfo(u8 Music, u8 *HZdata)
{
  /****************需要发送的文本**********************************/
  unsigned  char  Frame_Info[50];
  unsigned  char  HZ_Length;
  unsigned  char  ecc  = 0;  			//定义校验字节
  unsigned  int i = 0;
  HZ_Length = strlen((char*)HZdata); 			//需要发送文本的长度

  /*****************帧固定配置信息**************************************/
  Frame_Info[0] = 0xFD ; 			//构造帧头FD
  Frame_Info[1] = 0x00 ; 			//构造数据区长度的高字节
  Frame_Info[2] = HZ_Length + 3; 		//构造数据区长度的低字节
  Frame_Info[3] = 0x01 ; 			//构造命令字:合成播放命令
  Frame_Info[4] = 0x01 | Music << 4 ; //构造命令参数:背景音乐设定

  /*******************校验码计算***************************************/
  for(i = 0; i < 5; i++)   				//依次发送构造好的5个帧头字节
  {
    ecc = ecc ^ (Frame_Info[i]);		//对发送的字节进行异或校验
  }

  for(i = 0; i < HZ_Length; i++)   		//依次发送待合成的文本数据
  {
    ecc = ecc ^ (HZdata[i]); 				//对发送的字节进行异或校验
  }
  /*******************发送帧信息***************************************/
  memcpy(&Frame_Info[5], HZdata, HZ_Length);
  Frame_Info[5 + HZ_Length] = ecc;
  USART2_SendString(Frame_Info, 5 + HZ_Length + 1);
}


void SYN_FrameInfo(u8 Music, u8 *HZdata)是构造并发送命令帧的函数。

Frame_Info[50] char数组用于存储命令帧的数据

ecc存储组成命令帧的字节的异或校验结果,校验码是数据帧完整性的保障

发送语音

SYN_FrameInfo(0,"测试");

03-2 发送带有文本控制的语音

文本控制标记列表

在这里插入图片描述

  1. 控制标识需要按照语音合成命令的格式发送,特殊控制标记作为文本进行合成,即合成命令是帧头 + 数据区长度 + 合成命令字 + 文本编码格式 + 特殊控制标记文本的格式。

  2. 控制标识为全局控制标识,也就是只要用了一次,在不对芯片复位或者断电的条件下,其后发送给芯片的所有文本都会处于它的控制之下,除非用相应的 [d]恢复默认设置。

  3. 当芯片掉电或是复位后,芯片将恢复到所有的默认值,原来的设置过的标识失去了作用,需要重新设置。

  4. 不符合以上可识别的“控制标识”的或者格式不对的,一律按普通的字符和数字处理。

[d], [v?], [m?]可以单独作为命令格式发送,也可以放在播放文本的开头与播放文本一起发送。

/**************语音合成芯片设置命令*********************/
//选择背景音乐2。(0:无背景音乐  1-15:背景音乐可选)
//m[0~16]:0背景音乐为静音,16背景音乐音量最大
//v[0~16]:0朗读音量为静音,16朗读音量最大
//t[0~5]:0朗读语速最慢,5朗读语速最快

SYN_FrameInfo(0, (uint8_t *)"[v4][m0][t5]测试语音合成 ");

03-2-2 带有和弦和提示音的语音合成

  1. [x1]使能和弦提示音
  2. 查阅和弦提示音 列表选择提示音

在这里插入图片描述

在这里插入图片描述

使能 [x1]sounda 等会自动被SYN6288 识别,但是提示音 前后紧跟英文字符时需要用空格或逗号把它们隔开,否则会识别不到。

发送音效和提示音


//m[0~16]:背景音乐
//v[0~16]:朗读音量
//t[0~5]:朗读语速

SYN_FrameInfo(0, (uint8_t *)"[v4][m0][t5][x1] 测试语音合成    测试音效  soundd  测试和弦提示音 msgc");

03-3 发送变量的值

snprintf函数

snprintf 是 C 标准库中的一个函数,用于将格式化的字符串写入一个字符数组中。它是一个灵活且强大的字符串处理工具,类似于 printf,但不会直接输出到控制台,而是将结果保存到指定的字符数组中。

函数原型: int sprintf(char *str, const char *format, ...);

str:指向目标字符数组,用于存储格式化后的字符串 , 需要足够的空间

size:最大写入的字符数(包括终止符 \0)。

format:格式化字符串,定义输出内容的格式。

用法和printf差不多 
# include <stdio.h>
char result[100]; 
int age = 20;
snprintf(result, 10, "今年%d岁", age);
// snprintf 的第二个参数表示了输出格式化字符串的最大字节数包括'\0'。参数值最好大一点
//在GBK编码模式下,一个汉字占用两个字节的大小; 按照ASCII码,一个数字占用一个字节
//分析 “今年20岁” == ‘今’ ‘年’ ‘2’ ‘0’ ‘岁’ ‘\0’ 占用9个字节

通过snprintf 这种方式可以实现在 SYN6288 中输出变量的值

# include<stdio.h>
	char result[100];
	int testNumber = 10+1;
	sprintf(result,"测试变量插入 %d ",testNumber);

	SYN_FrameInfo(0,(uint8_t *)result);

04 其他有关知识点

04-1 GB2312 GBK编码

  • GB2312:它是较早的汉字编码标准,包含的字符数量有限。包含6763个汉字和682个非汉字字符,如常见汉字“的”“是”及希腊字母“α”等。
  • GBK:GBK是GB2312的扩展,它包含了更多的字符.扩展至21003个汉字,增加生僻字、繁体字、少数民族文字等,如古籍生僻字“𠃋”。
编码方式
  • GB2312:双字节编码,范围0x21 - 0x77(高位、低位字节)。
  • GBK:也是双字节编码,但它扩展了编码范围。范围0x81 - 0xFE(高位字节)、0x40 - 0xFE(低位字节,除去0x7F)。
兼容性
  • GB2312:早期标准,用于老旧计算机系统和特定工业控制系统。
  • GBK:向下兼容GB2312.也就是说,GB2312中的所有字符在GBK中都有对应的编码,并且编码值是相同的。这使得GBK在很多场景下可以无缝替代GB2312。广泛用于现代中文操作系统和办公软件,如Windows记事本。

在这里插入图片描述

例子:Keil里的GB2312编码

  Frame_Info[4] = 0x01 | Music << 4 ; //构造命令参数:背景音乐设定
[M][u][s][i][c][0][0][1]  --> 命令参数的第三位是 001 代表GBK编码方式 
KEIL的编码方式是GB2312因为GBK兼容GB2312所以语音合成没有出现错误

04-2 异或校验

异或计算(XOR)的规则是:相同为0,不同为1

输入 A输入 B输出 A ⊕ B
000
011
101
110

异或校验(XOR Checksum)是一种简单的错误检测方法,通过对所有数据字节进行逐字节异或操作,生成一个校验值(通常也是一个字节)。接收端通过同样的异或操作验证数据的完整性。如果计算出的校验值与发送的校验值一致,则认为数据未发生错误。

异或校验的优缺点

优点

  1. 计算高效:异或运算简单,速度快,适合硬件实现,特别适用于高效实时校验场景。
  2. 实现成本低:逻辑简单,适合资源受限环境(如嵌入式系统)。
  3. 单比特错误检测:可检测单比特错误,适用于传输和存储过程中的单比特翻转问题。
  4. 轻量级:占用存储空间和计算资源极少,适合对资源严苛的场景。

缺点

  1. 错误检测能力有限
  2. 无法定位错误
  3. 不适合高可靠性场景
例子

原始数据(3个字节):

Byte1: 0xFF (11111111)
Byte2: 0x00 (00000000)
Byte3: 0xAA (10101010)

计算校验值:

Checksum = 0xFF XOR 0x00 XOR 0xAA
         = 0xFF XOR 0x00 = 0xFF
         = 0xFF XOR 0xAA = 0x55

校验值: 0x55

错误情况: 翻转 Byte1 的第0位和 Byte3 的第0位。

  • 翻转前:

    Byte1: 0xFF (11111111)
    Byte3: 0xAA (10101010)
    
  • 翻转后:

    Byte1: 0xFE (11111110)  // 第0位从1变为0
    Byte3: 0xAB (10101011)  // 第0位从0变为1
    

新的数据:

Byte1: 0xFE (11111110)
Byte2: 0x00 (00000000)
Byte3: 0xAB (10101011)

重新计算校验值:

Checksum' = 0xFE XOR 0x00 XOR 0xAB
          = 0xFE XOR 0x00 = 0xFE
          = 0xFE XOR 0xAB = 0x55

结果: 校验值仍为 0x55,与原始校验值相同,错误未被检测到

解释:两个不同字节的相同位被翻转,导致它们在异或校验中相互抵消,校验值保持不变。

05 完整代码

USART.C

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>
#include "Delay.h"
#include <stdlib.h>
//***语音合成***//

uint8_t USART2_RxData;		//定义串口接收的数据变量
uint8_t USART2_RxFlag;		//定义串口接收的标志位变量	

/**
  * 函    数:串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void USART2_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);	//开启USART2的时钟(语音合成)
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA2引脚初始化为复用推挽输出,PA2是TX
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA3引脚初始化为输入,PA3是RX
	
	/*USART初始化*/
	USART_InitTypeDef USART_InitStructure;					//定义结构体变量
	USART_InitStructure.USART_BaudRate = 9600;				//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择
	USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位
	USART_Init(USART2, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART2
	
	/*中断输出配置*/
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;		//选择配置NVIC的USART1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		//指定NVIC线路的响应优先级为
	NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*USART使能*/
	USART_Cmd(USART2, ENABLE);								//使能USART2,串口开始运行
}


/**
  * 函    数:串口发送一个字符串
  * 参    数:String 要发送字符串的首地址,长度
  * 返 回 值:无
  */
void USART2_SendString(u8 *DAT, u8 len)
{
	u8 i;
	for(i = 0; i < len; i++)
	{
		while((USART2->SR & 0X40) == 0);
		USART2->DR = (*DAT++);
	}
}

/**
  * 函    数:获取串口接收标志位
  * 参    数:无
  * 返 回 值:串口接收标志位,范围:0~1,接收到数据后,标志位置1,读取后标志位自动清零
  */
uint8_t USART2_GetRxFlag(void)
{
	if (USART2_RxFlag == 1)			//如果标志位为1
	{
		USART2_RxFlag = 0;
		return 1;					//则返回1,并自动清零标志位
	}
	return 0;						//如果标志位为0,则返回0
}

/**
  * 函    数:获取串口接收的数据
  * 参    数:无
  * 返 回 值:接收的数据,范围:0~255
  */
uint8_t USART2_GetRxData(void)
{
	return USART2_RxData;			//返回接收的数据变量
}

/**
  * 函    数:USART2中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */

void USART2_IRQHandler(void)
{
	if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断
	{
		USART2_RxData = USART_ReceiveData(USART2);				//读取数据寄存器,存放在接收的数据变量
		USART2_RxFlag = 1;										//置接收标志位变量为1
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);			//清除USART1的RXNE标志位
																//读取数据寄存器会自动清除此标志位
																//如果已经读取了数据寄存器,也可以不执行此代码
	}
}

USART.H

#ifndef __USART2_H
#define __USART2_H
#include "stdio.h"	
#include "stm32f10x.h"   

#define USART2_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART2_RX 			1		//使能(1)/禁止(0)串口1接收
	  	
void USART2_Init(void);
void USART2_SendString(u8 *DAT, u8 len);
uint8_t USART2_GetRxFlag(void);
uint8_t USART2_GetRxData(void);

#endif

syn6288.c

#include "syn6288.h"
#include "usart2.h"
#include "string.h"
#include "delay.h"
//***语音合成***//

//Music:选择背景音乐。0:无背景音乐,1~15:选择背景音乐
//HZdata字符串内输入空格可以使语音合成模块在文字之间停顿(相当于休止符)
void SYN_FrameInfo(u8 Music, u8 *HZdata)
{
  /****************需要发送的文本**********************************/
  unsigned  char  Frame_Info[50];
  unsigned  char  HZ_Length;
  unsigned  char  ecc  = 0;  			//定义校验字节
  unsigned  int i = 0;
  HZ_Length = strlen((char*)HZdata); 			//需要发送文本的长度

  /*****************帧固定配置信息**************************************/
  Frame_Info[0] = 0xFD ; 			//构造帧头FD
  Frame_Info[1] = 0x00 ; 			//构造数据区长度的高字节
  Frame_Info[2] = HZ_Length + 3; 		//构造数据区长度的低字节
  Frame_Info[3] = 0x01 ; 			//构造命令字:合成播放命令
  Frame_Info[4] = 0x01 | Music << 4 ; //构造命令参数:背景音乐设定

  /*******************校验码计算***************************************/
  for(i = 0; i < 5; i++)   				//依次发送构造好的5个帧头字节
  {
    ecc = ecc ^ (Frame_Info[i]);		//对发送的字节进行异或校验
  }

  for(i = 0; i < HZ_Length; i++)   		//依次发送待合成的文本数据
  {
    ecc = ecc ^ (HZdata[i]); 				//对发送的字节进行异或校验
  }
  /*******************发送帧信息***************************************/
  memcpy(&Frame_Info[5], HZdata, HZ_Length);
  Frame_Info[5 + HZ_Length] = ecc;
  USART2_SendString(Frame_Info, 5 + HZ_Length + 1);
}


/***********************************************************
* 名    称: YS_SYN_Set(u8 *Info_data)
* 功    能: 主函数	程序入口
* 入口参数: *Info_data:固定的配置信息变量
* 出口参数:
* 说    明:本函数用于配置,停止合成、暂停合成等设置 ,默认波特率9600bps。
* 调用方法:通过调用已经定义的相关数组进行配置。
**********************************************************/
void YS_SYN_Set(u8 *Info_data)
{
  u8 Com_Len;
  Com_Len = strlen((char*)Info_data);
  USART2_SendString(Info_data, Com_Len);
}


syn6288.h

#ifndef __SYN6288_H
#define __SYN6288_H

#include "stm32f10x.h" 

void SYN_FrameInfo(u8 Music, u8 *HZdata);
void YS_SYN_Set(u8 *Info_data);

#endif

参考
【嵌入式 | 数据校验】BCC校验(异或校验)-CSDN博客

历时80天,我们做出了自己的电子宠物_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值