【FPGA】i2c协议详解及eeprom的读写uart串口通信验证


通过I2C实现对EEPROM读写操作

概述

IIC简介
IIC (I2C,Inter-Integrated Circuit)即集成电路总线,是一种两线式串行总线,由 PHILIPS公司开发,用于连接微控制器及其外围设备。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。I2C 总线由数据线 SDA 和时钟线 SCL 构成通信线路,既可用于发送数据,也可接收数据,是一种半双工通信协议。总线上的主设备与从设备之间以字节(8 位)为单位进行双向的数据传输

标准模式:100Kbit/s
快速模式:400kbit/s
高速模式:3.4Mbit/s

在这里插入图片描述
iic器件和sdl、scl关系

空闲状态:
I2C 总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
起始信号:
在 SCL 保持高电平期间,SDA 的电平被拉低,称为 I2C 总线总线的起始信号,标志着一次数据传输开始。起始信号由主机主动建立的,在建立该信号之前 I2C 总线必须处于空闲状态。
停止信号:
在 SCL 保持高电平期间,SDA 被释放,返回高电平,称为 I2C 总线的停止信号,标志着一次数据传输的终止。停止信号由主机主动建立的,建立该信号之后,I2C 总线将返回空闲状态。

在这里插入图片描述
在这里插入图片描述在这张图表明I2C时钟频率MAX100 400 KHZ,高低电平保持时间,高低点电平转换时间
起始信号至少保持600-4000ns,停止信号至少保持600-4000ns

数据传输:
在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行时钟的配合下,在 SDA 上逐位地串行传送每一位数据。进行数据传送时,在 SCL 的高电平期间, SDA 上的电平必须保持稳定,只有在 SCL 为低电平期间,才允许 SDA 上的电平改变状态。即数据在 SCL 的上升沿到来之前就必须准备好,并在在下降沿到来之前必须保持稳定。

在这里插入图片描述
在前面的表格图中表明I2C起始信号至少保持600-4000ns,停止信号至少保持600-4000ns,数据总线在I2C时钟低电平可以改变值,高电平数据总线保持

应答信号:
I2C 总线上的所有数据都是以字节传送的,发送端每发送一个字节,就必须在第 9 个SCL 脉冲期间释放 SDA,由接收端反馈一个应答信号。应答信号为低电平时,称为有效应答位(ACK),表示接收端成功接收了该字节;应答信号为高电平时,称为非应答位(NACK),表示接收端接收该字节失败。对于反馈有效应答位 ACK 的要求是,接收端在第 9 个时钟脉冲之前的低电平期间将 SDA 拉低,并且确保在该时钟周期的高电平期间保持低电平。如果接收端是主控端,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送端结束数据发送,并释放 SDA 线,以便主控接收器发送停止信号。
在这里插入图片描述
I2C应答位和数据读写想同,都是时钟高电平有效 ACK=0,NACK=1;
在EEPROM写操作时写完最后一字节数据时观察时序,发现应答位为应答有效,可能手册写了我没看到,这里我没有管它,直接从机应答结束后给的停止信号.
EEPROM 简介
写操作时序
写操作,发起始位->写写控制字->接收 ACK->写字节地址->接收 ACK->写数据接收ACK-> 发停止位。
写控制字:{7’b101000,1’b0}
在这里插入图片描述
读操作时序
发起始位->写读控制字->接收 ACK->写读地址->接收读数据->发NACK->发停止位。
读控制字:{7’b101000,1’b1}。
随机读
在这里插入图片描述
顺序读
在这里插入图片描述

一、状态图

1、I2C主机状态图

在这里插入图片描述

2、I2C读写控制状态图

在这里插入图片描述

二、程序讲解

1、工程讲解

uart串口写入数据到FIFO1储存,当key_in0按下从FIFO读取数据通过I2C写入EEPROM,当key_in1按下从EEPROM读取数据暂存在FIFO2再通过uart读取出来

2、全部代码

param

//参数定义
//i2c时钟参数
`define  SCL_PERIOD  250
`define  SCL_HALF    125
`define  LOW_HLAF    65 
`define  HIGH_HALF   190

