wm8960驱动:裸机代码研读

来源:互联网 发布:文字复制软件 编辑:程序博客网 时间:2024/05/21 21:50

网上已经有基本的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;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

其中 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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

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; 
这里写图片描述 
这里写图片描述

原创粉丝点击