打印
[51单片机]

振南ZN-X开发板【51版】高难实验 之 SD卡MP3播放器+频谱显示...

[复制链接]
3781|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
znmcu|  楼主 | 2014-10-10 14:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
振南网站还在继续美化改进,暂时提供一个平台来发布振南的实验和相关资料!
http://www.znmcu.cn/znx_51_exp_sdmp3_mid.html
振南QQ:987582714  群:198521880
SD卡MP3播放器实验(SD卡+VS1003+TFT+文件信息、播放进度
与频谱柱状显示+按键控制)【51版】
【声明:】此实验是振南ZN-X开发板上的综合性高级实验,包含多个模块,在实验前,请先对各单个模块进行单独的测试,以确保各模块均工作正常!!!(模块单独测试实验请见本站实验发布!!)
实验说明:
基于振南的znFAT文件系统,振南作了很多的应用实例和实验,SD卡MP3播放器实验是诸多实验中比较精典的一个。这一实验振南又作了几个版本,简易的还有比较完整的。此实验就是比较完整的版本。
在此实验中,51单片机通过znFAT文件系统方案读取SD卡中的所有MP3文件,将其数据送到VS1003进行解码播放。同时,TFT液晶上显示MP3文件的相关信息、播放进度、柱状频谱等内容。还可以使用按键进行上一首、下一首的选择切换,音量控制、暂停、播放等控制。
为了让TFT显示得更加美观,振南在此实验中,增加了一个显示背景图的功能。上电之后,首先会将SD卡根目录下的znmcu.bmp图片文件显示在TFT上,在这个图片背景的基础上去显示MP3文件的信息、进度、频谱等内容。
【此实验已被振南收录到《振南znFAT--嵌入式FAT32文件系统》一书中,书中有更为详细的讲解,请关注详阅!!】
书的电子版请见:
此实验中需要的硬件有:
ZN-X开发板基板(51版)【也可使用自己的开发板,不过TFT液晶的接口信号比较多,自己接线可能会比较麻烦】
USB转串口模块(用于串口通信与程序下载)
TFT液晶(320X240像素,控制器为ILI9325)
VS1003 MP3解码播放模块
电路原理图及详解:
原理图注解:上图是此实验的ZN-X基板(51版)主要原理图(与此实验无关的部分均已裁掉)。ZN-X开发板上有两个SD卡模块接口,如图中⑤,它们由2x5的双排母接口与SD卡模块相插接,如下图所示。
此实验中使用SD卡模块接口2(不用SD卡1的原因是按钮与SD卡1的IO复用了)。SD卡2与单片机的连接如下表(使用51单片机的硬件SPI3进行驱动):
SD接口2信号
连到51 IO
SD2-CS
P27
SD2-SI
P40/MOSI3
SD2-SO
P41/MISO3
SD2-CLK
P43/SCLK3
SD卡模块的电路原理图:
上图是SD卡模块的原理图,对照模块实物图,不难理解其意义。
在此实验中可以看到我们将SD卡模块上的跳线设置为J3-J2,就是因为我们直接使用了ZN-X基板,它所使用的CPU(51)是3.3V的,而无需分压电路。
图中③为USB串口模块专用接口,主要用于完成供电、串口通信与程序下载,如下图。
图中④是51芯片,型号为STC15L2K60S2(60K ROM、2K RAM,主频最高33MHz)。
(1)是ZN-X基板上TFT液晶模块的接口。如下图:
图中上方的2X20排母接口就是此实验中TFT模块要插接的位置。有些人询问下方的2X17的接口是作什么用的。因为ZN-X可以支持多种TFT液晶模块,它们的接口都不尽相同,所以在ZN-X基板上有两个TFT液晶模块的接口。同时这些接口为其它模块的扩展提供了余地和机会,从而使得ZN-X开发板更加灵活,实验资源更加丰富。
②是TFT液晶的RD信号(至于RD信号为什么要经过74HC32或门,请详见《振南ZN-X开发板上RD信号的关键处理》一文)。
⑥是5个小按键,直接与单片机的IO相连,可用于按键控制。如下图所示:
⑦是VS1003 MP3解码播放模块接口
模块原理图:
模块与单片机的连接如下表:
VS1003 模块信号
连到51 IO
说明
MP3-SI
P23
SPI从机输入
MP3-SCK
P21
SPI时钟
MP3-XDCS
P46
数据片选
MP3-XCS
P47
命令片选
MP3-XRST
P45
复位
MP3-DREQ
P35
数据请求
TFT液晶模块说明:
此实验中所使用的TFT液晶模块控制器为ILI9325,分辨率为320x240(QVGA),16位色(6万5千色)。接口定义如下图:
ILI9325支持8位与16位接口模式(J3断开为8位模式,短接为16位模式)。此实验中使用的是8位模式。8位模式更适合低端单片机,比如51、AVR等,可以节省IO资源,不过每读写一个像素都要有两个数据读写过程,显示速度并不太理想。16位模式适合ARM这种比较高端的芯片,可以得到较高的显示速度。实际上,使用ARM来驱动TFT,如果使用8位模式反而会比较麻烦,而且效率低下。(此实验中并不需要太高的显示速度,只是简单得显示一些文字、矩形等,所以使用8位接口足够了!)
TFT与单片机的连接如下表:
TFT信号
51 引脚
说明
TFT-RDCLK-SEL
P27/A15
TFT RD信号复用选择 0选TFT
TFT-CS
P25/A13
TFT 片选 0有效
TFT-RS
P24/a12
TFT 数据/命令 选择 1数 0命
TFT-DATA
P0/AD0~7
TFT 的8位数据接口(16位接口高8位)
TFT-WR
P42/WR
TFT 写使能
TFT-RD
P44/RD
TFT 读使能
TFT-RST
P45
TFT 复位
实验方法与流程:
主要代码如下:『代码的核心是振南的znFAT文件系统』(《振南znFAT》一书已出版,详见本网站znFAT版块,里面有电子版可以试读)。上电之后,首先对SD卡、VS1003模块、TFT液晶进行初始化。成功之后,首先打开根目录下的znmcu.bmp图片文件,通过znFAT读取其像素数据,显示在TFT上,这是一个背景。然后开始依次读取SD卡根目录下的所有MP3文件,将其数据送至VS1003模块进行解码播放。在此期间,可以按键控制切到上一首,下一首(如果已经是第1首或最后一首,则按键无效),还可以控制音量大小、播放/暂停!正是有了这些功能,此实验才能称得上是一个简单的完整的MP3播放器!
主要代码:
#include "vs1003.h"
#include "myfun.h"
#include "uart.h"
#include "iospi.h"
#include "tft.h"
#include "znfat/znfat.h"
unsigned int idata i=0,j=0,res=0,color=0,n=0,len=0,pos=0,old_pos=-1;
unsigned long idata c=0;
unsigned int vol_val=0;
struct znFAT_Init_Args idata Init_Args; //初始化参数集合
struct FileInfo idata fileinfo;
unsigned char buf[800];
sbit KEY_PRE=P1^7;
sbit KEY_NEX=P1^6;
sbit KEY_VUP=P1^3;
sbit KEY_VDN=P1^4;
sbit KEY_PAU=P1^5;
#define COLOR(r,g,b) (((((((unsigned int)r)>>3)<<6)
      |(((unsigned int)g)>>2))<<5)|(((unsigned int)b)>>3))         
                               //r红色分量 0~31  g绿色分量 0~63  b蓝色分量 0~31