//i2c命令参数
`define CMD_START   4'b0001
`define CMD_WRITE   4'b0010
`define CMD_READ    4'b0100
`define CMD_STOP    4'b1000

//i2c write mode
//`define BYTE_WRITE              //字写
`define PAGE_WRITE              //页写

`ifdef BYTE_WRITE
    `define WR_BYTE 3         //从机地址 1字节 + 数据写入地址 1字节 + 数据 1字节
`elsif PAGE_WRITE
    `define WR_BYTE 18        //页写最大写入数据 16字节   从机地址 1字节 + 数据写入地址 1字节 + 数据 16字节
`endif                       

//i2c read mode
//`define RANDOM_READ             //随机读
`define SEQUENTIAL_READ         //顺序读

`ifdef RANDOM_READ
    `define RE_BYTE 4           //从机地址 1字节 写命令 + 数据写入地址1字节 +从机地址1字节  读命令 + 数据 (顺序读多少字节看自己设置多少)
`elsif SEQUENTIAL_READ
    `define RE_BYTE 19 
`endif 


//从机ID定义
`define WR_ID 6'b1010_00
`define RD_ID 6'b1010_00






//波特率
`define  BAUD_9600   5208
`define  BAUD_19200  2604
`define  BAUD_38400  1302
`define  BAUD_115200 434

`define STOP_BIT  1'b1      //数据停止位
`define START_BIT 1'b0      //数据开始位

test

module test( 
    input				clk		,
    input				rst_n	,
    input       [2:0]   key_in  ,
    input               uart_rxd,
    output              uart_txd,
    inout               sda     ,
    output              scl
);								 			 
                        
    //中间信号定义	
    wire        [7:0]                   rx_byte         ;
    wire                                rx_byte_vld     ;

    wire        [2:0]                   key_out         ;

    wire        [7:0]                   dout_m          ;
    wire        [7:0]                   dout_tx         ;
    wire                                dout_tx_vld     ;
    wire                                req             ;
    wire        [3:0]                   cmd             ;

    wire                                dq_in           ;
    wire                                done            ;
    wire                                dq_out          ;
    wire                                dq_en           ;
    wire                                slave_ack       ;
    wire        [7:0]                   dout            ;
    wire                                dout_vld        ;

    wire                                busy            ;



