基于FPGA的IIC协议详解——EEPROM控制器(1)

常用IIC协议使用地方

熟悉一个协议一定要知道这个协议应该用到什么地方,IIC协议作为飞利浦公司定义的一个慢速传输协议,常用于:
1、芯片寄存器的配置;
2、eeprom的读写;
本次实验我们将使用eeprom来讲解IIC协议以及FPGA代码的编写,本次实验所用到的软硬件环境如下:
硬件:黑金A7102开发板
软件:vivado 19.1
黑金A7102开发板上eeprom的型号位:24LC04,是单字节地址的eeprom。再比如小梅哥AC620开发板上的eeprom为双字节地址的器件。为了让同学们了解两者,本次实验我们以24LC64为例进行实验,然后更改为单字节的24LC04并在板上验证。

常见IIC协议的注意点

1、总线空闲状态:SDA为高电平,SCL为高电平
2、起始位:SCL为高电平时,SDA产生一个下降沿
3、结束位:SCL为高电平时,SDA产生一个上升沿
IIC总线的三种模式如下:
标准模式:100Kbit/s
快速模式:400Kbit/s
高速模式:3.4Mbit/s

24LC64芯片读写命令的时序图

24LC64芯片的单字节写命令时序图如下:
在这里插入图片描述
连续字节写命令如下:
在这里插入图片描述
24LC64芯片的单字节读命令时序图如下:
在这里插入图片描述
连续字节读命令如下:
在这里插入图片描述
同理这里也给出24LC04命令的读写时序图,单字节写命令时序图如下:
在这里插入图片描述
连续字节写命令如下:
在这里插入图片描述
24LC04芯片的单字节读命令时序图如下:
在这里插入图片描述
连续字节读命令如下:
在这里插入图片描述

eeprom控制器的系统框图

该eeprom控制器的系统框图主要由以下四部分构成:
顶层模块:
在这里插入图片描述
详细大家从引脚的定义便可以明白该信号的作用,如果实在明白不了可以进群拿到现成的工程代码,进行仿真观察。
sclk:主时钟信号——50Mhz
rst_n:复位信号低电平有效
wr_reg:写请求信号,单时钟脉冲
rd_req:读请求信号,单时钟脉冲
wr_data:写数据
addr:读写eeprom地址
rd_valid:读数据有效信号,单时钟脉冲
rd_data:读出来的eeprom数据
busy:控制器内部忙信号,1为忙
iic_sck:IIC协议时钟线
iic_sda:IIC协议的数据线

该模块作为顶层模块,主要包括以下三种模块:
1、eeprom_read模块:eeprom的读模块
2、eeprom_write模块:eeprom的写模块
3、ctrl模块:eeprom的读写判决模块
eeprom_read模块的系统框图如下:
在这里插入图片描述
信号的含义如下:
rd_flag:读触发信号,高电平有效
rd_done:读操作结束信号

eeprom_write模块的系统框图如下:
在这里插入图片描述
信号的含义如下:
wr_flag:写触发信号,高电平有效
wr_done:写操作完成信号

ctrl模块的系统框图如下:
在这里插入图片描述
信号的含义如下::
wr_data_r:数据寄存信号
addr_r:地址寄存信号

时序图设计

eeprom_write模块的时序图设计如下:
在这里插入图片描述
该时序图知识近似设计,结合该时序图于代码可以直接理解该模块的编写。
eeprom_read模块的时序图设计如下:
在这里插入图片描述
ctrl模块的状态机为:
在这里插入图片描述

代码设计

这里将给出整个控制器的逻辑代码,如下:
top模块的代码如下:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : 
// Module Name  : top.v
// Create Time  : 2020-01-14 11:23:02
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module top(
    input                   sclk            ,
    input                   rst_n           ,
    input                   wr_req          ,
    input                   rd_req          ,
    input           [15:0]  addr            ,
    output  wire    [ 7:0]  rd_data         ,
    output  wire            rd_valid        ,
    input           [ 7:0]  wr_data         ,
    output  wire            busy            ,
    output  wire            iic_sck         ,
    inout                   iic_sda     
);

 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
wire                       rd_done         ;
wire                       wr_done         ;
wire			           wr_flag         ;
wire			           rd_flag         ;
wire			   [15:0]  addr_r          ;
wire			   [ 7:0]  wr_data_r       ;
wire     				   iic_sck_wr 	   ;
wire     				   iic_sck_rd 	   ;


 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
ctrl ctrl_inst(
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    .wr_req                 (wr_req                 ),
    .rd_req                 (rd_req                 ),
    .rd_done                (rd_done                ),
    .wr_done                (wr_done                ),
    .addr                   (addr                   ),
	.iic_sck_wr 			(iic_sck_wr 			),
	.iic_sck_rd 			(iic_sck_rd 			),
    .wr_data                (wr_data                ),
    .wr_flag         		(wr_flag         		),
    .rd_flag         		(rd_flag         		),
    .addr_r          		(addr_r          		),
    .wr_data_r       		(wr_data_r       		),
    .busy                   (busy                   ),
	.iic_sck				(iic_sck				) 		
);