void main()
{
UART_Init();
VS_Reset();//初始化VS1003
VS_sin_test(100); //正弦测试
TFT_Init();
TFT_Clear(0xffff);
LoadPatch(); //为VS1003打补丁
znFAT_Device_Init(); //存储设备初始化
UART_Send_Str("SD卡初始化完毕\r\n");
znFAT_Select_Device(0,&Init_Args); //选择设备
res=znFAT_Init(); //文件系统初始化
if(!res) //文件系统初始化成功
{
  UART_Send_Str("Suc. to init FS\r\n");
  UART_Put_Inf("BPB_Sector_No:",Init_Args.BPB_Sector_No);   
  UART_Put_Inf("Total_SizeKB:",Init_Args.Total_SizeKB);
  UART_Put_Inf("BytesPerSector:",Init_Args.BytesPerSector);
  UART_Put_Inf("FATsectors:",Init_Args.FATsectors);  
  UART_Put_Inf("SectorsPerClust:",Init_Args.SectorsPerClust);
  UART_Put_Inf("FirstFATSector:",Init_Args.FirstFATSector);
  UART_Put_Inf("FirstDirSector:",Init_Args.FirstDirSector);
  UART_Put_Inf("FSsec:",Init_Args.FSINFO_Sec);
  UART_Put_Inf("Next_Free_Cluster:",Init_Args.Next_Free_Cluster);
  UART_Put_Inf("FreenCluster:",Init_Args.Free_nCluster);
}
else //文件系统初始化失败
{
UART_Put_Inf("Fail to init FS, Err Code:",res);
while(1); //如果初始化失败,则死在这里
}
res=znFAT_Open_File(&fileinfo,"/znmcu.bmp",0,1); //打开文件
if(!res)
{
  UART_Send_Str("Suc. to open file \r\n");
  UART_Send_Str("================================\n");
  UART_Send_Str("File_Name:");
  UART_Send_Str(fileinfo.File_Name);UART_Send_Enter();
  UART_Put_Inf("File_Size:",fileinfo.File_Size);
  UART_Send_Str("File_CDate:");
  UART_Put_Num(fileinfo.File_CDate.year); UART_Send_Str("年");
  UART_Put_Num(fileinfo.File_CDate.month);UART_Send_Str("月");
  UART_Put_Num(fileinfo.File_CDate.day);  UART_Send_Str("日");
  UART_Put_Num(fileinfo.File_CTime.hour); UART_Send_Str("时");
  UART_Put_Num(fileinfo.File_CTime.min);  UART_Send_Str("分");
  UART_Put_Num(fileinfo.File_CTime.sec);  UART_Send_Str("秒\r\n");
  UART_Put_Inf("File_StartClust:",fileinfo.File_StartClust);
  UART_Put_Inf("File_CurClust:",fileinfo.File_CurClust);
  UART_Put_Inf("File_CurSec:",fileinfo.File_CurSec);
  UART_Put_Inf("File_CurPos:",fileinfo.File_CurPos);
  UART_Put_Inf("File_CurOffset:",fileinfo.File_CurOffset);
  UART_Send_Str("================================\n");
  
  znFAT_ReadData(&fileinfo,0,54,buf);
  
  for(i=0;i<320;i++)
  {
   znFAT_ReadData(&fileinfo,fileinfo.File_CurOffset,720,buf);
   for(j=0;j<240;j++)
   {
    color=COLOR(buf[j*3+2],buf[j*3+1],buf[j*3+1]);
  TFT_Write_Data8(color>>8,color);
   }
  }
}
else
{
  UART_Send_Str("fail to open file \r\n");
}
//======================================================================
while(!znFAT_Open_File(&fileinfo,"/*.mp3",n++,1))
{
TFT_PutString(0,1,"            ");
TFT_PutString(0,3,"            ");
TFT_PutString(0,1,fileinfo.File_Name);
TFT_PutNum(0,3,fileinfo.File_Size);
while(len=znFAT_ReadData(&fileinfo,fileinfo.File_CurOffset,800,buf))
{
pos=fileinfo.File_CurOffset*10/fileinfo.File_Size;
if(pos!=old_pos) {TFT_Draw_proBar(pos,0xf800);old_pos=pos;}
VS_XDCS=0;    //打开SDI,此时可以向VS1003写入音频数据
len/=32;
for(i=0;i<len;i++)
{
VS_DREQ=1;
    while(!VS_DREQ);  //VS1003的DREQ为高才能写入数据
for(j=0;j<32;j++)
IOSPI_WriteByte(buf[i*32+j]);
   }
   VS_XDCS=1;         //关闭SDI
   VS_Write_Reg(VS_WRAMADDR,0x18,0x04);        //启动频谱数据传输
   TFT_Clear_Half(0xffff);
   for(j=0;j<10;j++)  //读取14个频谱值(VS1003输出14个频段的频)
   {
    TFT_Draw_Bar(9-j,VS_Read_Reg(VS_WRAM)/600,0x001f);
   }
KEY_PRE=1;
if(KEY_PRE==0)
{
  delay(100);
  if(KEY_PRE==0) {if(n>1) n-=2; else n=0; break;}
   }
KEY_NEX=1;
if(KEY_NEX==0)
{
  delay(100);
  if(KEY_NEX==0) {break;}
   }
KEY_VUP=1;
if(KEY_VUP==0)
{
  delay(100);
  if(KEY_VUP==0)
      {
       VS_Write_Reg(VS_VOL,vol_val?vol_val-10:0,vol_val?vol_val-10:0)
       ;vol_val?vol_val-=10:vol_val;}
      }
KEY_VDN=1;
if(KEY_VDN==0)
{
  delay(100);
  if(KEY_VDN==0) {VS_Write_Reg(VS_VOL   ,vol_val+10,vol_val+10);vol_val+=10;}
     }
KEY_PAU=1;
if(KEY_PAU==0)
{
  delay(100);
  if(KEY_PAU==0)
  {
   while(!KEY_PAU);
   while(KEY_PAU);
      }
     }         
    }
for(i=0;i<10;i++) TFT_Draw_proBar(i,0xffff);
pos=0;old_pos=-1;
VS_Flush_Buffer();//清空VS1003的数据缓冲区,此函数中最后将VS_XDCS置高,即关闭SDI
}
znFAT_Flush_FS(); //刷新文件系统  
while(1);
}
实验结果如下图所示:
  
源代码下载:


相关帖子

沙发
卢台长| | 2014-10-10 18:08 | 只看该作者
前排站位,此贴必火。。。。。

使用特权

评论回复
板凳
wangyin2111| | 2015-1-22 16:53 | 只看该作者
已经准备买书了,一套全买,支持!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:振南的znFAT -- 单片机上上的FAT32文件系统 www.znmcu.cn

4474

主题

5195

帖子

33

粉丝