8266+DS3231时钟之语音MP3-TF-16P模块使用【五】

这个时钟系列目前五篇分别是:

《8266+DS3231时钟之开发个时钟遇到的N个坑【一】》
《8266+ds3231时钟之arduino官网发布的DS3231库的分析【二】》
《8266+DS3231时钟之DS3231具体实现及代码【三】》
《8266+DS3231时钟之显示TM1638的使用【四】上》
《8266+DS3231时钟之显示TM1638的使用【四】下》
《8266+DS3231时钟之语音MP3-TF-16P模块使用【五】》
有兴趣的可以去看看,如果觉得对你有帮助,请点个赞。

前面几篇,完成了8266NodeMCU、DS3231时钟模块、TM1638及数码管显示、按键。做为一个时钟完整的功能,还缺一个声音装置用以播放震铃音。在网上我淘了一圈终于找到一个性价比超高的MP3模块。该模块即可以播放铃音,更重要的还能将时钟变成一个音乐播放器。
下面详细介绍模块的使用方法。分为硬件及软件部分。硬件部分是摘录自模块的厂家使用手册。同于内容较多,读者可以在购买模块时向商家索要详细的数据手册。软件是根据手册写的一个实现了基本通信的驱动,没有太复杂的应用功能。读者在理解完驱动的前提下,可以根据使用的需求再不断扩展驱动的应用层功能。

一、MP3模块基本情况

1.1简介

MP3-TF-16P是广州锐欣电子生产的一个模块,提供了一个串口模块,能与8266能完美结合。使8266能通过软件指令控制MP3的所以行为。该MP3串口可以只接RX口即只接受8266的控制信号,如果能接受损失部分回传功能的情况下,该特性不失为节约8266宝贵通讯引脚的一大优势。同时串口指令结构很简单 ,无需繁琐的底层操作,即可完成播放指定的音乐,和播放模式控制等。
在这里插入图片描述

1.2功能

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、多种控制模式可选。IO 控制模式、串口模式、AD 按键控制模式
5、广播语插播功能,可以暂停正在播放的背景音乐。广告播放完毕回到背景音继续播放
6、音频数据按文件夹排序,最多支持 100 个文件夹,每隔文件夹可以分配 255 首曲目
7、30 级音量可调,6 级 EQ 可调

1.3硬件及管脚

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

二、具体电路介绍

由于本文是8266+DS3231时钟系列中的一篇,根据文章内容的延伸,其中涉及到的硬件连接图及原理图都是根据功能的需要展现局部的原理图。这点,读者在阅读时要注意一下。
时钟的MP3功能原理图主要分为两个方向的连接,一个是与8266NodeMCU的连接,这个很简单,MPE模块的RX脚与NodeMCU的GIPO2(D4/TxD1)相连接。二是语音部分由MP3模块的DAC_R、DAC_L输出到一个外接的小功率功放模块(HW_12V_20W)。如果对语音功率要求没有那么高,那么可以从SPK1和SPK2外接一个2W左右的小喇叭就行了。三是从MP3引出一个USB的接口。具体见下图:
在这里插入图片描述

三、串口通讯协议

通讯格式

支持异步串口通讯模式,通过串口接受上位机发送的命令
通讯标准:9600 bps
数据位 :1
校验位 :none
流控制 :none
在这里插入图片描述
举个例子,如果我们指定播放发 SD 卡,就需要发送:7E FF 06 09 00 00 02 FF F0 EF
数据长度为 6 ,这 6 个字节分别是[FF 06 09 00 00 02] 。不计算起始、结束、和校验。再然后对得到
的结果进行校验和的运算。
校验和运算:指令串,去掉起始和结束位。将中间的6个字节累加,
(按上例 累加sum=0xff+0x06+0x09+0x00+0x00+0x02)最后取补码。补码可以用两种算法:
一种是:sum=~sum+1
一种是:sum=0-sum

通讯指令

