【摘要】 VS1053是一款硬件編解碼的音頻芯片,提供SPI接口和IIS接口兩種通信協議,這篇文章是介紹在Linux下如果模擬SPI時序來操作VS1053完成錄音、播放音頻歌曲功能。
1. 前言
VS1053是一款硬件編解碼的音頻芯片,提供SPI接口和IIS接口兩種通信協議,這篇文章是介紹在Linux下如果模擬SPI時序來操作VS1053完成錄音、播放音頻歌曲功能。但是沒有注冊標準的音頻驅動,沒有對接音頻框架,只是在驅動層完成VS1053的直接控制,本篇的重點主要是介紹如何初始化開發板的GPIO口,使用Linux的延時函數,模擬SPI時序,代碼寫了兩種版本,一種是直接通過ioremap
直接映射GPIO口地址,完成配置,一種是直接調用官方內核提供的庫函數接口,完成GPIO口初始化,控制。
當前采用的開發板是友善之臂的Tiny4412,芯片是三星的EXYNOS4412,這款芯片出來有很長一段時間了,之前用在三星的S系列手機上的,最高主頻是1.5GZ,穩定推薦主頻是1.4GHZ,內核是三星提供的demon,友善之臂在基礎上完成了移植適配,也就是現在拿到的Tiny4412開發板內核,Linux 版本是3.5,不支持設備樹。
2. VS1053硬件介紹
VS1053這款編碼解碼芯片在單片機里用的較多,性價比很高,因為支持SPI接口,所以單片機操作起來也比較容易,編碼解碼都是芯片內部完成,不消耗CPU資源,芯片的電壓支持是3.3V。
可以使用VS1053設計MP3播放器,比如:用在跑步機上聽歌,用在便攜式音箱里放歌,做復讀機、錄音筆 等等。
解碼的音頻格式支持: MP3、OGG、WMA、WAV、MIDI、AAC、FLAC(需要加載 patch)
編碼的音頻格式支持: WAV(PCM/IMA ADPCM)、OGG(需要加載 patch)
VS1053使用的12.288M 的晶振, 在12.288MHz時鐘下,最高到48000HZ的所有采樣率都可以正常使用。
當前我采用的VS1053是正點原子設計的完整模塊,方便杜邦線與開發板進行測試。
模塊引出的接口功能: 這是SPI接口引腳

下面是SPI接口硬件的功能描述:

SPI讀時序:

SPI寫時序:

VS1053模塊與單片機之間的連線圖:

