基于单片机与BY8301-16P 语音模块间的串口通信,实现播放指定音频_by8301语音模块
基于单片机与BY8301-16P 语音模块间的串口通信,实现播放指定音频
一、串口语音模块
这里我们使用的具体型号是:CH32V307VCT6开发板和BY8301-16P 语音模块。
以下是BY8301-16P语音模块的一些主要特点:
- 高品质音频播放: 支持MP3和WAV两种主流音频格式解码,提供高品质的声音输出。具有24位DAC输出,支持90dB的动态范围和85dB的信噪比(SNR)。
- 内置存储介质: 内置SPI-FLASH作为存储介质,通常有4MB(32Mbit)或更大的容量,最大可支持16M Byte容量的SPI FLASH(例如GD25Q128)。
- 方便的音频更新: 配有Micro USB接口,无需上位机软件,可以直接通过数据线连接电脑(支持XP和WIN7系统)自由更换FLASH中的音频内容,操作简便。
- 集成功放: 模块内置3W功放,可以直接驱动3W/4Ω或2W/8Ω的喇叭,方便使用,无需额外添加功放电路。
- 多种控制方式:
- IO口触发播放: 支持21段语音一对一触发播放,通过3个IO口(A、B、C)配合3.3K电阻接地或不接,可以选择8种硬件触发模式,应用广泛。
- UART异步串口控制: 内置标准UART异步串口接口(3.3V TTL电平),可以通过串口命令进行播放、暂停、上下曲、音量加减、选曲播放、广告插播等操作。这使得它可以通过单片机、电脑串口等进行灵活控制。
- 内置记忆功能: 支持音量、曲目、EQ(均衡器)掉电记忆功能。
- 小巧封装: 采用标准的2.54mm间距DIP16封装,体积小巧,方便集成到各种产品中。
- 宽电压供电: 工作电压范围为3.6V-5V,推荐值4.2V。
模块引脚原理图:
二、基础知识
1、引脚连接
BY8301-16P语音模块采用标准的UART异步串口接口(3.3V TTL电平)进行通信。这里我们使用CH32的串口三进行串口通信:
2、通信协议
- 数据格式:通信数据格式为1个起始位、8个数据位、无奇偶校验位和1个停止位。
- 控制功能:通过串口命令,可以实现播放、暂停、上下曲切换、音量调节等多种操作。
3、命令结构
- 格式:所有命令都遵循特定的十六进制格式:起始码、长度、操作码、参数1、参数2、校验和、结束码。
- 校验和:校验和通过异或(XOR)运算计算,用于确保数据传输的完整性。
4、主要控制命令
模块支持一系列控制命令,包括:
- 播放 (0x01):启动音频播放。
- 暂停 (0x02):暂停当前播放。
- 音量调节 (0x05/0x06):增加或减小音量。
- 曲目选择 (0x41):选择特定曲目进行播放。
- 设备切换 (0x35):如果连接了多个设备,可以切换播放源。
5、查询命令
模块还允许您查询其状态:
- 播放状态 (0x10):返回当前的播放状态(例如,正在播放、已暂停、已停止)。
- 其他查询:您还可以查询当前的音量、均衡器模式以及文件夹中的歌曲数量。
6、重要注意事项
- 命令间隔:发送连续命令时,确保每个命令之间至少有20毫秒的间隔。
- 组合播放命令:对于组合播放命令,间隔应小于6毫秒。
三、通信程序
1、 Usart3_Init()
串口三初始化函数
配置CH32微控制器的USART3(通用同步/异步收发器3)进行串口通信。以下是代码的详细分解:
void Usart3_Init(){ //结构体定义 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; //时钟使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3 , ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //GPIO 引脚配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); //USART3 参数配置 USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; USART_Init(USART3, &USART_InitStructure); USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); //中断配置 NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //使能 USART 和清除标志位 USART_Cmd(USART3, ENABLE); USART_ClearFlag(USART3,USART_FLAG_TC);//清空串口3的发送标志位}
-
结构体定义
这里定义了三个结构体变量,分别用于配置:
GPIO_InitStructure
:GPIO引脚。USART_InitStructure
:USART3模块。NVIC_InitStructure
:嵌套向量中断控制器,用于中断配置。
-
时钟使能
这两行代码的作用是使能相关外设的时钟:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3 , ENABLE);
:使能USART3模块的时钟。USART3挂载在APB1总线上。RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
:使能GPIOB端口的时钟。USART3的引脚(PB10和PB11)位于GPIOB端口。
-
GPIO 引脚配置
这段代码配置了GPIOB的PB10引脚用于USART3通信:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
:指定要配置的引脚是PB10。GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
:设置引脚的输出速度为50MHz,这对于高速串口通信是常见的设置。GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
:将引脚模式设置为复用推挽输出。对于USART,TX(发送)引脚通常需要配置为复用推挽输出。
-
USART3 参数配置
这段代码配置了USART3的通信参数:
USART_BaudRate = 9600;
:设置波特率为9600bps。这是BY8301-16P语音模块的常用波特率。USART_WordLength = USART_WordLength_8b;
:设置数据字长为8位。USART_StopBits = USART_StopBits_1;
:设置停止位为1位。USART_Parity = USART_Parity_No;
:禁用奇偶校验。USART_HardwareFlowControl = USART_HardwareFlowControl_None;
:禁用硬件流控制。USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
:使能USART3的发送和接收功能
-
中断配置
这段代码配置了USART3的接收中断:
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
:使能USART3的接收非空中断。这意味着当接收缓冲区有新的数据时,会触发中断。NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
:指定中断通道为USART3的中断。NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
:设置抢占优先级为1。NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
:设置子优先级为1。NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
:使能USART3中断。NVIC_Init(&NVIC_InitStructure);
:初始化NVIC
-
使能 USART 和清除标志位
USART_Cmd(USART3, ENABLE);
:使能USART3模块,使其开始工作。USART_ClearFlag(USART3,USART_FLAG_TC);
:清除USART3的发送完成标志位。这通常在发送数据之前或发送数据后进行,以确保发送状态的正确性
2、 audio_proc()
:语音处理函数
通过USART3(串口3)向BY8301-16P语音模块发送命令,播放指定编号的音频文件。
void audio_proc(uint8_t num){ // 定义一个包含串口发送命令的字节数组 uint8_t string[]={0x7E,0x05,0x41,0x00,num,0x05^0x41^0x00^num,0xEF}; uint8_t i = 0; // 循环计数器 for(i=0;i<7;i++) // 循环发送7个字节的命令 { USART_SendData(USART3, string[i]); // 发送当前字节 // 等待发送完成标志位被置位,确保当前字节发送完毕才发送下一个字节 while( USART_GetFlagStatus(USART3, USART_FLAG_TC)==0 ); }}
1. 、函数声明
void audio_proc(uint8_t num)
:void
: 表示函数没有返回值。audio_proc
: 函数名称,表示“音频处理”或“播放音频”。uint8_t num
: 函数接收一个名为num
的无符号8位整数作为参数。这个num
就是你想要播放的音频文件在模块内部的编号。
2. 定义串口发送命令数组
-
uint8_t string[]={0x7E,0x05,0x41,0x00,num,0x05^0x41^0x00^num,0xEF};
这行代码定义了一个字节数组string
,它包含了发送给BY8301-16P语音模块的串口命令。根据之前对BY8301-16P串口通信协议的了解,这是一个标准的**“选曲播放”**命令。让我们逐个分析这些字节:0x7E
: 起始码 (Start Code)。所有命令的开始字节。0x05
: 长度 (Length)。表示从“长度”到“校验码”之间的字节数。这里是0x05,0x41, 0x00, num,0x05^0x41^0x00^num
,共5个字节。所以实际长度是 5字节。0x41
: 操作码 (Operation Code)。根据手册,0x41
是“选曲播放”命令的操作码。0x00
: 参数1 (Parameter 1)。在选曲播放命令中,这个通常表示文件夹编号。0x00
通常代表根目录或不区分文件夹。num
: 参数2 (Parameter 2)。这就是传入函数的音频文件编号。例如,如果num
是1,模块会播放编号为1的音频文件。0x05^0x41^0x00^num
: 校验和 (Checksum)。这是从“长度”字节开始到“参数2”的所有字节的异或和。0x05
(长度)^ 0x41
(操作码)^ 0x00
(参数1)^ num
(参数2)。校验和用于验证数据传输的完整性。0xEF
: 结束码 (End Code)。所有命令的结束字节。
3. 循环发送字节
uint8_t i = 0;
:定义一个循环计数器i
,初始化为0。for(i=0;i<7;i++)
: 这是一个for
循环,将迭代7次。由于string
数组有7个字节,这个循环会逐个发送string
数组中的所有字节。USART_SendData(USART3, string[i]);
: 在每次循环中,这行代码将string
数组中的当前字节 (string[i]
) 发送给 USART3。while( USART_GetFlagStatus(USART3, USART_FLAG_TC)==0 );
: 这是一个阻塞等待循环。USART_GetFlagStatus(USART3, USART_FLAG_TC)
: 这个函数用于检查 USART3 的 发送完成标志位 (Transmission Complete flag)。当 USART3 成功发送完一个字节的数据后,这个标志位会被置位。==0
: 表示标志位没有被置位(即发送尚未完成)。while(...)
: 只要USART_FLAG_TC
标志位为0,程序就会一直停在这个while
循环中,等待当前字节发送完毕。这样做的目的是确保每个字节都完全发送出去之后,才发送下一个字节,避免数据混乱或丢失。
4、总结作用:
audio_proc
函数的作用是:
- 根据传入的
num
值(音频文件编号),构造一个完整的BY8301-16P语音模块的“选曲播放”命令帧。 - 通过USART3(串口3)将这个命令帧中的每一个字节逐个发送出去。
- 在发送每个字节后,会等待直到该字节完全发送完成,才继续发送下一个字节,确保数据的正确传输。
简单来说,调用 audio_proc(5);
就会尝试让BY8301-16P语音模块播放编号为5的音频文件。
3、audio_volume()
:音量设置函数
其主要目的是通过USART3(串口3)向BY8301-16P语音模块发送命令,用于设置播放音量。
void audio_volume(u8 volume){ // 定义一个包含串口发送命令的字节数组 u8 string[]={0x7e,0x04,0x31,volume,0x04^0x31^yinliang,0xef}; u8 i; // 循环计数器 for(i=0;i<6;i++) // 循环发送6个字节的命令 { USART_SendData(USART3,string[i]); // 发送当前字节 // 等待发送完成标志位被置位,确保当前字节发送完毕才发送下一个字节 while( USART_GetFlagStatus(USART3, USART_FLAG_TC)==0 ); }}
简单来说,调用 audio_volume(20);
就会尝试将BY8301-16P语音模块的音量设置为20(十六进制 0x14
)。通常,音量范围是0到30(0x00到0x1E)。
USART_SendData(USART3,string[i]); // 发送当前字节
// 等待发送完成标志位被置位,确保当前字节发送完毕才发送下一个字节
while( USART_GetFlagStatus(USART3, USART_FLAG_TC)==0 );
}
}
简单来说,调用 audio_volume(20);
就会尝试将BY8301-16P语音模块的音量设置为20(十六进制 0x14
)。通常,音量范围是0到30(0x00到0x1E)。
注意:如何把音频文件更新进BY8301-16P语音模块,请看官方的使用说明书。