在这里插入图片描述
3.4.1 指定歌曲播放指令
我们给出的指令是支持指定曲目播放的,歌曲的选择范围为 0~2999.其实是可以支持更多的,
因为涉及到文件系统的原因,支持过多的歌曲,会导致系统操作缓慢,一般的应用也不需要支持这
么多的文件。如果客户有非常规的应用,请事前和我们沟通。
(1)、例如选择第一首歌播放,串口的发送部分 7E FF 06 03 00 00 01 FF E6 EF
7E — 起始命令
FF — 版本信息
06 — 数据长度(不包含校验)
03 — 代表命令字节
00 — 是否需要应答[0x01:需要应答,0x00:不需要返回应答]
00 — 曲目的高字节[DH]
01 — 曲目的低字节[DL],这里代表的是第一首歌播放
FF — 校验的高字节
E6 — 校验的低字节
EF — 结束命令
(2)、对于选曲,如果选择第 100 首,首先将 100 转化为 16 进制,默认为双字节,就为 0x0064。
DH = 0x00 ; DL = 0x64
(3)、如果选择第 1000 首进行播放,首先将 1000 转化为 16 进制,默认为双字节,就为 0x03E8
DH = 0x03 ; DL = 0xE8
(4)、其它的操作依次类推即可,因为在嵌入式领域采用 16 进制是最为方便的一种操作。
3.4.2 指定音量播放指令
(1)、我们系统上电默认的音量为 30 级,如果要设置音量的话,直接发送相应的指令即可
(2)、例如指定音量为 15 级,串口发送的指令:7E FF 06 06 00 00 0F FF D5 EF
(3)、DH = 0x00 ; DL = 0x0F ,15 转化为 16 进制为 0x000F。可以参照播放曲目部分的说明
3.4.3 指定播放设备
(1)、我们的模块默认是支持 4 种类型的播放设备,只有设备在线才能指定设备去播放
设备是否在线,我们软件会自动检测,无需用户关系。
(2)、看下表,选择合适的指令发送
(3)、指定设备之后。模块会自动进入停止解码状态,等待用户指定曲目播放。从接收到指定设备到
模块内部完成初始化文件系统。大概需要 200ms。请等待 200ms 之后再发送指定曲目的指令。
在这里插入图片描述
3.4.4 指定文件夹播放
在这里插入图片描述
(1)、指定文件夹播放是我们制定的扩展功能,默认文件夹的命名方式为"01","11"这样的方式因为我
们的模块不支持汉字名称的文件夹名称识别,为了系统的稳定性和歌曲切换的速度,每个文件夹下
默认最大支持 255 首歌,最多支持 99 个文件夹的分类,如果客户有特殊要求,需要按照英文名称来
分类,我们也是可以实现的,但是名称只能是“GUSHI”、“ERGE”等英文名称组成。但是 mp3 文
件是需要增加前缀的,可以在“不得不爱.mp3”基础上改成“002 不得不爱.mp3”.
(2)、例如指定"01"文件夹的 100xxx.MP3 文件,串口发送的指令为:7E FF 06 0F 00 01 64 xx xx EF
DH:代表的是文件夹的名字,默认支持 99 个文件,即 01 – 99 的命名
DL:代表的是曲目,默认最多 255 首歌,即 0x01 ~ 0xFF
(3)、为了模块的标准性,必须同时指定文件夹和文件名,来锁定一个文件。单独指定文件夹或者单
独指定文件名也是可以的,但是这样文件的管理会变差。指定文件夹和指定曲目是支持 MP3、WAV
(4)、下面截两个图说明文件夹和文件名的指定[分左右两个图]
在这里插入图片描述
3.4.5 指定 MP3 文件夹中的曲目播放
在这里插入图片描述
(1)、在指定文件夹和文件名的基础上,我们扩展单个文件夹的功能,文件夹的命名必须为“MP3”
(2)、最多支持 65536 首曲目,但是鉴于文件系统的操作速度,可能会随着文件的增大,曲目切换的
速度会相应的减慢。
(3)、指定的文件命名如下:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

串口操作流程

在这里插入图片描述

四、驱动

