FPGA音訊編解碼驅動及I2C寫入程式碼
FPGA音訊編解碼驅動及I2C寫入程式碼
使用音訊編解碼晶片為WM8731,其通過I2C對WM8731進行暫存器寫入,將需要寫入的資料放入例化的ROM塊中,通過狀態機控制資料的寫入;通過對50M和24M的時鐘分頻提供WM8731的主時鐘和位寫入時鐘,資料沒有進行處理,僅僅是資料的採集,後期資料可以進行FFT等數字訊號處理操作。程式碼最後邊有一個波形的模擬圖,程式碼裡有一點需要修改,在模擬圖裡可以看出來。共勉
程式碼如下:
module memory( //I2C資料通訊協議
clk, //系統輸入
clk24, //系統輸入24M時鐘
rst_n, //系統復位
sclk, //I2C時鐘線
sdin, //I2C資料線
mclk, //wm8731的主時鐘
bclk, //傳送資料的bit時鐘
dadat, //DA接受的資料
addat, //AD傳送的資料
dalrc, //DA的聲道選擇
adlrc, //AD的聲道選擇
data,
I2Caddress
);
input clk,rst_n,clk24;
output sclk,sdin;
output mclk,bclk,dalrc,adlrc,dadat; //使用WM8731作為slave模式
input addat;
output [8:0] data;
output [6:0] I2Caddress;
reg sclk,sdin;
reg bclk,dalrc,adlrc,dadat;
wire mclk;
romrom_inst ( //ROM的例化程式
.address (I2Caddress ),
.clock ( clk ),
.q ( data)
);
memorypllmemorypll_inst ( //例化的Pll塊
.areset ( rst_n ),
.inclk0 ( clk ),
.c0 ( mclk ),
.locked ( locked_sig )
);
//assign data = q;
//assign I2Caddress = address;
reg flag; //應答訊號
reg [6:0] I2Caddre; //I2C的訪問地址
reg [6:0] I2Caddress; // //記憶體的地址
wire [8:0] data; //記憶體的資料
reg [5:0] state; //狀態描述
reg [31:0] dadataddress; //DA存放資料記憶體
reg [31:0] addataddress; //AD存放資料記憶體
parameter IDLE = 6'd0; //復位初始化狀態
parameter START = 6'd1; //開始狀態
parameter I2CADDR0 = 6'd2; //I2C的地址資料
parameter I2CADDR1 = 6'd3;
parameter I2CADDR2 = 6'd4;
parameter I2CADDR3 = 6'd5;
parameter I2CADDR4 = 6'd6;
parameter I2CADDR5 = 6'd7;
parameter I2CADDR6 = 6'd8;
parameter REWE = 6'd9; //對於從機的讀寫選擇
parameter I2Caddress0 = 6'd10; //記憶體的地址
parameter I2Caddress1 = 6'd11;
parameter I2Caddress2 = 6'd12;
parameter I2Caddress3 = 6'd13;
parameter I2Caddress4 = 6'd14;
parameter I2Caddress5 = 6'd15;
parameter I2Caddress6 = 6'd16;
parameter DATA0 = 6'd17; //記憶體的資料
parameter DATA1 = 6'd18;
parameter DATA2 = 6'd19;
parameter DATA3 = 6'd20;
parameter DATA4 = 6'd21;
parameter DATA5 = 6'd22;
parameter DATA6 = 6'd23;
parameter DATA7 = 6'd24;
parameter DATA8 = 6'd25;
parameter STOP = 6'd26; //關閉狀態
parameter ACK0 = 6'd27; //應答狀態
parameter ACK1 = 6'd28;
parameter ACK2 = 6'd29;
parameter READY = 6'd30;
parameter PRESTOP = 6'd31;
parameter N = 625;
reg [9:0] count; //計數
always @(posedge clk or negedge rst_n) //I2C時鐘的調配,輸入時鐘50M,輸出時鐘40k,對其1250分頻
begin
if(!rst_n)
begin
count <= 10'd0;
sclk <= 1'b0;
end
else if( count==N )
begin
sclk <= ~sclk;
count <= 10'd0;
end
else count <= count + 1'b1;
end
//
//
//
always @(sclk ) //對於狀態機的描述
begin
if(!rst_n)
begin
state <= IDLE;
flag <= 0;
I2Caddress <= 7'd0;
end
else
begin
case(state) //對於29種狀態的描述
IDLE : if(sclk == 1) //起始狀態
begin
sdin <= 1;
flag <= 0;
state <= READY;
end
else state <= IDLE;
READY : if(sclk == 1) //準備狀態
begin
sdin <= 0;
flag <= 0;
state <= START;
end
else state <= READY;
START : if(sclk == 0) //起始狀態
begin
sdin <= 0;
flag <= 0;
state <= I2CADDR0;
end
else state <= START;
I2CADDR0 : if(sclk == 0) //I2C地址
begin
sdin <= 0;
flag <= 0;
state <= I2CADDR1;
end
else state <= I2CADDR0;
I2CADDR1 : if(sclk == 0)
begin
sdin <= 1;
flag <= 0;
state <= I2CADDR2;
end
else state <= I2CADDR1;
I2CADDR2 : if(sclk == 0)
begin
sdin <= 1;
flag <= 0;
state <= I2CADDR3;
end
else state <= I2CADDR2;
I2CADDR3 : if(sclk == 0)
begin
sdin <= 0;
flag <= 0;
state <= I2CADDR4;
end
else state <= I2CADDR3;
I2CADDR4 : if(sclk == 0)
begin
sdin <= 1;
flag <= 0;
state <= I2CADDR5;
end
else state <= I2CADDR4;
I2CADDR5 : if(sclk == 0)
begin
sdin <= 0;
flag <= 0;
state <= I2CADDR6;
end
else state <= I2CADDR5;
I2CADDR6 : if(sclk == 0)
begin
sdin <= 0;
flag <= 0;
state <= REWE;
end
else state <= I2CADDR6;
REWE : if(sclk == 0) //讀寫狀態
begin
sdin <= 0;
flag <= 0;
state <= ACK0;
end
else state <= REWE;
ACK0 : if(sclk == 0) //應答狀態
begin
flag <= 0;
sdin <= I2Caddress[6];
state <= I2Caddress0;
end
else state <= ACK0;
I2Caddress0 : if(sclk == 0) //資料地址狀態
begin
flag <= 0;
sdin <= I2Caddress[5];
state <= I2Caddress1;
end
else state <= I2Caddress0;
I2Caddress1 : if(sclk == 0)
begin
flag <= 0;
sdin <= I2Caddress[4];
state <= I2Caddress2;
end
else state <= I2Caddress1;
I2Caddress2 : if(sclk == 0)
begin
flag <= 0;
sdin <= I2Caddress[3];
state <= I2Caddress3;
end
else state <= I2Caddress2;
I2Caddress3 : if(sclk == 0)
begin
flag <= 0;
sdin <= I2Caddress[2];
state <= I2Caddress4;
end
else state <= I2Caddress3;
I2Caddress4 : if(sclk == 0)
begin
flag <= 0;
sdin <= I2Caddress[1];
state <= I2Caddress5;
end
else state <= I2Caddress4;
I2Caddress5 : if(sclk == 0)
begin
flag <= 0;
sdin <= I2Caddress[0];
state <= I2Caddress6;
end
else state <= I2Caddress5;
I2Caddress6 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[8];
state <= ACK1;
end
else state <= I2Caddress6;
DATA0 : if(sclk == 0) //應答狀態
begin
flag <= 0;
sdin <= 0;
state <= ACK1;
end
else state <= DATA0;
ACK1 : if(sclk == 0) //資料狀態
begin
flag <= 0;
sdin <= data[7];
state <= DATA1;
end
else state <= ACK1;
DATA1 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[6];
state <= DATA2;
end
else state <= DATA1;
DATA2 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[5];
state <= DATA3;
end
else state <= DATA2;
DATA3 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[4];
state <= DATA4;
end
else state <= DATA3;
DATA4 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[3];
state <= DATA5;
end
else state <= DATA4;
DATA5 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[2];
state <= DATA6;
end
else state <= DATA5;
DATA6 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[1];
state <= DATA7;
end
else state <= DATA6;
DATA7 : if(sclk == 0)
begin
flag <= 0;
sdin <= data[0];
state <= DATA8;
end
else state <= DATA7;
DATA8 : if(sclk == 0)
begin
flag <= 0;
sdin <= 0;
state <= ACK2;
end
else state <= DATA8;
ACK2 : if(sclk == 1) //應答狀態
begin
flag <= 0;
sdin <= 0;
state <= PRESTOP;
end
else state <= ACK2;
PRESTOP : if(sclk == 1) //結束前狀態
begin
flag <= 1;
sdin <= 1;
state <= STOP;
end
else state <= PRESTOP;
STOP : if(sclk == 0)
begin
flag <= 0;
sdin <= 0;
state <= IDLE;
I2Caddress <= I2Caddress + 1;
end
else state <= STOP;
default : begin
state <= IDLE;
flag <= 0;
end
endcase
end
end
//
//
//
reg [9:0] count2;
always @(posedge clk24 or negedge rst_n) //將24Mhz的時鐘分為48Khz
begin
if(!rst_n)
begin
count2 <= 10'd0;
bclk <= 1'b0;
end
else if( count2==500 )
begin
bclk <= ~bclk;
count2 <= 10'd0;
end