//模块例化
uart_rx u_uart_rx
(
    /*input                       */.clk         (clk),
    /*input                       */.rst_n       (rst_n),
    /*input                       */.rx_din      (uart_rxd),
    /*input           [1:0]       */.baud_sel    (2'b0),
    /*output          [7:0]       */.rx_byte     (rx_byte),
    /*output                      */.rx_byte_vld (rx_byte_vld)
);

key_filter_fsm # (.KEY_W(3),.TIME_20MS(1_000_000))u_key_filter_fsm
(
    /*input 			            */.clk		(clk            ),
    /*input 			            */.rst_n	(rst_n          ),
    /*input 		[KEY_W - 1:0]	*/.key_in	(key_in         ),
    /*output 		[KEY_W - 1:0]	*/.key_out	(key_out        )
);
control u_control
(
    /*input                       */.clk         (clk           ),
    /*input                       */.rst_n       (rst_n         ),
    /*input       [2:0]           */.key_in      (key_out       ),
    /*input                       */.slave_ack   (slave_ack     ),
    /*input       [7:0]           */.rx_din      (rx_byte       ),
    /*input                       */.rx_din_vld  (rx_byte_vld   ),
    /*input       [7:0]           */.m_din       (dout          ),
    /*input                       */.m_din_vld   (dout_vld      ),
    /*input                       */.busy        (busy          ),
    /*inout                       */.done        (done          ),
    /*output      [7:0]           */.dout_m      (dout_m        ),
    /*output      [7:0]           */.dout_tx     (dout_tx       ),
    /*output                      */.dout_tx_vld (dout_tx_vld   ),
    /*output                      */.req         (req           ),
    /*output      [3:0]           */.cmd         (cmd           )   

);

i2c_master u_i2c_master
(
    /*input                       */.clk        (clk            ),
    /*input                       */.rst_n      (rst_n          ),
    /*input                       */.req        (req            ),
    /*input        [3:0]          */.cmd        (cmd            ),
    /*input        [7:0]          */.din        (dout_m         ),
    /*input                       */.dq_in      (dq_in          ),
    /*output                      */.done       (done           ),
    /*output    reg               */.dq_out     (dq_out         ),
    /*output    reg               */.dq_en      (dq_en          ),
    /*output                      */.scl        (scl            ),
    /*output    reg               */.slave_ack  (slave_ack      ),
    /*output      [7:0]           */.dout       (dout           ),
    /*output                      */.dout_vld   (dout_vld       )                  
);

uart_tx u_uart_tx
(
    /*input                       */.clk         (clk           ),
    /*input                       */.rst_n       (rst_n         ),
    /*input           [1:0]       */.baud_sel    (2'b0          ),
    /*input           [7:0]       */.tx_byte     (dout_tx       ),
    /*input                       */.tx_byte_vld (dout_tx_vld   ),
    /*output                      */.tx_dout     (uart_txd      ),
    /*output                      */.busy        (busy          )
);

//三态门
    assign sda = dq_en?dq_out:1'bz;
    assign dq_in = sda;

                        
endmodule

control

`include "param.v"
module control 
(
    input                       clk         ,
    input                       rst_n       ,
    input       [2:0]           key_in      ,
    input                       slave_ack   ,
    input       [7:0]           rx_din      ,
    input                       rx_din_vld  ,
    input       [7:0]           m_din       ,
    input                       m_din_vld   ,
    input                       busy        ,
    inout                       done        ,
    output      [7:0]           dout_m      ,
    output      [7:0]           dout_tx     ,
    output                      dout_tx_vld ,
    output                      req         ,
    output      [3:0]           cmd          

);

//参数定义
    localparam IDLE    = 6'b000_001,
               WR_REQ  = 6'b000_010,
               WAIT_WR = 6'b000_100,
               RE_REQ  = 6'b001_000,
               WAIT_RE = 6'b010_000,
               DONE    = 6'b100_000;

//信号定义
//状态机信号定义
    reg             [5:0]                   state_c         ;
    reg             [5:0]                   state_n         ;

    reg             [4:0]                   cnt_byte        ;
    wire                                    add_cnt_byte    ;
    wire                                    end_cnt_byte    ;

//读写地址
    reg             [7:0]                   wr_add          ;
    reg             [7:0]                   re_add          ;
//task TX 请求、命令、输出数据
    reg                                     tx_req          ;     
    reg             [3:0]                   tx_cmd          ;
    reg             [7:0]                   tx_data         ;

    reg                                     flag            ;
//rx_fifo
    wire            [7:0]                   rx_fdin         ;
    wire                                    rx_frden        ;
    wire                                    rx_fwren        ;
    wire                                    rx_fempty       ;
    wire                                    rx_ffull        ;
    wire            [7:0]                   rx_fdout        ;
    wire            [7:0]                   rx_fusedw       ;
//fifo_tx
    wire            [7:0]                   f_txdin         ;
    wire                                    f_txrden        ;
    wire                                    f_txwren        ;
    wire                                    f_txempty       ;
    wire                                    f_txfull        ;
    wire            [7:0]                   f_txdout        ;
    wire            [7:0]                   f_txusedw       ;

//状态机转换条件定义
    wire                                    idle2wr_req     ;
    wire                                    idle2re_req     ;
    wire                                    wr_req2wait_wr  ;
    wire                                    re_req2wait_re  ;
    wire                                    wait_wr2wr_req  ;
    wire                                    wait_re2re_req  ;
    wire                                    wait_wr2done    ;
    wire                                    wait_re2done    ;
    wire                                    done2idle       ;

//状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end

    always @(*)begin
        casez (state_c)
        IDLE    :begin if (idle2wr_req) begin
            state_n = WR_REQ;
            
        end
        else if (idle2re_req) begin
            state_n = RE_REQ;
        end
        else begin
            state_n = IDLE;
        end
            
        end
        WR_REQ  :begin if (wr_req2wait_wr) begin
            state_n = WAIT_WR;
        end
        else begin
            state_n = WR_REQ;
        end
        end
        WAIT_WR :begin if (wait_wr2wr_req) begin
            state_n = WR_REQ;
        end
        
        else  if (wait_wr2done) begin
            state_n = DONE;
        end
        else begin
            state_n = WAIT_WR;
        end
        end
        RE_REQ  :begin if (re_req2wait_re) begin
            state_n = WAIT_RE;
        end 
        else  begin
            state_n = RE_REQ;
        end
        end
        WAIT_RE :begin if (wait_re2re_req) begin
            state_n = RE_REQ;
        end
        else 
        if (wait_re2done) begin
            state_n =  DONE;
        end 
        else begin
            state_n = WAIT_RE;
        end    
        end
        DONE    :begin if (done2idle) begin
            state_n = IDLE;
        end
        else begin
            state_n = DONE;
        end 
        end
            default: state_n = state_c; 
        endcase
    end

//状态机转换条件
    assign idle2wr_req    = state_c == IDLE && key_in[0] && rx_fusedw >= `WR_BYTE -2;
    assign idle2re_req    = state_c == IDLE && key_in[1];
    assign wr_req2wait_wr = state_c == WR_REQ && 1'b1;
    assign re_req2wait_re = state_c == RE_REQ && 1'b1;
    assign wait_wr2wr_req = state_c == WAIT_WR && (done && cnt_byte < (`WR_BYTE-1)); 
    assign wait_re2re_req = state_c == WAIT_RE && (done && cnt_byte < (`RE_BYTE-1)); 
    assign wait_wr2done   = state_c == WAIT_WR && (end_cnt_byte | done & slave_ack);
    assign wait_re2done   = state_c == WAIT_RE && (end_cnt_byte | done & slave_ack);
    assign done2idle      = state_c == DONE && 1'b1;

//计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 0;
        end 
        else if(add_cnt_byte)begin 
                if(end_cnt_byte)begin 
                    cnt_byte <= 0;
                end
                else begin 
                    cnt_byte <= cnt_byte + 1;
                end 
        end
       else  begin
           cnt_byte <= cnt_byte;
        end
    end 
    
    assign add_cnt_byte = ((state_c == WAIT_RE) || (state_c == WAIT_WR)) && done;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == ((state_c == WAIT_RE)?(`RE_BYTE-1):(`WR_BYTE-1));
    //assign end_cnt_byte = add_cnt_byte && cnt_byte == 18-1;

//输出
    always @(*)begin 
        if(!rst_n)begin
            TX(1'b0,4'b0,8'b0);
        end 
        else if(state_c == WAIT_WR)begin 
            case (cnt_byte)
               0          : TX(1'b1,{`CMD_START|`CMD_WRITE},{`WR_ID,wr_add[7],1'b0});
               1          : TX(1'b1,`CMD_WRITE,wr_add);
               `WR_BYTE-1 : TX(1'b1,{`CMD_WRITE|`CMD_STOP},rx_fdout);
                default: TX(1'b1,`CMD_WRITE,rx_fdout);
            endcase
        end 
        else if (state_c == WAIT_RE) begin
            case (cnt_byte)
                0          : TX(1'b1,{`CMD_START|`CMD_WRITE},{`RD_ID,wr_add[7],1'b0});
                1          : TX(1'b1,`CMD_WRITE,re_add);
                2          : TX(1'b1,{`CMD_START|`CMD_WRITE},{`RD_ID,wr_add[7],1'b1});
                `RE_BYTE-1 : TX(1'b1,{`CMD_READ|`CMD_STOP},0);  
                default: TX(1'b1,`CMD_READ,0);  
            endcase
        end
        else begin
            TX(1'b0,tx_cmd,tx_data);  
        end
    end

//task发送请求、命令、数据
    task TX;
            input                   req_r    ;
            input       [3:0]       command_r;
            input       [7:0]       data_r   ;
            begin
                tx_req = req_r;
                tx_cmd = command_r;
                tx_data = data_r;
            end
    endtask
    
//req cmd dout_m  
    assign req = tx_req;
    assign cmd = tx_cmd;
    assign dout_m = tx_data;
  
//wr_add & re_add
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            wr_add <= 8'b0;
        end 
        else if(wait_wr2done  && (state_c == WAIT_WR))begin 
            wr_add <= wr_add+`WR_BYTE-2;
        end 
        else begin 
            wr_add <= wr_add;
        end 
    end

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            re_add <= 8'b0;
        end 
        else if(wait_re2done  && (state_c == WAIT_RE))begin 
            re_add <= re_add+`RE_BYTE-3;
        end 
        else begin 
            re_add <= re_add;
        end 
    end

//flag
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        flag <= 0;
    end 
    else if(f_txusedw >= (`RE_BYTE-3))begin 
        flag <= 1;
    end 
    else if(f_txempty)begin 
        flag <= 0;
    end 
end

//rx_fifo
rx_fifo	u_rx_fifo
(
	.aclr  ( ~rst_n    ),
	.clock ( clk       ),
	.data  ( rx_fdin   ),
	.rdreq ( rx_frden  ),
	.wrreq ( rx_fwren  ),
	.empty ( rx_fempty ),
	.full  ( rx_ffull  ),
	.q     ( rx_fdout  ),
	.usedw ( rx_fusedw )
);

    assign rx_fdin = rx_din;
    assign rx_frden = state_c == WAIT_WR && (cnt_byte>1) && done && (!rx_fempty);
    assign rx_fwren = rx_din_vld && (!rx_ffull);
//fifo_tx
fifo_tx	u_fifo_tx
(
	.aclr  ( ~rst_n    ),
	.clock ( clk       ),
	.data  ( f_txdin   ),
	.rdreq ( f_txrden  ),
	.wrreq ( f_txwren  ),
	.empty ( f_txempty ),
	.full  ( f_txfull  ),
	.q     ( f_txdout  ),
	.usedw ( f_txusedw )
);

    assign f_txwren = !f_txfull && m_din_vld;
    assign f_txrden = !f_txempty && (busy==0) && flag;
    assign f_txdin = m_din;
    //输出
    assign dout_tx = f_txdout;
    assign dout_tx_vld = f_txrden;          //fifo_tx数据有效


endmodule

i2c_master

/**************************************功能介绍***********************************
Copyright:i2c_master			
Date     :2022/2/17			
Author   :li_lys			
Version  :			
Description:		
*********************************************************************************/

`include "param.v"
module i2c_master
(
    input                       clk         ,
    input                       rst_n       ,
    input                       req         ,
    input        [3:0]          cmd         ,
    input        [7:0]          din         ,
    input                       dq_in       ,
    output                      done        ,
    output    reg               dq_out      ,
    output    reg               dq_en       ,
    output                      scl         ,
    output    reg               slave_ack   ,
    output      [7:0]           dout        ,
    output                      dout_vld                   
);

//参数定义
//状态机
    localparam  IDLE  = 7'b000_0001,
                START = 7'b000_0010,
                WRITE = 7'b000_0100,
                RACK  = 7'b000_1000,
                READ  = 7'b001_0000,
                SACK  = 7'b010_0000,
                STOP  = 7'b100_0000;

//信号定义
    reg     [6:0]                   state_c         ;
    reg     [6:0]                   state_n         ;
    reg                             clk_scl         ;

    reg     [7:0]                   cnt_scl         ;
    wire                            add_cnt_scl     ;
    wire                            end_cnt_scl     ;

    reg     [3:0]                   cnt_bit         ;
    wire                            add_cnt_bit     ;
    wire                            end_cnt_bit     ;
    reg     [3:0]                   bit_num         ;
    reg                             master_ack      ;

    reg                             cnt_scl_flag    ;
    reg     [7:0]                   dout_data       ;
    reg                             dout_data_vld   ;

//状态机转换条件
    wire                            idle2start      ;
    wire                            idle2write      ;
    wire                            idle2read       ;
    wire                            start2write     ;
    wire                            start2read      ;
    wire                            write2rack      ;
    wire                            read2sack       ;
    wire                            rack2stop       ;
    wire                            sack2stop       ;      
    wire                            rack2idle       ;
    wire                            sack2idle       ;
    wire                            stop2idle       ;


//状态机
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end

    always @(posedge clk or negedge rst_n)begin 
        case (state_c)
            IDLE : begin if (idle2start) begin
                state_n = START; 
            end
            else if (idle2write) begin
                state_n = WRITE; 
            end
            else if (idle2read) begin
                state_n = READ;
            end 
            else begin
                state_n = state_c;
            end   
            end
            START : begin if (start2write) begin
                state_n = WRITE;
            end
            else if (start2read) begin
                state_n = READ;
            end    
            else begin
                state_n = state_c;
            end
            end
            WRITE : begin if (write2rack) begin
                state_n = RACK;
            end
            else begin
                state_n = state_c;
            end    
            end
            RACK : begin  if (rack2idle) begin
                state_n = IDLE;
            end
            else if (rack2stop) begin
                state_n = STOP;
            end
            else begin
                state_n = state_c;
            end
            end
            READ : begin if (read2sack) begin
                state_n = SACK;
            end
            else begin
                state_n = state_c;
            end   
            end
            SACK : begin if (sack2idle) begin
                state_n = IDLE;
            end 
            else if (sack2stop) begin
                state_n = STOP;
            end   
            else begin
                state_n = state_c;
            end
            end
            STOP : begin if (stop2idle) begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end    
            end     
            default: state_n = state_c;
        endcase
    end

    assign idle2start  = state_c == IDLE  && req && (`CMD_START & cmd);
    assign idle2write  = state_c == IDLE  && req && (`CMD_WRITE & cmd); 
    assign idle2read   = state_c == IDLE  && req && (`CMD_READ & cmd);
    assign start2write = state_c == START && end_cnt_bit && (`CMD_WRITE & cmd);
    assign start2read  = state_c == START && end_cnt_bit && (`CMD_READ & cmd);
    assign write2rack  = state_c == WRITE && (end_cnt_bit);
    assign read2sack   = state_c == READ  && (end_cnt_bit);
    assign rack2stop   = state_c == RACK  && (end_cnt_bit && (`CMD_STOP & cmd));
    assign sack2stop   = state_c == SACK  && (end_cnt_bit && (`CMD_STOP & cmd));
    assign rack2idle   = state_c == RACK  && (end_cnt_bit && ((`CMD_STOP & cmd) ==0));
    assign sack2idle   = state_c == SACK  && (end_cnt_bit && ((`CMD_STOP & cmd) ==0));
    assign stop2idle   = state_c == STOP  && (end_cnt_bit);

//计数器
//cnt_scl计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_scl <= 0;
        end 
        else if(add_cnt_scl)begin 
                if(end_cnt_scl)begin 
                    cnt_scl <= 0;
                end
                else begin 
                    cnt_scl <= cnt_scl + 1;
                end 
        end
       else  begin
           cnt_scl <= cnt_scl;
        end
    end 
    
    assign add_cnt_scl = cnt_scl_flag;
    assign end_cnt_scl = add_cnt_scl && cnt_scl == `SCL_PERIOD-1;

//cnt_bit计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_bit <= 0;
    end 
    else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1;
            end 
    end
   else  begin
       cnt_bit <= cnt_bit;
    end
end 

assign add_cnt_bit = end_cnt_scl;
assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_num - 1;

//cnt_scl_flag
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_scl_flag <= 0;
        end 
        else if(idle2start)begin 
            cnt_scl_flag <= 1;
        end 
        else if(stop2idle)begin 
            cnt_scl_flag <= 0;
        end
        else begin
            cnt_scl_flag <= cnt_scl_flag;
        end 
    end

//clk_scl
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            clk_scl <= 1;
        end 
        else if (idle2start | idle2write | idle2read ) begin
            clk_scl <= 0;
        end
        else if(add_cnt_scl && cnt_scl == `HIGH_HALF-1)begin 
            clk_scl <= 1;
        end 
        else if(end_cnt_scl && (!stop2idle))begin 
            clk_scl <= 0;
        end 
        else begin
            clk_scl <= clk_scl;
        end
    end

//bit_num
always @(*)begin 
    if(!rst_n) begin
         bit_num = 0;
    end
    else if(state_c == WRITE || state_c == READ)begin 
           bit_num = 8;
       end 
    else begin 
        bit_num = 1;
    end 
end

//dout_data
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dout_data <= 0;
        end 
        else if((state_c == READ) && (cnt_scl == `HIGH_HALF))begin 
            dout_data[7-cnt_bit] <=dq_in;
        end 
    end

//dout_data_vld
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dout_data_vld <= 0;
        end 
        else if((state_c == READ) && (cnt_bit == 7) && (cnt_scl == `HIGH_HALF))begin 
            dout_data_vld <=1;
        end 
        else begin 
            dout_data_vld <= 0;
        end 
    end

//输出
//dq_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dq_out <= 1;
        end 
        else if(state_c == START)begin          //检测其实信号,将数据总线sdl拉高确保从机能检测到起始信号
            if(cnt_scl == `LOW_HLAF) begin
                dq_out <= 1;
            end
            else if(cnt_scl == `HIGH_HALF) begin    //将数据总线拉低产生起始信号
                    dq_out <= 0;
            end
        end 
        else if((state_c == SACK) && (cnt_scl == `SCL_HALF))begin  //scl 低电平改变传输数据   cnt_scl== `SCL_HALF ||  cnt_scl== `LOW_HLAF        
            dq_out <= master_ack;                                            //主机应答信号
        end
        else if((state_c == WRITE) && (cnt_scl == `SCL_HALF))begin          
            dq_out <= din[7 - cnt_bit];
        end 
        else if (state_c == STOP )begin          //停止信号
            if(cnt_scl == `LOW_HLAF)begin           //低电平将数据总线sdl拉低确保从机能检测到停止信号
                dq_out <= 0;
            end
            else if (cnt_scl == `HIGH_HALF) begin //将数据总线拉高产生停止信号
                dq_out <= 1;
            end
        end
    end

//master_ack   主机应答信号
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            master_ack <= 0;
        end 
        else if(cmd &`CMD_STOP)begin 
            master_ack <= 1;
        end 
        else if(cmd&`CMD_START)begin 
            master_ack <= 0;
        end 
    end

//dq_en
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dq_en <= 0;
        end 
        else if(idle2start || idle2write || read2sack || rack2stop || sack2stop)begin 
            dq_en <= 1;
        end 
        else if(idle2read || start2read || write2rack || stop2idle)begin 
            dq_en <= 0;
        end 
    end

//slave_ack     从机应答信号
    always @(posedge clk or negedge rst_n)begin             
        if(!rst_n)begin
            slave_ack <= 1;
        end 
        else if(state_c == RACK && cnt_scl == `HIGH_HALF)begin //高电平接收应答
            slave_ack <=dq_in;
        end 
        else begin
            slave_ack <= slave_ack;
        end
    end

    assign done = (stop2idle | rack2idle | sack2idle);   

// scl dout dout_vld
    assign scl=clk_scl;
    assign dout=dout_data;
    assign dout_vld=dout_data_vld;


endmodule

key_filter_fsm

https://blog.csdn.net/li_lys/article/details/121849916

uart

https://blog.csdn.net/li_lys/article/details/122887251?spm=1001.2014.3001.5502

三、验证

signal Tap 抓取结果在这里插入图片描述在这里插入图片描述
这里可以直观看出读写数据的正确性
在这里插入图片描述
工程链接:
https://blog.csdn.net/li_lys/article/details/124870664?utm_source=app&app_version=5.4.0&code=app_1562916241&uLinkId=usr1mkqgl919blen

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值