您的位置:首页 > 编程语言

wm8960驱动:裸机代码研读

2017-01-08 21:26 411 查看
网上已经有基本的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;





这是IISMOD寄存器,其中bit0无意义,所以不列出

我们已经得到了I2SCLK,那么用它干啥?



在获得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获得方式上面已经讲过,不再赘述。

Codec端时钟设置:





SYSCLK SRC选择为MCLK的时钟,也就是11.2896Mhz

SYSCLK的CLKDIV选择1,SYSCLK=MCLK=11.2896Mhz

DACDIV=ADCDIV=SYSCLK/256=44.1Khz

0x4=0;



0x5=0



选择I2S format,word length 16 bit

0x7=2;



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: