【摘要】 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); //读ramvoid vs1053_writeram(u16 addr,u16 val); //写ramvoid 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_data100)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)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 188static 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 188int 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/ -fobj-m +=vs1053_drv.o
论述无功补偿在三相不平衡治理中的应用和重要性
东芝微控制器助力物联网设备安全,均获Mbed OS认证
常开与常闭的转换解决的方法分析
2020年OPPO手机西欧市场出货量增长超过两倍
PC互联网时代已经结束?谷歌宣布优先抓取移动版网页
Linux驱动开发-编写VS1053芯片音频驱动
美国拟对华为制裁再加码 台积电或将首先遭遇寒流
上能电气与欣旺达签署战略合作协议深化风电+储能、光伏+储能合作
密钥证明的六个小贴士介绍
5G元年的2019 手机市场黑科技百花齐放
x25045应用电路
浅谈低压电容补偿柜的构造和功效
串口数据与固定ID的CAN数据转换
荷兰在5G建设中可能不会事先排除华为
全球最大的电子展览/墨西哥电子产品展/墨西哥电子展/俄罗斯电
TouchStone (SNP,S2P) 格式详解
如何使用微处理器复位电压检测器IC降低线圈的保持电流
剖析FPGA的高精度电磁信号采集系统设计
飞利浦发布新款入耳式真无线高级耳机
一文解析BF518F Blackfin处理器开发评估方案