I2S是數字音頻的接口,這裏不用多說,請讀者自己查閱相關資料。
本文中要設計的是FPGA與數字音頻芯片的I2S接口時序。簡單點說,就是通過FPGA向音頻芯片寫數據,通過的是I2S總線,因爲這個總線比較麻煩,我在這裏做成接口模塊,其它模塊直接拿來用就可以了。
提示,I2S總線的接口信號如下:
1、LRCLK:左右聲道控制,高電平時,SDATA上爲左聲道數據,低電平時,SDATA上爲右聲道數據。(也有相反的情況,請參考不同的音頻芯片的手冊)
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;
end
always@(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、找一個mp3或者其它音頻文件,48KHz的採樣率以上,如果採樣率不是48KHz的,通過Adobe Audition(原Cool Edit)軟件調整採樣率(升採樣率會出現雜音,你懂的)。
2、用Matlab打開,可以看到在計算機上的音頻文件的數據是經過歸一化的。將他們轉化成16位的二進制數(unsigned int類型的也一樣),然後另存爲二進制文件。
3、通過USB接口(見EZ-USB與FPGA的通信接口設計),自己編寫的軟件,將這個二進制文件發送下去。FPGA端連續不斷的將數據輸出即可聽到聲音。(軟件通過USB發送數據下去的時候,最好將文件切成1K的段發下去,因爲FPGA的FIFO緩衝區沒那麼大,USB發送數據的延時等待也要設置成200ms以上,不然數據流會斷掉)
轉載:https://www.cnblogs.com/lifan3a/articles/4874798.html