1. 程式人生 > >verilog中24LC04B iic(i2c)讀寫通訊設計步驟,以及程式常見寫法錯誤。

verilog中24LC04B iic(i2c)讀寫通訊設計步驟,以及程式常見寫法錯誤。

板子使用的是黑金的是xilinx spartan—6開發板,首先準備一份24LC04B晶片資料,讀懂資料後列出關鍵引數。

如下:

1、空閒狀態為SDA和SCL都為高電平

2、開始狀態為:保持SCL,SDA高電平不變,SDA 保持最少4us,之後SDA為低,保持最少4us

3、結束狀態為:保持SCL為高、SDA為低電平不變,保持最少4us,SDA為高保持最少4us

4、時間間隔4us要求來源(上面資料為24LC04,下面資料為24LC04B)

初步估算了一下時鐘要求,100k(10us)符合要求,由於start有效位是在SCL高電平中間,所以SCL的上升沿和下降沿之間可以假設2個時鐘週期,由上升沿觸發,之後可以開始寫程式了。

 一、寫流程


寫暫存器的標準流程為:
1.    Master發起START
2.    Master傳送I2C addr(7bit)和w操作0(1bit),等待ACK
3.    Slave傳送ACK
4.    Master傳送reg addr(8bit),等待ACK
5.    Slave傳送ACK
6.    Master傳送data(8bit),即要寫入暫存器中的資料,等待ACK
7.    Slave傳送ACK
8.    第6步和第7步可以重複多次,即順序寫多個暫存器
9.    Master發起STOP

二、資料寫入晶片時間

由晶片資料得到3ms,為了方便程式中設定為4ms,其中為了方便除錯,放入了很多測試點以及led燈顯示。

三、讀流程

1.    Master傳送I2Caddr(7bit)和 w操作0(1bit),等待ACK
2.    Slave傳送ACK
3.    Master傳送reg addr(8bit),等待ACK
4.    Slave傳送ACK
5.    Master發起START
6.    Master傳送I2C addr(7bit)和R讀1位,等待ACK
7.    Slave傳送ACK
8.   Slave傳送data(8bit),即暫存器裡的值
9.   Master傳送ACK
10.    第8步和第9步可以重複多次,即順序讀多個暫存器

四、程式

top和iic檔案,時鐘檔案自己寫吧,採用10us為週期的時鐘。

top檔案

`timescale 1ns / 1ps

module model(
                        CLK_50M,
                        reset,
                        led,
                        SDA,SCL,
                        clk_1s,
                        clk_100ms,
                        clk_10ms,
                        clk_1ms,
                        clk_10us,
                        bit_state,
                        write_down,read_down,
                        test0,test1,test2,test3,test4,test5,test6,test7,test8,test9,
                        read_data
                        
);
input CLK_50M,reset;
output SCL,write_down,read_down,test0,test1,test2,test3,test4,test5,test6,test7,test8,test9;
output [3:0]led;
output [7:0]bit_state;
output [7:0]read_data;
inout SDA;
output clk_1s,clk_100ms,clk_10ms,clk_1ms,clk_10us;
//output [3:0] led; 
//output [5:0] sel;              
//output [7:0] LED;
wire clk_1s,clk_100ms,clk_10ms,clk_1ms,clk_10us;
//分析儀取樣頻率
clkdiv u0(.reset                            (reset),
            .CLK_50M                         (CLK_50M),
            .clkout                    (clk_1s),
            .clkout1                   (clk_100ms),
            .clkout2                   (clk_10ms),    
            .clkout3                   (clk_1ms),
            .clkout4                   (clk_10us)                    
);
wirte_iic u1(
            .clk_10us                  (clk_10us),
            .reset                            (reset),
            .led                                (led),
            .SDA                               (SDA),
            .SCL                                (SCL),
            .bit_state                        (bit_state),
            .write_down                        (write_down),
            .read_down                        (read_down),
            .test0                            (test0),
            .test1                            (test1),
            .test2                            (test2),
            .test3                            (test3),
            .test4                            (test4),        
            .test5                            (test5),
            .test6                            (test6),
            .test7                            (test7),
            .test8                            (test8),
            .test9                            (test9),            
            .read_data                        (read_data)
            
);

/***************************/
//chipscope icon和ila, 用於觀察訊號//
/***************************/    
wire [35:0]   CONTROL0;
wire [255:0]  TRIG0;
chipscope_icon icon_debug (
    .CONTROL0(CONTROL0) // INOUT BUS [35:0]
);

chipscope_ila ila_filter_debug (
    .CONTROL(CONTROL0), // INOUT BUS [35:0]
   // .CLK(dma_clk),    // IN
    .CLK(clk_10us),      // IN, chipscope的取樣時鐘
    .TRIG0(TRIG0)       // IN BUS [255:0], 取樣的訊號
    //.TRIG_OUT(TRIG_OUT0)
);                                                     
assign  TRIG0[0]  = test0;  //取樣
assign  TRIG0[1]  = test1;  //取樣
assign  TRIG0[2]  = test2;  //取樣
assign  TRIG0[3]  = test3;  //取樣
assign  TRIG0[4]  = test4;  //取樣
assign  TRIG0[5]  = test5;  //取樣
assign  TRIG0[6]  = test6;  //取樣
assign  TRIG0[7]  = test7;  //取樣
assign  TRIG0[8]  = test8;  //取樣
assign  TRIG0[9]  = test9;  //取樣
assign  TRIG0[10]  = read_down;  //取樣
assign  TRIG0[11]  = write_down;  //取樣
assign  TRIG0[12]  = SDA;  //取樣
assign  TRIG0[13]  = clk_1ms;  //取樣

assign  TRIG0[21:14] = bit_state;
assign  TRIG0[29:22]  = read_data;  //取樣

 //取樣

endmodule
 

iic檔案

`timescale 1ns / 1ps

