網上已經有基本的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;