【摘要】 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
-
音频
+关注
关注
31文章
3135浏览量
84938 -
Linux
+关注
关注
88文章
11628浏览量
218009 -
vs1053
+关注
关注
0文章
12浏览量
13235
发布评论请先 登录
VS1053实现MP3解码
音频编解码芯片 通讯 VS1003 VS1053 VS1011 VS1063
音频编解码芯片 VS1003 VS1053 VS1063 VS1005 VS1011
vs1053 vs1003 vs1063 vs1011 vs1005 音频编解码芯片代理,提供资料
vs1053如何降低功耗
请问VS1053的GBUF引脚可以悬空吗?
VS1053简介
如何把wavplayer软件包与vs1053编解码芯片驱动对接起来
VS1053 ram测试无法读到83ff

Linux驱动开发-编写VS1053芯片音频驱动
评论