0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于FPGA的模拟I²C协议系统设计

FPGA威廉希尔官方网站 江湖 来源:FPGA威廉希尔官方网站 江湖 2023-02-03 09:23 次阅读

导读

I²C(Inter-Integrated Circuit),其实是I²CBus 简称,中文就是集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统手机用以连接低速周边设备而发展。I²C的正确读法为“I平方C”("I-squared-C"),而“I二C”("I-two-C")则是另一种错误但被广泛使用的读法。自2006年10月1日起,使用 I²C 协议已经不需要支付专利费,但制造商仍然需要付费以获取 I²C 从属设备地址。

I²C简单来说,就是一种串行通信协议,I²C的通信协议和通信接口在很多工程中有广泛的应用,如数据采集领域的串行 AD,图像处理领域的摄像头配置,工业控制领域的 X 射线管配置等等。除此之外,由于I²C协议占用的 IO 资源特别少,连接方便,所以工程中也常选用I²C接口做为不同芯片间的通信协议。I²C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I²C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。

在现代电子系统中,有为数众多的 IC 需要进行相互之间以及与外界的通信。为了简化电路的设计,Philips 公司开发了一种用于内部 IC 控制的简单的双向两线串行总线I²C(Intel-Integrated Circuit bus)。1998 年当推出I²C总线协议 2.0 版本时,I²C协议实际上已经成为一个国际标准。

在进行 FPGA 设计时,经常需要和外围提供I²C接口的芯片通信。例如低功耗的 CMOS 实时时钟/日历芯片 PCF8563、LCD 驱动芯片 PCF8562、并行口扩展芯片 PCF8574、键盘/LED 驱动器 ZLG7290 等都提供I²C接口。因此在 FPGA 中模拟I²C接口已成为 FPGA 开发必要的步骤。

本篇将详细讲解在 FPGA 芯片中使用 VHDL/Verilog HDL interwetten与威廉的赔率体系 I²C协议,以及编写 TestBench仿真和测试程序的方法。

第三篇内容摘要:本篇会介绍程序的仿真与测试,包括主节点的仿真、从节点的仿真、仿真主程序、仿真结果以及总结等相关内容。

四、程序的仿真与测试

I²C 协议的模拟程序完成后,还需要通过仿真程序对程序的功能进行测试。对本程序的仿真包括 3 个部分:第一部分是主节点的仿真,模拟数据读/写;第二部分是从节点的仿真,模拟数据的接收和应答;第三部分是仿真主程序,负责整个仿真过程的控制。

4.1 主节点的仿真

主节点仿真的内容包括读数据、写数据和比较数据 3 部分,代码如下:

`include "timescale.v"
//模块定义
module wb_master_model(clk, rst, adr, din, dout, cyc, stb, we, sel, ack, err, rty);
    //参数
    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
        
    // 写数据周期
    task wb_write;
        input delay;
        integer delay;
        input [awidth -1:0] a;
        input [dwidth -1:0] d;
        begin
            // 延迟
            repeat(delay) @(posedge clk);
            // 设置信号值
            #1;
            adr = a;
            dout = d;
            cyc = 1'b1;
            stb = 1'b1;
            we = 1'b1;
            sel = {dwidth/8{1'b1}};
            @(posedge clk);
            // 等待从节点的应答信号
            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
    
    // 读数据周期
    task wb_read;
        input delay;
        integer delay;
        input [awidth -1:0]a;
        output [dwidth -1:0] d;
        begin
            // 延迟
            repeat(delay) @(posedge clk);
            // 设置信号值
            #1;
            adr = a;
            dout = {dwidth{1'bx}};
            cyc = 1'b1;
            stb = 1'b1;
            we = 1'b0;
            sel = {dwidth/8{1'b1}};
            @(posedge clk);
            // 等待从节点应答信号
            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
    
    // 比较数据
    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("Datacompareerror.Received%h,expected%hattime%t",q,d_exp,$time);
        end
    endtask
endmodule

4.2 从节点的仿真

从节点仿真程序需要模拟从主节点接收数据,并发出应答信号,代码如下:

`include "timescale.v"
//模块定义
module i2c_slave_model (scl, sda);
    // 参数
    // 地址
    parameter I2C_ADR = 7'b001_0000;
    
    // 输入、输出
    input scl;
    inout sda;
    
    // 变量申明
    wire debug = 1'b1;
    reg [7:0] mem [3:0]; // 初始化内存
    reg [7:0] mem_adr; // 内存地址
    reg [7:0] mem_do; // 内存数据输出
    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;
    
    // 状态机的状态定义
    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
        
    // 产生移位寄存器
    always @(posedge scl)
        sr <= #1 {sr[6:0],sda};
        
    //检测到访问地址与从节点一致
    assign my_adr = (sr[7:1] == I2C_ADR);
    
    //产生位寄存器
    always @(posedge scl)
        if(ld)
            bit_cnt <= #1 3'b111;
        else
            bit_cnt <= #1 bit_cnt - 3'h1;
            
    //产生访问结束标志
    assign acc_done = !(|bit_cnt);
    
    // sda 延迟
    assign #1 sda_dly = sda;
    
    //检测到开始状态
    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;
    
    // 检测到停止状态信号
    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;
            
    //产生 I2C 的 RESET 信号
    assign i2c_reset = sta || sto;
    
    // 状态机
    always @(negedge scl or posedge sto)
        if (sto || (sta && !d_sta) )
            begin
                state <= #1 idle; // reset 状态机
                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 状态
                    if (acc_done && my_adr)
                        begin
                            state <= #1 slave_ack;
                            rw <= #1 sr[0];
                            sda_o <= #1 1'b0; // 产生应答信号
                            #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: // 等待内存地址
                        if(acc_done)
                            begin
                                state <= #1 gma_ack;
                                mem_adr <= #1 sr; // 保存内存地址
                                sda_o <= #1 !(sr <= 15); // 收到合法地址信号后发出应答信号
                            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: // 接收数据
                        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
                        
    // 从内存读数据
    always @(posedge scl)
    if(!acc_done && rw)
    mem_do <= #1 {mem_do[6:0], 1'b1};
    
    // 产生三态
    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); // 开始状态信号
        $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // 停止状态信号
        $setup(posedge tst_sta, posedge tst_scl, normal_sta_sto);
    endspecify
    
endmodule

4.3 仿真主程序

仿真主程序完成主节点数据到从节点的控制,代码如下:

`include "timescale.v"
//模块定义
moduletst_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 保存状态寄存器内容
    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; //高位地址
    parameterCTR=3'b010;//控制寄存器地址,(7)使能位|6中断使能位|5-0其余保留位
    parameterRXR=3'b011;//接收寄存器地址,(7)接收到的最后一个字节的数据
    parameter TXR = 3'b011; //传输寄存器地址,(7)传输地址时最后一位为读写位,1 为读
    parameter CR = 3'b100; //命令寄存器地址,

//(7)开始|6结束|5读|4写|3应答(作为接收方时,发送应答信号,“0”为应答,“1”为不应答)|2保留位|1保留位|0中断应答位,这八位自动清除
parameterSR=3'b100;//状态寄存器地址,(7)接收应答位(“0”为接收到应答)|6忙位(产生开始信号后变为1,结束信号后变为0)|5仲裁位|4-2保留位|1传输中位(1表示正在传输数据,0表示传输结束)|中断标志位
    parameter TXR_R = 3'b101;
    parameter CR_R = 3'b110;
    
    // 产生时钟信号,一个时间单位为 1ns,周期为 10ns,频率为 100MHz。
    always #5 clk = ~clk;
    
    //连接 master 模拟模块
    wb_master_model #(8, 32) u0 (
            .clk(clk), //时钟
            .rst(rstn), //重起
            .adr(adr), //地址
            .din(dat_i), //输入的数据
            .dout(dat_o), //输出的数据
            .cyc(cyc),
            .stb(stb),
            .we(we),
            .sel(),
            .ack(ack), //应答
            .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), //数据输入接口
            .wb_dat_o(dat_i), //数据从接口输出
            .wb_we_i(we), //写使能信号
            .wb_stb_i(stb), //片选信号,应该一直为高
            .wb_cyc_i(cyc),
            .wb_ack_o(ack), //应答信号输出到 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 模拟模块产生 scl 和 sda 的三态缓冲
    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("
 状态: %t I2C 接口测试开始!

", $time);
            // 初始值
            clk = 0;
            //重起系统
            rstn = 1'b1; // negate reset
            #2;
            rstn = 1'b0; // assert reset
            repeat(20) @(posedge clk);
            rstn = 1'b1; // negate reset
            $display("状态: %t 完成系统重起!", $time);
            @(posedge clk);
            // 对接口编程
            // 写内部寄存器
            // 分频 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 完成分频寄存器操作!", $time);
            //读分频寄存器内容
            u0.wb_cmp(0, PRER_LO, 8'hc8);
            u0.wb_cmp(0, PRER_HI, 8'h00);
            $display("状态: %t 完成分频寄存器确认操作!", $time);
            //接口使能
            u0.wb_write(1, CTR, 8'h80);
            $display("状态: %t 完成接口使能!", $time);
            // 驱动 slave 地址
            // h'a0=b'1010_0000,地址+写状态,写入的地址为 h'50
            u0.wb_write(1, TXR, 8'ha0);
            //命令内容为 b'1001_0000,产生开始位,并设置写状态
            u0.wb_write(0, CR, 8'h90);
            $display("状态: %t 产生开始位, 然后写命令 a0(地址+写),命令开始!", $time);
            // 检查状态位信息
            // 检查传输是否结束
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(0, SR, q);
            $display("状态: %t 地址驱动写操作完成!", $time);
            // 待写的地址为 h'01
            u0.wb_write(1, TXR, 8'h01);
            // 产生写命令 b'0001_0000
            u0.wb_write(0, CR, 8'h10);
            $display("状态: %t 待写地址为 01,命令开始!", $time);
            // 检查状态位
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(0, SR, q);
            $display("状态: %t 写操作完成!", $time);
            // 写入内容
            u0.wb_write(1, TXR, 8'ha5);
            u0.wb_write(0, CR, 8'h10);
            $display("状态: %t 写入内容为 a5,开始写入过程!", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %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 写 5a 到下一个地址,产生停止位!", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q); // poll it until it is zero
            $display("状态: %t 写第二个地址结束!", $time);
            // 读
            // 驱动 slave 地址
            u0.wb_write(1, TXR, 8'ha0);
            u0.wb_write(0, CR, 8'h90);
            $display("状态: %t 产生开始位,写命令 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 slave 地址驱动完成!", $time);
            // 发送地址
            u0.wb_write(1, TXR, 8'h01);
            u0.wb_write(0, CR, 8'h10);
            $display("状态: %t 发送地址 01!", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %t 地址发送完成!", $time);
            // 驱动 slave 地址,1010_0001,h'50+read
            u0.wb_write(1, TXR, 8'ha1);
            u0.wb_write(0, CR, 8'h90);
            $display("状态: %t 产生重复开始位, 读地址+开始位", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %t 命令结束!", $time);
            // 读数据
            u0.wb_write(1, CR, 8'h20);
            $display("状态: %t 读+应答命令", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %t 读结束!", $time);
            // 检查读的内容
            u0.wb_read(1, RXR, qq);
            if(qq !== 8'ha5)
                $display("
 错误: 需要的是 a5, received %x at time %t", qq, $time);
            // 读下一个地址内容
            u0.wb_write(1, CR, 8'h20);
            $display("状态: %t 读+ 应答", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %t 第二个地址读结束!", $time);
            u0.wb_read(1, RXR, qq);
            if(qq !== 8'h5a)
            $display("
 错误: 需要的是 5a, received %x at time %t", qq, $time);
            // 读
            u0.wb_write(1, CR, 8'h20);
            $display("状态: %t 读 + 应答", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %t 第三个地址读完成!", $time);
            u0.wb_read(1, RXR, qq);
            $display("状态: %t 第三个地址内容是 %x !", $time, qq);
            // 读
            u0.wb_write(1, CR, 8'h28);
            $display("状态: %t 读 + 不应答!", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %t 第四个地址读完成!", $time);
            u0.wb_read(1, RXR, qq);
            $display("状态: %t 第四个地址内容为 %x !", $time, qq);
            // 检查不存在的 slave 地址
            // drive slave address
            u0.wb_write(1, TXR, 8'ha0);
            u0.wb_write(0, CR, 8'h90);
$display("状态:%t 产生开始位, 发送命令 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 命令结束!", $time);
            // 发送内存地址
            u0.wb_write(1, TXR, 8'h10);
            u0.wb_write(0, CR, 8'h10);
            $display("状态: %t 发送 slave 内存地址 10!", $time);
            u0.wb_read(1, SR, q);
            while(q[1])
                u0.wb_read(1, SR, q);
            $display("状态: %t 地址发送完毕!", $time);
            // slave 发送不应答
            $display("状态: %t 检查不应答位!", $time);
            if(!q[7])
            $display("
 错误: 需要 NACK, 接收到 ACK
");
            // 从 slave 读数据
            u0.wb_write(1, CR, 8'h40);
            $display("状态: %t 产生'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 结束!", $time);
            #25000; // wait 25us
            $display("

 状态: %t 测试结束!", $time);
            $finish;
        end
endmodule

4.4 仿真结果

在 ModelSim 中可以看到仿真的结果。如图 7 所示是发送开始状态并写地址“a0”时的图形,此时在图上表示为 SCL 处于高时 SDA 的一个下降沿,然后是数据“1010,0000”。

7e776cac-a345-11ed-bfe3-dac502259ad0.png

图 7 发送开始信号并写地址 a0 如图 8 所示为发送数据“01”和“a5”时的图形,在图上表示为:数据“0000,0001”和“1010,0101”。

7e915248-a345-11ed-bfe3-dac502259ad0.png

图 8 发送数据“01”和“a5” 如图 9 所示的是发送停止状态信号和数据“5a”时的图形,在图上表示为 SCL 处于高时SDA 的一个上升沿,然后是数据“0101,1010”。

7ea8191a-a345-11ed-bfe3-dac502259ad0.png

图 9 发送停止状态信号和数据“5a” 仿真程序说明I²C程序符合I²C协议的时序和数据格式,可以实现模拟I²C协议的任务。

五、总结

本篇首先说明了I²C协议相关的内容,介绍协议基本概念和数据传输各个命令的具体含义以及协议对时序的要求。接下来介绍模拟I²C协议程序的框架,详细讲解框架中各个模块的功能并介绍详细代码。最后通过一个完成的仿真程序完成对程序的测试。I²C在应用中有着广泛的用途,本篇希望通过这个例子为各位大侠提供一个可行的解决方案。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1629

    文章

    21736

    浏览量

    603331
  • 集成电路
    +关注

    关注

    5388

    文章

    11547

    浏览量

    361808
  • SDA
    SDA
    +关注

    关注

    0

    文章

    124

    浏览量

    28134
  • SCL
    SCL
    +关注

    关注

    1

    文章

    239

    浏览量

    17079
  • LCD驱动芯片
    +关注

    关注

    0

    文章

    19

    浏览量

    7994

原文标题:往期精选:基于FPGA的模拟 I²C协议系统设计(附代码)

文章出处:【微信号:HXSLH1010101010,微信公众号:FPGA威廉希尔官方网站 江湖】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于FPGAI²C 协议相关的内容

    I²C 简单来说,就是一种串行通信协议I²C的通信协议和通信接口在很多工程中有广泛的应用,如数
    发表于 08-09 15:30 1224次阅读

    I²C协议基本概念和数据传输

    今天给大侠带来基于FPGA模拟 I²C 协议设计,由于篇幅较长,分三篇。今天带来第三篇,下篇,程序的仿真与测试。话不多说,上货。
    的头像 发表于 11-01 09:18 3009次阅读

    FPGAI²C 总线解析 I²C 总线是什么

    FPGA模拟 I²C 接口已成为 FPGA 开发必要的步骤。I2C
    发表于 09-29 09:37

    FPGA学习】模拟 I²C 接口程序的基本框架

    模拟 I²C 接口程序的基本框架如图 4-4 所示。1.程序接口用于和应用程序连接的接口,将应用程序的数据按照 I²C
    发表于 10-09 11:28

    基于 FPGA模拟 I²C协议设计使用

    今天给大侠带来基于FPGA模拟 I²C 协议设计,由于篇幅较长,分三篇。今天带来第一篇,上篇, I
    发表于 09-02 19:12

    I2C通讯协议

    目录 I2C协议 I2C 物理层I2C协议层STM32的硬件I2C软件
    发表于 08-11 07:36

    基于 FPGA模拟 I2C协议系统设计

    今天给大侠带来基于FPGA模拟 I2C 协议设计,由于篇幅较长,分三篇。今天带来第一篇,上篇, I2C 总线解析以及
    发表于 08-14 18:21

    I2C总线协议及其应用

    I2C总线协议及其应用 一、I2C总线介绍: ---- 由于大规模集成电路威廉希尔官方网站 的发展,在单个芯片集成CPU以及组成一个单独工作系统
    发表于 02-08 11:23 1569次阅读
    <b class='flag-5'>I2C</b>总线<b class='flag-5'>协议</b>及其应用

    基于FPGA的多协议隔离总线信号模拟器设计

    基于FPGA的多协议隔离总线信号模拟器设计
    发表于 01-07 19:08 0次下载

    80C51单片机模拟I2C总线的主机程序分享

    I2C总线协议程序 在使用的过程中一定要注意时序、时间的问题。 i2c.c /* I2C.c 标准80C51单
    发表于 12-05 15:39 3453次阅读
    80<b class='flag-5'>C</b>51单片机<b class='flag-5'>模拟</b><b class='flag-5'>I2C</b>总线的主机程序分享

    关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结(fishing_8)

    趁着帮老师代上嵌入式实验课的机会,又重新熟悉了一遍stm32的通信协议:串口协议、SPI协议I2C协议、RS485
    发表于 12-05 13:51 3次下载
    关于stm32通信<b class='flag-5'>协议</b>:软件<b class='flag-5'>模拟</b>SPI、软件<b class='flag-5'>模拟</b><b class='flag-5'>I2C</b>的总结(fishing_8)

    硬件I2C模拟I2C

    配置;而软件I2C是没有寄存器这个概念的。 软件I2C一般是使用GPIO管脚,用软件控制SCL,SDA线输出高低电平,模拟i2c协议的时序
    发表于 12-28 19:14 81次下载
    硬件<b class='flag-5'>I2C</b>与<b class='flag-5'>模拟</b><b class='flag-5'>I2C</b>

    一文看懂I2C协议

    电子行业最常用的3种串行通讯协议:UART、SPI和I2C。前面介绍了串口通讯协议及其FPGA实现,SPI协议。本篇文章介绍
    发表于 01-25 18:32 40次下载
    一文看懂<b class='flag-5'>I2C</b><b class='flag-5'>协议</b>

    基于FPGA模拟 I²C协议设计

    今天给大侠带来基于FPGA模拟 I²C 协议设计,由于篇幅较长,分三篇。今天带来第三篇,下篇,程序的仿真与测试。话不多说,上货。
    发表于 08-09 15:34 716次阅读

    I2C协议的基础知识

    本文从I2C协议的概述开始,描述协议的历史、不同速度模式、物理层和数据帧结构,最后介绍I2C混合电压系统中电平兼容性以及上拉电阻大小计算。
    的头像 发表于 10-22 15:51 505次阅读
    <b class='flag-5'>I2C</b><b class='flag-5'>协议</b>的基础知识