eeprom_write eeprom_write_inst(
    .sclk            		(sclk            		),
    .rst_n           		(rst_n           		),
    .wr_flag         		(wr_flag				),
    .wr_data         		(wr_data_r				),
    .addr            		(addr_r					),
    .wr_done         		(wr_done 				),
    .iic_sck         		(iic_sck_wr 			),
    .iic_sda     			(iic_sda				)
);


eeprom_read eeprom_read_inst(
    .sclk            		(sclk            		),
    .rst_n           		(rst_n           		),
    .rd_flag         		(rd_flag         		),
    .rd_data         		(rd_data         		),
	.rd_valid 		 		(rd_valid 		 		),
    .addr            		(addr_r 				),
    .rd_done         		(rd_done         		),
    .iic_sck         		(iic_sck_rd       		),
    .iic_sda     			(iic_sda     			)
);


endmodule

eeprom_write模块的代码如下:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : 
// Module Name  : eeprom_write.v
// Create Time  : 2020-01-14 09:51:46
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module eeprom_write(
    input                   sclk            ,
    input                   rst_n           ,
    input                   wr_flag         ,
    input           [ 7:0]  wr_data         ,
    input           [15:0]  addr            ,
    output  reg             wr_done         ,
    output  reg             iic_sck         ,
    inout                   iic_sda     
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter  DIV_CNT      =   249             ;
parameter  DEVICE_ADDR  =   8'hA0           ;

reg                         wr_busy         ;
reg                 [ 7:0]  cnt_250         ;
reg                 [ 5:0]  cnt_bit         ;
reg                         sda_oe          ;
reg                         iic_sda_r       ;


 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign  iic_sda         =       (sda_oe && !iic_sda_r) ? 1'b0:1'bz;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_busy         <=      1'b0;
    else if(wr_flag == 1'b1)
        wr_busy         <=      1'b1;
    else if(wr_done == 1'b1)
        wr_busy         <=      1'b0;

always @(posedge sclk)
    if(rst_n == 1'b0)
        cnt_250         <=      8'd0;     
    else if(cnt_250 == DIV_CNT || wr_done == 1'b1)
        cnt_250         <=      8'd0;
    else if(wr_busy == 1'b1)
        cnt_250         <=      cnt_250 + 1'b1;
    else
        cnt_250         <=      cnt_250;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_bit         <=      5'd0;
    else if(wr_done == 1'b1)
        cnt_bit         <=      5'd0;
    else if(cnt_250 == DIV_CNT)
        cnt_bit         <=      cnt_bit + 1'b1;
    else 
        cnt_bit         <=      cnt_bit;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        iic_sck         <=      1'b1;
    else if(cnt_bit != 'd37 && cnt_250 == DIV_CNT/2)
        iic_sck         <=      1'b0;
    else if(cnt_250 == 'd0)
        iic_sck         <=      1'b1;
    else
        iic_sck         <=      iic_sck;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        sda_oe          <=      1'b1;
    else if((cnt_bit == 8 || cnt_bit == 17 || cnt_bit == 26 || cnt_bit == 35) && cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
        sda_oe          <=      1'b0;
    else if((cnt_bit == 9 || cnt_bit == 18 || cnt_bit == 27|| cnt_bit == 36) && cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
        sda_oe          <=      1'b1;
    else
        sda_oe          <=      sda_oe;
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        iic_sda_r       <=      1'b1;
    else case(cnt_bit)
        0   :               if(cnt_250 == (DIV_CNT/4))
                                iic_sda_r       <=      1'b0;
                            else if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      DEVICE_ADDR[7-cnt_bit];
                            else
                                iic_sda_r       <=      iic_sda_r;                                  
        1,2,3,4,5,6     :   if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      DEVICE_ADDR[7-cnt_bit];
                            else
                                iic_sda_r       <=      iic_sda_r;
        7               :   if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      1'b0;
                             else
                                iic_sda_r       <=      iic_sda_r;
        8               :   if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      1'b0;
                            else 
                                iic_sda_r       <=      iic_sda_r;
        12,13,14,15,16  :   if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      addr[24-cnt_bit];
                            else
                                iic_sda_r       <=      iic_sda_r;
        17              :   if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      1'b0;
                            else 
                                iic_sda_r       <=      iic_sda_r;
        18,19,20,21,22,23,24,25 :
                            if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      addr[25-cnt_bit];
                            else
                                iic_sda_r       <=      iic_sda_r;
        26              :   if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      1'b0;
                            else 
                                iic_sda_r       <=      iic_sda_r;
        27,28,29,30,31,32,33,34 :
                            if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      wr_data[34-cnt_bit];
                            else
                                iic_sda_r       <=      iic_sda_r;
        35              :   if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
                                iic_sda_r       <=      1'b0;
                            else 
                                iic_sda_r       <=      iic_sda_r;
        36              :   iic_sda_r       <=      iic_sda_r;
        37              :   if(cnt_250 == (DIV_CNT/4))
                                iic_sda_r       <=      1'b1;
        default         :   iic_sda_r       <=      1'b1;
    endcase 

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_done         <=      1'b0;
    else if(cnt_bit == 'd37 && cnt_250 == (DIV_CNT/2)) 
        wr_done         <=      1'b1;
    else
        wr_
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值