module wirte_iic(    clk_10us,
                        reset,
                        led,
                        SDA,
                        SCL,
                        bit_state,
                        write_down,
                        test0,test1,test2,test3,test4,,test5,test6,test7,test8,test9,
                        read_data,
                        read_down
    );
input clk_10us,reset;
inout SDA;
output SCL;
output reg [3:0]led;
output reg [7:0]bit_state;//bit_state為第幾個訊號  ,byte_state
output reg write_down,read_down,test0,test1,test2,test3,test4,test5,test6,test7,test8,test9;
output reg [7:0]read_data;
reg SCL;   
reg isOut,tx_SDA;
reg [1:0]cnt;
reg [4:0]byte_state;
reg [7:0]write_data; 
reg [16:0]    buf_time;//stop後,需要至少3ms寫入頁     
parameter             write_address      =8'b1010_0000,
                        write_chipaddress  =8'b0000_0000,
                        write_bit          =8'b0001_1000;

assign SDA = isOut ? tx_SDA : 1'bz;

always @(posedge clk_10us or negedge reset)
begin
    if(!reset) 
        cnt <=0;
    else begin
            if(cnt ==3)
                cnt <=0;
            else
                cnt <= cnt +1'b1;
    end
end    

always @(posedge clk_10us or negedge reset)
begin
    if(!reset)begin
            tx_SDA <=0;
            SCL <=0;
            isOut <=0;
            led<=4'b0000;
            bit_state <=0;
            byte_state <=0;    //第幾個8位包
            write_data <=0;
            write_down <=0;
            read_down  <=0;
            read_data  <=0;
            test0<=0;test1<=0;test2<=0;test3<=0;test4<=0;
            test5<=0;test6<=0;test7<=0;test8<=0;test9<=0;
            buf_time<=0;
        end 
    else if(write_down==0&&read_down==0) begin
        case (bit_state)
        0:begin//起始位  
            if(cnt==0)begin tx_SDA<=1;SCL<=1;isOut <=1;end
            if(cnt==1)begin tx_SDA<=1;SCL<=1;end
            if(cnt==2)begin tx_SDA<=0;SCL<=1;end
            if(cnt==3)begin tx_SDA<=0;SCL<=0;bit_state<=bit_state +1;write_data<=write_address;    end
            end
        1,2,3,4,5,6,7,8:begin//資料寫入
            if(cnt==0)begin SCL<=0; tx_SDA <= write_data[8-bit_state];isOut <=1;end //高位先發送
            if(cnt==1)begin SCL<=1;end
            if(cnt==2)begin SCL<=1;end
            if(cnt==3)begin SCL<=0;bit_state <= bit_state +1;end
            end            
        9:begin//判斷ACK
            if(cnt==0)begin isOut<=0; end                //釋放SDA
            if(cnt==1)begin SCL<=1;end
            if(cnt==2)begin            
                if(SDA==0&&byte_state==0)begin    
                    byte_state <=1;write_data<=write_chipaddress;end
                else if(SDA==0&&byte_state==1)begin
                    byte_state <=2;write_data<=write_bit;end
                else if(SDA==0&&byte_state==2)begin
                    byte_state <=3;write_data<=write_address;    end               
                else begin    SCL<=1;end                                              
            end 
            if(cnt==3)begin
                if(byte_state==1)begin 
                    SCL<=0;bit_state<=1;end
                else if(byte_state==2)begin
                    SCL<=0;bit_state<=1;    end
                else if(byte_state==3)begin
                    SCL<=0;bit_state<=10;end    
                else begin SCL<=0; end
            end
          end  
        10:begin//停止位
            if(cnt==0)begin tx_SDA<=0;SCL<=0;isOut<=1;end
            if(cnt==1)begin tx_SDA<=0;SCL<=1;end
            if(cnt==2)begin tx_SDA<=1;SCL<=1;end
            if(cnt==3)begin tx_SDA<=1;SCL<=0;bit_state<=11; end
            end
        11:begin//快取3ms寫入資料
            if(cnt==0)begin tx_SDA<=1'bz;SCL<=1'bz;isOut<=0;end
            if(cnt==1)begin tx_SDA<=1'bz;SCL<=1'bz;end
            if(cnt==2)begin tx_SDA<=1'bz;SCL<=1'bz;end
            if(cnt==3)begin 
                    if(buf_time==100)begin
                        write_down <=1;
                        bit_state<=0; end
                    else  begin
                        bit_state<=11;
                        buf_time<=buf_time+1;end
                end
        end
        default:begin isOut <=0;led<=4'b1111; end
        endcase
        end
    else if(write_down==1&&read_down==0)begin//讀取資料
    case(bit_state)
            0:begin//起始位  
            if(cnt==0)begin tx_SDA<=1;SCL<=1;isOut <=1;end
            if(cnt==1)begin tx_SDA<=1;SCL<=1;end
            if(cnt==2)begin tx_SDA<=0;SCL<=1;end
            if(cnt==3)begin tx_SDA<=0;SCL<=0;bit_state<=bit_state +1;
                if(byte_state==5)write_data<=8'b10100001;else write_data<=write_address;end
            end
        1,2,3,4,5,6,7,8:begin//資料寫入
            if(cnt==0)begin SCL<=0; tx_SDA <= write_data[8-bit_state];isOut <=1;end//高位先發送
            if(cnt==1)begin SCL<=1;end
            if(cnt==2)begin SCL<=1;end
            if(cnt==3)begin SCL<=0;bit_state <= bit_state +1;end
            end     
        9:begin//判斷ACK
            if(cnt==0)begin isOut<=0;end                //釋放SDA
            if(cnt==1)begin SCL<=1;    end
            if(cnt==2)begin            
                if(SDA==0&&byte_state==3)begin    
                    byte_state <=4;write_data<=write_chipaddress;end
                else if(SDA==0&&byte_state==4)begin
                    byte_state <=5;    end
                else if(SDA==0&&byte_state==5)begin
                    byte_state <=6;    end
                else begin    SCL<=1;end                                              
            end 
            if(cnt==3)begin         
                if(byte_state==4)begin 
                    SCL<=0;bit_state<=1;end
                else if(byte_state==5)begin
                    SCL<=0;bit_state<=0;    end
                else if(byte_state==6)begin
                    SCL<=0;bit_state<=10;end    
                else begin SCL<=0; end
            end
          end  
        10,11,12,13,14,15,16,17:begin//資料讀
            if(cnt==0)begin SCL<=0; isOut<=0; end
            if(cnt==1)begin SCL<=1;end
            if(cnt==2)begin
                    if(byte_state==6)begin
                        SCL<=1; 
                        read_data[17-bit_state] <= SDA;end
            end
            if(cnt==3)begin SCL<=0;bit_state <= bit_state +1;end
            end        
        18:begin//NACK to 1
            if(cnt==0)begin SCL<=0;isOut<=0;    end    
            if(cnt==1)begin SCL<=1;end
            if(cnt==2)begin
                if(SDA==1)begin    
                        SCL<=1;end
                else begin    SCL<=1;bit_state<=0;end
            end
            if(cnt==3)begin  SCL<=0; bit_state <= bit_state +1; end 
            end
        19:begin//stop
            if(cnt==0)begin tx_SDA<=0;SCL<=0;isOut<=1;end
            if(cnt==1)begin tx_SDA<=0;SCL<=1;end
            if(cnt==2)begin tx_SDA<=1;SCL<=1;end
            if(cnt==3)begin tx_SDA<=1;SCL<=0;bit_state <= bit_state +1; end
            end    
        20:begin//空閒訊號只能在邊沿變化,同步非常重要
            if(cnt==0)begin tx_SDA<=1;SCL<=1;isOut<=1;end    
            if(cnt==1)begin tx_SDA<=1;SCL<=1; end
            if(cnt==2)begin tx_SDA<=1;SCL<=1; end
            if(cnt==3)begin read_down<=1;bit_state<=0; led<=4'b0001;end
        end
        default:begin isOut <=0;end
        endcase        
        end
    else if(write_down==1&&read_down==1)begin
            if(cnt==0)begin tx_SDA<=1;SCL<=1;isOut<=0;end    
            if(cnt==1)begin tx_SDA<=1;SCL<=1; end
            if(cnt==2)begin tx_SDA<=1;SCL<=1; end
            if(cnt==3)begin tx_SDA<=1;SCL<=1; end
        end
    else begin led<=4'b1111;end

end

endmodule