閱讀388 返回首頁    go 汽車大全


ZED Board從入門到精通係列(七)——Vivado+SDK實現MP3播放

本文將給出通過Vivado IDE開發Zynq平台上PS裸機應用程序的流程。通過與本係列博客(三)對比,讀者將看到Vivado開發更高效、快捷。

 

MP3我們都聽過,現在我們可以用ZED-Board來聽。板子上有音頻芯片ADAU1761,可以實現錄音、放音,但不具有MP3解碼功能。Zynq 雙核ARM9做MP3軟件解碼應該是可以實現的,但是博主本人有一顆VS1003,可以實現MP3硬件解碼,軟件將得以簡化,對MP3解碼原理感興趣的可以深入研究如何利用CortexA9+ADAU1761實現MP3播放。電路圖如下:

 

利用Zynq MIO實現VS1003控製,這樣隻和PS有關,PL完全可以丟棄。在本節基礎上,讀者可以嚐試將SPI模塊移到PL上實現,這樣可以降低PS部分IO讀寫頻率,提高CPU利用率。實物連接圖如下:

Zynq板子外接用排母,為了使用杜邦線,需要一個雙公排針,可以用普通單排2.54mm排針壓製而成

下麵介紹軟件開發流程。建立Vivado工程,命名為MP3Player,過程遵循上節Vivado建立工程步驟,略。

 

進入IDE後,點擊左側流程管理器中的IPI Integrator下的Create Block Design。 這個工具是2013.1版本後才出現的,將取代XPS完成係統集成。

在編輯區右鍵,選擇Add IP...,名稱保持默認design_1.bd

搜索框中輸入zynq,雙擊第一個,添加IP到電路圖中。

添加完成後,自動進行布線連接,點下圖中圓圈區域 Run Block Automation。

等待完成,結果如下圖所示。

可以看到,DDR和固定IO自動進行了連接。這是因為我們建立工程時選擇了ZedBoard DVK,這樣就能按照板子描述自動連接引腳到相應外設。

另外看到,默認狀態下使能了M_AXI_GP0,可以將PL部分帶AXI從接口的IP連接到PS進行控製。本節不需要,所以必須禁用,否則驗證設計時會報錯。雙擊方塊,見下圖

看到了熟悉又陌生的畫麵,有些像XPS中Zynq視圖,但精簡了很多。單擊左側“PS-PL Configuration",界麵如下:

將AXI GP0接口後的勾取消選擇,確認,回到IPI。

驗證設計,在空白處右鍵,點擊Validate Design。無誤,點確認即可。

在上圖位置點Generate Block Design,確認。

在Sources窗口中找到design_1,右鍵選擇生成頂層HDL包裝。確認。

直接點左側流程中的Generate Bitstream,一步到位。完成比特流大約需要5~8min。

完成後,先Open Implementated Design,再導出到SDK。

 

完成後,先Open Implementated Design,再導出到SDK。如果沒有做這一步,上圖中第二項會變成灰色。

後麵就是SDK開發了,和本係列教程(三)中相同。建立Application工程,C工程,模板helloworld。將代碼改為下麵:

#include <stdio.h>
#include "platform.h"
#define MIO_BASE 0xE000A000
#define DATA0	0x40
#define DATA0_RO  0x60
#define DIRM_0	0x204
#define OEN_0	0x208
void delay(unsigned int t)
 {
 	unsigned int i,j;
	for(j=0;j<t;j++)
	{
		for(i=0;i<600;i++);
	}
 }


/*---------------------------------------------------------------------------------------------------------*/
/* MAIN function                                                                                          */
/*---------------------------------------------------------------------------------------------------------*/

#define VS_XRESET_0		DrvGPIO_ClrBit(MIO_BASE + DATA0,12)
#define VS_XRESET_1		DrvGPIO_SetBit(MIO_BASE + DATA0,12)
#define VS_DREQ     DrvGPIO_GetBit(MIO_BASE + DATA0_RO,11)
#define VS_XDCS_0	DrvGPIO_ClrBit(MIO_BASE + DATA0,10)
#define VS_XDCS_1	DrvGPIO_SetBit(MIO_BASE + DATA0,10)
#define VS_XCS_0	DrvGPIO_ClrBit(MIO_BASE + DATA0,13)
#define VS_XCS_1	DrvGPIO_SetBit(MIO_BASE + DATA0,13)
#define SPI_MOSI_0	DrvGPIO_ClrBit(MIO_BASE + DATA0,0)
#define SPI_MOSI_1	DrvGPIO_SetBit(MIO_BASE + DATA0,0)
#define SPI_SCL_0	DrvGPIO_ClrBit(MIO_BASE + DATA0,9)
#define SPI_SCL_1	DrvGPIO_SetBit(MIO_BASE + DATA0,9)


