i2s是數字音訊的介面,這裡不用多說,請讀者自己查閱相關資料。
本文中要設計的是fpga與數字音訊晶元的i2s介面時序。簡單點說,就是通過fpga向音訊晶元寫資料,通過的是i2s匯流排,因為這個匯流排比較麻煩,我在這裡做成介面模組,其它模組直接拿來用就可以了。
提示,i2s匯流排的介面訊號如下:
2、bclk:跟sdata上資料對應的時鐘,上公升沿採資料,也可能在下降沿採資料,請注意對應音訊晶元手冊上的說明。
3、sdata:序列資料,乙個bclk對應乙個。
時序圖如下,ws就是lrclk,bclk就是sck。
一、設計思路,資料流向,如下:
二、分析
左聲道和右聲道的資料,分別設計成兩個fifo即可。重點在於如何將兩路資料拼裝到一起,再轉換成序列的資料。
三、設計
1、lrclk和bclk的產生
提示,如果數字音訊的資料是16位的,那麼bclk就是lrclk的16倍。即在乙個lrclk中,有32個bclk,16個左聲道資料,16個右聲道資料。同樣,如果資料是12位的,那麼bclk就是lrclk的24倍。
verilog**如下:
// lrclk & bclk generate
reg [7:0] lrclk_cnt = 0;
reg [2:0] bclk_cnt = 0;
always@(posedge clk) begin
lrclk_cnt <= lrclk_cnt + 1;
if (lrclk_cnt == 127) audio_lrclk <= 1'b1;
if (lrclk_cnt == 255) audio_lrclk <= 1'b0;
endalways@(posedge clk) begin
bclk_cnt <= bclk_cnt + 1;
if (bclk_cnt == 3) audio_bclk <= 1'b1;
if (bclk_cnt == 7) audio_bclk <= 1'b0;
end說明,如果音訊資料的取樣率是48khz,那麼,一般情況下,clk應該是取樣率的256、384或者512倍。比較常見的是256倍,那麼,這裡的clk=44.8khz*256=12.288mhz。
之所以用這種計數器的方式產生lrclk和bclk,是為下面的裝入資料做準備的。
2、sdata資料的載入
// dac data assembling
reg [15:0] lbuf = 16'd0;
reg [15:0] rbuf = 16'd0;
always@(negedge clk) begin
case(lrclk_cnt)
// left
0: audio_sdata <= lbuf[15];
8: audio_sdata <= lbuf[14];
16: audio_sdata <= lbuf[13];
24: audio_sdata <= lbuf[12];
32: audio_sdata <= lbuf[11];
40: audio_sdata <= lbuf[10];
48: audio_sdata <= lbuf[9];
56: audio_sdata <= lbuf[8];
64: audio_sdata <= lbuf[7];
72: audio_sdata <= lbuf[6];
80: audio_sdata <= lbuf[5];
88: audio_sdata <= lbuf[4];
96: audio_sdata <= lbuf[3];
104: audio_sdata <= lbuf[2];
112: audio_sdata <= lbuf[1];
120: audio_sdata <= lbuf[0];
// right
128: audio_sdata <= rbuf[15];
136: audio_sdata <= rbuf[14];
144: audio_sdata <= rbuf[13];
152: audio_sdata <= rbuf[12];
160: audio_sdata <= rbuf[11];
168: audio_sdata <= rbuf[10];
176: audio_sdata <= rbuf[9];
184: audio_sdata <= rbuf[8];
192: audio_sdata <= rbuf[7];
200: audio_sdata <= rbuf[6];
208: audio_sdata <= rbuf[5];
216: audio_sdata <= rbuf[4];
224: audio_sdata <= rbuf[3];
232: audio_sdata <= rbuf[2];
240: audio_sdata <= rbuf[1];
248: audio_sdata <= rbuf[0];
endcase
end說明,至於在計數器的哪個值上將資料賦值,以上的**都是經過**和實測的,讀者可以自己**觀察一下就知道了。
3、fifo資料的讀取
第2節**中可以看到,sdata的資料是從lbuf和rbuf中取的,那麼下面的模組就是如何將資料從fifo中取出,並放到lbur和rbuf中了。
// fetch audio data from fifo
assign lfifo_rd_clk = clk;
assign rfifo_rd_clk = clk;
always@(negedge clk) begin
case(lrclk_cnt)
125:
begin
if(!rfifo_empty) rfifo_rd_en <= 1;
end
126:
begin
rfifo_rd_en <= 0;
rbuf <= rfifo_dout;
end
253:
begin
if(!lfifo_empty) lfifo_rd_en <= 1;
end
254:
begin
lfifo_rd_en <= 0;
lbuf <= lfifo_dout;
end
endcase
end說明,上面取資料對應的計數器值也是經過**和實測的,沒有問題,讀者可以自己**觀察下。
最後,上面的**都是經過作者實測的。
測試情況:
1、找乙個***或者其它音訊檔案,48khz的取樣率以上,如果取樣率不是48khz的,通過adobe audition(原cool edit)軟體調整取樣率(公升取樣率會出現雜音,***)。
2、用matlab開啟,可以看到在計算機上的音訊檔案的資料是經過歸一化的。將他們轉化成16位的二進位制數(unsigned int型別的也一樣),然後另存為二進位制檔案。
3、通過usb介面(見ez-usb與fpga的通訊介面設計),自己編寫的軟體,將這個二進位制檔案傳送下去。fpga端連續不斷的將資料輸出即可聽到聲音。(軟體通過usb傳送資料下去的時候,最好將檔案切成1k的段發下去,因為fpga的fifo緩衝區沒那麼大,usb傳送資料的延時等待也要設定成200ms以上,不然資料流會斷掉)
I2S匯流排介面設計(Verilog)
i2s是數字音訊的介面,這裡不用多說,請讀者自己查閱相關資料。本文中要設計的是fpga與數字音訊晶元的i2s介面時序。簡單點說,就是通過fpga向音訊晶元寫資料,通過的是i2s匯流排,因為這個匯流排比較麻煩,我在這裡做成介面模組,其它模組直接拿來用就可以了。提示,i2s匯流排的介面訊號如下 2 bc...
I2S音訊匯流排學習(四)I2S介面設計
圖1 傳送端 隨著ws訊號的改變,匯出乙個wsp脈衝訊號,進入並行移位暫存器裝入data left或data right,從而輸出資料被啟用。序列資料在時鐘下降沿移出。序列資料的預設輸入是0,因此所有位於最低位 lsb 後的資料將被設定為0。圖2 接收端 隨著第乙個ws訊號的改變,wsp在sck訊號...
I2S音訊匯流排學習(四)I2S介面設計
圖1 傳送端 隨著ws訊號的改變,匯出乙個wsp脈衝訊號,進入並行移位暫存器裝入data left或data right,從而輸出資料被啟用。序列資料在時鐘下降沿移出。序列資料的預設輸入是0,因此所有位於最低位 lsb 後的資料將被設定為0。圖2 接收端 隨著第乙個ws訊號的改變,wsp在sck訊號...