网上已经有基本的wm8960驱动的demo。可以播放特定频率的wav文件。这个程序很具有参考性。
我们知道,初始化wm8960,需要大概的步骤如下:
1.初始化I2C总线,通过I2C接口给wm8960下配置命令。
2.将声音文件加载到memory中
3.初始化I2S,并把内存中的数据通过I2S总线送给wm8960,从而wm8960通过解码播放出声音
我们大概来看一下,这个代码的实现过程:
void main(void)
{
printf("Audio Test\r\n");
int offset = 0x2E; // 音频数据开始的地方
short * p = (short *)0x22000000; // 音频文件应该位于的位置
iic_init(); // 初始化i2c
wm8960_init(); // 初始化wm8960
iis_init(); // 初始化iis
// 循环播放音频文件
while (1)
{
// polling Primary Tx FIFO0 full status indication.
while((IISCON & (1<<8)) == (1<<8));
IISTXD = *(p+offset); // 每次发送2byte
offset++;
if (offset > (WAV_SIZE2-0x2e) /2) // 有多少个2byte = (文件大小-偏移)/2
offset = 0x2E;
}
}
其中 wm8960_init()用来初始化wm8960.具体代码:
void wm8960_init(void)
{
// bit[7:1]: 0x1a
// bit[0]:0: write
#define WM8960_DEVICE_ADDR 0x34
// 重置
iic_write(WM8960_DEVICE_ADDR, 0xf, 0x0);
// 设置电源
iic_write(WM8960_DEVICE_ADDR, 0x19, 1<<8 | 1<<7 | 1<<6);
iic_write(WM8960_DEVICE_ADDR, 0x1a, 1<<8 | 1<<7 | 1<<6 | 1<<5 | 1<<4 | 1<<3);
iic_write(WM8960_DEVICE_ADDR, 0x2F, 1<<3 | 1<<2);
// 设置时钟
//Mclk--div1-->SYSCLK---DIV256--->DAC/ADC sample Freq=11.289(MCLK)/256=44.1KHZ
iic_write(WM8960_DEVICE_ADDR, 0x4, 0x0);
// 设置ADC-DAC
iic_write(WM8960_DEVICE_ADDR, 0x5, 0x0);
// 设置audio interface
//I2S format 16 bits word length
iic_write(WM8960_DEVICE_ADDR, 0x7, 0x2);
// 设置OUTPUTS
iic_write(WM8960_DEVICE_ADDR, 0x2, 0xFF | 0x100);
iic_write(WM8960_DEVICE_ADDR, 0x3, 0xFF | 0x100);
// 设置DAC VOLUME
iic_write(WM8960_DEVICE_ADDR, 0xa, 0xFF | 0x100);
iic_write(WM8960_DEVICE_ADDR, 0xb, 0xFF | 0x100);
// 设置mixer
iic_write(WM8960_DEVICE_ADDR, 0x22, 1<<8 | 1<<7);
iic_write(WM8960_DEVICE_ADDR, 0x25, 1<<8 | 1<<7);
return;
}
I2S的初始化:
void iis_init(void)
{
int N;
// 配置引脚用于i2s功能
GPICON = 0x22222222;
// 设置i2s相关时钟
// step 1: EPLL output 67.7Mhz (see p361 of s5pv210.pdf)
// EPLL_CON0/ EPLL_CON1, R/W, Address = 0xE010_0110/0xE010_0114)
// FOUT = (MDIV+K/65536) X FIN / (PDIV X 2SDIV)
// Fout = (0x43+0.7)*24M / (3*2^3) = 80*24M/24 = 67.7Mhz
#define EPLL_CON0 (*(volatile unsigned int *)0xe0100110)
#define EPLL_CON1 (*(volatile unsigned int *)0xe0100114)
EPLL_CON0 = 0xa8430303; // MPLL_FOUT = 67.7Mhz
EPLL_CON1 = 0xbcee; // from linux kernel setting
// step 2: Mux_I2S AUDIO subsystem clock selection (see P1868 P1875 of s5pv210.pdf)
#define CLK_CON (*(volatile unsigned int *)0xEEE10000)
CLK_CON = 0x1; // 1 = FOUT_EPLL MUXI2S_A 00 = Main CLK
// 设置i2s控制器
// step 3: Divider of IIS (67.7 -> 11.289Mhz)
// N + 1 = (67.7Mhz) / (256 * 44.1Khz) = 5.99
// IISCDCLK 11.289Mhz = 44.1K * 256fs
// IISSCLK 1.4112Mhz = 44.1K * 32fs
// IISLRCLK 44.1Khz
N = 5;
IISPSR = 1<<15 | N<<8;
// IIS interface active (start operation). 1 = Active
IISCON |= 1<<0 | (unsigned)1<<31;
// [9:8] 10 = Transmit and receive simultaneous mode
// 1 = Using I2SCLK (use EPLL)
IISMOD = 1<<9 | 0<<8 | 1<<10;
}
I2S的初始化,主要完成时钟的初始化.
MPLL_FOUT=67.7Mhz
Codec clock=11.289Mhz—–也称作MCLK=256*fs
I2S SerialCLK(I2SSLK)=1.4122Mhz =2* fs *采样位数
I2S LRCLK ==44.1KHZ
不同采样位数的wav文件,对应的时钟信号有所不同~
这里详细展开下wm8960和s5pv210的时钟配置.
选择s5pv210为主设备,wm8960为从设备。
1. wm8960的时钟
scaler作为Master, codec作为从设备,scaler要向codec提供IIS root clock (codec clock)
还有Bit clock.
2. 产生这些clk需要时钟源,使用三星S5PV210的话,具体的时钟路由如下:
1) 获得FOUTEPLL ,XXTI是外部晶振,从外部晶振获得FINPLL,由FINPLL经过EPLL模块倍频后,产生
2) 获得IISSCLK,先通过CLKMUX_ASS选择FOUTEPLL时钟为Main CLK,再通过MUXIISA选择MAIN clk为IISCLKSRC,然后通过预分频设定分频,最后产生IISCLK
3) 最终痛过RCLKSRC选择 IIS CLK为 RCLKSRC,再通过分频器分出RCLK,也成为Root clk,或者codec clk.
4) 同时root clk再次通过分频器分出BCLK ,也称作bit clock/serial clk
到此为止,scaler产生MCLK和BCLK给codec.scaler端的时钟设置完毕.
Codec端设置:
同理,codec端也需要配置:
1) 设置SYSCLK由MCLK分频1产生
2) 设置DAC/ADC 频率由SYSCLK分频256产生
频率44100hz 16bit的wav文件
LRCLK =音频本身的频率(44.1Khz)
BCLK =2*fs*采样位数(1.411Mhz)
MCLK=256*fs(11.289Mhz)
s5pv210寄存器配置:
Fout = (0x43+0.7)*24M / (3*2^3) = 80*24M/24 = 67.7Mhz
EPLL_CON0 = 0xa8430303;
EPLL_CON1 = 0xbcee;
到此为止:FOUTEPLL已经配置完成,此时FOUTEPLL=67.7Mhz
这边通过配置 AUDIO SUBSYSTEMCLK SRC REG(AUDIO_CLK)
AUDIO_CLK =0x01;
Main clk由 CLKMUX_ASS选择器选择源时钟为上面得到的FOUTEPLL时钟,同时MUXI2S_A选择器设置为00,选择得到的main clk为I2SCLK的源时钟.
寄存器AUDIO_SUBSYSTEMCLK DIV中 I2S_A_RATIO 默认为0,也就是说I2SCLK=上面得到的I2SCLK的时钟源分频1
上面是I2SCON寄存器
I2SCON =1<<0 | (unsigned)1<<31;
在获得RCLKSRC时我们需要配置IISMOD[10],选择I2SCLK为时钟源,所以
IISMOD[10]=1 //use I2SCLK
这边的配置基本上就差不多了:RCLKSRC经过一个分频器获得RCLK,RCLK在经过一个分频器获得BCLKmaster.
1) 我们设置scaler为Master模式.codec为slave模式,所以有IISMOD[11]=0
2) SDF描述了IIS信号的传输格式,IIS,左对齐,右对齐,这边选择IIS模式,所以有IISMOD[6:5]=0
3) TXR描述传输方向,选择同时支持发送和接收,所以有IISMOD[9:8]=10
4) CDCLKCON选择codec的时钟,我们是IC内部提供CDCLK给codec.这里的CDCLK指的就是RCLK(root clk),所以有IISMOD[12]=0
5) BLC 描述了每个声道传送的bit数,我们配置为16bits per channel,这里有IISMOD[14:13]=00
6) CDD1,CDD2描述发送端是否丢弃数据,我们选择不丢弃,IISMOD[21:20]=0.IISMOD[19:18]=0
7) BLC_S, BLC_P描述second,primary fifo的每个通道的bit数,16bit,所以有IISMOD[27:26]=0,IISMOD[25:24]=0
8) OP_MUX_SEL描述了数据获取方式是从寄存器还是内部的DMA,我们选择前者,所以有IISMOD[20]=0
9) OP_CLK 描述了时钟输出的方向,scaler向外部codec输出时钟,所以有IISMOD[31:30]=0
继续前面的例子,音频44.1Khz.—–Codec的clk=256*fs =11.2896Mhz
所以此时的N+1=67.738/11.2896=6
这里设置所谓的预分频RCLK= RCLKSRC/(N+1)=11.2896Mhz
所以IISPSR= 1<<15 | 5<<8;
到此为止,codec的时钟已经产生,但还需要其他几个时钟,一个是SCLK/bit clk,另一个是LRSCLK:
这两个时钟配置在IISMOD寄存器厘米那设置:
一个是RFS(IIS Root clk freq select),一个是BFS(bit clock freq select )
所以RFS=256,也就是说Rootclk是 fs的256倍数
BFS选择为32*fs,也就是说Bit clk是fs的32倍数
这边的设置还是有些疑问的,首先我们的所有频率都是根据输入的*.wav的rate和bit决定的。所以我们才有了最开始的前提:44100Khz 16bit的wav文件。16bit的音频,BFS可以选择32或者48,这里我们选择32.
确定BFS 分频系数后,RFS可以选择256FS,384FS,512FS,768FS。这里我们选择256FS
所以最开始的FOUTepll我们是根据RFS,BFS确定之后才得到的频率。
我们确定BFS,RFS后,开始反向推导
BFS :bit clock frequency select
我们设置BFS=32,所以BCLK/SCLK =32*fs=1.4112MHZ
RFS:root clock frequency select
我们设置RFS=256,所以 RCLK/MCLK =256*fs =11.2896MHZ
所以我们的fs是最先获得的参数,只有根据fs,才能获得BCLK,MCLK,LRCLK等频率
fs参数包含在*.wav文件描述中,通过一定的格式展开获得.
这样就清晰起来了。RCLK获得方式上面已经讲过,不再赘述。
SYSCLK SRC选择为MCLK的时钟,也就是11.2896Mhz
SYSCLK的CLKDIV选择1,SYSCLK=MCLK=11.2896Mhz
DACDIV=ADCDIV=SYSCLK/256=44.1Khz
0x4=0;