void DrvGPIO_ClrBit(volatile unsigned int * p,int idx);
void DrvGPIO_SetBit(volatile unsigned int * p,int idx);
unsigned char DrvGPIO_GetBit(volatile unsigned int * p,int idx);
void init_vs1003(void);
void VS_Reset(void); //VS1003軟複位及初始化
void VS_Write_Reg(unsigned char addr,unsigned char hdat,unsigned char ldat); //向VS1003的功能寄存器寫入一個字
unsigned int VS_Read_Reg(unsigned char addr); //從VS1003的功能寄存器讀取一個字
void VS_Send_Dat(unsigned char dat); //向VS1003發送音頻數據
void VS_Flush_Buffer(void); //清空VS1003的數據緩衝區
void VS_sin_test(unsigned char x); //正弦測試
void LoadPatch(void); //為VS1003打補丁
void SPI_WriteByte(unsigned char x);


#include "mp3.h"

void print(char *str);

int main()
{
    init_platform();

    print("Hello World\n\r");
	unsigned int i;


	init_vs1003();

	VS_Reset(); //VS1003複位初始化
	VS_sin_test(200); //正弦測試,可以聽到一聲滴
	VS_Flush_Buffer();
	for(i = 0;i<sizeof(mp3_table);i++)
	{
		VS_Send_Dat(mp3_table[i]);
	}
	while(1)
	{
        DrvGPIO_ClrBit(MIO_BASE + DATA0,7);
		delay(40000);
		DrvGPIO_SetBit(MIO_BASE + DATA0,7);
		delay(40000);
	}
    return 0;
}





void DrvGPIO_ClrBit(volatile unsigned int * p,int idx)
{
	(*p) &= ~(1<<idx);
}
void DrvGPIO_SetBit(volatile unsigned int * p,int idx)
{
	(*p) |= (1<<idx);
}
unsigned char DrvGPIO_GetBit(volatile unsigned int * p,int idx)
{
	return (((*p)&(1<<idx))>>idx);
}
void init_vs1003(void)
{
	DrvGPIO_SetBit(MIO_BASE + OEN_0,7);
	DrvGPIO_SetBit(MIO_BASE + DIRM_0,7);
	DrvGPIO_SetBit(MIO_BASE + OEN_0,0);
	DrvGPIO_SetBit(MIO_BASE + DIRM_0,0);
	DrvGPIO_SetBit(MIO_BASE + OEN_0,9);
	DrvGPIO_SetBit(MIO_BASE + DIRM_0,9);
	DrvGPIO_SetBit(MIO_BASE + OEN_0,10);
	DrvGPIO_SetBit(MIO_BASE + DIRM_0,10);
	DrvGPIO_SetBit(MIO_BASE + OEN_0,12);
	DrvGPIO_SetBit(MIO_BASE + DIRM_0,12);
	DrvGPIO_SetBit(MIO_BASE + OEN_0,13);
	DrvGPIO_SetBit(MIO_BASE + DIRM_0,13);

}


void SPI_WriteByte(unsigned char x)
{
	unsigned char i=0;
	for(i=0;i<8;i++)
	{
		if(x&0x80)
		{
			SPI_MOSI_1;
		}
		else
		{
			SPI_MOSI_0;
		}

		SPI_SCL_0;

		SPI_SCL_1;

		x<<=1;
	}
}
/******************************************************************
 - 功能描述:向VS1003的功能寄存器中寫入數據(一個字,即兩個字節)
 - 隸屬模塊:VS1003B模塊
 - 函數屬性:外部,用戶可調用
 - 參數說明:addr是功能寄存器的地址
             hdat是要寫入的高字節
             ldat是要寫入的低字節
 - 返回說明:無返回
 ******************************************************************/

void VS_Write_Reg(unsigned char addr,unsigned char hdat,unsigned char ldat)
{
	while(!VS_DREQ);     //VS1003的DREQ為高電平時才接收數據
	VS_XCS_0;            //打開片選,SCI有效,這樣才能對功能寄存器進行讀寫
	SPI_WriteByte(0x02);  //寫入操作碼0x02   00000010 (功能寄存器寫操作)
	SPI_WriteByte(addr);  //寫入寄存器地址
	SPI_WriteByte(hdat);  //寫入高字節
	SPI_WriteByte(ldat);  //寫入低字節
	VS_XCS_1;            //關閉片選,SCI無效
}
/******************************************************************
 - 功能描述:VS1003軟複位及初始化(設置時鍾頻率及音量)
 - 隸屬模塊:VS1003B模塊
 - 函數屬性:外部,用戶可調用
 - 參數說明:無
 - 返回說明:無
 ******************************************************************/

