49.3 软件设计 打开上一章的工程,首先在HARDWARE文件夹所在的文件夹下新建一个APP的文件夹。在该文件夹里面新建mp3player.c和mp3player.h两个文件。并将APP文件夹加入头文件包含路径。 然后在HARDWARE文件夹下新建一个VS10XX的文件夹,在该文件夹里面新建VS10XX.c、VS10XX.h和flac.h等三个文件。 首先打开VS10XX.c,里面的代码我们不一一贴出了,这里挑几个重要的函数给大家介绍一下,首先要介绍的是VS_Soft_Reset,该函数用于软复位VS1003,其代码如下:
//软复位VS10XX void VS_Soft_Reset(void) { u8 retry=0; while(VS_DQ==0); //等待软件复位结束 VS_SPI_ReadWriteByte(0Xff);//启动传输 retry=0; while(VS_RD_Reg(SPI_MODE)!=0x0800)// 软件复位,新模式 { VS_WR_Cmd(SPI_MODE,0x0804);// 软件复位,新模式 delay_ms(2);//等待至少1.35ms if(retry++>100)break; } while(VS_DQ==0);//等待软件复位结束 retry=0; while(VS_RD_Reg(SPI_CLOCKF)!=0X9800)//设置VS10XX的时钟,3倍频 ,1.5xADD { VS_WR_Cmd(SPI_CLOCKF,0X9800);//设置VS10XX的时钟,3倍频 ,1.5xADD if(retry++>100)break; } delay_ms(20); }
该函数比较简单,先配置一下VS1053的模式顺便执行软复位操作,在软复位结束之后,再设置好时钟,完成一次软复位。接下来,我们介绍一下VS_WR_Cmd函数,该函数用于向VS1003写命令,代码如下: //向VS10XX写命令 //address:命令地址 //data:命令数据 void VS_WR_Cmd(u8 address,u16 data) { while(VS_DQ==0);//等待空闲 VS_SPI_SpeedLow();//低速 VS_XDCS=1; ; VS_XCS=0; VS_SPI_ReadWriteByte(VS_WRITE_COMMAND);//发送VS10XX的写命令 VS_SPI_ReadWriteByte(address); //地址 VS_SPI_ReadWriteByte(data>>8); //发送高八位 VS_SPI_ReadWriteByte(data); //第八位 VS_XCS=1; VS_SPI_SpeedHigh(); //高速 } 该函数用于向VS1053发送命令,这里要注意VS1053的写操作比读操作快(写1/4 CLKI,读1/7 CLKI),虽然说写寄存器最快可以到1/4CLKI,但是经实测在1/4CLKI的时候会出错,所以在写寄存器的时候最好把SPI速度调慢点,然后在发送音频数据的时候,就可以1/4CLKI的速度了。有写命令的函数,当然也有读命令的函数了。VS_RD_Reg用于读取VS1053的寄存器的内容。该函数代码如下: //读VS10XX的寄存器 //address:寄存器地址 //返回值:读到的值 //注意不要用倍速读取,会出错 u16 VS_RD_Reg(u8 address) { u16 temp=0; while(VS_DQ==0);//非等待空闲状态 VS_SPI_SpeedLow();//低速 VS_XDCS=1; VS_XCS=0; VS_SPI_ReadWriteByte(VS_READ_COMMAND); //发送VS10XX的读命令 VS_SPI_ReadWriteByte(address); //地址 temp=VS_SPI_ReadWriteByte(0xff); //读取高字节 temp=temp<<8; temp+=VS_SPI_ReadWriteByte(0xff); //读取低字节 VS_XCS=1; VS_SPI_SpeedHigh();//高速 return temp; } 该函数的作用和VS_WR_Cmd的作用基本相反,用于读取寄存器的值。VS10XX.c的剩余代码、VS10XX.h以及flac.h的代码,这里就不贴出来了,其中flac.h仅仅用来存储播放flac格式所需要的patch文件,以支持flac解码。大家可以去光盘查看他们的详细源码。保存VS10XX.c、VS10XX.h和flac.h三个文件。把VS10XX.c加入到HARDWARE组下。然后我们打开mp3player.c,该文件我们仅介绍一个函数,其他代码请看光盘的源码。这里要介绍的是mp3_play_song函数,该函数代码如下: //播放一曲指定的歌曲 //返回值:0,正常播放完成;1,下一曲;2,上一曲;0XFF,出现错误了; u8 mp3_play_song(u8 *pname) { FIL* fmp3; u16 br; u8 res,rval; u8 *databuf; u16 i=0; u8 key; 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,(const TCHAR*)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) { case KEY_RIGHT: rval=1; break; //下一曲 case KEY_LEFT: rval=2; break; //上一曲 case KEY_UP: //音量增加 if(vsset.mvol<250) { vsset.mvol+=5; VS_Set_Vol(vsset.mvol); }else vsset.mvol=250; mp3_vol_show((vsset.mvol-100)/5);//音量限制在: 100~ //250,显示时,按照公式(vol-100)/5,显示,也就是0~30 break; case KEY_DOWN: //音量减 if(vsset.mvol>100) { vsset.mvol-=5; VS_Set_Vol(vsset.mvol); }else vsset.mvol=100; mp3_vol_show((vsset.mvol-100)/5); break; } mp3_msg_show(fmp3->fsize);//显示信息 } }while(i<4096);//循环发送4096个字节 if(br!=4096||res!=0) {rval=0; break;}//读完了. } f_close(fmp3); }else rval=0XFF;//出现错误 } myfree(SRAMIN,databuf); myfree(SRAMIN,fmp3); return rval; } 该函数,就是我们解码MP3的核心函数了,该函数在初始化VS1053后,根据文件格式选择是否加载patch(如果是flac格式,则需要加载patch),最后在死循环里面等待DREQ信号的到来,每次VS_DQ变高,就通过VS_Send_MusicData函数向VS1053发送32个字节,直到整个文件读完。此段代码还包含了对按键的处理(音量调节、上一首、下一首)及当前播放的歌曲的一些状态(码率、播放时间、总时间)显示。 mp3player.c的其他代码和mp3player.h在这里就不详细介绍了,请大家直接参考光盘源码。最后,我们在test.c里面修改main函数如下: int main(void) { Stm32_Clock_Init(9); //系统时钟设置 delay_init(72); //延时初始化 uart_init(72,9600); //串口1初始化 LCD_Init(); //初始化液晶 LED_Init(); //LED初始化 KEY_Init(); //按键初始化 Audiosel_Init(); //初始化音源选择 usmart_dev.init(72); //usmart初始化 mem_init(SRAMIN); //初始化内部内存池 VS_Init(); //初始化VS1053 exfuns_init(); //为fatfs相关变量申请内存 f_mount(0,fs[0]); //挂载SD卡 f_mount(1,fs[1]); //挂载FLASH. POINT_COLOR=RED; while(font_init()) //检查字库 { LCD_ShowString(60,50,200,16,16,"Font Error!"); delay_ms(200); LCD_Fill(60,50,240,66,WHITE);//清除显示 } Show_Str(60,50,200,16,"战舰 STM32开发板",16,0); Show_Str(60,70,200,16,"音乐播放器实验",16,0); Show_Str(60,90,200,16,"广州星翼电子",16,0); Show_Str(60,110,200,16,"2012年9月20日",16,0); Show_Str(60,130,200,16,"KEY0:NEXT KEY2:PREV",16,0); Show_Str(60,150,200,16,"KEY_UP:VOL+ KEY1:VOL-",16,0); while(1) { Audiosel_Set(0); //音频通道选择MP3音源 LED1=0; Show_Str(60,170,200,16,"存储器测试...",16,0); printf("Ram Test:0X%04X\r\n",VS_Ram_Test());//打印RAM测试结果 Show_Str(60,170,200,16,"正弦波测试...",16,0); VS_Sine_Test(); Show_Str(60,170,200,16,"<<音乐播放器>>",16,0); LED1=1; mp3_play(); } } 该函数先检测外部flash是否存在字库,然后选择音频通道为MP3音源,之后执行VS1053的RAM测试和正弦测试,这两个测试结束后,调用mp3_play函数开始播放SD卡MUSIC文件夹里面的音乐。软件部分就介绍到这里。 49.4 下载验证 在代码编译成功之后,我们下载代码到ALIENTEK战舰STM32开发板上,程序先执行字库监测,然后对VS1053进行RAM测试和正弦测试。 当检测到SD卡根目录的MUSIC文件夹有有效音频文件(VS1053所支持的格式)的时候,就开始自动播放歌曲了,如图49.4.1所示:
图49.4.1 MP3播放中 从上图可以看出,当前正在播放第4首歌曲,总共4首歌曲,歌曲名、播放时间、总时长、码率、音量等信息等也都有显示。此时DS0会随着音乐的播放而闪烁,2秒闪烁一次。 只要我们在开发板的PHONE端子插入耳机,就能听到歌曲的声音了。同时,我们可以通过按KEY0和KEY2来切换下一曲和上一曲,通过WK_UP按键来控制音量增加,通过KEY1控制音量减小。 本实验,我们还可以通过USMART来测试VS1053的其他功能,通过将VS10XX.c里面的部分函数加入USMART管理,我们可以很方便的设置/获取VS1053各种参数,达到验证测试的目的。有兴趣的朋友,可以实验测试一下。 至此,我们就完成了一个简单的MP3播放器了,在此基础上进一步完善,就可以做出一个比较实用的MP3了。大家可以自己发挥想象,做出一个你心仪的MP3。 |