加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

基于FPGA的模擬 I2C協(xié)議系統(tǒng)設(shè)計(附代碼)

2023/12/18
1859
閱讀需 41 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。

今天給大俠帶來基于FPGA的 模擬?I2C?協(xié)議設(shè)計,由于篇幅較長,分三篇。今天帶來第三篇,下篇,程序的仿真與測試。話不多說,上貨。

之前也有相關(guān)文章介紹,這里超鏈接一下,僅供各位大俠參考。

源碼系列:基于FPGA的 IIC 設(shè)計(附源工程)

這里也給出前兩篇的超鏈接:

基于 FPGA 的模擬 I2C協(xié)議設(shè)計(上)

基于 FPGA 的模擬 I2C協(xié)議設(shè)計(中)

I2C(Inter-Integrated Circuit),其實是?I2C?Bus 簡稱,中文就是集成電路總線,它是一種串行通信總線,使用多主從架構(gòu),由飛利浦公司在1980年代為了讓主板、嵌入式系統(tǒng)或手機用以連接低速周邊設(shè)備而發(fā)展。I2C的正確讀法為“I平方C”("I-squared-C"),而“I二C”("I-two-C")則是另一種錯誤但被廣泛使用的讀法。自2006年10月1日起,使用 I2C 協(xié)議已經(jīng)不需要支付專利費,但制造商仍然需要付費以獲取 I2C 從屬設(shè)備地址。

I2C?簡單來說,就是一種串行通信協(xié)議,I2C的通信協(xié)議和通信接口在很多工程中有廣泛的應(yīng)用,如數(shù)據(jù)采集領(lǐng)域的串行 AD,圖像處理領(lǐng)域的攝像頭配置,工業(yè)控制領(lǐng)域的 X 射線管配置等等。除此之外,由于?I2C?協(xié)議占用的 IO 資源特別少,連接方便,所以工程中也常選用?I2C?接口做為不同芯片間的通信協(xié)議。I2C?串行總線一般有兩根信號線,一根是雙向的數(shù)據(jù)線SDA,另一根是時鐘線SCL。所有接到?I2C?總線設(shè)備上的串行數(shù)據(jù)SDA都接到總線的SDA上,各設(shè)備的時鐘線SCL接到總線的SCL上。

在現(xiàn)代電子系統(tǒng)中,有為數(shù)眾多的 IC 需要進行相互之間以及與外界的通信。為了簡化電路的設(shè)計,Philips 公司開發(fā)了一種用于內(nèi)部 IC 控制的簡單的雙向兩線串行總線?I2C(Intel-Integrated Circuit bus)。1998 年當(dāng)推出?I2C?總線協(xié)議 2.0 版本時,I2C?協(xié)議實際上已經(jīng)成為一個國際標準。

在進行 FPGA 設(shè)計時,經(jīng)常需要和外圍提供?I2C?接口的芯片通信。例如低功耗CMOS 實時時鐘/日歷芯片 PCF8563、LCD 驅(qū)動芯片 PCF8562、并行口擴展芯片 PCF8574、鍵盤/LED 驅(qū)動器 ZLG7290 等都提供?I2C?接口。因此在 FPGA 中模擬?I2C?接口已成為 FPGA 開發(fā)必要的步驟。

本篇將詳細講解在 FPGA 芯片中使用 VHDL/Verilog HDL 模擬?I2C?協(xié)議,以及編寫 TestBench仿真和測試程序的方法。

第三篇內(nèi)容摘要:本篇會介紹程序的仿真與測試,包括主節(jié)點的仿真、從節(jié)點的仿真、仿真主程序、仿真結(jié)果以及總結(jié)等相關(guān)內(nèi)容。

四、程序的仿真與測試

I2C 協(xié)議的模擬程序完成后,還需要通過仿真程序?qū)Τ绦虻墓δ苓M行測試。對本程序的仿真包括 3 個部分:第一部分是主節(jié)點的仿真,模擬數(shù)據(jù)讀/寫;第二部分是從節(jié)點的仿真,模擬數(shù)據(jù)的接收和應(yīng)答;第三部分是仿真主程序,負責(zé)整個仿真過程的控制。