void VS_Reset(void)
{
	VS_XRESET_1;
	delay(100);
	VS_XRESET_0;
	delay(100);
	VS_XRESET_1; //硬件複位,XRESET低電平有效
	delay(100);

	VS_Write_Reg(0x00,0x08,0x04);//軟件複位,向0號寄存器寫入0x0804   SM_SDINEW為1   SM_RESET為1
	VS_Write_Reg(0x03,0x98,0x00);//時鍾設置,向3號寄存器寫入0x9800   SC_MULT  為4   SC_ADD  為3   SC_FREQ為0
	VS_Write_Reg(0x0b,0x00,0x00);//音量設置,左右聲道均最大音量

	VS_XDCS_0;	     //打開數據片選,注意此時XCS(片選)為高電平,SDI有效
	SPI_WriteByte(0);    //寫入數據,這裏寫入4個0,是無關數據,用來啟動數據傳輸
	SPI_WriteByte(0);
	SPI_WriteByte(0);
	SPI_WriteByte(0);
	VS_XDCS_1;	    //關閉數據片選,SDI無效
}

/******************************************************************
 - 功能描述:向VS1003寫入一個字節的音頻數據(即用於播放的數據)
                注:調用前先將VS_XDCS置為0,打開數據片選
 - 隸屬模塊:VS1003B模塊
 - 函數屬性:外部,用戶可調用
 - 參數說明:dat是要寫入的字節
 - 返回說明:無
 ******************************************************************/

void VS_Send_Dat(unsigned char dat)
{
	VS_XDCS_0;    //打開SDI,此時可以向VS1003寫入音頻數據
	while(!VS_DREQ);  //VS1003的DREQ為高才能寫入數據
	SPI_WriteByte(dat);//通過SPI向VS1003寫入一個字節的音頻數據
	VS_XDCS_1;   //關閉SDI
}

/******************************************************************
 - 功能描述:向VS1003寫入2048個0,用於清空VS1003的數據緩衝區
             注:在播放完一個完整的音頻(如一首完整的MP3)後,調用
             此函數,清空VS1003數據緩衝區,為下麵的音頻數據(如下
             一首MP3)作準備。
 - 隸屬模塊:VS1003B模塊
 - 函數屬性:外部,用戶可調用
 - 參數說明:無
 - 返回說明:無
 ******************************************************************/

void VS_Flush_Buffer(void)
{
	unsigned int i;
	VS_XDCS_0;	   //打開數據片選,即開啟SDI傳輸
	for(i=0;i<2048;i++)
	{
		VS_Send_Dat(0);
	}
	VS_XDCS_1;        //關閉數據片選
}

/******************************************************************
 - 功能描述:正弦測試,這是測試VS1003芯片是否正常的有效手段!!
 - 隸屬模塊:VS1003B模塊
 - 函數屬性:外部,用戶可調用
 - 參數說明:x決定了正弦測試中產生的正弦波的頻率,直接影響聽到的
             聲音的頻率
 - 返回說明:無
 ******************************************************************/

void VS_sin_test(unsigned char x)
{
	VS_Write_Reg(0x00,0x08,0x20);//啟動測試,向0號寄存器寫入0x0820   SM_SDINEW為1   SM_TEST為1
	while(!VS_DREQ);   //等待DREQ變為高電平
	VS_XDCS_0;	    //打開數據片選 SDI有效
	SPI_WriteByte(0x53);//寫入以下8個字節,進入正弦測試
	SPI_WriteByte(0xef);
	SPI_WriteByte(0x6e);
	SPI_WriteByte(x);   //參數x用來調整正弦測試中正弦波的頻率   FsIdx (b7~b5):采樣率表索引   S (b4~b0):正弦波的躍速   頻率F=Fs X S / 128
	SPI_WriteByte(0);   //比如x=126 (0b 011 11110) FsIdx=011=3   Fs=22050Hz   S=11110=30    F=22050Hz X 30 /128 =5168 Hz
	SPI_WriteByte(0);
	SPI_WriteByte(0);
	SPI_WriteByte(0);
	delay(6000);      //這裏延時一段時間,為了聽到“正弦音”
	SPI_WriteByte(0x45);//寫入以下8個字節,退出正弦測試
	SPI_WriteByte(0x78);
	SPI_WriteByte(0x69);
	SPI_WriteByte(0x74);
	SPI_WriteByte(0);
	SPI_WriteByte(0);
	SPI_WriteByte(0);
	SPI_WriteByte(0);
	VS_XDCS_1;	    //關閉數據片選 ,SDI無效
}


 

音頻文件需要轉換為C頭文件,可以用matlab實現:

clear;
clc;
close all;

f = fopen('222.mp3','rb');
a = fread(f,'uint8');
fclose(f);
fb = fopen('D:\Tutor_My\MP3Player\MP3Player.sdk\SDK\SDK_Export\mp3\src\mp3.h','w');
fprintf(fb,'const unsigned char mp3_table[] = {\r\n');
fprintf(fb,'0x%02x,\r\n',a(1:end));
fprintf(fb,'\r\n};');
fclose(fb);


下載比特流,運行。通過耳機可以聽到你轉換的mp3。

 

完成上述工程,隻需要10min,操作完全由Vivado+SDK完成,操作十分簡單集中。

最後更新:2017-04-03 12:54:48

  上一篇:go xmpp即時通訊四
  下一篇:go 使用Pear安裝PHPUnit