首页 > 技术知识 > 正文

【摘要】 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接口引脚

Linux驱动开发-编写VS1053芯片音频驱动-linux驱动和芯片驱动

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

Linux驱动开发-编写VS1053芯片音频驱动-linux驱动和芯片驱动1

SPI读时序:

Linux驱动开发-编写VS1053芯片音频驱动-linux驱动和芯片驱动2

SPI写时序:

Linux驱动开发-编写VS1053芯片音频驱动-linux驱动和芯片驱动3

VS1053模块与单片机之间的连线图:

Linux驱动开发-编写VS1053芯片音频驱动-linux驱动和芯片驱动4

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

猜你喜欢