MP3,MP4,數碼相機曾經是奢侈品。科技發展了,這些東西也就成了普通手機的附屬品了。但是作為手藝人學習這些芯片了解這些高性能芯片是很有必要的。學習了解它既拓展了自己的眼界,也可以提升自己對嵌入式系統的認識。原子戰艦板上就板載了一顆高性能MP3音樂播放器芯片VS1053B芯片。通過原子源碼對一些參數的修改,也能欣賞美妙的音樂。這是一款真正的數字產品。還可以作為錄音機使用。
VS1053是繼VS1003后荷蘭VLSI公司出品的又一款高性能解碼芯片。該芯片可以實現對MP3/OGG/WMA/FLAC/WAV/AAC/MIDI等音頻格式的解碼,同時還可以支持ADPCM/OGG等格式的編碼,性能相對以往的VS1003提升不少。VS1053擁有一個高性能的DSP處理器核VS_DSP,16K的指令RAM,0.5K的數據RAM,通過SPI控制,具有8個可用的通用IO口和一個串口,芯片內部還帶了一個可變采樣率的立體聲ADC(支持咪頭/咪頭+線路/2線路)、一個高性能立體聲DAC及音頻耳機放大器。
VS1053既然是高性能DSP,也就是說它本身就可以作為主機象普通MCU一樣使用。同普通MCU一樣DSP也具有一樣的最小系統,所以在電路設計中自然就必須要晶振和復位電路。通過讀datasheet知道,VS1053B也有中斷功能,同樣有GPIO!操作寄存器可以控制時鐘頻率。
下面是電路圖:

下面是音頻驅動電路圖:即耳機驅動電路。