下面是MP3模块的驱动原码,由于选择的该模块相当的简单易用,因此驱动结构也相对简洁。
为了方便阅读,所以定义及实现都放在mp3.h这个单一文件里。
驱动里只有一个MP3的类定义和具体实现。该对象用了模板对象编程,主要是为了同时适用于硬件串口或软串口。由于不同的MCU平台如8266和arduino的串口数量不一样,为了适用于不同的平台,采用了模板对象后,该驱动就使MP3模块即可以接硬件串口,也可以接软件串口,使用起来更加灵活。
驱动结构上只完成了两件工作。一件是计算命令字串的校验码,另一件是完成命令字串与入MP3。
这两项是最基本的工作。在主程序调用MP3对象内的写命令函数 SetCmdByte(uint8_t len, uint8_t cmd, uint8_t feedback, uint8_t paral1, uint8_t paral2);后,即可以完成命令的写入。因此在使用上可能没那么方便,但是更灵活,如果读者感觉不方便,可以在该驱动的基础上再扩展更严格的应用函数。这个留给读者自由发挥,必竟萝卜白菜各有所爱。
下面是驱动的具体代码,已做了非常详细的注释,希望都能很快看懂。
<mp3.h>

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <HardwareSerial.h>

    const uint8_t CMD_next= 0x01;   //下一曲
    const uint8_t CMD_prev=0x02;    //上一曲
    const uint8_t CMD_assign=0x03;      //指定曲目1-2999
    const uint8_t CMD_vol_add=0x04;     //音量+
    const uint8_t CMD_vol_reduce=0x05;  //音量-
    const uint8_t CMD_assign_vol=0x06 ; //指定音量0-30级
    const uint8_t CMD_EQ=0x07;           //指定EQ  0 Normal 1 Pop 2 Rock 3 Jazz 4 Classic 5 Bass
    const uint8_t CMD_assign_cyclic=0x08;  //0-2999指定单曲循环
    const uint8_t CMD_source=0x09;      //指定播放设备  1 U 2 SD 3 AUX 4 SLEEP 5 FLASH
    const uint8_t  CMD_sleep=0x0A ;      //进入睡眠
    const uint8_t CMD_reset=0x0C  ;    //模块复位
    const uint8_t CMD_play=0x0D  ;       //播放
    const uint8_t CMD_pause=0x0E ;       //暂停
    const uint8_t CMD_assign_dir=0x0F ; //指定文件夹播放  1-10需要自己设定
    const uint8_t CMD_all_cyclic=0x11;    //全部循环播放
    const uint8_t CMD_assign_dir_file=0x12;    //指定MP3文件夹曲目0-9999
    const uint8_t CMD_advertising=0x13;    //插播广告 
    const uint8_t CMD_stop=0x16;     //停止播放
    const uint8_t CMD_on_off_DAC=0x1A ;     //开启和关闭DAC



template <class SerialClass>
class Mp3{
    private:
        SerialClass *Serial_x;       //串口类型的指针,可以适应各种串口传递
        unsigned char CmdByte[20];   //存储命令字节
        unsigned char checksum[2];   //校验和,0放低字节,1放高字节

    public:
        //void Mp3();
        void Begin(SerialClass *Serial); //初始化MP3
      //校验和计算函数
        void CheckSum(const unsigned char *CmdByte_z  , unsigned char *checksum_z); //计算校验码
        void SetCmdByte(uint8_t len, uint8_t cmd, uint8_t feedback, uint8_t paral1, uint8_t paral2); //生成命令字串
        void WriteCmd(); //合并校验码,通过串口写入命令字节
};

//MP3初始化函数,传递串口的指针,并使mp3复位
template <class SerialClass>
void Mp3<SerialClass>::Begin(SerialClass *Serial){
    Serial_x=Serial;
    CmdByte[0]=0x7E;
    CmdByte[1]=0xFF;
    SetCmdByte(0x06,CMD_assign_cyclic,0x00,0x00,0x00);//模块复位    
}

//命令字串的设置函数,由外部调用设置命令字数组
template <class SerialClass>
void Mp3<SerialClass>::SetCmdByte(uint8_t len, uint8_t cmd, uint8_t feedback, uint8_t paral1, uint8_t paral2){
    CmdByte[0]=0x7E;
    CmdByte[1]=0xFF;
    CmdByte[2]=len;
    CmdByte[3]=cmd;
    CmdByte[4]=feedback;
    CmdByte[5]=paral1;
    CmdByte[6]=paral2;
    CheckSum(CmdByte,checksum);
    CmdByte[7]=checksum[1];
    CmdByte[8]=checksum[0];
    CmdByte[9]=0xEF;

    WriteCmd();  //把命令字写入MP3模块
}