3. 驅動代碼
3.1 驅動端代碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mp3_data.h"
#include /*雜項字符設備頭文件*/
#define VS_WRITE_COMMAND 0x02 //寫命令
#define VS_READ_COMMAND 0x03 //讀命令
//VS10XX寄存器定義
#define SPI_MODE 0x00
#define SPI_STATUS 0x01
#define SPI_BASS 0x02
#define SPI_CLOCKF 0x03
#define SPI_DECODE_TIME 0x04
#define SPI_AUDATA 0x05
#define SPI_WRAM 0x06
#define SPI_WRAMADDR 0x07
#define SPI_HDAT0 0x08
#define SPI_HDAT1 0x09
#define SPI_AIADDR 0x0a
#define SPI_VOL 0x0b
#define SPI_AICTRL0 0x0c
#define SPI_AICTRL1 0x0d
#define SPI_AICTRL2 0x0e
#define SPI_AICTRL3 0x0f
#define SM_DIFF 0x01
#define SM_JUMP 0x02
#define SM_RESET 0x04
#define SM_OUTOFWAV 0x08
#define SM_PDOWN 0x10
#define SM_TESTS 0x20
#define SM_STREAM 0x40
#define SM_PLUSV 0x80
#define SM_DACT 0x100
#define SM_SDIORD 0x200
#define SM_SDISHARE 0x400
#define SM_SDINEW 0x800
#define SM_ADPCM 0x1000
#define SM_ADPCM_HP 0x2000
#define I2S_CONFIG 0XC040
#define GPIO_DDR 0XC017
#define GPIO_IDATA 0XC018
#define GPIO_ODATA 0XC019
/*
Tiny4412與VS1053硬件連接:
VCC--3V~5V
GND--0V
SCK---SCLK:GPB_0
SI---MOSI:GPB_3
SO---MISO:GPB_2
XCS--CS :GPB_1
DREQ-----:GPB_5
XDCS-----:GPB_4
RST------:GPB_6
*/
void VS1053_Init(void);
u16 VS1053_ReadReg(u8 address); //讀寄存器
u16 VS1053_ReadRAM(u16 addr); //讀RAM
void VS1053_WriteRAM(u16 addr,u16 val); //寫RAM
void VS1053_WriteData(u8 data); //寫數據
void VS1053_WriteCmd(u8 address,u16 data); //寫命令
u8 VS1053_Reset(void); //硬復位
void VS1053_SoftReset(void); //軟復位
u8 VS1053_SPI_ReadWriteByte(u8 data); //SPI接口,讀寫一個字節
void VS1053_SoftReset(void); //初始化VS1053
u8 VS1053_SendMusicData(u8* buf); //向VS10XX發送32字節
void VS1053_SetVol(u8 volx); //設置主音量
/*
函數功能:移植接口--SPI時序讀寫一個字節
函數參數:data:要寫入的數據
返 回 值:讀到的數據
*/
u8 VS1053_SPI_ReadWriteByte(u8 tx_data)
{
u8 rx_data=0;
u8 i;
for(i=0;i<8;i++)
{
gpio_set_value(EXYNOS4_GPB(0), 0);
if(tx_data&0x80){gpio_set_value(EXYNOS4_GPB(3), 1);}
else {gpio_set_value(EXYNOS4_GPB(3), 0);}
tx_data<<=1;
gpio_set_value(EXYNOS4_GPB(0), 1);
rx_data<<=1;
if(gpio_get_value(EXYNOS4_GPB(2)))rx_data|=0x01;
}
return rx_data;
}
/*
函數功能:軟復位VS10XX
*/
void VS1053_SoftReset(void)
{
u8 retry=0;
while(gpio_get_value(EXYNOS4_GPB(5))==0); //等待軟件復位結束
VS1053_SPI_ReadWriteByte(0Xff); //啟動傳輸
retry=0;
while(VS1053_ReadReg(SPI_MODE)!=0x0800) // 軟件復位,新模式
{
VS1053_WriteCmd(SPI_MODE,0x0804); // 軟件復位,新模式
msleep(2);//等待至少1.35ms
if(retry++>100)break;
}
while(gpio_get_value(EXYNOS4_GPB(5))==0); //等待軟件復位結束
retry=0;
while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800) //設置VS10XX的時鐘,3倍頻 ,1.5xADD
{
VS1053_WriteCmd(SPI_CLOCKF,0X9800); //設置VS10XX的時鐘,3倍頻 ,1.5xADD
if(retry++>100)break;
}
msleep(20);
}
/*
函數 功 能:硬復位MP3
函數返回值:1:復位失敗;0:復位成功
*/
u8 VS1053_Reset(void)
{
u8 retry=0;
gpio_set_value(EXYNOS4_GPB(6), 0);
msleep(20);
gpio_set_value(EXYNOS4_GPB(4), 1);//取消數據傳輸
gpio_set_value(EXYNOS4_GPB(1), 1); //取消數據傳輸
gpio_set_value(EXYNOS4_GPB(6), 1);
while(gpio_get_value(EXYNOS4_GPB(5))==0&&retry<200)//等待DREQ為高
{
retry++;
udelay(50);
};
msleep(20);
if(retry>=200)return 1;
else return 0;
}
/*
函數功能:向VS10XX寫命令
函數參數:
address:命令地址
data :命令數據
*/
void VS1053_WriteCmd(u8 address,u16 data)
{
while(gpio_get_value(EXYNOS4_GPB(5))==0); //等待空閑
gpio_set_value(EXYNOS4_GPB(4), 1);
gpio_set_value(EXYNOS4_GPB(1), 0);
VS1053_SPI_ReadWriteByte(VS_WRITE_COMMAND);//發送VS10XX的寫命令
VS1053_SPI_ReadWriteByte(address); //地址
VS1053_SPI_ReadWriteByte(data>>8); //發送高八位
VS1053_SPI_ReadWriteByte(data); //第八位
gpio_set_value(EXYNOS4_GPB(1), 1);
}
/*
函數參數:向VS1053寫數據
函數參數:data:要寫入的數據
*/
void VS1053_WriteData(u8 data)
{
gpio_set_value(EXYNOS4_GPB(4), 0);
VS1053_SPI_ReadWriteByte(data);
gpio_set_value(EXYNOS4_GPB(4), 1);
}
/*
函數功能:讀VS1053的寄存器
函數參數:address:寄存器地址
返回值:讀到的值
*/
u16 VS1053_ReadReg(u8 address)
{
u16 temp=0;
while(gpio_get_value(EXYNOS4_GPB(5))==0);//非等待空閑狀態
gpio_set_value(EXYNOS4_GPB(4), 1);
gpio_set_value(EXYNOS4_GPB(1), 0);
VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//發送VS10XX的讀命令
VS1053_SPI_ReadWriteByte(address); //地址
temp=VS1053_SPI_ReadWriteByte(0xff); //讀取高字節
temp=temp<<8;
temp+=VS1053_SPI_ReadWriteByte(0xff); //讀取低字節
gpio_set_value(EXYNOS4_GPB(1), 1);
return temp;
}
/*
函數功能:讀取VS1053的RAM
函數參數:addr:RAM地址
返 回 值:讀到的值
*/
u16 VS1053_ReadRAM(u16 addr)
{
u16 res;
VS1053_WriteCmd(SPI_WRAMADDR, addr);
res=VS1053_ReadReg(SPI_WRAM);
return res;
}
/*
函數功能:寫VS1053的RAM
函數參數:
addr:RAM地址
val:要寫入的值
*/
void VS1053_WriteRAM(u16 addr,u16 val)
{
VS1053_WriteCmd(SPI_WRAMADDR,addr); //寫RAM地址
while(gpio_get_value(EXYNOS4_GPB(5))==0); //等待空閑
VS1053_WriteCmd(SPI_WRAM,val); //寫RAM值
}
/*
函數參數:發送一次音頻數據,固定為32字節
返 回 值:0,發送成功
1,本次數據未成功發送
*/
u8 VS1053_SendMusicData(u8* buf)
{
u8 n;
if(gpio_get_value(EXYNOS4_GPB(5))!=0) //送數據給VS10XX
{
gpio_set_value(EXYNOS4_GPB(4), 0);
for(n=0;n<32;n++)
{
VS1053_SPI_ReadWriteByte(buf[n]);
}
gpio_set_value(EXYNOS4_GPB(4), 1);
}else return 1;
return 0;//成功發送了
}
/*
函數功能:設定VS1053播放的音量
函數參數:volx:音量大小(0~254)
*/
void VS1053_SetVol(u8 volx)
{
u16 volt=0; //暫存音量值
volt=254-volx; //取反一下,得到最大值,表示最大的表示
volt<<=8;
volt+=254-volx; //得到音量設置后大小
VS1053_WriteCmd(SPI_VOL,volt);//設音量
}
/*
函數功能:VS1053初始化
Tiny4412硬件連接:
VCC--3V~5V
GND--0V
SCK---SCLK:GPB_0
SI---MOSI:GPB_3
SO---MISO:GPB_2
XCS--CS :GPB_1
DREQ-----:GPB_5
XDCS-----:GPB_4
RST------:GPB_6
*/
void VS1053SpiInit(void)
{
/*1. 注冊GPIO*/
gpio_request(EXYNOS4_GPB(0), "VS1053_CLK-SCLK");
gpio_request(EXYNOS4_GPB(1), "VS1053_CS");
gpio_request(EXYNOS4_GPB(2), "VS1053_MISO");
gpio_request(EXYNOS4_GPB(3), "VS1053_MOSI");
gpio_request(EXYNOS4_GPB(4), "VS1053_XDCS");
gpio_request(EXYNOS4_GPB(5), "gpio_get_value(EXYNOS4_GPB(5))");
gpio_request(EXYNOS4_GPB(6), "VS1053_RST");
/*2. 配置GPIO口模式*/
s3c_gpio_cfgpin(EXYNOS4_GPB(0), S3C_GPIO_OUTPUT); //時鐘
s3c_gpio_cfgpin(EXYNOS4_GPB(1), S3C_GPIO_OUTPUT); //片選
s3c_gpio_cfgpin(EXYNOS4_GPB(2), S3C_GPIO_INPUT); //輸入模式
s3c_gpio_cfgpin(EXYNOS4_GPB(3), S3C_GPIO_OUTPUT); //輸出模式
s3c_gpio_cfgpin(EXYNOS4_GPB(4), S3C_GPIO_OUTPUT); //輸出模式
s3c_gpio_cfgpin(EXYNOS4_GPB(5), S3C_GPIO_INPUT); //輸入模式
s3c_gpio_cfgpin(EXYNOS4_GPB(6), S3C_GPIO_OUTPUT); //輸出模式
/*3. 上拉GPIO口*/
gpio_set_value(EXYNOS4_GPB(0), 1);
gpio_set_value(EXYNOS4_GPB(1), 1);
gpio_set_value(EXYNOS4_GPB(3), 1);
gpio_set_value(EXYNOS4_GPB(4), 1);
gpio_set_value(EXYNOS4_GPB(6), 1);
}
/*****************************************************************************************************/
static int tiny4412_open(struct inode *my_inode, struct file *my_file)
{
printk("VS1053 open函數調用成功!\r\n");
return 0;
}
static int tiny4412_release(struct inode *my_inode, struct file *my_file)
{
printk("VS1053 release函數調用成功!\r\n");
return 0;
}
static u8 Music_buff[32];
static ssize_t tiny4412_write(struct file *my_file, const char __user *buf, size_t len, loff_t *loff)
{
if(0!=copy_from_user(Music_buff,buf,len))printk("拷貝錯誤!\r\n"); //每次接收32個字節數據
while(VS1053_SendMusicData(Music_buff)); //給VS10XX發送音頻數據
return len;
}
#define VS1053_INIT_SET 188
static long tiny4412_unlocked_ioctl(struct file *my_file, unsigned int cmd, unsigned long data)
{
switch(cmd)
{
case VS1053_INIT_SET:
VS1053_Reset(); //硬復位MP3
VS1053_SoftReset(); //軟復位VS10XX
VS1053_SetVol(250); //設置音量
printk("VS1053設置成功!\r\n");
break;
}
return 0;
}
/*文件操作集合*/
static struct file_operations tiny4412_fops=
{
.open=tiny4412_open,
.write=tiny4412_write,
.release=tiny4412_release,
.unlocked_ioctl=tiny4412_unlocked_ioctl
};
/*
核心結構體
*/
static struct miscdevice tiny4412_misc=
{
.minor=MISC_DYNAMIC_MINOR, /*自動分配次設備號*/
.name="tiny4412_vs1053", /*設備文件,指定/dev/生成的文件名稱*/
.fops=&tiny4412_fops
};
static int __init VS1053_init(void)
{
VS1053SpiInit(); //初始化GPIO口
/*雜項設備注冊*/
misc_register(&tiny4412_misc);
return 0;
}
static void __exit VS1053_exit(void)
{
/*釋放GPIO口*/
gpio_free(EXYNOS4_GPB(0));
gpio_free(EXYNOS4_GPB(1));
gpio_free(EXYNOS4_GPB(2));
gpio_free(EXYNOS4_GPB(3));
gpio_free(EXYNOS4_GPB(4));
gpio_free(EXYNOS4_GPB(5));
gpio_free(EXYNOS4_GPB(6));
/*雜項設備注銷*/
misc_deregister(&tiny4412_misc);
printk("VS1053 driver exit ok!\n");
}
module_exit(VS1053_exit);
module_init(VS1053_init);
MODULE_LICENSE("GPL");
3.2 應用層代碼
#include
#include
#include
#include
#define VS1053_INIT_SET 188
int main(int argc,char **argv)
{
char buff[32];
int cnt,i=0;
int vs1053_fd,file_fd;
if(argc!=2)
{
printf("argv: ./app \r\n");
return -1;
}
vs1053_fd=open("/dev/tiny4412_vs1053",O_RDWR);
file_fd=open(argv[1],2);
if(vs1053_fd<0||file_fd<0) /*判斷文件是否打開成功*/
{
printf("vs1053 driver open error!\n");
return -1;
}
ioctl(vs1053_fd,VS1053_INIT_SET);
while(1)
{
cnt=read(file_fd,buff,32);
write(vs1053_fd,buff,cnt);
if(cnt!=32)break;
i++;
}
close(vs1053_fd);
close(file_fd);
return 0;
}
3.3 Makefile 代碼
KER_DRI=/work/Tiny4412/linux-3.5/
all:
make -C $(KER_DRI) M=`pwd` modules
cp ./*.ko /work/rootfs/tmp/
make -C $(KER_DRI) M=`pwd` modules clean
rm ./*.ko -rf
arm-linux-gcc vs1053_app.c -o vs1053_app
cp vs1053_app /work/rootfs/tmp/ -f
obj-m +=vs1053_drv.o
-
音頻
+關注
關注
29文章
2997瀏覽量
82888 -
Linux
+關注
關注
87文章
11432瀏覽量
212466 -
vs1053
+關注
關注
0文章
12瀏覽量
13058
發布評論請先 登錄
VS1053實現MP3解碼
音頻編解碼芯片 通訊 VS1003 VS1053 VS1011 VS1063
音頻編解碼芯片 VS1003 VS1053 VS1063 VS1005 VS1011
vs1053 vs1003 vs1063 vs1011 vs1005 音頻編解碼芯片代理,提供資料
vs1053如何降低功耗
請問VS1053的GBUF引腳可以懸空嗎?
VS1053簡介
如何把wavplayer軟件包與vs1053編解碼芯片驅動對接起來
VS1053輸出端沒有聲音什么情況
VS1053 ram測試無法讀到83ff

評論