【雕爷学编程】Arduino动手做(119)---JQ6500语音模块
37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来—小小的进步或是搞不掂的问题,希望能够抛砖引玉。
【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)
实验一百一十九:JQ6500语音模块 MP3模块 MCU串口控制播报 一对一5路控制 音乐IC
JQ6500
是一个提供串口的MP3芯片,完美的集成了MP3、WMV的硬解码。同时软件支持TF卡驱动,支持电脑直接更新spi flash的内容,支持FAT16、FAT32文件系统。通过简单的串口指令即可完成播放指定的音乐,以及如何播放音乐等功能,无需繁琐的底层操作,使用方便,稳定可靠是此款产品的最大特点。另外该芯片也是深度定制的产品,专为固定语音播放领域开发的低成本解决方案。
JQ6500语音模块功能
1、支持采样率(KHz):8/11.025/12/16/22.05/24/32/44.1/48
2、24 位 DAC 输出,动态范围支持 90dB,信噪比支持 85dB
3、完全支持 FAT16、FAT32 文件系统,最大支持 32G 的 TF 卡,支持 32G 的 U 盘、64M 字节的NORFLASH
4、多种控制模式,串口模式、AD 按键控制模式
5、广播语插播功能,可以暂停正在播放的背景音乐
6、音频数据按文件夹排序,最多支持 100 个文件夹,每隔文件夹可以分配 1000 首歌曲
7、30 级音量可调,10 级 EQ 可调
8、可以外挂 spi flash,连接电脑可以显示 spi flash 的盘符进行更新内容
9、可以通过单片机串口进行控制播放指定的音乐
10、在按键模式下,可以进行播放模式选择:脉冲可重复、脉冲不可重复、电平非保持可循环、电平保持可循环。
JQ6500语音模块应用
1、 车载导航语音播报
2、 公路运输稽查、收费站语音提示;
3、 火车站、汽车站安全检查语音提示;
4、 电力、通信、金融营业厅语音提示;
5、 车辆进、出通道验证语音提示;
6、 公安边防检查通道语音提示;
7、 多路语音告警或设备操作引导语音;
8、 电动观光车安全行驶语音告示;
9、 机电设备故障自动报警;
10、消防语音报警提示;
11、自动广播设备,定时播报。
Arduino实验开源代码
/* 【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程) 实验一百一十九:JQ6500语音模块 MP3模块 MCU串口控制播报 一对一5路控制 音乐IC GitHub官网:https://github.com/sleemanj/JQ6500_Serial#jq6500_serial 库文件 下载:http://sparks.gogo.co.nz/JQ6500_Serial.zip 项目:MP3播放器的最小示例,只需循环播放所有曲目。 */ #include <Arduino.h> #include <SoftwareSerial.h> #include <JQ6500_Serial.h> // Create the mp3 module object, // Arduino Pin 8 is connected to TX of the JQ6500 // Arduino Pin 9 is connected to one end of a 1k resistor, // the other end of the 1k resistor is connected to RX of the JQ6500 // If your Arduino is 3v3 powered, you can omit the 1k series resistor JQ6500_Serial mp3(8,9); void setup() { mp3.begin(9600); mp3.reset(); mp3.setVolume(20); mp3.setLoopMode(MP3_LOOP_ALL); mp3.play(); } void loop() { // Do nothing, it's already playing and looping :-) }
Arduino实验场景图
项目:语音模块JQ6500库说明
/* 【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程) 实验一百一十九:Q6500语音模块 MP3模块 MCU串口控制播报 GitHub官网:https://github.com/sleemanj/JQ6500_Serial#jq6500_serial 库文件 下载:http://sparks.gogo.co.nz/JQ6500_Serial.zip 项目:语音模块JQ6500库说明 */ 语音模块JQ6500库说明 play() 开始播放当前文件。 restart() 重新播放 pause() 暂停 next() 下一曲 volumeUp() 音量+ volumeDn() 音量- prev() 播放前一个文件。 nextFolder() 播放下一个文件夹。 prevFolder() 播放前一个文件夹。 setVolume(byte volumeFrom0To30) 音量设置 为一个特定的水平(0至30)。 setSource(byte source) 设置源以读取MP3数据 以下两种模式: MP3_SRC_BUILTIN -从板上闪存读取文件 MP3_SRC_SDCARD -从SD卡读取文件MP3_LOOP_NONE (JQ6500-28Ponly) setEqualizer(byte equalizerMode) 设置均衡器 将均衡器设置为6种预置模式中的一种。 以下是6种模式: MP3_EQ_NORMAL MP3_EQ_POP MP3_EQ_ROCK MP3_EQ_JAZZ MP3_EQ_CLASSIC MP3_EQ_BASS setLoopMode(byte loopMode) 设置循环模式 下面的5种模式 * * MP3_LOOP_ALL -循环遍历所有文件. * * MP3_LOOP_FOLDER -循环遍历同一文件夹中的所有文件(SD Cardonly) * * MP3_LOOP_ONE -循环的一个文件. * * MP3_LOOP_RAM -循环的一个文件(不确定它是如何不同于以往!) * * MP3_LOOP_NONE -没有循环,只需播放一个文件,然后停止。 (akaMP3_LOOP_ONE_STOP) playFileByIndexNumber(unsignedint fileNumber) 播放指定的语音 发挥基于其特定的文件(FAT表)指数。注意索引号 *与文件名无关(除非您上传/复制到文件名中的媒体)。 *对SD卡FAT表进行排序,为操作系统搜索FAT排序实用程序 *选择。 playFileNumberInFolderNumber(unsignedint folderNumber, unsigned int fileNumber) *根据文件夹和文件的名称在特定文件夹中播放特定文件。 *仅适用于SD卡。 *要使用这个函数,文件夹必须从00到99以及文件夹中的文件命名。 *必须命名为从000.mp3到999.mp3 所以对sd卡进行“/ 03 / 006播放MP3文件,使用MP3。 playfilenumberinfoldernumber(3, 6); sleep() 睡眠模式 如果使用SD卡,不推荐使用。原因:SD卡会出现无法读取,直到设备再次上电。 reset() 软复位 ;在实际操作中,有时可能需要对设备进行电源循环,这可能是必要的,因为有时它可能会有点混乱,尤其是如果改变了SD卡的状态。所以如果设计一个PCB /电路包括jq6500模块可能是值得包括这样的能力(即动力装置通过MOSFET可以打开/关闭时)。 getStatus() 状态查询命令,从设备获取状态。谨慎!这是由于以下原因,有些是不可靠的 * 1。工作的时候从上次的记忆(MP3_SRC_BUILTIN),停了下来,似乎永远不会回来,只有播放和暂停 * 2。有时停下来还当它是工作,要抓住这getStatus()其实查询模块几次,以确保它是真的知道它告诉了我们什么。 getVolume() 获取当前 音量 between 0 and 30 getEqualizer() 获取当前 均很器模式 getLoopMode() 获取当前 循环模式 getVersion() 获取版本 countFiles(byte source) 在指定媒体 计数文件数量。 *@param源之一mp3_src_builtin和mp3_src_sdcard * @返回文件数量上的媒体。 countFolders(byte source) 计数文件夹的数量在指定媒体。 *请注意,只有SD卡可以有文件夹。 *@param源之一mp3_src_builtin和mp3_src_sdcard * @return数量的文件夹上的媒体。 currentFileIndexNumber(bytesource) 当前播放(或暂停,或将播放下一个停止的文件)文件,返回文件的(FAT表)索引号。 *这个数可以用playfilebyindexnumber(); *@param源之一mp3_src_builtin和mp3_src_sdcard * @return文件数。 currentFilePositionInSeconds() *对于当前播放或暂停的文件,返回当前位置(以秒为单位)。 * @return当前播放的文件的秒数。 currentFileLengthInSeconds() *对于当前播放或暂停的文件,将文件的总长度以秒为单位返回。 * @return音频文件长度以秒为单位。 currentFileName(char *buffer,unsigned int bufferLength) 对“当前”文件名,在SD卡。 *当前文件是正在播放、暂停或停止的文件,然后可以是下一个播放,也可以是最后播放的,不确定的。 *最好只在播放或暂停时咨询,您知道SD卡是激活源。 *不幸的是,没有办法查询该设备,以找出哪些媒体是活动的来源(至少不是我所知道的)。 sendCommand(byte command, bytearg1, byte arg2, char *responseBuffer, unsigned int bufferLength) 发送命令到jq6500模块 其他版本: sendCommand(bytecommand); sendCommand(bytecommand, byte arg1); sendCommand(bytecommand, byte arg1, byte arg2); *@param command 从数据表发送的字节值。. *@param arg1 第一(如果有的话)参数字节 *@param arg2 第二个参数(如果存在)字节 *@param responseBuffer 缓冲区来存储一行回应,如果为空,没有响应读。 *@param buffLength 响应缓冲区包括空终止符的长度。 sendCommandWithUnsignedIntResponse(bytecommand) 发送命令到jq6500模块,并得到回应 *为查询命令的jq6500通常发送一个整数的响应(在UART为4进制数字)。 *@param命令字节的值将从数据表。 * @return模块响应。
Arduino实验开源代码之二
/* 【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程) 实验一百一十九:Q6500语音模块 MP3模块 MCU串口控制播报 GitHub官网:https://github.com/sleemanj/JQ6500_Serial#jq6500_serial 库文件 下载:http://sparks.gogo.co.nz/JQ6500_Serial.zip 项目:测试MP3播放器模块 */ #include <Arduino.h> #include <SoftwareSerial.h> #include <JQ6500_Serial.h> // Create the mp3 module object, // Arduino Pin 8 is connected to TX of the JQ6500 // Arduino Pin 9 is connected to one end of a 1k resistor, // the other end of the 1k resistor is connected to RX of the JQ6500 // If your Arduino is 3v3 powered, you can omit the 1k series resistor JQ6500_Serial mp3(8,9); void setup() { // put your setup code here, to run once: Serial.begin(9600); mp3.begin(9600); mp3.reset(); statusAndHelpOutput(); } void loop() { byte b; if(Serial.available()) { b = Serial.read(); switch(b) { case 'p': Serial.println("Play"); mp3.play(); return; case 'r': Serial.println("Restart"); mp3.restart(); return; case ' ': Serial.println("Pause"); mp3.pause(); return; case '>': Serial.println("Next"); mp3.next(); return; case '<': Serial.println("Prev"); mp3.prev(); return; case ']': Serial.println("Next Folder"); mp3.nextFolder(); return; case '[': Serial.println("Prev Folder"); mp3.prevFolder(); return; case '+': Serial.println("Vol +"); mp3.volumeUp(); return; case '-': Serial.println("Vol -"); mp3.volumeDn(); return; case 'm': Serial.println("Vol 0"); mp3.setVolume(0); return; case 'v': { char volBuff[10]; memset(volBuff, 0, sizeof(volBuff)); Serial.readBytesUntil('\n',volBuff, sizeof(volBuff)-1); mp3.setVolume(max(0,min(30, atoi(volBuff)))); Serial.print("Vol "); Serial.println(max(0,min(30, atoi(volBuff)))); } return; case 'e': { do { while(!Serial.available()); // Wait b = Serial.read(); if(b != ' ') break; // Allow "e N" or "eN" etc... } while(1); Serial.print("Equalizer "); switch(b) { case 'N': Serial.println("Normal"); mp3.setEqualizer(MP3_EQ_NORMAL); break; case 'P': Serial.println("Pop"); mp3.setEqualizer(MP3_EQ_POP); break; case 'R': Serial.println("Rock"); mp3.setEqualizer(MP3_EQ_ROCK); break; case 'J': Serial.println("Jazz"); mp3.setEqualizer(MP3_EQ_JAZZ); break; case 'C': Serial.println("Classic"); mp3.setEqualizer(MP3_EQ_CLASSIC); break; case 'B': Serial.println("Bass"); mp3.setEqualizer(MP3_EQ_BASS); break; } } return; case 'l': { do { while(!Serial.available()); // Wait b = Serial.read(); if(b != ' ') break; // Allow "e N" or "eN" etc... } while(1); Serial.print("Loop "); switch(b) { case 'A': Serial.println("All"); mp3.setLoopMode(MP3_LOOP_ALL); break; // Plays the tracks one after another and repeats case 'F': Serial.println("Folder"); mp3.setLoopMode(MP3_LOOP_FOLDER); break; // Loop within folder case 'O': Serial.println("One (repeat playing same file)"); mp3.setLoopMode(MP3_LOOP_ONE); break; // | These seem to do the same, repeat the same track over and over case 'R': Serial.println("??? - Don't know what it means exactly, in the datasheet it is \"RAM\""); mp3.setLoopMode(MP3_LOOP_RAM); break; //- case 'N': case 'S': Serial.println("None (play file and stop)"); mp3.setLoopMode(MP3_LOOP_ONE_STOP); break; // Default, plays track and stops } } return; case 's': { do { while(!Serial.available()); // Wait b = Serial.read(); if(b != ' ') break; // Allow "e N" or "eN" etc... } while(1); Serial.print("Source "); switch(b) { case 'S': Serial.println("SD Card (if available)."); mp3.setSource(MP3_SRC_SDCARD); break; case 'B': Serial.println("on board memory.");mp3.setSource(MP3_SRC_BUILTIN); break; } } return; case 'f': { char fnumBuff[10]; memset(fnumBuff, 0, sizeof(fnumBuff)); Serial.readBytesUntil('\n',fnumBuff, sizeof(fnumBuff)-1); unsigned int fnum = strtoul(fnumBuff, NULL, 10); Serial.println(); Serial.print("Play file #"); Serial.print(fnum); Serial.println(F(" (if it exists).")); mp3.playFileByIndexNumber(fnum); // 48 == ord('0') return; } return; case 'F': { char fnumBuff[10]; memset(fnumBuff, 0, sizeof(fnumBuff)); Serial.readBytesUntil('/',fnumBuff, sizeof(fnumBuff)-1); unsigned int folnum = strtoul(fnumBuff, NULL, 10); memset(fnumBuff, 0, sizeof(fnumBuff)); Serial.readBytesUntil('\n',fnumBuff, sizeof(fnumBuff)-1); unsigned int fnum = strtoul(fnumBuff, NULL, 10); fnum = max(1,min(fnum, 999)); folnum = max(1,min(folnum, 99)); Serial.print("Play "); if(folnum < 10) Serial.print('0'); Serial.print(folnum); Serial.print('/'); if(fnum < 10) Serial.print("00"); else if(fnum < 10) Serial.print('0'); Serial.print(fnum); Serial.println(".mp3 (if it exists)."); mp3.playFileNumberInFolderNumber(folnum, fnum); // 48 == ord('0') } return; case '?': statusAndHelpOutput(); return; case 'S': Serial.println("Sleep"); mp3.sleep(); return; case 'z': Serial.println("Reset"); mp3.reset(); return; } } static unsigned long m = millis(); if(millis() > 1000 && m < (millis() - 1000)) { if((mp3.getStatus() == MP3_STATUS_PLAYING)) { Serial.print(F("Playing, Current Position: ")); Serial.print(mp3.currentFilePositionInSeconds()); Serial.print(F("s / ")); Serial.print(mp3.currentFileLengthInSeconds()); Serial.println('s'); } m = millis(); } } void statusAndHelpOutput() { Serial.println(); Serial.println(F("JQ6500 MP3 Player Demo")); Serial.println(F("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")); Serial.print(F("Status : ")); switch(mp3.getStatus()) { case MP3_STATUS_STOPPED: Serial.println(F("Stopped")); break; case MP3_STATUS_PLAYING: Serial.println(F("Playing")); break; case MP3_STATUS_PAUSED: Serial.println(F("Paused")); break; } Serial.print(F("Volume (0-30) : ")); Serial.println(mp3.getVolume()); Serial.print(F("Equalizer : ")); switch(mp3.getEqualizer()) { case MP3_EQ_NORMAL: Serial.println(F("Normal")); break; case MP3_EQ_POP: Serial.println(F("Pop")); break; case MP3_EQ_ROCK: Serial.println(F("Rock")); break; case MP3_EQ_JAZZ: Serial.println(F("Jazz")); break; case MP3_EQ_CLASSIC: Serial.println(F("Classic")); break; case MP3_EQ_BASS: Serial.println(F("Bass")); break; } Serial.print(F("Loop Mode : ")); switch(mp3.getLoopMode()) { case MP3_LOOP_ALL: Serial.println(F("Play all tracks, then repeat.")); break; case MP3_LOOP_FOLDER: Serial.println(F("Play all tracks in folder, then repeat.")); break; case MP3_LOOP_ONE: Serial.println(F("Play one track then repeat (loop track).")); break; case MP3_LOOP_RAM: Serial.println(F("Unknown function exactly, seems to play one track then repeat?")); break; case MP3_LOOP_ONE_STOP: Serial.println(F("Play one track then stop.")); break; } Serial.println(); Serial.print(F("# of On Board Memory Files : ")); Serial.println(mp3.countFiles(MP3_SRC_BUILTIN)); Serial.print(F("\"Current\" On Board Memory File Index: ")); Serial.println(mp3.currentFileIndexNumber(MP3_SRC_BUILTIN)); Serial.println(); Serial.print(F("# of SD Card Files : ")); Serial.println(mp3.countFiles(MP3_SRC_SDCARD)); Serial.print(F("# of SD Card Folders : ")); Serial.println(mp3.countFolders(MP3_SRC_SDCARD)); Serial.print(F("\"Current\" SD Card File Index: ")); Serial.println(mp3.currentFileIndexNumber(MP3_SRC_SDCARD)); Serial.print(F("\"Current\" SD Card File Name : ")); char buff[120]; mp3.currentFileName(buff, sizeof(buff)); Serial.println(buff); Serial.println(); Serial.println(F("Controls (type in serial monitor and hit send): ")); Serial.println(F("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")); Serial.println(F("? Display this menu.\n")); Serial.println(F("p Play\t\t> Next\t\t< Prev\n[space] Pause\tr Restart from start of file\n] Next folder\t[ Prev folder\n")); Serial.println(F("f[1-65534] Play file by (FAT table) index number\nF[01-99]/[001-999].mp3 Play [001-999].mp3 in folder [01-99]\n")); Serial.println(F("+ Vol up\t- Vol down\tm Mute\nv[0-30] Set volume\n\ne[N/P/R/J/C/B] Equalizer (N)ormal, (P)op, (R)ock, (J)azz, (C)lassic, (B)ass\nl[A/F/O/R/N] Loop (A)ll, (F)older, (O)ne, (R)???, (N)o Loop\ns[S/B] Switch to (S)D Card/(B)uilt In Memory\n\n")); }
实验串口返回情况