//计算校验码后,把命令字串写入MP3模块
template <class SerialClass>
void Mp3<SerialClass>::WriteCmd(){
    CheckSum(CmdByte,checksum);
    CmdByte[CmdByte[2]+1]=checksum[1]; //高位
    CmdByte[CmdByte[2]+2]=checksum[0];  //低位
    CmdByte[CmdByte[2]+3]=0xEF; //结束位
    for (int i=0 ; i<CmdByte[2]+4;i++){
        Serial.print("CmdByte "+String(i)+"=");
        Serial.println(CmdByte[i],HEX);
        (*Serial_x).write(CmdByte[i]);
    }
}

//计算命令字校验位
template <class SerialClass>
void Mp3<SerialClass>::CheckSum(const unsigned char *CmdByte_z  , unsigned char *checksum_z){
/****************************************************************************** 
 - 功能描述:求和校验
 - 和校验的思路如下: 
 发送的指令,去掉起始和结束。将中间的 6 个字节进行累加,最后取补码(反码后又+1)也可以用0-累加后数。
 接收端就将接收到的一帧数据,去掉起始和结束。将中间的数据累加,再加上接收到的校验字节。刚好
为 0.这样就代表接收到的数据完全正确。
 //0      1       2         3      4      5      6      7     8     9    
  //7E    FF     数据长度    命令   应答?  数据H   数据L   校验H  校验L EF
  //           (不包含校验)
  //校验不计起始位,7E 不计数据长度位,则  FF+3+4+5+6
******************************************************************************/
    uint16_t sum=0x0000;          //16位数据,
    uint8_t *Mp3=(uint8_t*)CmdByte_z;    //取出指针,便于下面计算
    uint8_t *checksum=(uint8_t*)checksum_z;
  //从命令字(数据下标1开始加。(数组下标2)是内容长度位。取出数据长度就是要加的次数
    for (int i=0;i<*(Mp3+2);i++){  
          sum+=*(Mp3+1+i);    
    }
    sum=~sum+1 ;   //求反码
    //Serial.print("校验和为:");
    //Serial.println(sum,HEX);
  //移位,存入checksum
    *checksum=(uint8_t)(sum & 0x00FF);  //取得校验码的低8位
    *(checksum+1)=(uint8_t)(sum >> 8) ;//取得校验码的高8位
}

五、具体应用代码编写

根据上述的驱动,在具体使用时,只需要掌握好如下的几个结构就行,下面给出应用代码结构,读者可以自由就用。

#include <mp3.h>
.........
mp3相关的变量
Mp3<HardwareSerial> mp3;  //创建 MP3对象
//由于我的这硬件连接是选择了8266的硬件串口serial1,所以这里模板形参用的是HardwareSerial类
//如果用的是软串口,这里模板形参可以改为SoftwareSerial

..................
void setup()
{
  Serial.begin(9600);   
  Serial1.begin(9600);//设置Serial1波特率为9600

  mp3.Begin(&Serial1);  //mp3对象初始化,把Serial1的指针传给对象
................

 //播放开机欢迎曲
    mp3.SetCmdByte(0x06,CMD_assign_dir,0x00,0x01,0x01);  //01文件夹01文件是欢迎曲
   .................................
}

以上内容基本讲清了MP3-tf-16p模块在8266平台下使用的方法。是不是很简单 。数据手册如需详细文档,可以度娘上搜或找购买的商家下载。或私信我。但是请读者在阅读厂家的手册时一定要注意分辨,在文档中会出现很多错误。这里提个醒。
好了,到了今天这篇,已全部讲完了8266制作时钟的所有模块的内容。回头看看,内容还是挺多的,涉及了三种不同接口。对单片机开发能力的提升还是很有帮助的。麻雀虽小,五脏俱全。但仅有以上内容还不完整,要完成一个方便使用的物联网时钟,还需要有手机端的人机接口等应用。
从下一篇开始,进入手机端应用内容的讲解。并在最后一篇会有完整电路原理图及全部功能的一个总结结。希望对大家有所帮助。

  • 11
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑牛唱剧本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值