【摘要】 VS1053是一款硬件編解碼的音頻芯片,提供SPI接口和IIS接口兩種通信協(xié)議,這篇文章是介紹在Linux下如果模擬SPI時(shí)序來(lái)操作VS1053完成錄音、播放音頻歌曲功能。
1. 前言
VS1053是一款硬件編解碼的音頻芯片,提供SPI接口和IIS接口兩種通信協(xié)議,這篇文章是介紹在Linux下如果模擬SPI時(shí)序來(lái)操作VS1053完成錄音、播放音頻歌曲功能。但是沒有注冊(cè)標(biāo)準(zhǔn)的音頻驅(qū)動(dòng),沒有對(duì)接音頻框架,只是在驅(qū)動(dòng)層完成VS1053的直接控制,本篇的重點(diǎn)主要是介紹如何初始化開發(fā)板的GPIO口,使用Linux的延時(shí)函數(shù),模擬SPI時(shí)序,代碼寫了兩種版本,一種是直接通過ioremap
直接映射GPIO口地址,完成配置,一種是直接調(diào)用官方內(nèi)核提供的庫(kù)函數(shù)接口,完成GPIO口初始化,控制。
當(dāng)前采用的開發(fā)板是友善之臂的Tiny4412,芯片是三星的EXYNOS4412,這款芯片出來(lái)有很長(zhǎng)一段時(shí)間了,之前用在三星的S系列手機(jī)上的,最高主頻是1.5GZ,穩(wěn)定推薦主頻是1.4GHZ,內(nèi)核是三星提供的demon,友善之臂在基礎(chǔ)上完成了移植適配,也就是現(xiàn)在拿到的Tiny4412開發(fā)板內(nèi)核,Linux 版本是3.5,不支持設(shè)備樹。
2. VS1053硬件介紹
VS1053這款編碼解碼芯片在單片機(jī)里用的較多,性價(jià)比很高,因?yàn)橹С諷PI接口,所以單片機(jī)操作起來(lái)也比較容易,編碼解碼都是芯片內(nèi)部完成,不消耗CPU資源,芯片的電壓支持是3.3V。
可以使用VS1053設(shè)計(jì)MP3播放器,比如:用在跑步機(jī)上聽歌,用在便攜式音箱里放歌,做復(fù)讀機(jī)、錄音筆 等等。
解碼的音頻格式支持: MP3、OGG、WMA、WAV、MIDI、AAC、FLAC(需要加載 patch)
編碼的音頻格式支持: WAV(PCM/IMA ADPCM)、OGG(需要加載 patch)
VS1053使用的12.288M 的晶振, 在12.288MHz時(shí)鐘下,最高到48000HZ的所有采樣率都可以正常使用。
當(dāng)前我采用的VS1053是正點(diǎn)原子設(shè)計(jì)的完整模塊,方便杜邦線與開發(fā)板進(jìn)行測(cè)試。
模塊引出的接口功能: 這是SPI接口引腳
下面是SPI接口硬件的功能描述:
SPI讀時(shí)序:
SPI寫時(shí)序:
VS1053模塊與單片機(jī)之間的連線圖:
3. 驅(qū)動(dòng)代碼
3.1 驅(qū)動(dòng)端代碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mp3_data.h"
#include /*雜項(xiàng)字符設(shè)備頭文件*/
#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); //寫數(shù)據(jù)
void VS1053_WriteCmd(u8 address,u16 data); //寫命令
u8 VS1053_Reset(void); //硬復(fù)位
void VS1053_SoftReset(void); //軟復(fù)位
u8 VS1053_SPI_ReadWriteByte(u8 data); //SPI接口,讀寫一個(gè)字節(jié)
void VS1053_SoftReset(void); //初始化VS1053
u8 VS1053_SendMusicData(u8* buf); //向VS10XX發(fā)送32字節(jié)
void VS1053_SetVol(u8 volx); //設(shè)置主音量
/*
函數(shù)功能:移植接口--SPI時(shí)序讀寫一個(gè)字節(jié)
函數(shù)參數(shù):data:要寫入的數(shù)據(jù)
返 回 值:讀到的數(shù)據(jù)
*/
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;
}
/*
函數(shù)功能:軟復(fù)位VS10XX
*/
void VS1053_SoftReset(void)
{
u8 retry=0;
while(gpio_get_value(EXYNOS4_GPB(5))==0); //等待軟件復(fù)位結(jié)束
VS1053_SPI_ReadWriteByte(0Xff); //啟動(dòng)傳輸
retry=0;
while(VS1053_ReadReg(SPI_MODE)!=0x0800) // 軟件復(fù)位,新模式
{
VS1053_WriteCmd(SPI_MODE,0x0804); // 軟件復(fù)位,新模式
msleep(2);//等待至少1.35ms
if(retry++>100)break;
}
while(gpio_get_value(EXYNOS4_GPB(5))==0); //等待軟件復(fù)位結(jié)束
retry=0;
while(VS1053_ReadReg(SPI_CLOCKF)!=0X9800) //設(shè)置VS10XX的時(shí)鐘,3倍頻 ,1.5xADD
{
VS1053_WriteCmd(SPI_CLOCKF,0X9800); //設(shè)置VS10XX的時(shí)鐘,3倍頻 ,1.5xADD
if(retry++>100)break;
}
msleep(20);
}
/*
函數(shù) 功 能:硬復(fù)位MP3
函數(shù)返回值:1:復(fù)位失敗;0:復(fù)位成功
*/
u8 VS1053_Reset(void)
{
u8 retry=0;
gpio_set_value(EXYNOS4_GPB(6), 0);
msleep(20);
gpio_set_value(EXYNOS4_GPB(4), 1);//取消數(shù)據(jù)傳輸
gpio_set_value(EXYNOS4_GPB(1), 1); //取消數(shù)據(jù)傳輸
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;
}
/*
函數(shù)功能:向VS10XX寫命令
函數(shù)參數(shù):
address:命令地址
data :命令數(shù)據(jù)
*/
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);//發(fā)送VS10XX的寫命令
VS1053_SPI_ReadWriteByte(address); //地址
VS1053_SPI_ReadWriteByte(data>>8); //發(fā)送高八位
VS1053_SPI_ReadWriteByte(data); //第八位
gpio_set_value(EXYNOS4_GPB(1), 1);
}
/*
函數(shù)參數(shù):向VS1053寫數(shù)據(jù)
函數(shù)參數(shù):data:要寫入的數(shù)據(jù)
*/
void VS1053_WriteData(u8 data)
{
gpio_set_value(EXYNOS4_GPB(4), 0);
VS1053_SPI_ReadWriteByte(data);
gpio_set_value(EXYNOS4_GPB(4), 1);
}
/*
函數(shù)功能:讀VS1053的寄存器
函數(shù)參數(shù):address:寄存器地址
返回值:讀到的值
*/
u16 VS1053_ReadReg(u8 address)
{
u16 temp=0;
while(gpio_get_value(EXYNOS4_GPB(5))==0);//非等待空閑狀態(tài)
gpio_set_value(EXYNOS4_GPB(4), 1);
gpio_set_value(EXYNOS4_GPB(1), 0);
VS1053_SPI_ReadWriteByte(VS_READ_COMMAND);//發(fā)送VS10XX的讀命令
VS1053_SPI_ReadWriteByte(address); //地址
temp=VS1053_SPI_ReadWriteByte(0xff); //讀取高字節(jié)
temp=temp<<8;
temp+=VS1053_SPI_ReadWriteByte(0xff); //讀取低字節(jié)
gpio_set_value(EXYNOS4_GPB(1), 1);
return temp;
}
/*
函數(shù)功能:讀取VS1053的RAM
函數(shù)參數(shù):addr:RAM地址
返 回 值:讀到的值
*/
u16 VS1053_ReadRAM(u16 addr)
{
u16 res;
VS1053_WriteCmd(SPI_WRAMADDR, addr);
res=VS1053_ReadReg(SPI_WRAM);
return res;
}
/*
函數(shù)功能:寫VS1053的RAM
函數(shù)參數(shù):
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值
}
/*
函數(shù)參數(shù):發(fā)送一次音頻數(shù)據(jù),固定為32字節(jié)
返 回 值:0,發(fā)送成功
1,本次數(shù)據(jù)未成功發(fā)送
*/
u8 VS1053_SendMusicData(u8* buf)
{
u8 n;
if(gpio_get_value(EXYNOS4_GPB(5))!=0) //送數(shù)據(jù)給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;//成功發(fā)送了
}
/*
函數(shù)功能:設(shè)定VS1053播放的音量
函數(shù)參數(shù):volx:音量大小(0~254)
*/
void VS1053_SetVol(u8 volx)
{
u16 volt=0; //暫存音量值
volt=254-volx; //取反一下,得到最大值,表示最大的表示
volt<<=8;
volt+=254-volx; //得到音量設(shè)置后大小
VS1053_WriteCmd(SPI_VOL,volt);//設(shè)音量
}
/*
函數(shù)功能: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. 注冊(cè)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); //時(shí)鐘
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函數(shù)調(diào)用成功!\r\n");
return 0;
}
static int tiny4412_release(struct inode *my_inode, struct file *my_file)
{
printk("VS1053 release函數(shù)調(diào)用成功!\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("拷貝錯(cuò)誤!\r\n"); //每次接收32個(gè)字節(jié)數(shù)據(jù)
while(VS1053_SendMusicData(Music_buff)); //給VS10XX發(fā)送音頻數(shù)據(jù)
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(); //硬復(fù)位MP3
VS1053_SoftReset(); //軟復(fù)位VS10XX
VS1053_SetVol(250); //設(shè)置音量
printk("VS1053設(shè)置成功!\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
};
/*
核心結(jié)構(gòu)體
*/
static struct miscdevice tiny4412_misc=
{
.minor=MISC_DYNAMIC_MINOR, /*自動(dòng)分配次設(shè)備號(hào)*/
.name="tiny4412_vs1053", /*設(shè)備文件,指定/dev/生成的文件名稱*/
.fops=&tiny4412_fops
};
static int __init VS1053_init(void)
{
VS1053SpiInit(); //初始化GPIO口
/*雜項(xiàng)設(shè)備注冊(cè)*/
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));
/*雜項(xiàng)設(shè)備注銷*/
misc_deregister(&tiny4412_misc);
printk("VS1053 driver exit ok!\n");
}
module_exit(VS1053_exit);
module_init(VS1053_init);
MODULE_LICENSE("GPL");
3.2 應(yīng)用層代碼
#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
-
音頻
+關(guān)注
關(guān)注
29文章
2868瀏覽量
81491 -
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209322 -
vs1053
+關(guān)注
關(guān)注
0文章
12瀏覽量
12910
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論