stm32_MP3學(xué)習(xí)筆記 一、整體介紹 話不多說,先看看整體原理圖:
原理圖.png (352.87 KB, 下載次數(shù): 127)
下載附件
2020-5-23 21:05 上傳
制作出來的實(shí)物圖如下:
IMG_20200518_155134_BURST028.jpg (1.89 MB, 下載次數(shù): 89)
下載附件
2020-5-23 21:10 上傳
整體上,其實(shí)也就四個(gè)部分: (1) STM32主控部分; (2) VS1053B音頻解碼部分; (3) 按鍵控制部分; (4) OLED顯示部分; 上電,通過一系列的測試和檢測后,進(jìn)入音樂播放界面,如圖: 第一排顯示的是我的標(biāo)簽, 第二行,顯示的是當(dāng)前播放歌曲的索引、總歌曲數(shù)目以及當(dāng)前聲音大小, 第三行,顯示的是當(dāng)前歌曲的播放進(jìn)度以及位率, 第四行,顯示的是當(dāng)前歌曲名(因?yàn)闆]有取字庫,所以我就把他翻譯成了英文,肯定是不準(zhǔn)確的,主要目的只是提示自己)。 此外,通過按鍵,可以對(duì)播放歌曲進(jìn)行切換和音量的控制。 下面我們來分別看看這四個(gè)部分: 二、模塊介紹 1. stm32主控 這里我們使用的是stm32F103VET6,內(nèi)部Flash有512K,100pin的外部引腳,屬于大容量芯片,足夠我們diy。對(duì)于本設(shè)計(jì),stm32這一塊用到的知識(shí)點(diǎn)有:SDIO驅(qū)動(dòng)SD卡,SPI驅(qū)動(dòng)VS1053B芯片,IIC驅(qū)動(dòng)OLED;對(duì)于程序,用的是stm32標(biāo)準(zhǔn)庫,小伙伴們可以根據(jù)自己的需要,自行決定是否需要補(bǔ)一下相關(guān)方面的知識(shí)。 主控就不再多做介紹了,因?yàn)樘R娏耍@里只是提一下。 2. VS1053B音頻解碼 這一部分的原理圖如圖:
下載.png (173.59 KB, 下載次數(shù): 94)
下載附件
2020-5-23 21:05 上傳
VS1053B,是一款功能比較強(qiáng)大的音頻解碼芯片,該芯片可以實(shí)現(xiàn)對(duì)MP3/OGG/WMA/FLAC/WAV/AAC/MIDI等音頻格式的解碼,同時(shí)還可以支持ADPCM/OGG等格式的編碼,經(jīng)過我的測試,建議大家用最常用的.MP3 格式的音樂文件; 具體的介紹,請(qǐng)看資料里面的資料手冊(cè),那里說的很清楚,我再多說,顯得就很尷尬了。 3. 按鍵 這個(gè)常見到不能再常見了,因?yàn)樾枰存I對(duì)歌曲進(jìn)行切換和音量大小的控制,所以,這里只是簡單的把他列出來而已。 4. OLED顯示 OLED只是用來顯示提示的作用,這里我們用的是0.96寸4pin的IIC驅(qū)動(dòng)的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); //卡相對(duì)地址 printf("CardCapacity:%d MB\r\n",(u32)(SDCardInfo.CardCapacity>>20)); //顯示容量 printf("CardBlockSize:%d\r\n\r\n",SDCardInfo.CardBlockSize); //顯示塊大小 } intmain(void) { delay_init(); //延時(shí)函數(shù)初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設(shè)置中斷優(yōu)先級(jí)分組為組2:2位搶占優(yōu)先級(jí),2位響應(yīng)優(yōu)先級(jí) uart_init(115200); //串口初始化為115200 LED_Init(); //初始化與LED連接的硬件接口 KEY_Init(); //初始化按鍵 VS_Init(); //初始化VS1053 delay_ms(1000); //適當(dāng)延時(shí) OLED_Init(); //OLED初始化 OLED_ColorTurn(0);//0正常顯示,1 反色顯示 OLED_DisplayTurn(0);//0正常顯示 1 屏幕翻轉(zhuǎn)顯示 OLED_Refresh(); delay_ms(1000); //適當(dāng)延時(shí) my_mem_init(SRAMIN); //初始化內(nèi)部內(nèi)存池 exfuns_init(); //為fatfs相關(guān)變量申請(qǐng)內(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); //錯(cuò)誤提示信息閃爍 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(); //放歌操作 } } 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;//歌曲文件位率 //顯示播放時(shí)間,比特率 信息 //lenth:歌曲總長度 void mp3_msg_show(u32 lenth) { staticu16 playtime=0;//播放時(shí)間標(biāo)記 u16 time=0;// 時(shí)間變量 u16sec=0;// 時(shí)間變量 u16temp=0; if(f_kbps==0xffff)//未更新過 { playtime=0; f_kbps=VS_Get_HeadInfo(); //獲得比特率 } time=VS_Get_DecodeTime();//得到解碼時(shí)間 if(playtime==0)playtime=time; elseif((time!=playtime)&&(time!=0))//1s時(shí)間到,更新顯示數(shù)據(jù) { playtime=time;//更新時(shí)間 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;//非法位率 //顯示播放時(shí)間 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)文件的總個(gè)數(shù) //path:路徑 //返回值:總有效文件數(shù) u16 mp3_get_tnum(u8 *path) { u8res; u16rval=0; DIR tdir; //臨時(shí)目錄 FILINFOtfileinfo; //臨時(shí)文件信息 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); //讀取目錄下的一個(gè)文件 if(res!=FR_OK||tfileinfo.fname[0]==0)break; //錯(cuò)誤了/到末尾了,退出 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); //申請(qǐng)2*totmp3num個(gè)字節(jié)的內(nèi)存,用于存放音樂文件索引 while(mp3fileinfo.lfname==NULL||pname==NULL||mp3indextbl==NULL)//內(nèi)存分配出錯(cuò) { 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,顯示的時(shí)候,按照公式(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); //讀取目錄下的一個(gè)文件 if(res!=FR_OK||mp3fileinfo.fname[0]==0)break; //錯(cuò)誤了/到末尾了,退出 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); //讀取目錄下的一個(gè)文件 if(res!=FR_OK||mp3fileinfo.fname[0]==0)break; //錯(cuò)誤了/到末尾了,退出 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); //播放這個(gè)MP3 if(key==2) //上一曲 { if(curindex)curindex--; elsecurindex=totmp3num-1; }else if(key<=1)//下一曲 { curindex++; if(curindex>=totmp3num)curindex=0;//到末尾的時(shí)候,自動(dòng)從頭開始 }else break; //產(chǎn)生了錯(cuò)誤 } myfree(SRAMIN,mp3fileinfo.lfname); //釋放內(nèi)存 myfree(SRAMIN,pname); //釋放內(nèi)存 myfree(SRAMIN,mp3indextbl); //釋放內(nèi)存 } //播放一曲指定的歌曲 //返回值:0,正常播放完成 // 1,下一曲 // 2,上一曲 // 0XFF,出現(xiàn)錯(cuò)誤了 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));//申請(qǐng)內(nèi)存 databuf=(u8*)mymalloc(SRAMIN,4096); //開辟4096字節(jié)的內(nèi)存區(qū)域 if(databuf==NULL||fmp3==NULL)rval=0XFF;//內(nèi)存申請(qǐng)失敗. if(rval==0) { VS_Restart_Play(); //重啟播放 VS_Set_All(); //設(shè)置音量等信息 VS_Reset_DecodeTime(); //復(fù)位解碼時(shí)間 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個(gè)字節(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,顯示的時(shí)候,按照公式(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,顯示的時(shí)候,按照公式(vol-100)/5,顯示,也就是0~30 break; } mp3_msg_show(fmp3->fsize);//顯示信息 } }while(i<4096);//循環(huán)發(fā)送4096個(gè)字節(jié) if(br!=4096||res!=0) { rval=0; break;//讀完了. } } f_close(fmp3); }elserval=0XFF;//出現(xiàn)錯(cuò)誤 } myfree(SRAMIN,databuf); myfree(SRAMIN,fmp3); returnrval; }
全部程序51hei下載地址:
STM32_MP3.7z
(1.32 MB, 下載次數(shù): 211)
2020-5-24 00:25 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|