JQ8900语音播报模块串口驱动移植与TI MSPM0开发板应用实战
JQ8900语音播报模块串口驱动移植与TI MSPM0开发板应用实战最近在做一个需要语音提示的小项目用到了JQ8900这个语音模块。它价格便宜控制简单还能像U盘一样直接拷贝语音文件特别适合学生和快速原型开发。但网上资料比较零散尤其是针对TI MSPM0系列比如立创开发板的教程不多。今天我就把自己在MSPM0上移植JQ8900串口驱动的完整过程包括踩过的坑和调试心得手把手分享给大家。这篇文章适合正在使用TI MSPM0平台比如基于MSPM0G3507的立创开发板做项目需要给产品增加语音播报功能的同学。我会从模块介绍、硬件连接、软件配置、代码编写到最终测试一步步带你走完整个流程。学完你就能在自己的MSPM0工程里用串口轻松控制JQ8900播放任意语音了。1. 认识JQ8900语音模块三种控制模式与硬件连接在写代码之前咱们先搞清楚要驱动的对象。JQ8900是一个集成了MP3解码和功放的语音播报模块你只需要给它一个存储了声音文件的SPI Flash它就能播放出来。模块核心特点供电灵活输入电压范围是2.8V到5.5V咱们的开发板通常提供3.3V直接接上就能用。超低功耗待机电流只有500uA到10mA对电池供电的设备很友好。更换语音超方便这是它最大的优点模块上的SPI Flash在电脑上会被识别成一个U盘。你想换提示音不用任何专用软件直接把新的音频文件比如“叮咚.wav”拖进去按规则重命名如“00001.mp3”就行。出厂时里面已经存了10首测试语音。三种控制模式怎么选JQ8900给了我们三种“指挥”它播放的方式单独IO控制每个语音文件对应一个物理引脚给个高电平或低电平信号就播放。适合固定、简单的场景比如一个按钮触发一个提示音。一线串行控制只用一根数据线通过发送特定时序的脉冲信号来发送控制指令。节省引脚但时序要求严格编程稍复杂。两线串口控制就是我们今天重点要用的方式。通过标准的UARTTX RX两根线发送十六进制指令来控制比如播放第几首、暂停、调节音量等。这种方式最灵活也最常用。注意原始资料里提到两线串口模式的通信参数是固定的波特率9600数据位8位停止位1位无校验位N。这个参数在配置单片机串口时必须完全一致否则模块“听”不懂你的指令。硬件连接以立创开发板为例模块和开发板的连接非常简单主要是电源和串口线VCC- 开发板的3.3V引脚GND- 开发板的GND引脚RX- 开发板的UART TX引脚单片机发送模块接收TX- 开发板的UART RX引脚单片机接收模块发送可选接如果你不需要读取模块状态可以不接根据原始文章的指导我们选择使用开发板上PA8和PA9引脚的附加串口1功能。PA8作为TX PA9作为RX。2. 使用SYSCONFIG工具配置MSPM0的UART与GPIOTI为MSPM0系列提供了图形化的SYSCONFIG配置工具可以自动生成底层初始化代码非常方便。咱们跟着步骤来打开工程找到配置文件在你的CCS或IAR工程里找到并双击empty.syscfg这个文件你的工程可能不叫empty但后缀是.syscfg。启动SYSCONFIG工具在打开的界面里点击菜单栏的Tools然后选择SYSCONFIG。添加UART外设配置在SYSCONFIG界面点击ADD按钮在搜索框输入“UART”找到并添加UART外设。通常选择第一个可用的UART实例例如UART0或UART1。在配置面板中将Baud Rate波特率设置为9600。Data Size数据位设为8 bits。Stop Bits停止位设为1。Parity校验位选择None。最关键的一步在Pin Configuration引脚配置里将TX引脚指定为PA8RX引脚指定为PA9。工具会自动帮你把这些引脚配置为UART功能。添加GPIO配置为一线模式备用虽然我们主要用串口模式但驱动代码里也包含了一线控制的函数。所以我们需要配置一个GPIO引脚。再次点击ADD搜索“GPIO”添加一个GPIO实例。在引脚配置中选择一个空闲的GPIO比如PA10将其功能设置为GPIO Output。保存配置点击保存按钮。这时可能会弹出一个警告框提示你要覆盖或更新一些文件一定要选择Yes to All。生成代码保存后工具会自动生成或更新底层驱动代码。你可能会看到工程开始编译即使有警告也先不用管重点是配置已生效。完成这步后所有关于引脚和UART的宏定义都自动生成了并集中放在ti_msp_dl_config.h文件里。因为这个文件通常已经被包含在board.h中所以我们后续编程只需要包含board.h头文件就行了。3. 手把手编写JQ8900驱动代码配置好硬件底层接下来就是编写我们自己的应用层驱动了。我们需要创建两个文件bsp_jq8900.c和bsp_jq8900.h。3.1 头文件 (bsp_jq8900.h) 的编写头文件主要进行宏定义和函数声明。#ifndef _BSP_JQ8900_H_ #define _BSP_JQ8900_H_ #include board.h // 包含所有SYSCONFIG生成的配置 // 是否开启串口调试用于打印从JQ8900模块接收到的数据如果需要 #define DEBUG 1 // 串口接收缓冲区最大长度 #define JQ8900_RX_LEN_MAX 250 // 一线控制模式下的GPIO操作宏定义 // 假设我们在SYSCONFIG中为一线控制配置的引脚是PA10 // GPIO_PORT和GPIO_VPP_PIN需要在board.h或根据你的配置定义 #define SET_JQ8900_APP(X) ( (X) ? (DL_GPIO_setPins(GPIO_PORT, GPIO_VPP_PIN)) : (DL_GPIO_clearPins(GPIO_PORT, GPIO_VPP_PIN)) ) // 函数声明 void JQ8900_Init(void); void JQ8900_USART_send_String(unsigned char *str, unsigned int len); void JQ8900_USART_Send_Bit(unsigned char ch); void SendData (unsigned char addr); // 一线控制函数 void Clear_JQ8900_RX_BUFF(void); #endif3.2 源文件 (bsp_jq8900.c) 的编写这是驱动的核心包含了串口发送、接收以及一线控制的实现。#include bsp_jq8900.h #include stdio.h #include string.h // 定义接收缓冲区和相关状态变量 unsigned char JQ8900_RX_BUFF[JQ8900_RX_LEN_MAX]; unsigned char JQ8900_RX_FLAG 0; // 接收完成标志 unsigned char JQ8900_RX_LEN 0; // 接收到的数据长度 /** * brief 向JQ8900模块发送单个字节 * param ch 要发送的字符 * note 内部会等待串口发送空闲防止数据覆盖 */ void JQ8900_USART_Send_Bit(unsigned char ch) { // 等待串口1UART_1_INST发送缓冲区空闲 while( DL_UART_isBusy(UART_1_INST) true ); // 发送数据 DL_UART_Main_transmitData(UART_1_INST, ch); } /** * brief 向JQ8900模块发送字符串或指令数组 * param str 要发送的数据指针 * param len 要发送的数据长度 */ void JQ8900_USART_send_String(unsigned char *str, unsigned int len) { while( len-- ) { JQ8900_USART_Send_Bit(*str); } } /** * brief 清除接收缓冲区 */ void Clear_JQ8900_RX_BUFF(void) { unsigned char i JQ8900_RX_LEN_MAX - 1; while(i) { JQ8900_RX_BUFF[i--] 0; } JQ8900_RX_LEN 0; JQ8900_RX_FLAG 0; } /** * brief JQ8900模块初始化 * note 主要初始化串口中断用于接收模块返回的数据如果需要 */ void JQ8900_Init(void) { // 清除串口中断标志位 NVIC_ClearPendingIRQ(UART_1_INST_INT_IRQN); // 使能串口接收中断 NVIC_EnableIRQ(UART_1_INST_INT_IRQN); // 注意还需要在SYSCONFIG中使能UART的接收中断功能 } /** * brief 一线串行通信控制函数 * param addr 要发送的8位指令对应语音序号等 * note 通过一个GPIO引脚模拟特定时序来控制模块 */ void SendData (unsigned char addr) { unsigned char i; SET_JQ8900_APP(1); // 起始位先拉高电平 delay_us (500); SET_JQ8900_APP(0); // 开始引导码拉低电平 delay_ms (4); // 关键延时必须大于2ms建议4ms // 发送8位数据从最低位(LSB)开始 for ( i 0; i 8; i ) { SET_JQ8900_APP(1); // 每个数据位都以高电平开始 if ( addr 0x01 ) // 判断当前位是1还是0 { // 发送‘1’高电平持续1300us低电平持续500us delay_us (1300); SET_JQ8900_APP(0); delay_us (500); } else { // 发送‘0’高电平持续500us低电平持续1300us delay_us (500); SET_JQ8900_APP(0); delay_us (1300); } addr 1; // 右移准备发送下一位 } SET_JQ8900_APP(1); // 发送结束拉高电平 delay_ms(10); // 指令间延时建议10ms以上 } /** * brief 串口中断服务函数 * note 当串口收到JQ8900模块返回的数据时会自动进入此函数 */ void UART_1_INST_IRQHandler(void) { // 判断中断来源 switch( DL_UART_getPendingInterrupt(UART_1_INST) ) { case DL_UART_IIDX_RX: // 如果是接收中断 // 读取接收到的数据 JQ8900_RX_BUFF[ JQ8900_RX_LEN ] DL_UART_Main_receiveData(UART_1_INST); // 如果定义了DEBUG可以通过printf打印查看需重定向printf到串口 #if DEBUG printf(%c, JQ8900_RX_BUFF[ JQ8900_RX_LEN ]); #endif // 更新接收长度和缓冲区结束符 JQ8900_RX_LEN ( JQ8900_RX_LEN 1 ) % JQ8900_RX_LEN_MAX; JQ8900_RX_BUFF[JQ8900_RX_LEN] \0; // 设置接收完成标志主循环中可以检测此标志来处理数据 JQ8900_RX_FLAG 1; break; default: // 处理其他类型的中断如发送中断 break; } }提示UART_1_INST_IRQHandler这个函数名是SYSCONFIG工具根据你的UART实例名自动生成的。如果你配置的是UART0函数名可能就是UART_0_INST_IRQHandler。一定要去ti_msp_dl_config.h或生成的启动文件里确认一下正确的函数名。4. 在主程序中集成与测试驱动写好了最后就是在主函数里调用它进行功能验证。我们以“两线串口控制模式”播放下一曲为例。#include board.h #include stdio.h #include bsp_jq8900.h int main(void) { // JQ8900串口控制指令0xAA 0x06 0x00 0xB0 表示“播放下一曲” uint8_t send_buff[4] {0xAA, 0x06, 0x00, 0xB0}; // 开发板初始化时钟、外设等 board_init(); // 初始化JQ8900模块主要是串口中断 JQ8900_Init(); printf(JQ8900 Test Start...\r\n); while(1) { // 每隔3秒发送一次“下一曲”指令 JQ8900_USART_send_String(send_buff, 4); delay_ms(3000); // 延时3秒 // 如果需要测试一线控制模式可以调用 // SendData(0x01); // 播放第一首语音 // delay_ms(3000); } }上电效果如果连接和代码正确模块会从第一首语音开始播放每3秒自动切换到下一首直到播放完SPI Flash中所有的语音文件后又会从第一首开始循环。调试心得指令不生效先查硬件确保TX、RX线没有接反共地良好电源电压稳定在3.3V。波特率是关键务必确认单片机串口和JQ8900模块都严格设置在9600, 8N1。指令格式要对串口模式发送的是十六进制字节数组不是字符串。0xAA是帧头0xB0是校验和前面字节的和取低8位。不同的功能对应不同的指令需要查阅JQ8900的详细指令集文档。一线模式的时序是魔鬼如果使用一线模式delay_us(500)和delay_ms(4)这些延时必须尽可能精确。用示波器查看GPIO引脚波形与数据手册的时序图对比是调试的最好方法。移植成功的完整工程代码可以参考原始资料中提供的百度网盘链接进行下载和对照学习。希望这篇实战教程能帮你顺利在TI MSPM0开发板上驱动JQ8900让你的项目“开口说话”。