這兩張電路圖是戰艦板原理圖,電路圖設計是核心技術。本人對原子的電路設計只有學習和佩服的份。其中74HC4052和TDA1308分別是用作音頻選擇和耳機驅動,74HC4052芯片將板載的MP3,FM收音機和STM32發出的方波信號通過程序控制做出選擇;也就是說將眾多音頻信號集于一身可以分別單獨送到耳機驅動電路輸出,既節約了空間也節約了成本。設計非常精妙!
通過學習VS1053B,對電路分析能力也有提高。如果看不懂電路圖,即便c語言代碼分析的多么透徹也無濟于事。 通過學習類似VS1053B芯片的驅動,能真正提高手藝人的編程能力。通過數據手冊,寫出相應芯片的驅動程序實現一些簡單功能是一個程序員的核心競爭力!這里涉及到許多c語言編程技巧。一句話,目前我是沒有這個能力。也就只能好好學習別人源碼的份了。有所收獲就記錄下來。
VS1053B,LCD,RDA5802收音機芯片,ADXL345重力加速度傳感器芯片等等這些芯片,它們有一個共同點:寄存器,寄存器地址,數據幀,命令字,I2C, SPI 協議等等。學會這些外設芯片的驅動程序編寫方法,才能真正發揮ARM處理器的強大功能。
有感而發!
下面提供VS1003 VS1053B芯片的51單片機完整驅動程序和VS1053 pdf中文資料文件下載:http://m.zg4o1577.cn/f/VS1053.rar
下面是單片機程序的主要部分預覽:
/*
* MP3模塊測試程序
*
* 用途:MP3模塊測試程序
* vs1003 的硬件測試程序,主控芯片為STC12LE5A60S2
* 其他的微處理器(帶SPI接口的)只需稍加修改即可適用
* 對于不帶硬SPI接口的微處理器可以用IO進行SPI的時序模擬
*
* 作者 日期 備注
* Huafeng Lin 20010/09/10 新增
* Huafeng Lin 20010/09/10 修改
*
*/
#include "vs1003.h"
#include "MusicDataMP3.c"
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
#define bool bit
#define true 1
#define flase 0
//針對SD卡讀寫板調整引腳
#define uint8 unsigned char
sbit MP3_XRESET = P3^2;
#define Mp3PutInReset() { MP3_XRESET = 0; }
#define Mp3ReleaseFromReset() { MP3_XRESET =1; }
sbit MP3_XCS = P3^3;
#define Mp3SelectControl() { MP3_XCS = 0; }
#define Mp3DeselectControl() { MP3_XCS = 1; }
sbit MP3_XDCS = P3^4;
#define Mp3SelectData() { MP3_XDCS = 0; }
#define Mp3DeselectData() { MP3_XDCS = 1; }
sbit MP3_DREQ = P3^5;
sbit c_SPI_SI = P1^5;
sbit c_SPI_SO = P1^6;
sbit c_SPI_CLK = P1^7;
#define Macro_Set_SI_High() c_SPI_SI = 1
#define Macro_Set_SI_Low() c_SPI_SI = 0
#define Macro_Set_CLK_High() c_SPI_CLK = 1
#define Macro_Set_CLK_Low() c_SPI_CLK = 0
void LCD_write_english_string(unsigned char X,unsigned char Y,char *s);
//#define SPIWait() { while((S0SPSR & 0x80) == 0); }//等待SPI將數據發送完畢
//#define SPI_RESULT_BYTE S0SPDR
//extern long volatile timeval; //用于延時的全局變量
//1ms Delayfunction
//void Delay(uchar ucDelayCount)
void wait(uchar ucDelayCount)
{
uchar ucTempCount;
uchar uci;
for(ucTempCount=0; ucTempCount<ucDelayCount; ucTempCount++)
{
// uci = 200; //Err
// uci = 250; //OK
uci = 230;
while(uci--)
{
_nop_();
}
}
}
//#define wait(x) Delay(x)
/**********************************************************/
/* 函數名稱 : MSPI_Init */
/* 函數功能 : 初始化SPI接口,設置為主機。 */
/* 參數 : 無 */
/* 返回值 : 無 */
/*--------------------------------------------------------*/
void MSPI_Init(void)
{
/*
PINSEL0 = (PINSEL0 & 0xFFFF00FF) | 0x00005500; //選擇 SPI
S0SPCCR = 0x08; // SPI 時鐘設置
S0SPCR = (0 << 3) | // CPHA = 0,
(0 << 4) | // CPOL = 0,
(1 << 5) | // MSTR = 1,
(0 << 6) | // LSBF = 0,
(0 << 7); // SPIE = 0,
*/
c_SPI_SO = 1;
MP3_DREQ = 1;
}
/**********************************************************/
/* 函數名稱 : InitPortVS1003 */
/* 函數功能 : MCU與vs1003接口的初始化 */
/* 參數 : 無 */
/* 返回值 : 無 */
/*--------------------------------------------------------*/
void InitPortVS1003(void)
{
MSPI_Init();//SPI口的初始化
// IODIR &= 0xfffeffff; //其他接口線的設置,其中dreq 為輸入口
// IODIR |= MP3_XRESET | MP3_XCS | MP3_XDCS;//xRESET,xCS,xDS均為輸出口
// IOSET |= MP3_XRESET | MP3_XCS | MP3_XDCS;//xRESET,xCS,xDS默認輸出高電平
MP3_DREQ = 1; //置為輸入
MP3_XRESET = 1;
MP3_XCS = 1;
MP3_XDCS = 1;
}
//uint8 SD_SPI_ReadByte(void);
//void SD_SPI_WriteByte(uint8 ucSendData);
//#define SPI_RecByte() SD_SPI_ReadByte()
//#define SPIPutChar(x) SD_SPI_WriteByte(x)
#if 1
/**********************************************************/
/* 函數名稱 : SPIPutChar */
/* 函數功能 : 通過SPI發送一個字節的數據 */
/* 參數 : 待發送的字節數據 */
/* 返回值 : 無 */
/*--------------------------------------------------------*/
void SPIPutChar(unsigned char ucSendData)
{
// S0SPDR = c;
// while((S0SPSR & 0x80) == 0); //等待SPI將數據發送完畢
uchar ucCount;
uchar ucMaskCode;
ucMaskCode = 0x80;
for(ucCount=0; ucCount<8; ucCount++)
{
Macro_Set_CLK_Low();
if(ucMaskCode & ucSendData)
{
Macro_Set_SI_High();
}
else
{
Macro_Set_SI_Low();
}
Macro_Set_CLK_High();
ucMaskCode >>= 1;
}
}
/*******************************************************************************************************************
** 函數名稱: INT8U SPI_RecByte() Name: INT8U SPI_RecByte()
** 功能描述: 從SPI接口接收一個字節 Function: receive a byte from SPI interface
** 輸 入: 無 Input: NULL
** 輸 出: 收到的字節 Output: the byte that be received
********************************************************************************************************************/
static uchar SPI_RecByte(void)
{
uchar ucReadData;
uchar ucCount;
ucReadData = 0;
Macro_Set_SI_High();
for(ucCount=0; ucCount<8; ucCount++)
{
ucReadData <<= 1;
//降低時鐘頻率
Macro_Set_CLK_Low();
if(c_SPI_SO)
{
ucReadData |= 0x01;
}
Macro_Set_CLK_High();
}
return(ucReadData);
}
#endif
/*************************************************************/
/* 函數名稱 : Mp3WriteRegister */
/* 函數功能 : 寫vs1003寄存器 */
/* 參數 : 寄存器地址,待寫數據的高8位,待寫數據的低8位 */
/* 返回值 : 無 */
/*-----------------------------------------------------------*/
void Mp3WriteRegister(unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte)
{
Mp3DeselectData();
Mp3SelectControl();//XCS = 0
SPIPutChar(VS_WRITE_COMMAND); //發送寫寄存器命令
SPIPutChar(addressbyte); //發送寄存器的地址
SPIPutChar(highbyte); //發送待寫數據的高8位
SPIPutChar(lowbyte); //發送待寫數據的低8位
Mp3DeselectControl();
}
/*************************************************************/
/* 函數名稱 : Mp3ReadRegister */
/* 函數功能 : 寫vs1003寄存器 */
/* 參數 : 寄存器地址 */
/* 返回值 : vs1003的16位寄存器的值 */
/*-----------------------------------------------------------*/
unsigned int Mp3ReadRegister(unsigned char addressbyte)
{
unsigned int resultvalue = 0;
uchar ucReadValue;
Mp3DeselectData();
Mp3SelectControl();//XCS = 0
SPIPutChar(VS_READ_COMMAND); //發送讀寄存器命令
SPIPutChar((addressbyte)); //發送寄存器的地址
// SPIPutChar(0xff); //發送讀時鐘
// resultvalue = (SPI_RESULT_BYTE) << 8;//讀取高8位數據
ucReadValue = SPI_RecByte();
resultvalue = ucReadValue<<8;
// SPIPutChar(0xff); //發送讀時鐘
// resultvalue |= (SPI_RESULT_BYTE); //讀取低8位數據
ucReadValue = SPI_RecByte();
resultvalue |= ucReadValue;
Mp3DeselectControl();
return resultvalue; //返回16位寄存器的值
}
/**********************************************************/
/* 函數名稱 : Mp3SoftReset */
/* 函數功能 : vs1003軟件復位 */
/* 參數 : 無 */
/* 返回值 : 無 */
/*--------------------------------------------------------*/
void Mp3SoftReset(void)
{
Mp3WriteRegister (SPI_MODE, 0x08, 0x04); //軟件復位
wait(1); //延時1ms
while (MP3_DREQ == 0); //等待軟件復位結束
Mp3WriteRegister(SPI_CLOCKF, 0x98, 0x00);//設置vs1003的時鐘,3倍頻
Mp3WriteRegister (SPI_AUDATA, 0xBB, 0x81); //采樣率48k,立體聲
Mp3WriteRegister(SPI_BASS, 0x00, 0x55);//設置重音
Mp3SetVolume(10,10);//設置音量
wait(1); //延時1ms
//向vs1003發送4個字節無效數據,用以啟動SPI發送
Mp3SelectData();
SPIPutChar(0);
SPIPutChar(0);
SPIPutChar(0);
SPIPutChar(0);
Mp3DeselectData();
}
/**********************************************************/
/* 函數名稱 : Mp3Reset */
/* 函數功能 : vs1003硬件復位 */
/* 參數 : 無 */
/* 返回值 : 無 */
/*--------------------------------------------------------*/
void Mp3Reset(void)
{
Mp3PutInReset();//xReset = 0 復位vs1003
wait(200);//延時100ms
SPIPutChar(0xff);//發送一個字節的無效數據,啟動SPI傳輸
Mp3DeselectControl(); //xCS = 1
Mp3DeselectData(); //xDCS = 1
Mp3ReleaseFromReset(); //xRESET = 1
wait(200); //延時100ms
while (MP3_DREQ == 0);//等待DREQ為高
wait(200); //延時100ms
Mp3SetVolume(50,50);
Mp3SoftReset();//vs1003軟復位
}
bool CheckVS1003B_DRQ(void)
{
bool bResult;
bResult =MP3_DREQ;
return(bResult);
}
/***********************************************************/
/* 函數名稱 : VsSineTest */
/* 函數功能 : vs1003正弦測試,將該函數放在while循環中, */
/* 如果能持續聽到一高一低的聲音,證明測試通過 */
/* 參數 : 無 */
/* 返回值 : 無 */
/*---------------------------------------------------------*/
void VsSineTest(void)
{
Mp3PutInReset(); //xReset = 0 復位vs1003
wait(200); //延時100ms
SPIPutChar(0xff);//發送一個字節的無效數據,啟動SPI傳輸
Mp3DeselectControl();
Mp3DeselectData();
Mp3ReleaseFromReset();
wait(200);
Mp3SetVolume(50,50);
Mp3WriteRegister(SPI_MODE,0x08,0x20);//進入vs1003的測試模式
while (MP3_DREQ == 0); //等待DREQ為高
Mp3SelectData(); //xDCS = 1,選擇vs1003的數據接口
//向vs1003發送正弦測試命令:0x53 0xef 0x6e n 0x00 0x00 0x00 0x00
//其中n = 0x24, 設定vs1003所產生的正弦波的頻率值,具體計算方法見vs1003的datasheet
SPIPutChar(0x53);
SPIPutChar(0xef);
SPIPutChar(0x6e);
SPIPutChar(0x24);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
// wait(500);
wait(250);
wait(250);
Mp3DeselectData();//程序執行到這里后應該能從耳機聽到一個單一頻率的聲音
//退出正弦測試
Mp3SelectData();
SPIPutChar(0x45);
SPIPutChar(0x78);
SPIPutChar(0x69);
SPIPutChar(0x74);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
// wait(500);
wait(250);
wait(250);
Mp3DeselectData();
//再次進入正弦測試并設置n值為0x44,即將正弦波的頻率設置為另外的值
Mp3SelectData();
SPIPutChar(0x53);
SPIPutChar(0xef);
SPIPutChar(0x6e);
SPIPutChar(0x44);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
// wait(500);
wait(250);
wait(250);
Mp3DeselectData();
//退出正弦測試
Mp3SelectData();
SPIPutChar(0x45);
SPIPutChar(0x78);
SPIPutChar(0x69);
SPIPutChar(0x74);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
SPIPutChar(0x00);
// wait(500);
wait(250);
wait(250);
Mp3DeselectData();
}
void test_1003_PlayMP3File();
void TestVS1003B(void)
{
Mp3Reset();
VsSineTest();
Mp3SoftReset();
test_1003_PlayMP3File();
}
//寫寄存器,參數,地址和數據
void VS1003B_WriteCMD(unsigned char addr, unsigned int dat)
{
/*
VS1003B_XDCS_H();
VS1003B_XCS_L();
VS1003B_WriteByte(0x02);
//delay_Nus(20);
VS1003B_WriteByte(addr);
VS1003B_WriteByte(dat>>8);
VS1003B_WriteByte(dat);
//delay_Nus(200);
VS1003B_XCS_H();
*/
Mp3WriteRegister(addr,dat>>8,dat);
}
//讀寄存器,參數 地址 返回內容
unsigned int VS1003B_ReadCMD(unsigned char addr)
{
/*
unsigned int temp;
unsigned char temp1;
VS1003B_XDCS_H();
VS1003B_XCS_L();
VS1003B_WriteByte(0x03);
//delay_Nus(20);
VS1003B_WriteByte(addr);
temp= VS1003B_ReadByte();
temp=temp<<8;
temp1= VS1003B_ReadByte();
temp=temp|temp1;;
VS1003B_XCS_H();
return temp;
*/
return(Mp3ReadRegister(addr));
}
//寫數據,音樂數據
void VS1003B_WriteDAT(unsigned char dat)
{
// VS1003B_XDCS_L();
// VS1003B_WriteByte(dat);
// VS1003B_XDCS_H();
// VS1003B_XCS_H();
Mp3SelectData();
SPIPutChar(dat);
Mp3DeselectData();
Mp3DeselectControl();
}
//開啟環繞聲
void VS1003B_SetVirtualSurroundOn(void)
{
uchar ucRepeatCount;
uint uiModeValue;
ucRepeatCount =0;
while(1)//寫時鐘寄存器
{
uiModeValue = VS1003B_ReadCMD(0x00);
if(uiModeValue & 0x0001)
{
break;
}
else
{
uiModeValue |= 0x0001;
VS1003B_WriteCMD(0,uiModeValue);
}
ucRepeatCount++;
if(ucRepeatCount++ >10 )break;
}
}
//關閉環繞聲
void VS1003B_SetVirtualSurroundOff(void)
{
uchar ucRepeatCount;
uint uiModeValue;
ucRepeatCount =0;
while(1)//寫時鐘寄存器
{
uiModeValue = VS1003B_ReadCMD(0x00);
if(uiModeValue & 0x0001)
{
break;
}
else
{
uiModeValue |= 0x0001;
VS1003B_WriteCMD(0,uiModeValue);
}
ucRepeatCount++;
if(ucRepeatCount++ >10 )break;
}
}
//增強重音
//入口參數 1.強度0-15
// 2.頻率0-15 (X10Hz)
void VS1003B_SetBassEnhance(uchar ucValue, ucFrequencyID)
{
uchar ucRepeatCount;
uint uiWriteValue;
uint uiReadValue;
ucRepeatCount =0;
uiWriteValue = VS1003B_ReadCMD(0x02);
uiWriteValue &= 0xFF00;
uiWriteValue |= ucValue<<4;
uiWriteValue &= (ucFrequencyID & 0x0F);
while(1)//寫時鐘寄存器
{
VS1003B_WriteCMD(2,uiWriteValue);
uiReadValue = VS1003B_ReadCMD(0x02);
if(uiReadValue == uiWriteValue)
{
break;
}
ucRepeatCount++;
if(ucRepeatCount++ >10 )break;
}
}
uint uiVolumeCount; //當前音量值
//VS1003初始化,0成功 1失敗
unsigned char VS1003B_Init()
{
unsigned char retry;
/*
PORT_INI();
DDRB|=0xa0;
VS1003B_DDR &=~(1<<VS1003B_DREQ);
//delay_Nus(50);
VS1003B_XCS_H();
VS1003B_XDCS_H();
VS1003B_XRESET_L();
VS1003B_Delay(0xffff);
VS1003B_XRESET_H();//使能芯片
VS1003B_SPI_Low();//先以低頻操作
VS1003B_Delay(0xffff);//延時
*/
Mp3Reset();
retry=0;
while(VS1003B_ReadCMD(0x00) != 0x0800)//寫mode寄存器
{
VS1003B_WriteCMD(0x00,0x0800);
if(retry++ >10 )break;//{PORTB|=_BV(PB1);break;}
}
retry=0;
/*while(VS1003B_ReadCMD(0x02) != 0x75)//寫mode寄存器
{
VS1003B_WriteCMD(0x02,0x75);
if(retry++ >10 )break;//{PORTB|=_BV(PB1);break;}
}*/
retry=0;
while(VS1003B_ReadCMD(0x03) != 0x9800)//寫時鐘寄存器
{
VS1003B_WriteCMD(0x03,0x9800);
if(retry++ >10 )break;
}
retry=0;
// while(VS1003B_ReadCMD(0x0b) != 0x1111)//設音量
// {
// VS1003B_WriteCMD(0x0b,0x1111);
// if(retry++ >10 )break;
// }
while(VS1003B_ReadCMD(0x0b) != uiVolumeCount)//設音量
{
VS1003B_WriteCMD(0x0b,uiVolumeCount);
if(retry++ >10 )break;
}
// VS1003B_SPI_High();//提高速度,全速運行
if(retry > 10)return 1;
return 0;
}
//VS1003軟件復位
void VS1003B_SoftReset()
{
VS1003B_WriteCMD(0x00,0x0804);//寫復位
// VS1003B_Delay(0xffff);//延時,至少1.35ms
wait(2);
}
void VS1003B_Fill2048Zero()
{
unsigned char i,j;
for(i=0;i<64;i++)
{
if(CheckVS1003B_DRQ())
{
Mp3SelectData();
for(j=0;j<32;j++)
{
VS1003B_WriteDAT(0x00);
}
Mp3DeselectData();
}
}
}
void test_1003_PlayMP3File()
{
unsigned int data_pointer;unsigned char i;
unsigned int uiCount;
uiCount = sizeof(MusicData);
data_pointer=0;
VS1003B_SoftReset();
while(uiCount>0)
{
if(CheckVS1003B_DRQ())
{
for(i=0;i<32;i++)
{
VS1003B_WriteDAT(MusicData[data_pointer]);
data_pointer++;
}
uiCount -= 32;
}
}
VS1003B_Fill2048Zero();
}