基础板是Arduino UNO Ver3 Board,Ethernet Shield作为SD和WebServer扩展板,vs1003作为解码器,端口A0-A4用作数字IO模拟SPI来驱动VS1003。
以前用PIC32做了一个月才做出来,今天用Arduino UNO一天就能够给弄出来了。
不过Arduino UNO的芯片Atmega 328最多最多跑到20MHZ,数组申请过了512都hold不住(BootLoader和Text一起能用掉32K?深表怀疑)
应该是芯片处理能力的限制,使得其仅能解码32Kbps的音频,对于64Kbps以上的都很卡,当时用PIC32 80MHZ能够解码128KBps。
(1)
IDE:visual studio
programmer & loader:Xloader
(2)鲍照
(3)代码。主文件。还没有整理注释。
#include <SD.h> #include "cfg.h" #include "vs1003.h" #include "softSPI.h" /////////////////////////////////////////////////////////////////////////// //调节spi2速度,数字越小速度越大 unsigned int spi2_delay_time = SPI2_HIGH; #define DATA_BUFFER_LEN 512 unsigned char data_buffer[DATA_BUFFER_LEN]; /////////////////////////////////////////////////////////////////////////// void spi2_init(void) { pinMode(SPI2_CS,OUTPUT); pinMode(SPI2_MOSI,OUTPUT); pinMode(SPI2_CLK,OUTPUT); pinMode(SPI2_MISO,INPUT); } void spi2_high_speed_mode(){ spi2_delay_time = SPI2_HIGH; } void spi2_low_speed_mode(){ spi2_delay_time = SPI2_LOW; } unsigned char spi2_8TxRx(unsigned char byte) { unsigned char bit; SPI2_SELECT(); for (bit = 0; bit < 8; bit++) { /* write MOSI on trailing edge of previous clock */ if (byte & 0x80) SPI2_SETMOSI(); else SPI2_CLRMOSI(); byte <<= 1; /* half a clock cycle before leading/rising edge */ //if(spi2_delay_time){ // delay(spi2_delay_time); //} SPI2_SETCLK(); /* half a clock cycle before trailing/falling edge */ //if(spi2_delay_time) { // delay(spi2_delay_time); //} /* read MISO on trailing edge */ byte |= SPI2_READMISO(); SPI2_CLRCLK(); } SPI2_DESELECT(); return byte; } /////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ // write cmd to vs1003 // address,data //------------------------------------------------------------------------------ // write cmd as following: // 1.等待DREQ为高(当DREQ为低时,说明芯片还没有就绪) // 2.将XCS(命令片选)拉低 // 3.写入0x02 // 4.写入寄存器地址 // 5.分别写入数据的高字节与低字节 // 6.将XCS置高 //------------------------------------------------------------------------------ void Vs1003_CMD_Write(int8u_t address,int16u_t data) { unsigned char high = data >> 8; unsigned char low = data; spi2_init();//low speed init //wait for free while(MP3_DREQ_READ() == LOW) ; //MP3_DCS_DES(); //MP3_DATA_CS=1; //MP3_CCS_DES(); //delay(1); MP3_CCS_SEL(); //MP3_CMD_CS=0;pull down XCS spi2_8TxRx(VS_WRITE_COMMAND);//send write command spi2_8TxRx(address); //send addr //spi2_16TxRx(data); spi2_8TxRx(high); //8bits high of data spi2_8TxRx(low); //8bits low of data MP3_CCS_DES(); //MP3_CMD_CS=1; spi2_high_speed_mode();//high speed mode for data transport } int16u_t Vs1003_CMD_Read(int8u_t address) { int16u_t data; spi2_init();//low speed init //wait for free while(MP3_DREQ_READ() == LOW) ; //pull down XCS MP3_CCS_SEL(); //send cmd:VS_READ_COMMAND spi2_8TxRx(VS_READ_COMMAND); //senb addr spi2_8TxRx(address); //get the uint16 data data = spi2_8TxRx(0X00); data <<= 8; data += spi2_8TxRx(0X00); MP3_CCS_DES(); spi2_high_speed_mode();//high speed mode for data transport return data; } //------------------------------------------------------------------------------ // configure vs1003 // address,data //------------------------------------------------------------------------------ // 1.硬件复位:接XRESET拉低 // 2.延时,将XDCS、XCS、XRESET置高 // 3.向MODE中写入0X0804 // 4.等待DREQ为高 // 5.设置VS1003的时钟:SCI_CLOCKF=0x9800,3倍频 // 6.设置VS1003的采样率:SPI_AUDATA=0xbb81,采样率48k,立体声 // 7.设置重音:SPI_BASS=0x0055 // 8.设置音量:SCI_VOL=0x2020 // 9.这一步被很多人忽视,向VS1003发送4个字节的无效数据,用以启动SPI发送 //--------------------------------------------------------------------------- void Vs1003_Init(void) { spi2_init(); SPI2_SELECT(); // set pins pinMode(MP3_CMD_CS,OUTPUT); pinMode(MP3_XREST,OUTPUT); pinMode(MP3_DREQ,INPUT); pinMode(MP3_DATA_CS,OUTPUT); MP3_RST_SET(LOW); delay(10); MP3_CCS_DES(); //reset MP3_RST_SET(HIGH); MP3_DCS_DES(); //xcs = 0; Vs1003_CMD_Write(SPI_MODE,0x0804); //wait for DREQ high while( MP3_DREQ_READ() == LOW ) ; //step 5-9 Vs1003_CMD_Write(SPI_CLOCKF,0xe800); delay(1); Vs1003_CMD_Write(SPI_AUDATA,0XBB81); delay(1); Vs1003_CMD_Write(SPI_BASS,0x0055); delay(1); Vs1003_CMD_Write(SPI_VOL,0x2020); delay(1); //向vs1003发送4个字节无效数据,用以启动SPI发送 MP3_DCS_SEL();//选中数据传输 spi2_8TxRx(0XFF); spi2_8TxRx(0XFF); spi2_8TxRx(0XFF); spi2_8TxRx(0XFF); MP3_DCS_DES();//取消数据传输 delay(10); Serial.print("vs1003 init:init over\n"); spi2_high_speed_mode();//high speed mode for data transport } //------------------------------------------------------------------------- //正弦测试 // 1.进入VS1003的测试模式:SPI_MODE=0X0820 // 2.等待DREQ为高 // 3.将XDCS接低,而XCS要置高,选择VS1003的数据接口 // 4.向VS1003发送正弦测试命令:0X53 0XEF 0X6E 0X30 0X00 0X00 0X00 0X00 // 其中0X30为频率,用户可以修改为其它值 // 5.延时一段时间 // 6.退出正弦测试,发送命令:0X45 0X78 0X69 0X74 0X00 0X00 0X00 0X00 // 7.延时一段时间 // 8.循环以上流程 //------------------------------------------------------------------------- void Vs1003_Sine_Test(unsigned char x) { Vs1003_CMD_Write(SPI_MODE,0x0820);//进入vs1003的测试模式 //等待DREQ为高 while (MP3_DREQ_READ() == LOW) ; //向vs1003发送正弦测试命令:0x53 0xef 0x6e n 0x00 0x00 0x00 0x00 //其中n = 0x24, 设定vs1003所产生的正弦波的频率值,具体计算方法见vs1003的datasheet SPI2_SELECT(); MP3_CCS_DES(); MP3_DCS_SEL();//选中数据传输 spi2_8TxRx(0x53); spi2_8TxRx(0xef); spi2_8TxRx(0x6e); spi2_8TxRx(x); spi2_8TxRx(0x00); spi2_8TxRx(0x00); spi2_8TxRx(0x00); spi2_8TxRx(0x00); delay(100); spi2_8TxRx(0x45); spi2_8TxRx(0x78); spi2_8TxRx(0x69); spi2_8TxRx(0x74); spi2_8TxRx(0x00); spi2_8TxRx(0x00); spi2_8TxRx(0x00); spi2_8TxRx(0x00); MP3_DCS_DES(); } //------------------------------------------------------------------------------ // xiaoyang yi@2011.3.14 HIT // write data to vs1003 // address,data //------------------------------------------------------------------------------ // send data and paly music // 数据写入主要看DREQ信号,在VS1003的FIFO能够接受数据的时候输出高电平。 // 每次可以写入32个字节的数据。而DREQ变低时,单片机就要停止数据的发送。 // 具体的写数据的方法如下: // 1.将XDCS拉低 // 2.等待DREQ为高 // 3.通过SPI写入数据 // 4.在文件没有结束前不断重复2与3操作 // 5.在所有的数据都发送完毕后,最后发送2048个无效字节,用以清除VS1003的数据缓冲区 // 6.将XDCS置高 //------------------------------------------------------------------------------ void Vs1003_DATA_Write(int8u_t *data,int16u_t len) { unsigned int i = 0; //Serial.print("Vs1003_DATA_Write"); MP3_DCS_SEL(); //send data for(i = 0; i < len; i++) { //wait for DREQ high while (MP3_DREQ_READ() == LOW) ; spi2_8TxRx(data[i]); } MP3_DCS_DES(); } //------------------------------------------------------------------------------ // write data to vs1003 // address,data //------------------------------------------------------------------------------ // 用于测试vs1003的各个模块 //------------------------------------------------------------------------------ void Vs1003_Test() { int i = 0; int16u_t data; Vs1003_Init(); //raw testing do{ data = 0x0; Serial.print("Test Start,Reg Vol data=\n"); Serial.println(data,HEX); Vs1003_CMD_Write(SPI_VOL,0x2020); data = Vs1003_CMD_Read(SPI_VOL); Serial.print("Test end, Reg VOL data="); Serial.println(data,HEX); delay(10); }while(0); Serial.print("sine test..\n") ; for(i = 0; i< 15; i++) { Serial.print("sine test, i"); Serial.println(i); Vs1003_Sine_Test(i); } Serial.print("begin to play music\n"); //play data //while(1){ //#define data_length 20480 //the length of the mp3 stream //Vs1003_DATA_Write(mp3_data,20480); //} } /////////////////////////////////////////////////////////////////////////// /* * * return the bytes of a file. */ int get_file_size(char* filename){ File rfd; int buf_len = 0; if (!SD.exists(filename)) { Serial.println("test.txt doesn't exist."); return buf_len; } rfd = SD.open(filename, FILE_READ); if(rfd){ buf_len = rfd.available(); } rfd.close(); return buf_len; } int sd_test(char* filename){ File rfd; const int buf_len = 512; char buf[buf_len]; int rw_count = 0; if (!SD.exists(filename)) { Serial.println("test.txt doesn't exist."); return FAIL; } rfd = SD.open(filename, FILE_READ); if (rfd) { // read from the file until there's nothing else in it: while (rfd.available()) { rw_count = rfd.read(buf,buf_len); buf[rw_count] = '\0'; Serial.print(buf); } // close the file: rfd.close(); } else { // if the file didn't open, print an error: Serial.print("error opening:"); Serial.print(filename); } return SUC; } /////////////////////////////////////////////////////////////////////////// int play_file(char* filename){ File rfd; int rw_count = 0; if (!SD.exists(filename)) { Serial.println("File to paly doesn't exist."); return FAIL; } rfd = SD.open(filename, FILE_READ); if (rfd) { // read from the file until there's nothing else in it: while (rfd.available()) { rw_count = rfd.read(data_buffer,DATA_BUFFER_LEN); Vs1003_DATA_Write(data_buffer,rw_count); } // close the file: rfd.close(); } else { // if the file didn't open, print an error: Serial.print("error opening:"); Serial.print(filename); } return SUC; } void setup() { Serial.begin(9600); /*wait until serial is ok*/ while(!Serial){ Serial.print("Serial open error,try after 1000 ms."); delay(1000); ; } Serial.print("Initializing SD card..."); /* * On the Ethernet Shield, CS is pin 4. It's set as an output by default. * Note that even if it's not used as the CS pin, the hardware SS pin * (10 on most Arduino boards, 53 on the Mega) must be left as an output * or the SD library functions will not work. */ pinMode(10, OUTPUT); if (!SD.begin(4)) { Serial.print("nitialization failed."); delay(1000); return; } delay(3000); Serial.println("Begin vs1003 test."); Vs1003_Test(); Serial.println("initialization done."); } void loop() { //char* filename = "nokia.mp3"; char* filenames[] = { "desert.mp3", "memory2.mp3" }; //sd_test(filename); play_file(filenames[1]); delay(3000); play_file(filenames[0]); delay(3000); }