|
一、整體介紹 話不多說,先看看整體原理圖:
210513xlo6pe6se0esq0ll.png (390.22 KB)
下載附件
2020-5-24 14:59 上傳
制作出來的實物圖如下:
211030rnl2xdeolexne5u3.jpg (1.74 MB)
下載附件
2020-5-24 14:59 上傳
整體上,其實也就四個部分: (1) STM32主控部分; (2) VS1053B音頻解碼部分; (3) 按鍵控制部分; (4) OLED顯示部分; 上電,通過一系列的測試和檢測后,進(jìn)入音樂播放界面,如圖: 第一排顯示的是我的標(biāo)簽, 第二行,顯示的是當(dāng)前播放歌曲的索引、總歌曲數(shù)目以及當(dāng)前聲音大小, 第三行,顯示的是當(dāng)前歌曲的播放進(jìn)度以及位率, 第四行,顯示的是當(dāng)前歌曲名(因為沒有取字庫,所以我就把他翻譯成了英文,肯定是不準(zhǔn)確的,主要目的只是提示自己)。 此外,通過按鍵,可以對播放歌曲進(jìn)行切換和音量的控制。 下面我們來分別看看這四個部分: 二、模塊介紹 1. stm32主控 這里我們使用的是stm32F103VET6,內(nèi)部Flash有512K,100pin的外部引腳,屬于大容量芯片,足夠我們diy。對于本設(shè)計,stm32這一塊用到的知識點有:SDIO驅(qū)動SD卡,SPI驅(qū)動VS1053B芯片,IIC驅(qū)動OLED;對于程序,用的是stm32標(biāo)準(zhǔn)庫,小伙伴們可以根據(jù)自己的需要,自行決定是否需要補一下相關(guān)方面的知識。 主控就不再多做介紹了,因為太常見了,這里只是提一下。 2. VS1053B音頻解碼 這一部分的原理圖如圖:
210552f9jj2d9pxxxn2dnw.png (189.65 KB)
下載附件
2020-5-24 14:59 上傳
VS1053B,是一款功能比較強大的音頻解碼芯片,該芯片可以實現(xiàn)對MP3/OGG/WMA/FLAC/WAV/AAC/MIDI等音頻格式的解碼,同時還可以支持ADPCM/OGG等格式的編碼,經(jīng)過我的測試,建議大家用最常用的.MP3 格式的音樂文件; 具體的介紹,請看資料里面的資料手冊,那里說的很清楚,我再多說,顯得就很尷尬了。 3. 按鍵 這個常見到不能再常見了,因為需要按鍵對歌曲進(jìn)行切換和音量大小的控制,所以,這里只是簡單的把他列出來而已。 4. OLED顯示 OLED只是用來顯示提示的作用,這里我們用的是0.96寸4pin的IIC驅(qū)動的OLED,也是非常常見的玩意兒,不清楚使用的,可以看看相關(guān)的資料。 三、程序代碼: 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卡相關(guān)信息
- 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(); //延時函數(shù)初始化
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設(shè)置中斷優(yōu)先級分組為組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
- uart_init(115200); //串口初始化為115200
- LED_Init(); //初始化與LED連接的硬件接口
- KEY_Init(); //初始化按鍵
- VS_Init(); //初始化VS1053
- delay_ms(1000); //適當(dāng)延時
- OLED_Init(); //OLED初始化
- OLED_ColorTurn(0);//0正常顯示,1 反色顯示
- OLED_DisplayTurn(0);//0正常顯示 1 屏幕翻轉(zhuǎn)顯示
- OLED_Refresh();
- delay_ms(1000); //適當(dāng)延時
- my_mem_init(SRAMIN); //初始化內(nèi)部內(nèi)存池
- exfuns_init(); //為fatfs相關(guān)變量申請內(nèi)存
- 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卡相關(guān)信息
- //檢測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測試結(jié)果
- 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(); //放歌操作
- }
- }
復(fù)制代碼 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:當(dāng)前索引
- //total:總文件數(shù)
- void mp3_index_show(u16 index,u16 total)
- {
- //顯示當(dāng)前曲目的索引,及總曲目數(shù)
- OLED_ShowNum(0,16,index,3,16);
- OLED_ShowString(24,16,"/",16);
- OLED_ShowNum(32,16,total,3,16);
- }
- //顯示當(dāng)前音量
- 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;//播放時間標(biāo)記
- 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時間到,更新顯示數(shù)據(jù)
- {
- playtime=time;//更新時間
- temp=VS_Get_HeadInfo();//獲得比特率
- if(temp!=f_kbps)
- {
- f_kbps=temp;//更新KBPS
- }
- if(f_kbps)sec=(lenth/f_kbps)/125;//得到秒鐘數(shù)(文件長度(字節(jié))/(1000/8)/比特率=持續(xù)秒鐘數(shù)
- 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翻轉(zhuǎn)
- }
- }
- //得到path路徑下,目標(biāo)文件的總個數(shù)
- //path:路徑
- //返回值:總有效文件數(shù)
- 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); //為長文件緩存區(qū)分配內(nèi)存
- if(res==FR_OK&&tfileinfo.lfname!=NULL)
- {
- while(1)//查詢總的有效文件數(shù)
- {
- 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++;//有效文件數(shù)增加1
- }
- }
- }
- myfree(SRAMIN,tfileinfo.lfname);
- returnrval;
- }
- //播放音樂
- void mp3_play(void)
- {
- u8res;
- DIR mp3dir; //目錄
- FILINFOmp3fileinfo;//文件信息
- u8*fn; //長文件名
- u8*pname; //帶路徑的文件名
- u16totmp3num; //音樂文件總數(shù)
- u16curindex; //圖片當(dāng)前索引
- 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");//得到總有效文件數(shù)
- while(totmp3num==NULL)//音樂文件總數(shù)為0
- {
- OLED_ShowString(0,32,"nomusic file ",16); //沒有音樂文件提示
- delay_ms(200);
- }
- mp3fileinfo.lfsize=_MAX_LFN*2+1; //長文件名最大長度
- mp3fileinfo.lfname=mymalloc(SRAMIN,mp3fileinfo.lfsize); //為長文件緩存區(qū)分配內(nèi)存
- pname=mymalloc(SRAMIN,mp3fileinfo.lfsize); //為帶路徑的文件名分配內(nèi)存
- mp3indextbl=mymalloc(SRAMIN,2*totmp3num); //申請2*totmp3num個字節(jié)的內(nèi)存,用于存放音樂文件索引
- while(mp3fileinfo.lfname==NULL||pname==NULL||mp3indextbl==NULL)//內(nèi)存分配出錯
- {
- OLED_ShowString(0,32,"storageERR ",16);
- delay_ms(200);
- }
- VS_HD_Reset(); //VS1053硬復(fù)位
- VS_Soft_Reset(); //VS1053軟復(fù)位
- vsset.mvol=200; //默認(rèn)設(shè)置音量為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;//當(dāng)前索引為0
- while(1)//全部查詢一遍
- {
- temp=mp3dir.index; //記錄當(dāng)前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]); //改變當(dāng)前目錄索引
- 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/"); //復(fù)制路徑(目錄)
- 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; //產(chǎn)生了錯誤
- }
- myfree(SRAMIN,mp3fileinfo.lfname); //釋放內(nèi)存
- myfree(SRAMIN,pname); //釋放內(nèi)存
- myfree(SRAMIN,mp3indextbl); //釋放內(nèi)存
- }
- //播放一曲指定的歌曲
- //返回值:0,正常播放完成
- // 1,下一曲
- // 2,上一曲
- // 0XFF,出現(xiàn)錯誤了
- 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));//申請內(nèi)存
- databuf=(u8*)mymalloc(SRAMIN,4096); //開辟4096字節(jié)的內(nèi)存區(qū)域
- if(databuf==NULL||fmp3==NULL)rval=0XFF;//內(nèi)存申請失敗.
- if(rval==0)
- {
- VS_Restart_Play(); //重啟播放
- VS_Set_All(); //設(shè)置音量等信息
- VS_Reset_DecodeTime(); //復(fù)位解碼時間
- 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個字節(jié)
- i=0;
- do//主播放循環(huán)
- {
- if(VS_Send_MusicData(databuf+i)==0)//給VS10XX發(fā)送音頻數(shù)據(jù)
- {
- 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);//循環(huán)發(fā)送4096個字節(jié)
- if(br!=4096||res!=0)
- {
- rval=0;
- break;//讀完了.
- }
- }
- f_close(fmp3);
- }elserval=0XFF;//出現(xiàn)錯誤
- }
- myfree(SRAMIN,databuf);
- myfree(SRAMIN,fmp3);
- returnrval;
- }
復(fù)制代碼
【必讀】版權(quán)免責(zé)聲明
1、本主題所有言論和內(nèi)容純屬會員個人意見,與本論壇立場無關(guān)。2、本站對所發(fā)內(nèi)容真實性、客觀性、可用性不做任何保證也不負(fù)任何責(zé)任,網(wǎng)友之間僅出于學(xué)習(xí)目的進(jìn)行交流。3、對提供的數(shù)字內(nèi)容不擁有任何權(quán)利,其版權(quán)歸原著者擁有。請勿將該數(shù)字內(nèi)容進(jìn)行商業(yè)交易、轉(zhuǎn)載等行為,該內(nèi)容只為學(xué)習(xí)所提供,使用后發(fā)生的一切問題與本站無關(guān)。 4、本網(wǎng)站不保證本站提供的下載資源的準(zhǔn)確性、安全性和完整性;同時本網(wǎng)站也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的損失或傷害。 5、本網(wǎng)站所有軟件和資料均為網(wǎng)友推薦收集整理而來,僅供學(xué)習(xí)用途使用,請務(wù)必下載后兩小時內(nèi)刪除,禁止商用。6、如有侵犯你版權(quán)的,請及時聯(lián)系我們(電子郵箱1370723259@qq.com)指出,本站將立即改正。
|