Linux驱动开发-编写VS1053芯片音频驱动
Posted DS小龙哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发-编写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 <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/delay.h>
#include "mp3_data.h"
#include <linux/miscdevice.h> /*杂项字符设备头文件*/
#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)Linux驱动开发-编写PCF8591(ADC)芯片驱动
Linux——Linux驱动之iMX6ULL平台下多点触摸屏驱动开发实战(MT协议多点触摸API基于框架的触摸驱动编写触摸芯片驱动)
Linux——Linux驱动之iMX6ULL平台下多点触摸屏驱动开发实战(MT协议多点触摸API基于框架的触摸驱动编写触摸芯片驱动)