4.1 主節(jié)點的仿真

主節(jié)點仿真的內(nèi)容包括讀數(shù)據(jù)、寫數(shù)據(jù)和比較數(shù)據(jù) 3 部分,代碼如下:

`include "timescale.v"//模塊定義module wb_master_model(clk, rst, adr, din, dout, cyc, stb, we, sel, ack, err, rty);    //參數(shù)    parameter dwidth = 32;    parameter awidth = 32;        //輸入、輸出    input clk, rst;    output [awidth -1:0] adr;    input [dwidth -1:0] din;    output [dwidth -1:0] dout;    output cyc, stb;    output we;    output [dwidth/8 -1:0] sel;    input ack, err, rty;        //WIRE 定義    reg [awidth -1:0] adr;    reg [dwidth -1:0] dout;    reg cyc, stb;    reg we;    reg [dwidth/8 -1:0] sel;    reg [dwidth -1:0] q;        // 存儲邏輯    //初始化    initial        begin            adr = {awidth{1'bx}};            dout = {dwidth{1'bx}};            cyc = 1'b0;            stb = 1'bx;            we = 1'hx;            sel = {dwidth/8{1'bx}};            #1;        end            // 寫數(shù)據(jù)周期    task wb_write;        input delay;        integer delay;        input [awidth -1:0] a;        input [dwidth -1:0] d;        begin            // 延遲            repeat(delay) @(posedge clk);            // 設(shè)置信號值            #1;            adr = a;            dout = d;            cyc = 1'b1;            stb = 1'b1;            we = 1'b1;            sel = {dwidth/8{1'b1}};            @(posedge clk);            // 等待從節(jié)點的應(yīng)答信號            while(~ack) @(posedge clk);            #1;            cyc = 1'b0;            stb = 1'bx;            adr = {awidth{1'bx}};            dout = {dwidth{1'bx}};            we = 1'hx;            sel = {dwidth/8{1'bx}};        end    endtask        // 讀數(shù)據(jù)周期    task wb_read;        input delay;        integer delay;        input [awidth -1:0]a;        output [dwidth -1:0] d;        begin            // 延遲            repeat(delay) @(posedge clk);            // 設(shè)置信號值            #1;            adr = a;            dout = {dwidth{1'bx}};            cyc = 1'b1;            stb = 1'b1;            we = 1'b0;            sel = {dwidth/8{1'b1}};            @(posedge clk);            // 等待從節(jié)點應(yīng)答信號            while(~ack) @(posedge clk);            #1;            cyc = 1'b0;            stb = 1'bx;            adr = {awidth{1'bx}};            dout = {dwidth{1'bx}};            we = 1'hx;            sel = {dwidth/8{1'bx}};            d = din;        end    endtask        // 比較數(shù)據(jù)    task wb_cmp;        input delay;        integer delay;        input [awidth -1:0] a;        input [dwidth -1:0] d_exp;        begin            wb_read (delay, a, q);            if (d_exp !== q)????????????$display("Data?compare?error.?Received?%h,?expected?%h?at?time?%t",?q,?d_exp,$time);        end    endtaskendmodule

4.2 從節(jié)點的仿真

從節(jié)點仿真程序需要模擬從主節(jié)點接收數(shù)據(jù),并發(fā)出應(yīng)答信號,代碼如下:

`include "timescale.v"//模塊定義module i2c_slave_model (scl, sda);    // 參數(shù)    // 地址    parameter I2C_ADR = 7'b001_0000;        // 輸入、輸出    input scl;    inout sda;        // 變量申明    wire debug = 1'b1;    reg [7:0] mem [3:0]; // 初始化內(nèi)存    reg [7:0] mem_adr; // 內(nèi)存地址    reg [7:0] mem_do; // 內(nèi)存數(shù)據(jù)輸出    reg sta, d_sta;    reg sto, d_sto;    reg [7:0] sr; // 8 位移位寄存器    reg rw; // 讀寫方向    wire my_adr; // 地址    wire i2c_reset; // RESET 信號    reg [2:0] bit_cnt;    wire acc_done; // 傳輸完成    reg ld;    reg sda_o;    wire sda_dly;        // 狀態(tài)機的狀態(tài)定義    parameter idle = 3'b000;    parameter slave_ack = 3'b001;    parameter get_mem_adr = 3'b010;    parameter gma_ack = 3'b011;    parameter data = 3'b100;    parameter data_ack = 3'b101;    reg [2:0] state;        // 模塊主體    //初始化    initial        begin            sda_o = 1'b1;            state = idle;        end            // 產(chǎn)生移位寄存器    always @(posedge scl)        sr <= #1 {sr[6:0],sda};            //檢測到訪問地址與從節(jié)點一致    assign my_adr = (sr[7:1] == I2C_ADR);        //產(chǎn)生位寄存器    always @(posedge scl)        if(ld)            bit_cnt <= #1 3'b111;        else            bit_cnt <= #1 bit_cnt - 3'h1;                //產(chǎn)生訪問結(jié)束標志    assign acc_done = !(|bit_cnt);        // sda 延遲    assign #1 sda_dly = sda;        //檢測到開始狀態(tài)    always @(negedge sda)        if(scl)            begin                sta <= #1 1'b1;                    if(debug)                        $display("DEBUG i2c_slave; start condition detected at %t", $time);            end        else            sta <= #1 1'b0;                always @(posedge scl)        d_sta <= #1 sta;        // 檢測到停止?fàn)顟B(tài)信號    always @(posedge sda)        if(scl)            begin                sto <= #1 1'b1;                    if(debug)                        $display("DEBUG i2c_slave; stop condition detected at %t", $time);            end        else            sto <= #1 1'b0;                //產(chǎn)生 I2C 的 RESET 信號    assign i2c_reset = sta || sto;        // 狀態(tài)機    always @(negedge scl or posedge sto)        if (sto || (sta && !d_sta) )            begin                state <= #1 idle; // reset 狀態(tài)機                sda_o <= #1 1'b1;                ld <= #1 1'b1;            end        else            begin            // 初始化            sda_o <= #1 1'b1;            ld <= #1 1'b0;            case(state)                idle: // idle 狀態(tài)                    if (acc_done && my_adr)                        begin                            state <= #1 slave_ack;                            rw <= #1 sr[0];                            sda_o <= #1 1'b0; // 產(chǎn)生應(yīng)答信號                            #2;                            if(debug && rw)                                $display("DEBUG?i2c_slave;?command?byte?received?(read)?at?%t",$time);                            if(debug && !rw)                                $display("DEBUG?i2c_slave;?command?byte?received?(write)?at?%t",$time);                            if(rw)                                begin                                    mem_do <= #1 mem[mem_adr];                                        if(debug)                                            begin                                                #2?$display("DEBUG?i2c_slave;?data?block?read?%x?from address?%x?(1)",?mem_do,?mem_adr);                                                #2?$display("DEBUG?i2c_slave;?memcheck?[0]=%x,?[1]=%x, [2]=%x",?mem[4'h0],?mem[4'h1],?mem[4'h2]);                                            end                                end                        end                    slave_ack:                        begin                            if(rw)                                begin                                    state <= #1 data;                                    sda_o <= #1 mem_do[7];                                end                            else                                state <= #1 get_mem_adr;                                ld <= #1 1'b1;?????????????????????????end?????????????????????????????                    get_mem_adr: // 等待內(nèi)存地址                        if(acc_done)                            begin                                state <= #1 gma_ack;                                mem_adr <= #1 sr; // 保存內(nèi)存地址                                sda_o <= #1 !(sr <= 15); // 收到合法地址信號后發(fā)出應(yīng)答信號                            if(debug)                                #1?$display("DEBUG?i2c_slave;?address?received.?adr=%x,?ack=%b",sr,?sda_o);                            end                                                gma_ack:                        begin                            state <= #1 data;                            ld <= #1 1'b1;                        end                                       data: // 接收數(shù)據(jù)                        begin                            if(rw)                                sda_o <= #1 mem_do[7];                            if(acc_done)                                begin                                    state <= #1 data_ack;                                    mem_adr <= #2 mem_adr + 8'h1;                                    sda_o <= #1 (rw && (mem_adr <= 15) );                                if(rw)                                    begin                                        #3 mem_do <= mem[mem_adr];                                        if(debug)                                            #5?$display("DEBUG?i2c_slave;?data?block?read?%x?from address?%x?(2)",?mem_do,?mem_adr);                                    ????????????????????????????????????end                                if(!rw)                                    begin                                        mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory                                        if(debug)                                            #2?$display("DEBUG?i2c_slave;?data?block?write?%x?to address?%x",?sr,?mem_adr);                                    end                                end                            end                                                    data_ack:                            begin                                ld <= #1 1'b1;                                if(rw)                                    if(sda) //                                        begin                                            state <= #1 idle;                                            sda_o <= #1 1'b1;                                        end                                    else                                        begin                                            state <= #1 data;                                            sda_o <= #1 mem_do[7];                                        end                                    else                                        begin                                            state <= #1 data;                                            sda_o <= #1 1'b1;                                        end                                end                            endcase                        end                            // 從內(nèi)存讀數(shù)據(jù)    always @(posedge scl)    if(!acc_done && rw)    mem_do <= #1 {mem_do[6:0], 1'b1};        // 產(chǎn)生三態(tài)    assign sda = sda_o ? 1'bz : 1'b0;        // 檢查時序    wire tst_sto = sto;    wire tst_sta = sta;    wire tst_scl = scl;        //指定各個信號的上升沿和下降沿    specify        specparam normal_scl_low = 4700,            normal_scl_high = 4000,            normal_tsu_sta = 4700,            normal_tsu_sto = 4000,            normal_sta_sto = 4700,            fast_scl_low = 1300,            fast_scl_high = 600,            fast_tsu_sta = 1300,            fast_tsu_sto = 600,            fast_sta_sto = 1300;        $width(negedge scl, normal_scl_low);        $width(posedge scl, normal_scl_high);        $setup(negedge sda &&& scl, negedge scl, normal_tsu_sta); // 開始狀態(tài)信號        $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // 停止?fàn)顟B(tài)信號        $setup(posedge tst_sta, posedge tst_scl, normal_sta_sto);    endspecify    endmodule

