一、整体介绍 话不多说,先看看整体原理图: 制作出来的实物图如下:
整体上,其实也就四个部分: (1) STM32主控部分; (2) VS1053B音频解码部分; (3) 按键控制部分; (4) OLED显示部分; 上电,通过一系列的测试和检测后,进入音乐播放界面,如图: 第一排显示的是我的标签, 第二行,显示的是当前播放歌曲的索引、总歌曲数目以及当前声音大小, 第三行,显示的是当前歌曲的播放进度以及位率, 第四行,显示的是当前歌曲名(因为没有取字库,所以我就把他翻译成了英文,肯定是不准确的,主要目的只是提示自己)。 此外,通过按键,可以对播放歌曲进行切换和音量的控制。 下面我们来分别看看这四个部分: 二、模块介绍 1. stm32主控 这里我们使用的是stm32F103VET6,内部Flash有512K,100pin的外部引脚,属于大容量芯片,足够我们diy。对于本设计,stm32这一块用到的知识点有:SDIO驱动SD卡,SPI驱动VS1053B芯片,IIC驱动OLED;对于程序,用的是stm32标准库,小伙伴们可以根据自己的需要,自行决定是否需要补一下相关方面的知识。 主控就不再多做介绍了,因为太常见了,这里只是提一下。 2. VS1053B音频解码 这一部分的原理图如图: VS1053B,是一款功能比较强大的音频解码芯片,该芯片可以实现对MP3/OGG/WMA/FLAC/WAV/AAC/MIDI等音频格式的解码,同时还可以支持ADPCM/OGG等格式的编码,经过我的测试,建议大家用最常用的.MP3 格式的音乐文件; 具体的介绍,请看资料里面的资料手册,那里说的很清楚,我再多说,显得就很尴尬了。 3. 按键 这个常见到不能再常见了,因为需要按键对歌曲进行切换和音量大小的控制,所以,这里只是简单的把他列出来而已。 4. OLED显示 OLED只是用来显示提示的作用,这里我们用的是0.96寸4pin的IIC驱动的OLED,也是非常常见的玩意儿,不清楚使用的,可以看看相关的资料。 三、程序代码: main.c文件: - #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "led.h"
- #include "oled.h"
- #include "bmp.h"
- #include "key.h"
- #include "malloc.h"
- #include "sdio_sdcard.h"
- #include "vs10xx.h"
- #include "mp3player.h"
- #include "exfuns.h"
- //通过串口打印SD卡相关信息
- void show_sdcard_info(void)
- {
- switch(SDCardInfo.CardType)
- {
- caseSDIO_STD_CAPACITY_SD_CARD_V1_1:printf("Card Type:SDSCV1.1\r\n");break;
- caseSDIO_STD_CAPACITY_SD_CARD_V2_0:printf("Card Type:SDSCV2.0\r\n");break;
- caseSDIO_HIGH_CAPACITY_SD_CARD:printf("Card Type:SDHC V2.0\r\n");break;
- caseSDIO_MULTIMEDIA_CARD:printf("Card Type:MMC Card\r\n");break;
- }
- printf("Card ManufacturerID:%d\r\n",SDCardInfo.SD_cid.ManufacturerID); //制造商ID
- printf("CardRCA:%d\r\n",SDCardInfo.RCA); //卡相对地址
- printf("CardCapacity:%d MB\r\n",(u32)(SDCardInfo.CardCapacity>>20)); //显示容量
- printf("CardBlockSize:%d\r\n\r\n",SDCardInfo.CardBlockSize); //显示块大小
- }
- intmain(void)
- {
- delay_init(); //延时函数初始化
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
- uart_init(115200); //串口初始化为115200
- LED_Init(); //初始化与LED连接的硬件接口
- KEY_Init(); //初始化按键
- VS_Init(); //初始化VS1053
- delay_ms(1000); //适当延时
- OLED_Init(); //OLED初始化
- OLED_ColorTurn(0);//0正常显示,1 反色显示
- OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
- OLED_Refresh();
- delay_ms(1000); //适当延时
- my_mem_init(SRAMIN); //初始化内部内存池
- exfuns_init(); //为fatfs相关变量申请内存
- f_mount(fs[0],"0:",1); //挂载SD卡
- f_mount(fs[1],"1:",1); //挂载FLASH.
- while(SD_Init())//检测不到SD卡
- {
- OLED_ShowString(0,0,"SD_ERROR!!",16); //错误提示信息闪烁
- delay_ms(200);
- OLED_ShowString(0,0," ",16);
- delay_ms(200);
- LED1=!LED1;//DS1闪烁
- }
- show_sdcard_info(); //打印SD卡相关信息
- //检测SD卡成功
- OLED_ShowString(0,0,"SD_OK ",16);
- delay_ms(1000);
- OLED_ShowString(0,0,"LHSMD- MP3",16);
- while(1)
- {
- LED1=0;
- OLED_ShowString(0,16,"storagetest",16);
- printf("RamTest:0X%04X\r\n",VS_Ram_Test());//打印RAM测试结果
- OLED_ShowString(0,16,"sintest ",16);
- VS_Sine_Test(); //正弦波测试
- delay_ms(1000);
- LED1=1;
- OLED_Clear();
- OLED_ShowString(0,0," LHSMD - MP3",16);
- mp3_play(); //放歌操作
- }
- }
复制代码 mp3player.c文件:- #include "mp3player.h"
- #include "vs10xx.h"
- #include "delay.h"
- #include "led.h"
- #include "key.h"
- //#include "lcd.h"
- #include "malloc.h"
- //#include "text.h"
- #include "string.h"
- #include "exfuns.h"
- #include "ff.h"
- #include "flac.h"
- #include "usart.h"
- #include "oled.h"
- //显示曲目索引
- //index:当前索引
- //total:总文件数
- void mp3_index_show(u16 index,u16 total)
- {
- //显示当前曲目的索引,及总曲目数
- OLED_ShowNum(0,16,index,3,16);
- OLED_ShowString(24,16,"/",16);
- OLED_ShowNum(32,16,total,3,16);
- }
- //显示当前音量
- void mp3_vol_show(u8 vol)
- {
- OLED_ShowString(64,16,"VOL:",16);
- OLED_ShowNum(105,16,vol,2,16); //显示音量
- }
- u8 time_buf[16];
- u16 f_kbps=0;//歌曲文件位率
- //显示播放时间,比特率 信息
- //lenth:歌曲总长度
- void mp3_msg_show(u32 lenth)
- {
- staticu16 playtime=0;//播放时间标记
- u16 time=0;// 时间变量
- u16sec=0;// 时间变量
- u16temp=0;
- if(f_kbps==0xffff)//未更新过
- {
- playtime=0;
- f_kbps=VS_Get_HeadInfo(); //获得比特率
- }
- time=VS_Get_DecodeTime();//得到解码时间
- if(playtime==0)playtime=time;
- elseif((time!=playtime)&&(time!=0))//1s时间到,更新显示数据
- {
- playtime=time;//更新时间
- temp=VS_Get_HeadInfo();//获得比特率
- if(temp!=f_kbps)
- {
- f_kbps=temp;//更新KBPS
- }
- if(f_kbps)sec=(lenth/f_kbps)/125;//得到秒钟数(文件长度(字节)/(1000/8)/比特率=持续秒钟数
- elsesec=0;//非法位率
- //显示播放时间
- sprintf((char*)time_buf,"%02d:%02d/%02d:%02d%003d",time/60,time%60,sec/60,sec%60,f_kbps);
- OLED_ShowString(0,32,time_buf,16);
- LED1=!LED1; //DS0翻转
- }
- }
- //得到path路径下,目标文件的总个数
- //path:路径
- //返回值:总有效文件数
- u16 mp3_get_tnum(u8 *path)
- {
- u8res;
- u16rval=0;
- DIR tdir; //临时目录
- FILINFOtfileinfo; //临时文件信息
- u8*fn;
- res=f_opendir(&tdir,(const TCHAR*)path); //打开目录
- tfileinfo.lfsize=_MAX_LFN*2+1; //长文件名最大长度
- tfileinfo.lfname=mymalloc(SRAMIN,tfileinfo.lfsize); //为长文件缓存区分配内存
- if(res==FR_OK&&tfileinfo.lfname!=NULL)
- {
- while(1)//查询总的有效文件数
- {
- res=f_readdir(&tdir,&tfileinfo); //读取目录下的一个文件
- if(res!=FR_OK||tfileinfo.fname[0]==0)break; //错误了/到末尾了,退出
- fn=(u8*)(*tfileinfo.lfname?tfileinfo.lfname:tfileinfo.fname);
- res=f_typetell(fn);
- if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件
- {
- rval++;//有效文件数增加1
- }
- }
- }
- myfree(SRAMIN,tfileinfo.lfname);
- returnrval;
- }
- //播放音乐
- void mp3_play(void)
- {
- u8res;
- DIR mp3dir; //目录
- FILINFOmp3fileinfo;//文件信息
- u8*fn; //长文件名
- u8*pname; //带路径的文件名
- u16totmp3num; //音乐文件总数
- u16curindex; //图片当前索引
- u8key; //键值
- u16 temp;
- u16*mp3indextbl; //音乐索引表
- while(f_opendir(&mp3dir,"0:/music"))//打开图片文件夹
- {
- OLED_ShowString(0,32,"musicfile ERR!",16);
- delay_ms(200);
- OLED_ShowString(0,32," ",16);
- delay_ms(200);
- }
- totmp3num=mp3_get_tnum("0:/music");//得到总有效文件数
- while(totmp3num==NULL)//音乐文件总数为0
- {
- OLED_ShowString(0,32,"nomusic file ",16); //没有音乐文件提示
- delay_ms(200);
- }
- mp3fileinfo.lfsize=_MAX_LFN*2+1; //长文件名最大长度
- mp3fileinfo.lfname=mymalloc(SRAMIN,mp3fileinfo.lfsize); //为长文件缓存区分配内存
- pname=mymalloc(SRAMIN,mp3fileinfo.lfsize); //为带路径的文件名分配内存
- mp3indextbl=mymalloc(SRAMIN,2*totmp3num); //申请2*totmp3num个字节的内存,用于存放音乐文件索引
- while(mp3fileinfo.lfname==NULL||pname==NULL||mp3indextbl==NULL)//内存分配出错
- {
- OLED_ShowString(0,32,"storageERR ",16);
- delay_ms(200);
- }
- VS_HD_Reset(); //VS1053硬复位
- VS_Soft_Reset(); //VS1053软复位
- vsset.mvol=200; //默认设置音量为200.
- mp3_vol_show((vsset.mvol-100)/5); //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30
- //记录索引
- res=f_opendir(&mp3dir,"0:/music"); //打开目录
- if(res==FR_OK)
- {
- curindex=0;//当前索引为0
- while(1)//全部查询一遍
- {
- temp=mp3dir.index; //记录当前index
- res=f_readdir(&mp3dir,&mp3fileinfo); //读取目录下的一个文件
- if(res!=FR_OK||mp3fileinfo.fname[0]==0)break; //错误了/到末尾了,退出
- fn=(u8*)(*mp3fileinfo.lfname?mp3fileinfo.lfname:mp3fileinfo.fname);
- res=f_typetell(fn);
- if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件
- {
- mp3indextbl[curindex]=temp;//记录索引
- curindex++;
- }
- }
- }
- curindex=0; //从0开始显示
- res=f_opendir(&mp3dir,(constTCHAR*)"0:/music"); //打开目录
- while(res==FR_OK)//打开成功
- {
- dir_sdi(&mp3dir,mp3indextbl[curindex]); //改变当前目录索引
- res=f_readdir(&mp3dir,&mp3fileinfo); //读取目录下的一个文件
- if(res!=FR_OK||mp3fileinfo.fname[0]==0)break; //错误了/到末尾了,退出
- fn=(u8*)(*mp3fileinfo.lfname?mp3fileinfo.lfname:mp3fileinfo.fname);
- strcpy((char*)pname,"0:/music/"); //复制路径(目录)
- strcat((char*)pname,(constchar*)fn); //将文件名接在后面
- OLED_ShowString(0,48," ",16); //清楚之前的显示
- OLED_ShowString(0,48,fn,16); //显示歌曲名字
- mp3_index_show(curindex+1,totmp3num);
- key=mp3_play_song(pname); //播放这个MP3
- if(key==2) //上一曲
- {
- if(curindex)curindex--;
- elsecurindex=totmp3num-1;
- }else if(key<=1)//下一曲
- {
- curindex++;
- if(curindex>=totmp3num)curindex=0;//到末尾的时候,自动从头开始
- }else break; //产生了错误
- }
- myfree(SRAMIN,mp3fileinfo.lfname); //释放内存
- myfree(SRAMIN,pname); //释放内存
- myfree(SRAMIN,mp3indextbl); //释放内存
- }
- //播放一曲指定的歌曲
- //返回值:0,正常播放完成
- // 1,下一曲
- // 2,上一曲
- // 0XFF,出现错误了
- u8 mp3_play_song(u8 *pname)
- {
- FIL* fmp3;
- u16 br;
- u8res,rval;
- u8*databuf;
- u16i=0;
- u8key;
-
- rval=0;
- fmp3=(FIL*)mymalloc(SRAMIN,sizeof(FIL));//申请内存
- databuf=(u8*)mymalloc(SRAMIN,4096); //开辟4096字节的内存区域
- if(databuf==NULL||fmp3==NULL)rval=0XFF;//内存申请失败.
- if(rval==0)
- {
- VS_Restart_Play(); //重启播放
- VS_Set_All(); //设置音量等信息
- VS_Reset_DecodeTime(); //复位解码时间
- res=f_typetell(pname); //得到文件后缀
- if(res==0x4c)//如果是flac,加载patch
- {
- VS_Load_Patch((u16*)vs1053b_patch,VS1053B_PATCHLEN);
- }
- res=f_open(fmp3,(constTCHAR*)pname,FA_READ);//打开文件
- if(res==0)//打开成功.
- {
- VS_SPI_SpeedHigh(); //高速
- while(rval==0)
- {
- res=f_read(fmp3,databuf,4096,(UINT*)&br);//读出4096个字节
- i=0;
- do//主播放循环
- {
- if(VS_Send_MusicData(databuf+i)==0)//给VS10XX发送音频数据
- {
- i+=32;
- }else
- {
- key=KEY_Scan(0);
- switch(key)
- {
- caseKEY1_PRES:
- rval=1; //下一曲
- break;
- caseKEY3_PRES:
- rval=2; //上一曲
- break;
- caseKEY2_PRES: //音量增加
- if(vsset.mvol<250)
- {
- vsset.mvol+=5;
- VS_Set_Vol(vsset.mvol);
- }elsevsset.mvol=250;
- mp3_vol_show((vsset.mvol-100)/5);//音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30
- break;
- caseKEY4_PRES: //音量减
- if(vsset.mvol>100)
- {
- vsset.mvol-=5;
- VS_Set_Vol(vsset.mvol);
- }else vsset.mvol=100;
- mp3_vol_show((vsset.mvol-100)/5); //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30
- break;
- }
- mp3_msg_show(fmp3->fsize);//显示信息
- }
- }while(i<4096);//循环发送4096个字节
- if(br!=4096||res!=0)
- {
- rval=0;
- break;//读完了.
- }
- }
- f_close(fmp3);
- }elserval=0XFF;//出现错误
- }
- myfree(SRAMIN,databuf);
- myfree(SRAMIN,fmp3);
- returnrval;
- }
复制代码
【必读】版权免责声明
1、本主题所有言论和内容纯属会员个人意见,与本论坛立场无关。2、本站对所发内容真实性、客观性、可用性不做任何保证也不负任何责任,网友之间仅出于学习目的进行交流。3、对提供的数字内容不拥有任何权利,其版权归原著者拥有。请勿将该数字内容进行商业交易、转载等行为,该内容只为学习所提供,使用后发生的一切问题与本站无关。 4、本网站不保证本站提供的下载资源的准确性、安全性和完整性;同时本网站也不承担用户因使用这些下载资源对自己和他人造成任何形式的损失或伤害。 5、本网站所有软件和资料均为网友推荐收集整理而来,仅供学习用途使用,请务必下载后两小时内删除,禁止商用。6、如有侵犯你版权的,请及时联系我们(电子邮箱1370723259@qq.com)指出,本站将立即改正。
|