STM32之音樂播放器
1.硬件平臺
- CPU:STM32F103ZE
- 屏幕:3.5寸TFTLCD屏
- 音頻解碼器: VS1053
- SD卡、外擴(kuò)Sram
2.示例效果
3.軟件設(shè)計
??VS1053b 是單片 Ogg Vorbis/MP3/AAC/WMA/MIDI 音頻解碼器,及 IMA ADPCM 編碼器和用戶加載的 OggVorbis 編碼器。
??支持: MP3/WMA/OGG/WAV/FLAC/MIDI/AAC 等音頻格式的解碼,并支持: OGG/WAV 音頻格式的錄音,支持高低音調(diào)節(jié)設(shè)置, 功能十分強(qiáng)大。
??它包含了一個高性能、有專利的低功耗 DSP 處理器內(nèi)核VS_DSP4、工作數(shù)據(jù)存儲器、供用戶應(yīng)用程序和任何固化解碼器一起運(yùn)行的 16 KiB 指令 RAM 及 0.5KiB 多的數(shù)據(jù) RAM、串行的控制和輸入數(shù)據(jù)接口、最多 8 個可用的通用 I/O 引腳、一個 UART、并有一個優(yōu)質(zhì)的可變采樣率立體聲 ADC(“咪”、“線路”、“線路+咪”或“線路*2”) 和立體聲 DAC、和跟隨的一個耳機(jī)功放及一個公共電壓緩沖器。
3.1 硬件接口
文章來源:http://www.zghlxwxcb.cn/news/detail-407963.html
引腳 | GPIO | 說明 |
---|---|---|
VS_MISO | PA6 | 主機(jī)輸入 |
VS_MOSI | PA7 | 主機(jī)輸出 |
VS_SCK | PA5 | 時鐘 |
VS_XCS | PF7 | 命令片選(低電平有效) |
VS_XDCS | PF6 | 數(shù)據(jù)片選(低電平有效) |
VS_DREQ | PC13 | 數(shù)據(jù)請求線(高電平表示可以接收數(shù)據(jù)) |
VS_RST | PE6 | 復(fù)位腳(低電平復(fù)位) |
3.2 VS1053驅(qū)動
/**************硬件接口*****************
**VS_MISO -- PA6 主機(jī)輸入
**VS_MOSI -- PA7 主機(jī)輸出
**VS_SCK -- PA5 時鐘
**VS_XCS -- PF7 命令片選(低電平有效)
**VS_XDCS -- PF6 數(shù)據(jù)片選(低電平有效)
**VS_DREQ -- PC13 數(shù)據(jù)請求線(高電平表示可以接收數(shù)據(jù))
**VS_RST -- PE6 復(fù)位腳(低電平復(fù)位)
**
*****************************************/
void VS1053_Init(void)
{
/*1. 開時鐘*/
RCC->APB2ENR|=1<<2;//PA
RCC->APB2ENR|=1<<4;//PC
RCC->APB2ENR|=1<<6;//PE
RCC->APB2ENR|=1<<7;//PF
GPIOA->CRL&=0x000FFFFF;
GPIOA->CRL|=0x38300000;
GPIOF->CRL&=0x00FFFFFF;
GPIOF->CRL|=0x33000000;
GPIOC->CRH&=0xFF0FFFFF;
GPIOC->CRH|=0x00800000;
GPIOE->CRL&=0xF0FFFFFF;
GPIOE->CRL|=0x03000000;
VS_XCS=1;
VS_XDCS=1;
VS1053_RST();
VS1053_SetVoice(255,255);
/*2.配置時鐘寄存器*/
VS1053_WriteRegDat(VS1053_CLOCKF,0x9800);
}
/*SPI收發(fā)一個字節(jié)*/
u8 VS1053_SPI_ReadWriteData(u8 data_tx)
{
u8 data_rx=0;
u8 i=0;
for(i=0;i<8;i++)
{
VS_SCK=0;
if(data_tx&0x80)VS_MOSI=1;
else VS_MOSI=0;
VS_SCK=1;
data_tx<<=1;
data_rx<<=1;
if(VS_MISO)data_rx|=0x01;
}
return data_rx;
}
/********************往寄存器中寫入數(shù)據(jù)******************
**
**形參:u8 addr --地址
** u16 data -- 寫入的數(shù)據(jù)
**********************************************************/
void VS1053_WriteRegDat(u8 addr,u16 data)
{
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑
VS_XDCS=1;//數(shù)據(jù)片選拉高
VS_XCS=0;//命令片選拉低
VS1053_SPI_ReadWriteData(0x02);//寫指令
VS1053_SPI_ReadWriteData(addr); //寄存器地址
VS1053_SPI_ReadWriteData(data>>8);
VS1053_SPI_ReadWriteData(data>>0);//寫入數(shù)據(jù)
VS_XCS=1;
}
/*******************從寄存器中讀取數(shù)據(jù)*******************/
u16 VS1053_ReadRegDat(u8 addr)
{
u16 data=0;
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑
VS_XDCS=1;//數(shù)據(jù)片選拉高
VS_XCS=0;//命令片選拉低
VS1053_SPI_ReadWriteData(0x03);//讀指令
VS1053_SPI_ReadWriteData(addr); //寄存器地址
data=VS1053_SPI_ReadWriteData(0xff)<<8;
data|=VS1053_SPI_ReadWriteData(0xff);
VS_XCS=1;
return data;
}
/****************音量調(diào)節(jié)*****************
**
**形參:u8 vol_l -- 左聲道 0~254
** u8 vol_r -- 右聲道 0~254
**每個增量表示0.5db的衰減,值越大,音量越小
**注意:如果設(shè)置 VOL 的值為 0xFFFF,將使芯片進(jìn)入掉電模式。
**右聲道是高 8 位 左聲道是低 8 位
*******************************************/
void VS1053_SetVoice(u8 vol_l,u8 vol_r)
{
u16 temp=vol_r<<8|vol_l;
VS1053_WriteRegDat(VS1053_VOL,temp);
}
/***************VS1053硬件復(fù)位**************/
void VS1053_RST(void)
{
//硬件復(fù)位
VS_RST=0;
Delay_Ms(20);
VS_XDCS=1;//取消數(shù)據(jù)傳輸
VS_XCS=1;//取消命令傳輸
VS_RST=1;//完成復(fù)位
//軟件復(fù)位
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑
VS1053_WriteRegDat(VS1053_MODE,0x0804);//設(shè)置為新模式,進(jìn)行軟件復(fù)位
Delay_Ms(2);
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑,復(fù)位完成
}
/****獲取解碼時間******/
u16 VS1053_Get_Time(void)
{
u16 time=0;
time=VS1053_ReadRegDat(VS1053_DECODE_TIME);
return time;
}
/****清除解碼時間******/
void VS1053_Clear_Time(void)
{
VS1053_WriteRegDat(VS1053_DECODE_TIME,0);
}
3.3 播放音樂,歌詞同步,音樂切換
static unsigned char music_lrc[4096];//存放從文件中讀取出來的歌詞
static unsigned char music_lrc_str[100][50];//存放篩選過后的歌詞
static u16 music_time[200];//保存每句歌詞時間
u8 buff_music[4096];
/**************播放音樂****************************/
u8 VS1053_PlayOneMusic(const char *music_file,u8 display_lrc)
{
u16 i=0,time1,time2;
u32 k=0;
u16 y=32;
u8 vol_l=50,vol_r=50;
FIL fp;
FRESULT res;
UINT br;
u8 key=0;
res=f_open(&fp,music_file,FA_READ);//只讀
if(res!=FR_OK)
{
//printf("%s文件打開失敗err:%d\r\n",music_file,res);
return 1;
}
//printf("VS1053復(fù)位成功\r\n");
LCD_ShowStr2(0,16,(u8 *)music_file,WHITE);//顯示歌名
LCD_Refresh();//更新顯示
VS1053_Clear_Time();//清除解碼時間
/*3.設(shè)置音量*/
VS1053_SetVoice(50,50);
while(!f_eof(&fp))//判斷是否到文件尾
{
key=Key_Scan();
if(key==1)
{
break;//切換下一首
}
else if(key==2)//聲音加
{
if(vol_l<250)
{
vol_l+=50;
vol_r+=50;
}
VS1053_SetVoice(vol_l,vol_r);
}
else if(key==3)//聲音減
{
if(vol_l>0)
{
vol_l-=50;
vol_r-=50;
}
VS1053_SetVoice(vol_l,vol_r);
}
if(f_read(&fp,buff_music,sizeof(buff_music),&br)!=FR_OK)//讀取音頻數(shù)據(jù)
{
//printf("讀取文件失敗");
f_close(&fp);
return 2;
}
// printf("讀取數(shù)據(jù)成功\r\n");
for(i=0;i<br;i++)
{
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑
VS_XDCS=0;//片選拉低,開始發(fā)送音樂數(shù)據(jù)
VS1053_SPI_ReadWriteData(buff_music[i]);
VS_XDCS=1;
}
time1=VS1053_Get_Time();//獲取解碼時間
if(time1!=time2)
{
time2=time1;
if(display_lrc==1)//是否顯示歌詞
{
if(time2>=music_time[k])//通過時間判斷顯示對應(yīng)歌詞
{
if(y>=(LCD_HIGHT-48))//換頁顯示歌詞
{
y=32;
LCD_ReflashBack();//重畫背景
LCD_ShowStr2(0,16,(u8 *)music_file,WHITE);//顯示歌名
}
LCD_ShowStr2(y,16,music_lrc_str[k],WHITE);//顯示當(dāng)前行
if(k>=1 && y>=48)
{
LCD_ShowStr2(y-16,16,music_lrc_str[k-1],GRAY);//將上一行清為底色
}
LCD_Refresh();//更新顯示
y+=16;
k++;
}
}
}
}
return 0;
}
3.4 歌詞解析
/********************歌詞解析******************/
u8 Vs1053_GetLrc_Music(const char *musiclrc)
{
FIL fil;
FRESULT res;
UINT br;
FILINFO fno;
char *p=NULL;
char buff[10];
u32 time=0,i=0,j=0,k=0,cnt=0,count=0;//記錄每句歌詞的播放起始時間
/*1.打開文件*/
res=f_open(&fil,musiclrc,FA_READ);
if(res!=FR_OK)
{
printf("%s文件打開失敗\r\n",musiclrc);
return 1;
}
memset(music_lrc,0,sizeof(music_lrc));
memset(music_lrc_str,0,sizeof(music_lrc_str));
/*2.讀取歌詞*/
res=f_stat (musiclrc, &fno);//獲取文件狀態(tài)
if(res!=FR_OK)
{
printf("文件狀態(tài)獲取失敗\r\n");
return 2;
}
//printf("size=%d\r\n",fno.fsize);
res=f_read(&fil,music_lrc,fno.fsize,&br);//讀取歌詞
if(res!=FR_OK || br!=fno.fsize)
{
printf("讀取文件失敗\r\n");
return 3;
}
/*歌詞解析*/
j=0;
k=0;
p=strstr((char *)music_lrc,"[0");//找到歌詞的起始位置
p++;
while(p[i]!='\0')
{
buff[j]=p[i];
j++;
i++;
if(p[i]==']')
{
//[00:00.65]李榮浩 - 年少有為
time=0;
if(buff[6]>=7)time+=1;//最后兩位時間大于5,總秒數(shù)+1
time+=((buff[0]-'0')*10+(buff[1]-'0'))*60+(buff[3]-'0')*10+(buff[4]-'0');//歌詞起始秒數(shù)
j=0;
music_time[cnt++]=time;
// printf("time:%d\r\n",music_time[cnt]);
/*獲取歌詞*/
i++;
//[00:00.65]李榮浩 - 年少有為
while(p[i]!='[')//保存一句歌詞
{
music_lrc_str[count][k++]=p[i++];
if(p[i]=='\0')break;
}
music_lrc_str[count][k++]='\0';//保存一行歌詞
i++;
k=0;
count++;//記錄第幾行
}
}
memset(music_lrc,0,sizeof(music_lrc));
k=32;
for(i=0;i<25;i++)
{
strcpy((char *)music_lrc,(char *)music_lrc_str[i]);
LCD_ShowStr2(k,16,music_lrc,GRAY);//顯示一屏幕歌詞
k+=16;
}
LCD_Refresh();//更新顯示
return 0;
}
3.5 讀取音樂文件,查找歌詞,播放音樂
/***********音樂播放***************/
u8 Vs1053_play_Music(const char *music_file)
{
u32 i=0;
u8 stat=0;
DIR dp;
char *p=NULL;
FILINFO fno;
FRESULT res;
char buff1[50];
char buff2[50];
res=f_opendir(&dp,music_file);
if(res!=FR_OK)
{
printf("目錄打開失敗err:%d\r\n",res);
return 1;
}
printf("目錄打卡成功\r\n");
while(1)
{
res=f_readdir(&dp,&fno);
if(res!=FR_OK || fno.fname[0]==0)
{
break;
}
// printf("%s\r\n",fno.fname);
p=strstr(fno.fname,".mp3");//查找文件中的音頻文件
if(p)
{
i=0;
//G.E.M. 鄧紫棋 - 我的秘密
while(1)
{
buff1[i]=fno.fname[i];
if((fno.fname[i]=='.') && (fno.fname[i+1]=='m') && (fno.fname[i+2]=='p') && (fno.fname[i+3]=='3'))break;
i++;
}
buff1[i]='\0';
//顯示歌名
LCD_ReflashBack();//重畫背景
LCD_Refresh();//更新顯示
//printf("歌名:%s\r\n",buff1);
snprintf((char *)buff2,sizeof(buff2),"%s/%s.lrc",music_file,buff1);
//printf("buff2:%s\r\n",buff2);
stat=Vs1053_GetLrc_Music(buff2);//歌詞解析
//if(stat==0)printf("獲取歌詞成功\r\n");
snprintf((char *)buff2,sizeof(buff2),"%s/%s.mp3",music_file,buff1);
stat=VS1053_PlayOneMusic(buff2,!stat);//播放音樂
if(stat==0)
{
printf("音樂播放完成\r\n");
}
else
{
printf("stat=%d\r\n",stat);
}
}
}
f_closedir(&dp);
return 0;
}
3.6 主函數(shù):LCD初始化、SD卡掛載
int main()
{
FRESULT ret;
FATFS fs;
Beep_Init();
Led_Init();
Key_Init();
Usartx_Init(USART1,115200,72);
TIMx_Init(TIM2,72,20*1000);
W25Q64_Init();//W25Q64初始化
IIC_Init();//IIC初始化
NT35310_Init();//LCD初始化
SRAM_Init();
AA:
/*掛載磁盤*/
ret=f_mount(&fs,"",1);
if(ret==FR_OK)
{
printf("磁盤掛載成功\n");
}
else
{
printf("請檢查SD卡是否插入!!\r\n");
Delay_Ms(1000);
goto AA;
}
VS1053_Init();
while(1)
{
Vs1053_play_Music("0:/music");
}
}
4.工程示例
工程示例:https://download.csdn.net/download/weixin_44453694/85117817文章來源地址http://www.zghlxwxcb.cn/news/detail-407963.html
到了這里,關(guān)于STM32之音樂播放器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!