4.3 仿真主程序

仿真主程序完成主節(jié)點數(shù)據(jù)到從節(jié)點的控制,代碼如下:

`include "timescale.v"//模塊定義module?tst_bench_top();    //連線和寄存器    reg clk;    reg rstn;    wire [31:0] adr;    wire [ 7:0] dat_i, dat_o;    wire we;    wire stb;    wire cyc;    wire ack;    wire inta;        //q 保存狀態(tài)寄存器內(nèi)容    reg [7:0] q, qq;    wire scl, scl_o, scl_oen;    wire sda, sda_o, sda_oen;        //寄存器地址    parameter PRER_LO = 3'b000; //分頻寄存器低位地址    parameter PRER_HI = 3'b001; //高位地址    parameter?CTR?=?3'b010;?//控制寄存器地址,(7)使能位|6?中斷使能位|5-0其余保留位    parameter?RXR?=?3'b011;?//接收寄存器地址,(7)接收到的最后一個字節(jié)的數(shù)據(jù)    parameter TXR = 3'b011; //傳輸寄存器地址,(7)傳輸?shù)刂窌r最后一位為讀寫位,1 為讀    parameter CR = 3'b100; //命令寄存器地址,???????//(7)開始|6?結(jié)束|5?讀|4?寫|3?應(yīng)答(作為接收方時,發(fā)送應(yīng)答信號,“0”為應(yīng)答,“1”為不應(yīng)答)|2?保留位|1?保留位|0?中斷應(yīng)答位,這八位自動清除????parameter?SR?=?3'b100;?//狀態(tài)寄存器地址,(7)接收應(yīng)答位(“0”為接收到應(yīng)答)|6?忙位(產(chǎn)生開始信號后變?yōu)?1,結(jié)束信號后變?yōu)?0)|5?仲裁位|4-2?保留位|1?傳輸中位(1?表示正在傳輸數(shù)據(jù),0?表示傳輸結(jié)束)|中斷標志位    parameter TXR_R = 3'b101;    parameter CR_R = 3'b110;        // 產(chǎn)生時鐘信號,一個時間單位為 1ns,周期為 10ns,頻率為 100MHz。    always #5 clk = ~clk;        //連接 master 模擬模塊    wb_master_model #(8, 32) u0 (            .clk(clk), //時鐘            .rst(rstn), //重起            .adr(adr), //地址            .din(dat_i), //輸入的數(shù)據(jù)            .dout(dat_o), //輸出的數(shù)據(jù)            .cyc(cyc),            .stb(stb),            .we(we),            .sel(),            .ack(ack), //應(yīng)答            .err(1'b0),            .rty(1'b0)        );        //連接 i2c 接口    i2c_master_top i2c_top (            //連接到 master 模擬模塊部分            .wb_clk_i(clk), //時鐘            .wb_rst_i(1'b0), //同步重起位            .arst_i(rstn), //異步重起            .wb_adr_i(adr[2:0]), //地址輸入            .wb_dat_i(dat_o), //數(shù)據(jù)輸入接口            .wb_dat_o(dat_i), //數(shù)據(jù)從接口輸出            .wb_we_i(we), //寫使能信號            .wb_stb_i(stb), //片選信號,應(yīng)該一直為高            .wb_cyc_i(cyc),            .wb_ack_o(ack), //應(yīng)答信號輸出到 master 模擬模塊            .wb_inta_o(inta), //中斷信號輸出到 master 模擬模塊                        //輸出的 i2c 信號,連接到 slave 模擬模塊            .scl_pad_i(scl),            .scl_pad_o(scl_o),            .scl_padoen_o(scl_oen),            .sda_pad_i(sda),            .sda_pad_o(sda_o),            .sda_padoen_o(sda_oen)        );        //連接到 slave 模擬模塊    i2c_slave_model #(7'b1010_000) i2c_slave (            .scl(scl),            .sda(sda)        );            //為 master 模擬模塊產(chǎn)生 scl 和 sda 的三態(tài)緩沖    assign scl = scl_oen ? 1'bz : scl_o; // create tri-state buffer for i2c_master scl line    assign sda = sda_oen ? 1'bz : sda_o; // create tri-state buffer for i2c_master sda line        //上拉    pullup p1(scl); // pullup scl line    pullup p2(sda); // pullup sda line        //初始化    initial        begin            $display("n 狀態(tài): %t I2C 接口測試開始!nn", $time);            // 初始值            clk = 0;            //重起系統(tǒng)            rstn = 1'b1; // negate reset            #2;            rstn = 1'b0; // assert reset            repeat(20) @(posedge clk);            rstn = 1'b1; // negate reset            $display("狀態(tài): %t 完成系統(tǒng)重起!", $time);            @(posedge clk);            // 對接口編程            // 寫內(nèi)部寄存器            // 分頻 100M/100K*5=O'200=h'C8            u0.wb_write(1, PRER_LO, 8'hc7);            u0.wb_write(1, PRER_HI, 8'h00);            $display("狀態(tài): %t 完成分頻寄存器操作!", $time);            //讀分頻寄存器內(nèi)容            u0.wb_cmp(0, PRER_LO, 8'hc8);            u0.wb_cmp(0, PRER_HI, 8'h00);            $display("狀態(tài): %t 完成分頻寄存器確認操作!", $time);            //接口使能            u0.wb_write(1, CTR, 8'h80);            $display("狀態(tài): %t 完成接口使能!", $time);            // 驅(qū)動 slave 地址            // h'a0=b'1010_0000,地址+寫狀態(tài),寫入的地址為 h'50            u0.wb_write(1, TXR, 8'ha0);            //命令內(nèi)容為 b'1001_0000,產(chǎn)生開始位,并設(shè)置寫狀態(tài)            u0.wb_write(0, CR, 8'h90);            $display("狀態(tài): %t 產(chǎn)生開始位, 然后寫命令 a0(地址+寫),命令開始!", $time);            // 檢查狀態(tài)位信息            // 檢查傳輸是否結(jié)束            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(0, SR, q);            $display("狀態(tài): %t 地址驅(qū)動寫操作完成!", $time);            // 待寫的地址為 h'01            u0.wb_write(1, TXR, 8'h01);            // 產(chǎn)生寫命令 b'0001_0000            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 待寫地址為 01,命令開始!", $time);            // 檢查狀態(tài)位            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(0, SR, q);            $display("狀態(tài): %t 寫操作完成!", $time);            // 寫入內(nèi)容            u0.wb_write(1, TXR, 8'ha5);            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 寫入內(nèi)容為 a5,開始寫入過程!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 寫 a5 到地址 h'01 中完成!", $time);            // 寫入下一個地址 5a            u0.wb_write(1, TXR, 8'h5a); // present data            // 寫入并停止            u0.wb_write(0, CR, 8'h50); // set command (stop, write)            $display("狀態(tài): %t 寫 5a 到下一個地址,產(chǎn)生停止位!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t 寫第二個地址結(jié)束!", $time);            // 讀            // 驅(qū)動 slave 地址            u0.wb_write(1, TXR, 8'ha0);            u0.wb_write(0, CR, 8'h90);            $display("狀態(tài): %t 產(chǎn)生開始位,寫命令 a0 (slave 地址+write)", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t slave 地址驅(qū)動完成!", $time);            // 發(fā)送地址            u0.wb_write(1, TXR, 8'h01);            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 發(fā)送地址 01!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 地址發(fā)送完成!", $time);            // 驅(qū)動 slave 地址,1010_0001,h'50+read            u0.wb_write(1, TXR, 8'ha1);            u0.wb_write(0, CR, 8'h90);            $display("狀態(tài): %t 產(chǎn)生重復(fù)開始位, 讀地址+開始位", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 命令結(jié)束!", $time);            // 讀數(shù)據(jù)            u0.wb_write(1, CR, 8'h20);            $display("狀態(tài): %t 讀+應(yīng)答命令", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 讀結(jié)束!", $time);            // 檢查讀的內(nèi)容            u0.wb_read(1, RXR, qq);            if(qq !== 8'ha5)                $display("n 錯誤: 需要的是 a5, received %x at time %t", qq, $time);            // 讀下一個地址內(nèi)容            u0.wb_write(1, CR, 8'h20);            $display("狀態(tài): %t 讀+ 應(yīng)答", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 第二個地址讀結(jié)束!", $time);            u0.wb_read(1, RXR, qq);            if(qq !== 8'h5a)            $display("n 錯誤: 需要的是 5a, received %x at time %t", qq, $time);            // 讀            u0.wb_write(1, CR, 8'h20);            $display("狀態(tài): %t 讀 + 應(yīng)答", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 第三個地址讀完成!", $time);            u0.wb_read(1, RXR, qq);            $display("狀態(tài): %t 第三個地址內(nèi)容是 %x !", $time, qq);            // 讀            u0.wb_write(1, CR, 8'h28);            $display("狀態(tài): %t 讀 + 不應(yīng)答!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 第四個地址讀完成!", $time);            u0.wb_read(1, RXR, qq);            $display("狀態(tài): %t 第四個地址內(nèi)容為 %x !", $time, qq);            // 檢查不存在的 slave 地址            // drive slave address            u0.wb_write(1, TXR, 8'ha0);            u0.wb_write(0, CR, 8'h90);????????????$display("狀態(tài):?%t 產(chǎn)生開始位, 發(fā)送命令 a0?(slave 地址+寫). 檢查非法地址!",$time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t 命令結(jié)束!", $time);            // 發(fā)送內(nèi)存地址            u0.wb_write(1, TXR, 8'h10);            u0.wb_write(0, CR, 8'h10);            $display("狀態(tài): %t 發(fā)送 slave 內(nèi)存地址 10!", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q);            $display("狀態(tài): %t 地址發(fā)送完畢!", $time);            // slave 發(fā)送不應(yīng)答            $display("狀態(tài): %t 檢查不應(yīng)答位!", $time);            if(!q[7])            $display("n 錯誤: 需要 NACK, 接收到 ACKn");            // 從 slave 讀數(shù)據(jù)            u0.wb_write(1, CR, 8'h40);            $display("狀態(tài): %t 產(chǎn)生'stop'位", $time);            u0.wb_read(1, SR, q);            while(q[1])                u0.wb_read(1, SR, q); // poll it until it is zero            $display("狀態(tài): %t 結(jié)束!", $time);            #25000; // wait 25us            $display("nn 狀態(tài): %t 測試結(jié)束!", $time);            $finish;        endendmodule

4.4 仿真結(jié)果

在 ModelSim 中可以看到仿真的結(jié)果。如圖 7 所示是發(fā)送開始狀態(tài)并寫地址“a0”時的圖形,此時在圖上表示為 SCL 處于高時 SDA 的一個下降沿,然后是數(shù)據(jù)“1010,0000”。

圖 7 發(fā)送開始信號并寫地址 a0

如圖 8 所示為發(fā)送數(shù)據(jù)“01”和“a5”時的圖形,在圖上表示為:數(shù)據(jù)“0000,0001”和“1010,0101”。

圖 8 發(fā)送數(shù)據(jù)“01”和“a5”

如圖 9 所示的是發(fā)送停止?fàn)顟B(tài)信號和數(shù)據(jù)“5a”時的圖形,在圖上表示為 SCL 處于高時SDA 的一個上升沿,然后是數(shù)據(jù)“0101,1010”。

圖 9 發(fā)送停止?fàn)顟B(tài)信號和數(shù)據(jù)“5a”

仿真程序說明 I2C 程序符合 I2C 協(xié)議的時序和數(shù)據(jù)格式,可以實現(xiàn)模擬 I2C 協(xié)議的任務(wù)。

五、總結(jié)

本篇首先說明了?I2C?協(xié)議相關(guān)的內(nèi)容,介紹協(xié)議基本概念和數(shù)據(jù)傳輸各個命令的具體含義以及協(xié)議對時序的要求。接下來介紹模擬?I2C?協(xié)議程序的框架,詳細講解框架中各個模塊的功能并介紹詳細代碼。最后通過一個完成的仿真程序完成對程序的測試。I2C?在應(yīng)用中有著廣泛的用途,本篇希望通過這個例子為各位大俠提供一個可行的解決方案。

本篇到此結(jié)束,各位大俠,有緣再見!

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
XC7A200T-2FBG676I 1 AMD Xilinx Field Programmable Gate Array, 16825 CLBs, 1286MHz, 215360-Cell, CMOS, PBGA676, FBGA-676

ECAD模型

下載ECAD模型
$356.55 查看
XC6SLX9-2CPG196I 1 AMD Xilinx Field Programmable Gate Array, 715 CLBs, 667MHz, 9152-Cell, CMOS, PBGA196, 8 X 8 MM, 0.50 MM PITCH, LEAD FREE, BGA-196
$50.98 查看
XC6SLX45-2CSG324I 1 AMD Xilinx Field Programmable Gate Array, 3411 CLBs, 667MHz, 43661-Cell, CMOS, PBGA324, 15 X 15 MM, 0.80 MM PITCH, LEAD FREE, BGA-324
$65.17 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

任何技術(shù)的學(xué)習(xí)就好比一個江湖,對于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。