Arduino+VS1003播放音頻


基礎板是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);
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM