资讯详情

FPGA开发(2)——IIC通信

1、IIC通信理论知识

I2C 通讯协议(Inter-Integrated Circuit)是由 Philips 公司开发的一条简单的双向二线系统同步串行总线,在连接到总线的设备之间传输信息只需要两条线。 I2C 通信协议和通信接口广泛应用于数据采集领域的串行领域 AD,图像处理领域的摄像头配置,工业控制领域 X 射线管配置等。另外,因为 I2C 协议占用的引脚很少,硬件简单,可扩展性强,现在广泛应用于系统中的多个集成电路(IC)间的通讯。

在这里插入图片描述 (1) 它是支持多设备的总线。总线是指多设备共用的信号线。 I2C 多个通信总线可以连接到通信总线 I2C 支持多个通信主机和多个通信从机的通信设备。 (2) 一个 I2C 总线只使用两条总线,一条双向串行数据线(SDA) ,串行时钟线(SCL)。数据线用于表示数据,时钟线用于数据收发同步。 (3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。 (4) 当总线通过上拉电阻接收电源时。 I2C 当设备有空时,它会输出高阻态,当所有设备都有空时,将总线从上拉电阻拉到高电平。 (5) 当多个主机同时使用总线时,为了防止数据冲突,仲裁决定哪个设备占用总线。 (6) 传输方式有三种:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I2C 该设备不支持高速模式。 (7) 连接到相同的总线 IC 总线最大电容量为总线最大电容 400pF 限制 。

(1) 图中标注①表示总线空闲状态 SCL 并串行数据信号 SDA 均保持高电平,此时无 I2C 设备工作。 (2) 图中标注②在 I2C 当总线处于空闲状态时,SCL 保持高电平时, SDA 从高电平到低电平的下降边缘,产生一个与总线相连的起始信号 I2C 检测到启动信号后,设备跳出空闲状态,等待字节输入控制。 (3) 图中标注③表示数据读写状态 I2C 通信设备的通信模式主从通信模式,通信双方分为主从。 当主机写入从机指令或数据时,串行数据线 SDA 串行时钟上的数据 SCL通常为高电写入从机设备,每次只写一个数据;串行数据线 SDA 串行时钟中的数据SCL 为了保证低电平时的数据更新 SCL 平时采集高电 SDA 数据的稳定性。 当一个完整字节的指令或数据传输完成后,从机器设备正确接收指令或数据时,通过降低 SDA 为低电平,向主机设备发送单比特响应信号,表示数据或指令写入成功。如果机器正确响应,下一个字节数据或指令的传输可以结束或开始,否则表示数据或指令写入失败,主机可以决定是否放弃或重新启动写入。 (4) 图中标注④表示停止信号 SCL 当串口数据信号保持高电平时 SDA 从低电平到高电平的上升沿产生停止信号,I2C 总线跳回总线空闲状态。

每个IIC所有通信设备都有自己的地址,出厂后设置,用户无法更改。该设备的地址一般为7位,如0V5640等。这边EEPROM设备地址为4位,1010A2A1A0,其中A2A1A0是用户根据电平设置的,这里的开发板都是拉低的,所以EEPROM设备地址为101000,加上写控制0或读控制1,形成完整的字节控制信号。 存储地址由寄存器或存储大小决定,AT24C64因为有64k存储空间需要两个字节的存储地址。 将不同数据值写入传入从机的控制命令的最低读写控制位,主机可以实现读写操作,读写控制位为 0 当间表示主机将数据写入从机;读写控制位于 1 说明主机要读取从机的数据。 I2C 本协议的读写操作分为读写操作两部分。 如下图所示,单字节存储时序图分别绘制单字节存储地址和双字节存储地址。 (1) 主机生成并向从机发送起始信号,将控制命令写入从机设备,读写控制位置设置为低电平,表示从机数据写作操作,控制命令写入前低位; (2) 接到控制指令后,回传响应信号,主机接收响应信号后开始写入存储地址。 2 如果单字节地址跳转到步骤(5); (3) 先从机写高 8 位置地址,高位在前低位在后; (4) 接收到从机回传的响应信号后,写入低点 8 位置地址,高位在前低位后,如果是 2字节地址,跳转到步骤(6); (5) 单字节存储地址按高位在前低位在后的顺序写入; (6) 地址写入完成后,主机收到从机回传的响应信号后,开始写入单字节数据; (7) 完成单字节数据写入,主机收到响应信号后,向从机发送停止信号,完成单字节数据写入。

单字节读操作和双字节读操作如下图所示。 (1) 主机生成并向从机发送起始信号,将控制命令写入从机设备,读写控制位置设置为低电平,表示从机数据写作操作,控制命令写入前低位; (2) 接到控制指令后,回传响应信号,主机接收响应信号后开始写入存储地址。 2 如果单字节地址跳转到步骤(5); (3) 先从机写高 8 位置地址,高位在前低位在后; (4) 待接收到从机回传的应答信号,再写入低 8 位置地址,高位在前低位后,如果是 2字节地址,跳转到步骤(6); (5) 单字节存储地址按高位在前低位在后的顺序写入; (6) 地址写入后,主机收到从机回传的响应信号后,主机再次向从机发送起始信号; (7) 主机向从机发送控制命令,读写控制位置设置为高电平,表示从机数据读取操作; (8) 接收到从机回传的响应信号后,主机开始接收从机回传的第一个单字节数据; (9) 数据接收完成后,主机将响应信号返回到机器,从机器接收响应信号开始下一个字节数据的传输。如果数据接收完成,则执行下一个操作步骤;如果数据接收未完成,则执行步骤(9); (10) 主机产生一小时的高电平无响应信号; (11) 主机向从机发送停止信号,顺序读取操作完成。

2、IIC实现EEPROM读写

实验完成对AT24C64这种EEPROM读写控制,往EEPROM将十个数据写入其中,然后将十个数据读取到数字管中进行显示。整个项目主要包括以下模块:按键滤波模块eeprom读写控制模块,eeprom控制模块、数码管显示模块。我们分别对这几个模块进行设计和验证。

按钮抖动模块的主要功能是接收外部按钮,然后延迟20ms判断按钮按下信息后,给出标志位信号。框图和时序图如下图所示。 其源代码直接给出如下图所示,因为模块比较简单,这里就不模拟了。

module key_control(     input clk,     input rst_n,     input key_in,     output reg key_flag );  parameter  CNT_20ms = 20'd999_999 ; reg [19:0] cnt; always @(posedge clk or negedge rst_n) begin if(~rst_n)begin cnt<=20'd0;     end     else if(key_in==1'b0 && cnt<CNT_20ms)begin cnt<=cnt 1'b1;     end     else if(key_in==1'b1)begin cnt<=1'b0;
    end
    else cnt=cnt;
end

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      key_flag<=1'b0; end else if(cnt==20'd999_998)begin
      key_flag<=1'b1; end else key_flag<=1'b0;
end

endmodule

数码管显示模块主要就是接收从eeprom读出的数据,送到fifo中进行缓存,之后在数码管显示。主要包含了时钟、复位、数据、数码管位选、数码管信号这些端口。 数码管的代码如下图所示,这边也不进行仿真,只做一些讲解。cnt进行周期的计数,计数到4999后输出一个flag标志位高电平信号。6位的位选信号收到flag标志位信号后进行移位,对数码管进行动态刷新。 将接收到的数据赋值给number,之后将number数据对应赋给8位的数码管seg。

module smg #(
  parameter  W = 4'd8 ) ( input clk, input rst_n, input [W-1:0] data, output reg[5:0] sel, output reg[7:0] seg ); reg [3:0] number; wire [3:0] data0; wire [3:0] data1; assign data0=data[3:0]; assign data1=data[7:4]; reg [12:0] cnt; reg flag; always @(posedge clk or negedge rst_n) begin if(~rst_n)begin cnt<=13'd0;
  end
  else if(cnt==13'd4999)begin cnt<=13'd0;
  end
  else cnt<=cnt+1'b1; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin flag<=1'b0;
  end
  else if(cnt==13'd4999)begin flag<=1'b1;
  end
  else flag<=1'b0; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin sel<=6'b111_110;
    end
    else if(flag==1)begin
      sel<={ 
       sel[4:0],sel[5]};
    end
    else sel<=sel;
end

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      number<=4'd0; end else begin case(sel) 6'b111_110:number<=data0;
        6'b111_101:number<=data1; 6'b111_011:number<=4'd0; 6'b110_111:number<=4'd0; 6'b101_111:number<=4'd0; 6'b011_111:number<=4'd0; default:number<=4'd0;
      endcase
    end
end

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      seg<=8'd0; end else begin case(number) 4'd0:seg<=8'b1100_0000; 4'd1:seg<=8'b1111_1001; 4'd2:seg<=8'b1010_0100; 4'd3:seg<=8'b1011_0000; 4'd4:seg<=8'b1001_1001; 4'd5:seg<=8'b1001_0010; 4'd6:seg<=8'b1000_0010; 4'd7:seg<=8'b1111_1000; 4'd8:seg<=8'b1000_0000; 4'd9:seg<=8'b1001_0000; 4'd10:seg<=8'b1000_1000; 4'd11:seg<=8'b1000_0011; 4'd12:seg<=8'b1100_0110; 4'd13:seg<=8'b1010_0001; 4'd14:seg<=8'b1000_0110; 4'd15:seg<=8'b1000_1110; default:seg<=8'b1100_0000;
      endcase
    end
end


endmodule 

该模块主要包含了8个输入信号,5个输出信号。 IIC读写操作设计到流程控制,这边利用状态机来控制整个流程,状态装换图如下图所示。 给出IIC控制模块读写操作的时序图如下图所示。首先是写操作控制流程图。具体流程:1、cnt_clk进行0-24的循环计数,实现对50M时钟的50分频,得到1MHz的时钟信号,就是i2c_clk。2、cnt_i2c_clk_en信号是时钟计数控制的使能信号,为下面控制SCL和SDA做准备,在接收到i2c_start信号后拉高使能信号。当状态机状态为STOP并且cnt_i2c_clk等于3,并且cnt_bit等于3时拉低使能信号。3、cnt_i2c_clk在cnt_i2c_clk_en使能信号拉高时进行计数,计数到3后清零,反复进行。4、cnt_bit是对写入的位数进行计数,具体操作可以看书序图。5、state是状态机的状态,这部分跳转比较复杂,可以对照代码和时序图查看。6、ACK是应答信号,在发送完一个字节数据时会接收到一位的低电平信号,在ACK1-5时进行判断,其他情况都是高电平。7、iic_sda_reg和rd_data_reg主要是对信号进行缓存,这部分可以对照波形图和代码查看。8、最后完成sda和scl信号的代码编写。 最后的iic控制模块代码如下图所示。

module i2c_control
#(
    parameter  DEVICE_ADDR = 7'b1010_000, parameter SYS_FREQ=26'd50_000_000,
    parameter  I2C_FREQ=18'd250_000 ) ( input clk, input rst_n, input wr_en, input rd_en, input i2c_start, input addr_num, input [15:0] byte_addr, input [7:0] wr_data, output reg i2c_clk, output reg i2c_end, output reg [7:0] rd_data, output reg i2c_scl, inout wire i2c_sda ); localparam IDLE=4'd0;
localparam START_1=4'd1; localparam SEND_D_ADDR=4'd2;
localparam ACK_1=4'd3; localparam SEND_B_ADDR_H=4'd4;
localparam ACK_2=4'd5; localparam SEND_B_ADDR_L=4'd6;
localparam ACK_3=4'd7; localparam WR_DATA=4'd8;
localparam ACK_4=4'd9; localparam STOP=4'd10;
localparam START_2=4'd11; localparam SEND_RD_ADDR=4'd12;
localparam ACK_5=4'd13; localparam RD_DATA=4'd14;
localparam N_ACK=4'd15; reg [7:0] cnt_clk; reg cnt_i2c_clk_en; reg [3:0] state; reg [1:0] cnt_i2c_clk; reg [2:0] cnt_bit; reg ack; reg i2c_sda_reg; reg [7:0] rd_data_reg; wire sda_en; wire sda_in; always @(posedge clk or negedge rst_n) begin if(~rst_n)begin cnt_clk<=8'd0;
    end
    else if(cnt_clk==((SYS_FREQ/I2C_FREQ)>>2'd3)-1'b1)begin cnt_clk<=8'd0; end else cnt_clk<=cnt_clk+1'b1; end always @(posedge clk or negedge rst_n) begin if(~rst_n)begin i2c_clk<=1'b0; end else if(cnt_clk==((SYS_FREQ/I2C_FREQ)>>2'd3)-1'b1)begin i2c_clk<=~i2c_clk; end else i2c_clk<=i2c_clk; end always @(posedge i2c_clk or negedge rst_n) begin if(~rst_n)begin cnt_i2c_clk_en<=1'b0; end else if(i2c_start==1'b1)begin cnt_i2c_clk_en<=1'b1; end else if(state==STOP && cnt_i2c_clk==2'd3 && cnt_bit==3'd3)begin cnt_i2c_clk_en<=1'b0; end else cnt_i2c_clk_en<=cnt_i2c_clk_en; end always @(posedge i2c_clk or negedge rst_n) begin if(~rst_n)begin cnt_i2c_clk<=2'd0; end else if(cnt_i2c_clk_en==1'b1)begin if(cnt_i2c_clk==2'd3)begin cnt_i2c_clk<=2'd0; end else cnt_i2c_clk<=cnt_i2c_clk+1'b1; end else cnt_i2c_clk<=2'd0; end always @(posedge i2c_clk or negedge rst_n) begin if(~rst_n)begin cnt_bit<=3'd0; end else if(state==IDLE || state== START_1 || state==START_2 || state==ACK_1 || state==ACK_2 || state==ACK_3 || state==ACK_4 || state==ACK_5 || state==N_ACK)begin cnt_bit<=3'd0; end else if(cnt_bit==3'd7 && cnt_i2c_clk==2'd3)begin cnt_bit<=3'd0; end else if(cnt_i2c_clk==3'd3)begin cnt_bit<=cnt_bit+1'b1; end end always@(posedge i2c_clk or negedge rst_n)begin if(rst_n == 1'b0) state <= IDLE; else case(state) IDLE: if(i2c_start == 1'b1) state <= START_1; else state <= state; START_1: if(cnt_i2c_clk == 3) state <= SEND_D_ADDR; else state <= state; SEND_D_ADDR: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_1;
            else
                state   <=  state;
        ACK_1:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(addr_num == 1'b1) state <= SEND_B_ADDR_H; else state <= SEND_B_ADDR_L; end else state <= state; SEND_B_ADDR_H: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_2;
            else
                state   <=  state;
        ACK_2:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  SEND_B_ADDR_L;
            else
                state   <=  state;
        SEND_B_ADDR_L:
            if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
                state   <=  ACK_3;
            else
                state   <=  state;
        ACK_3:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(wr_en == 1'b1) state <= WR_DATA; else if(rd_en == 1'b1)
                        state   <=  START_2;
                    else
                        state   <=  state;
                end
             else
                state   <=  state;
        WR_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_4;
            else
                state   <=  state;
        ACK_4:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  STOP;
            else
                state   <=  state;
        START_2:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_RD_ADDR;
            else
                state   <=  state;
        SEND_RD_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_5;
            else
                state   <=  state;
        ACK_5:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  RD_DATA;
            else
                state   <=  state;
        RD_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  N_ACK;
            else
                state   <=  state;
        N_ACK:
            if(cnt_i2c_clk == 3)
                state   <=  STOP;
            else
                state   <=  state;
        STOP:
            if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
                state   <=  IDLE;
            else
                state   <=  state;
        default:    state   <=  IDLE;
    endcase
end

always@(*)begin
    case    (state)
        IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
        WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK,STOP:
            ack <=  1'b1; ACK_1,ACK_2,ACK_3,ACK_4,ACK_5: if(cnt_i2c_clk == 2'd0)
                ack <=  sda_in;
            else
                ack <=  ack;
        default:    ack <=  1'b1; endcase end always@(*)begin case (state) IDLE: i2c_scl <= 1'b1;
        START_1:
            if(cnt_i2c_clk == 2'd3) i2c_scl <= 1'b0;
            else
                i2c_scl <=  1'b1; SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L, ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK: if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2)) i2c_scl <= 1'b1;
            else
                i2c_scl <=  1'b0; STOP: if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0)) i2c_scl <= 1'b0;
            else
                i2c_scl <=  1'b1; default: i2c_scl <= 1'b1;
    endcase
end

always@(*)begin
    case    (state)
        IDLE:
            begin
                i2c_sda_reg <=  1'b1; rd_data_reg <= 8'd0;
            end
        START_1:
            if(cnt_i2c_clk <= 2'd0) i2c_sda_reg <= 1'b1;
            else
                i2c_sda_reg <=  1'b0; SEND_D_ADDR: if(cnt_bit <= 3'd6)
                i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];
            else
                i2c_sda_reg <=  1'b0; ACK_1: i2c_sda_reg <= 1'b1;
        SEND_B_ADDR_H:
            i2c_sda_reg <=  byte_addr[15 - cnt_bit];
        ACK_2:
            i2c_sda_reg <=  1'b1; SEND_B_ADDR_L: i2c_sda_reg <= byte_addr[7 - cnt_bit]; ACK_3: i2c_sda_reg <= 1'b1;
        WR_DATA:
            i2c_sda_reg <=  wr_data[7 - cnt_bit];
        ACK_4:
            i2c_sda_reg <=  1'b1; START_2: if(cnt_i2c_clk <= 2'd1)
                i2c_sda_reg <=  1'b1; else i2c_sda_reg <= 1'b0;
        SEND_RD_ADDR:
            if(cnt_bit <= 3'd6) i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit]; else i2c_sda_reg <= 1'b1;
        ACK_5:
            i2c_sda_reg <=  1'b1; RD_DATA: if(cnt_i2c_clk == 2'd2)
                rd_data_reg[3'd7 - cnt_bit] <= sda_in; else rd_data_reg <= rd_data_reg; N_ACK: i2c_sda_reg <= 1'b1;
        STOP:
            if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
                i2c_sda_reg <=  1'b0; else i2c_sda_reg <= 1'b1;
        default:
            begin
                i2c_sda_reg <=  1'b1; rd_data_reg <= rd_data_reg; end endcase end always @(posedge i2c_clk or negedge rst_n) begin if(~rst_n)begin rd_data<=8'd0;
    end
    else if(state==RD_DATA && cnt_bit==3'd7 && cnt_i2c_clk==3'd3)begin
        rd_data<=rd_data_reg;
    end
end

always @(posedge i2c_clk or negedge rst_n) begin
    if(~rst_n)begin
      i2c_end<=1'b0; end else if(state==STOP && cnt_i2c_clk==2'd3 && cnt_bit==3'd3)begin i2c_end<=1'b1;
    end
    else i2c_end<=1'b0; end assign sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2) || (state == ACK_3) || (state == ACK_4) || (state == ACK_5)) ? 1'b0 : 1'b1; assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;
assign  sda_in = i2c_sda;

endmodule

编写完控制代码后对其进行modelsim仿真控制。仿真激励文件如下图所示,以及仿真结果图如下图所示,对照仿真结果和画的时序图,测试结果是没问题的。蓝色线是高阻态,可以看做是应答信号。

`timescale  1ns / 1ps

module tb_i2c_control;

// i2c_control Parameters
parameter PERIOD         = 10            ;
parameter DEVICE_ADDR    = 7'b1010_000 ; parameter SYS_FREQ = 26'd50_000_000;
parameter I2C_FREQ       = 18'd250_000 ; // i2c_control Inputs reg clk = 0 ; reg rst_n = 0 ; reg wr_en = 1 ; reg rd_en = 0 ; reg i2c_start = 0 ; reg addr_num = 1 ; reg [15:0] byte_addr = 16'h005a ;
reg   [7:0]  wr_data                       = 8'haa ;

// i2c_control Outputs
wire  i2c_clk                              ;
wire  i2c_end                              ;
wire  [7:0]  rd_data                       ;
wire  i2c_scl                              ;

// i2c_control Bidirs
wire  i2c_sda                              ;


initial
begin
    forever #(PERIOD/2) clk=~clk;
end

initial
begin
    #(PERIOD*2) rst_n = 1;
end

initial
begin
    #(PERIOD*10)
    i2c_start=1;
    #(PERIOD*100)
    i2c_start=0;
end

i2c_control #(
    .DEVICE_ADDR   ( DEVICE_ADDR   ),
    .SYS_FREQ      ( SYS_FREQ      ),
    .I2C_FREQ      ( I2C_FREQ      )
)
 u_i2c_control (
    .clk                     ( clk               ),
    .rst_n                   ( rst_n             ),
    .wr_en                   ( wr_en             ),
    .rd_en                   ( rd_en             ),
    .i2c_start               ( i2c_start         ),
    .addr_num                ( addr_num          ),
    .byte_addr               ( byte_addr  [15:0] ),
    .wr_data                 ( wr_data    [7:0]  ),

    .i2c_clk                 ( i2c_clk           ),
    .i2c_end                 ( i2c_end           ),
    .rd_data                 ( rd_data    [7:0]  ),
    .i2c_scl                 ( i2c_scl           ),

    .i2c_sda                 ( i2c_sda           )
);

initial
begin

end

endmodule

IIC读写控制模块主要包括了7个输入信号和6个输出信号。 下图所示是IIC读写控制模块的具体时序图。 1、cnt_wr和cnr_rd分别是写和读的计数器,其主要作用是模块接收到,read和write信号后,延时一段时间,输出一个较长时间的写和读控制信号write_vaild和read_vaild。这边加延迟是考虑到按键模块使用的时钟是50M,而读I2C控制是1M的时钟,所以延时一段时间使得1M时钟可以读取到写标志和读标志。 2、wr_en和rd_en分别是写使能和读使能,写使能和读使能在接收到标志信号高电平时拉高。写使能在i2c_end等于1且wr_i2c_data_num等于9时拉低,表示写操作结束,读操作也是类似的。 3、cnt_start是计数模块,控制每一位读取的控制间隔,这边让他在读使能有效或者写使能有效时进行累加至4999后清零。 4、wr_i2c_data_num写入的数据计数,当写使能有效并且i2c_end等于1时,代表写入一个字节完成,计数加1。 5、i2c_start是和i2c_end相对应,代表了一个字节信号开始传输。当写使能或者读使能有效并且cnt_start计数到4999时拉高,其他时间为0。 6、wr_data和byte_addr分别是要写入的数据和地址,这个和wr_i2c_data_num是对应的。用组合逻辑控制实现亦可。 7、fifo_rd_vaild是读fifo的使能信号。当data_num计数为10时,信号拉高,当data_num等于0并且cnt_wait计数到最大值时信号拉低,读结束。 8、cnt_wait是在读取fifo过程中的延时,其主要作用是让fifo读的慢一些,能看出在数码管显示的变化。 9、fifo_rd_en是读fifo的标志位,在拉高时进行读。 10、详细介绍可以看野火的文档。 最后根据上面的时序图可以编写verilog代码。

module i2c_rw_control(
    input clk,
    input rst_n,
    input i2c_clk,
    input write,
    input read,
    input i2c_end,
    input [7:0] rd_data,
    
    output reg wr_en,
    output reg rd_en,
    output reg i2c_start,
    output reg [15:0] byte_addr,
    output reg [7:0] wr_data,
    output wire [7:0] fifo_rd_data
);

parameter   DATA_NUM        =   8'd10 , CNT_START_MAX = 13'd5000    , 
            CNT_WR_RD_MAX   =   8'd200 , CNT_WAIT_MAX = 28'd500_000 ;

reg [7:0] cnt_wr;
reg [7:0] cnt_rd;
reg write_vaild;
reg read_vaild;
reg fifo_rd_vaild;
reg [7:0] wr_i2c_data_num;
reg [7:0] rd_i2c_data_num;
reg [12:0] cnt_start;
wire [7:0] data_num;
reg [27:0] cnt_wait;
reg fifo_rd_en;
reg [7:0] rd_data_num;


//写操作
/*
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      cnt_wr<=8'd0; end else if(write==1'b1 || cnt_wr!=8'd0)begin cnt_wr<=cnt_wr+1'b1;
    end
    else if(cnt_wr==CNT_WR_RD_MAX)begin
      cnt_wr<=8'd0; end end always @(posedge i2c_clk or negedge rst_n) begin if(~rst_n)begin write_vaild<=1'b0;
    end
    else if(cnt_wr>8'd0 && cnt_wr<CNT_WR_RD_MAX)begin write_vaild<=1'b1;
    end
    else write_vaild<=1'b0; end */ always @(posedge clk or negedge rst_n) begin if(~rst_n)begin cnt_wr<=8'd0;
    end
    else if(write_vaild==1'b1)begin cnt_wr<=cnt_wr+1'b1;
    end
    else if(write_vaild==1'b0)begin cnt_wr<=8'd0;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      write_vaild<=1'b0; end else if(cnt_wr == (CNT_WR_RD_MAX-1'b1))begin
      write_vaild<=1'b0; end else if(write==1'b1)begin
      write_vaild<=1'b1; end end always @(posedge i2c_clk or negedge rst_n) begin if(~rst_n)begin wr_en<=1'b0;
    end
    else if(write_vaild==1'b1)begin wr_en<=1'b1;
    end
    else if(i2c_end==1'b1 && wr_i2c_data_num==DATA_NUM-1'b1 && wr_en==1'b1)begin wr_en<=1'b0;
    end
    else wr_en<=wr_en;
end

always @(posedge i2c_clk or negedge rst_n) begin
    if(~rst_n)begin
      cnt_start<=13'd0; end else if(wr_en==1'b1 || rd_en==1'b1)begin if(cnt_start==CNT_START_MAX-1'b1)begin
        cnt_start<=13'd0; end else cnt_start<=cnt_start+1'b1;
    end
    else cnt_start<=13'd0; end always @(posedge i2c_clk or negedge rst_n) begin if(~rst_n)begin wr_i2c_data_num<=8'd0;
    end
    else if(wr_en==1'b1)begin if(i2c_end==1'b1)begin
          wr_i2c_data_num<=wr_i2c_data_num+1'b1; end end else wr_i2c_data_num<=8'd0;
end

always @(posedge i2c_clk or negedge rst_n) begin
    if(~rst_n)begin
      i2c_start<=1'b0; end else if(rd_en==1'b1 |

标签: d5d电阻x9315wp集成电路电阻3hab2916

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台