STM32|STM32之音乐播放器

STM32之音乐播放器 1.硬件平台

  • CPU:STM32F103ZE
  • 屏幕:3.5寸TFTLCD屏
  • 音频解码器: VS1053
  • SD卡、外扩Sram
2.示例效果 STM32|STM32之音乐播放器
文章图片

STM32|STM32之音乐播放器
文章图片

3.软件设计
??VS1053b 是单片 Ogg Vorbis/MP3/AAC/WMA/MIDI 音频解码器,及 IMA ADPCM 编码器和用户加载的 OggVorbis 编码器。
??支持: MP3/WMA/OGG/WAV/FLAC/MIDI/AAC 等音频格式的解码,并支持: OGG/WAV 音频格式的录音,支持高低音调节设置, 功能十分强大。
??它包含了一个高性能、有专利的低功耗 DSP 处理器内核VS_DSP4、工作数据存储器、供用户应用程序和任何固化解码器一起运行的 16 KiB 指令 RAM 及 0.5KiB 多的数据 RAM、串行的控制和输入数据接口、最多 8 个可用的通用 I/O 引脚、一个 UART、并有一个优质的可变采样率立体声 ADC(“咪”、“线路”、“线路+咪”或“线路*2”) 和立体声 DAC、和跟随的一个耳机功放及一个公共电压缓冲器。
3.1 硬件接口
STM32|STM32之音乐播放器
文章图片

引脚 GPIO 说明
VS_MISO PA6 主机输入
VS_MOSI PA7 主机输出
VS_SCK PA5 时钟
VS_XCS PF7 命令片选(低电平有效)
VS_XDCS PF6 数据片选(低电平有效)
VS_DREQ PC13 数据请求线(高电平表示可以接收数据)
VS_RST PE6 复位脚(低电平复位)
3.2 VS1053驱动
/**************硬件接口***************** **VS_MISO -- PA6 主机输入 **VS_MOSI -- PA7 主机输出 **VS_SCK-- PA5 时钟 **VS_XCS-- PF7 命令片选(低电平有效) **VS_XDCS -- PF6 数据片选(低电平有效) **VS_DREQ -- PC13 数据请求线(高电平表示可以接收数据) **VS_RST-- PE6 复位脚(低电平复位) ** *****************************************/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收发一个字节*/ 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; } /********************往寄存器中写入数据****************** ** **形参:u8 addr --地址 **u16 data-- 写入的数据 **********************************************************/ void VS1053_WriteRegDat(u8 addr,u16 data) { while(VS_DREQ==0){}//等待数据线空闲 VS_XDCS=1; //数据片选拉高 VS_XCS=0; //命令片选拉低 VS1053_SPI_ReadWriteData(0x02); //写指令 VS1053_SPI_ReadWriteData(addr); //寄存器地址 VS1053_SPI_ReadWriteData(data>>8); VS1053_SPI_ReadWriteData(data>>0); //写入数据 VS_XCS=1; } /*******************从寄存器中读取数据*******************/ u16 VS1053_ReadRegDat(u8 addr) { u16 data=https://www.it610.com/article/0; while(VS_DREQ==0){}//等待数据线空闲 VS_XDCS=1; //数据片选拉高 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; } /****************音量调节***************** ** **形参:u8 vol_l -- 左声道 0~254 **u8 vol_r -- 右声道 0~254 **每个增量表示0.5db的衰减,值越大,音量越小 **注意:如果设置 VOL 的值为 0xFFFF,将使芯片进入掉电模式。 **右声道是高 8 位 左声道是低 8 位 *******************************************/ void VS1053_SetVoice(u8 vol_l,u8 vol_r) { u16 temp=vol_r<<8|vol_l; VS1053_WriteRegDat(VS1053_VOL,temp); } /***************VS1053硬件复位**************/ void VS1053_RST(void) { //硬件复位 VS_RST=0; Delay_Ms(20); VS_XDCS=1; //取消数据传输 VS_XCS=1; //取消命令传输 VS_RST=1; //完成复位 //软件复位 while(VS_DREQ==0){}//等待数据线空闲 VS1053_WriteRegDat(VS1053_MODE,0x0804); //设置为新模式,进行软件复位 Delay_Ms(2); while(VS_DREQ==0){}//等待数据线空闲,复位完成 } /****获取解码时间******/ 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复位成功\r\n"); LCD_ShowStr2(0,16,(u8 *)music_file,WHITE); //显示歌名 LCD_Refresh(); //更新显示 VS1053_Clear_Time(); //清除解码时间 /*3.设置音量*/ 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)//读取音频数据 { //printf("读取文件失败"); f_close(&fp); return 2; } //printf("读取数据成功\r\n"); for(i=0; i=music_time[k])//通过时间判断显示对应歌词 { 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); //显示当前行 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); //获取文件状态 if(res!=FR_OK) { printf("文件状态获取失败\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,总秒数+1 time+=((buff[0]-'0')*10+(buff[1]-'0'))*60+(buff[3]-'0')*10+(buff[4]-'0'); //歌词起始秒数 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 主函数: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.工程示例 【STM32|STM32之音乐播放器】工程示例:https://download.csdn.net/download/weixin_44453694/85117817

    推荐阅读