FPGA学习笔记之IIC—EEPROM写和读

一、IIC总线协议特点及其工作原理
I2C(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
1I2C总线特点

I2C总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。
I2C总线的另一个优点是,它支持多主控(multimastering),
其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

2I2C总线工作原理

I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率 100kbps。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程
中,I2C总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制
量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂
在同一条总线上,却彼此独立,互不相关。

3)总线的构成及信号类型

I2C总线在传送数据过程中共有三种类型信号,
它们分别是:开始信号、结束信号和应答信号。
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单
元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。
4I2C总线操作

I2C规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。
总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。
SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。

控制字节
在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。

写操作
写操作分为字节写和页面写两种操作,对于页面写根据芯片的一次装载的字节不同有所不同。

读操作
读操作有三种基本操作:当前地址读、随机读和顺序读。

5)I2C总线应用

目前有很多半导体集成电路上都集成了I2C接口。带有I2C接口的单片机有:CYGNAL的 C8051F0XX系列,三星的S3C24XX系列,PHILIPSP87LPC7XX系列,MICROCHIP的PIC16C6XX系列等。很多外围器
件如存储器、监控芯片等也提供I2C接口。


 

二、IIC之EEPROM
1)下面是EEPROM24LC64)不同封装情况

数据手册中关于这八个引脚的介绍:

A0, A1,A2 :片选地址输入
SDA : 单bit数据线
SLC : 时钟线(200KHZ)
WP : 接地或者悬空时,可读可写,接电源VCC时,只读不可写。
注意:EEPROM(24LC64)工作的最大时钟为400KHZ,所以我们用系统50M时钟来分频一个400KHZ。
2I2C总线时序图

  1.     
    总线非忙状态(A段)数据线SDA 和时钟线SCL都保持高电平
  2.     
    启动数据传输(B段)当时钟线SCL为高电平时,数据线由高变低,此时认为是启动信号,有启动信号之后,后面的命令才有用。
  3.   
    停止数据传输(C段):  当时钟SCL为高电平时,数据线SDA由低到高的上升沿认为是停止信号。
  4.      
    数据有效(D段):  出现启动信号后,在时钟线SCL为高电平时,数据线是稳定的,这时候数据线的状态是稳定的,而在时钟线SCL为低电平的时候,允许数据线发生改变,每位数据占用一个时钟脉冲。
  5.    
    应答信号:  每个正在接收数据的从机EEPROM在接到一个字节后,通常需要发出一个应答信号ACK,用来告诉FPGA数据已经传输过来。此时EEPROM读写控制器必须产生一个与这个应答位相对应和联系的额外时钟脉冲。在EEPROM的读操作中,其读控制器对eeprom完成的最后一个字节产生一个高的应答为,这个信号叫做NOack(非应答位信号),随后给EEPROM一个结束信号。

 

控制字节
  在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。示意图如下:

读控制字节: 1010_0001

写控制字节: 1010_0000

.

(3) IIC总线读写时序

图 随机写

图 页面写

图  随机读

图 页面读

三  EEPROM 之随机写操作

 

写操作的时候就要考虑几个问题:

  1. 工作时钟的问题,晶振是50MHZ时钟,怎么确定时钟,在数据手册里我们发现最大时钟为400Khz,同时又要考虑到如何去保证scl为高电平的时候,sda从高到低的问题所以考虑用到两个时钟,先用晶振产生一个400Khz时钟,然后在用这个400khz时钟产生SCL时钟(200kHZ),在400Khz的上升沿的作用下,产生200Khz时钟SCL,在400Khz时钟的下降沿的作用下,记性状态的转换即数据的传输写入。
  2. 认识input和output之外的另一种端口三态门inout,因为要考虑到后面的应答信号的传输问题,应答信号是eeprom从机给FPGA的传输,而数据的传输是FPGA到EEPROM,所以这个sda端口应该是双向的,双向的就要考虑三态门。

  当ENB==1时,数据从FPGA到EEPROM,

             当ENB==0时,数据从EEPROM到FPGA。

实现语句: assign  sda = (flag==1) ? data :1’bz;

3.写时序的问题,要对照数据手册看到整个过程都有哪些,并捋清高低电平的问题,这里对前面几个状态进行详细阐述。

State0   启动信号,此时要搞清楚高低电平,在前边我介绍数据手册的时候,就说过了,当SCL为高电平的时候,sda此时也为高电平,此时应将flag三态开关打开,迎接数据的到来,其次sda线要拉低进入启动信号,同时给temp(数据缓存总线)赋控制字节

State1  控制字节的输入读取,我们通过阅读数据手册知道当SCL为高电平的时候为数据有效,所以只有在SCL为低电平的时候,对其进行赋值,数据发生改变,因为有8个字节,所以定义一个8位的cnt,进行判断是否数据已经输入完毕,数据的输入采用循环移位拼接的方法来实现,当移位完毕的时候,flag关闭,以迎接ack信号的到来,计数清零。

State2 应答信号(ack)的检测     接受完8bit数据后,会向IIC发送低电平脉冲,表示接收到了数据,此时时钟SCL为高电平的时候,SDA转为只读,表示接收到了ack信号。

State9  发送停止信号  scl为高时,让SDA由低到高表示一个停止信号。

在stop状态之前又加了一状态是为了给下一个状态让sda从低到高的上升沿信号,这样才能发送STOP信号。

其余的状态就是状态1和2的重复

module  IIC_rw (
     input    wire   sclk,
     input    wire   rst_n,
     output   reg    scl,
     inout    wire   sda
);

reg  [23:0] count;
reg  clk_400k;
reg  [7:0] state;
reg  flag;
reg  data;
reg  [7:0] temp;
reg  [7:0] cnt;

always @(posedge sclk  or  negedge rst_n)
   begin
      if(!rst_n)
       begin
         count <=0;
         clk_400k <=0;
       end
      else
        begin
         if(count==62)
           begin
             count <=0;
             clk_400k <=~clk_400k;
           end
         else
           count <= count +1;
        end
   end 

always @(posedge clk_400k or negedge rst_n)
  begin
    if(!rst_n)
       scl <=1;
    else
       scl <=~scl;
  end 

assign sda = flag ==1 ? data :1‘bz;

always @(negedge clk_400k or negedge rst_n)
   begin
    if(!rst_n)
      begin
        state <= 0;
        flag    <= 1;
        data    <= 1;
        temp  <= 0;
        cnt        <= 0;
      end
       else
           case (state)
            0: if(scl)  //启动信号
                        begin
                     flag  <=1;
                     state    <=1;
                     data    <=0;
                     temp    <=8‘b1010_0000;
                 end
               1: if(scl==0    &&    cnt<8) //控制信号
                       begin
                           flag    <=1;
                           data    <=temp[7];
                           cnt   <= cnt +1;
                           temp  <= {temp[6:0],temp[7]};//左移位像最低位移动
                       end
                 else if(scl==0&&cnt==8)
                       begin
                           flag    <=0;
                           state    <=2;
                           cnt    <=0;
                       end
                2:    if(scl==1) //ack
                        begin
//                            if(sda==0)
                            begin
                              state<=3;
                              temp<=8‘b0000_0000; //高字节
                            end
//                            else
//                             state <=0;
                        end
                3: if(scl==0&&cnt<8)//高字节
                   begin
                     flag <=1;
                     data <=temp[7];
                     cnt  <= cnt+1;
                     temp <={temp[6:0],temp[7]};
                   end
                   else if(scl ==0&&cnt==8)
                    begin
                     flag <=0;
                     cnt  <=0;
                     state<=4;
                    end
                4: if(scl==1) //ack
                        begin
//                            if(sda==0)
                            begin
                              state<=5;
                              temp<=8‘b0000_0000; //低字节
                            end
//                            else
//                             state <=0;
                        end
                5:  if(scl==0&&cnt<8)//低字节输入
                   begin
                     flag     <=1;
                     data     <=temp[7];
                     cnt     <= cnt+1;
                     temp     <={temp[6:0],temp[7]};
                   end
                   else if(scl ==0&&cnt==8)
                    begin
                     flag <=0;
                     cnt  <=0;
                     state<=6;
                    end
                6:  if(scl==1) //ACK
                        begin
//                            if(sda==0)
                            begin
                              state<=7;
                              temp<=8‘b0010_0101; //要输入的数据
                            end
//                            else
//                             state <=0;
                        end
                    7:if(scl==0&&cnt<8)
                   begin
                     flag <=1;
                     data <=temp[7];
                     cnt <= cnt+1;
                     temp <={temp[6:0],temp[7]};
                   end
                   else if(scl ==0&&cnt==8)
                    begin
                     flag <=0;
                     cnt  <=0;
                     state<=8;
                    end
                8:     if(scl==1) //ack
                        begin
//                            if(sda==0)
                            begin
                              state<=9;
                            end
//                            else
//                             state <=0;
                        end
                 9:  if(scl==0)//high to low
                    begin
                       flag     <=1;
                       data     <=0;
                       state    <=10;
                    end
                 10:    if(scl==1)//STOP
                         begin
                             flag    <=1;
                             data    <=1;
                             state    <=10;
                       end
                default :state<=0;
                endcase

           end 

endmodule

仿真波形:

四 EEPROM 之随机读操作

共13个状态,所以可以通过状态机来书写读操作

涉及重点和难点:

 NO ACK(非应答信号)
由上面随机读出的过程示意图就可以看到,NO ACK 信号是个高电平,只能由FPGA通过SDA 数据线向EEPROM 发送。
上一状态当SCL为低电平时,且数据读完后,SDA 开关打开(flag=1)data为高电平,在NOack状态下,SCL为低电平的时候,数据可读写,此时将data拉低,以备后来的STOP信号产生上升沿而用。
我们把NO ACK 和ACK 拉到一块
,就能联想出来,ACK 信号是EEPROM 收到八位数据后反馈给FPGA的信号,那么 NO ACK 信号就是 FPGA 读到八位数据后向EEPROM 发送的反馈信号。

module  IIC_read (     input    wire   sclk,     input    wire   rst_n,     output   reg    scl,     inout    wire   sda,     output   reg        [7:0]    result);

reg  [23:0] count;reg  clk_400k;reg  [7:0] state;reg  flag;reg  data;reg  [7:0] temp;reg  [7:0] cnt;

always @(posedge sclk  or  negedge rst_n)   begin      if(!rst_n)       begin         count <=0;         clk_400k <=0;       end       else         begin         if(count==62)           begin             count <=0;             clk_400k <=~clk_400k;           end         else            count <= count +1;        end   end    always @(posedge clk_400k or negedge rst_n)  begin    if(!rst_n)       scl <=1;    else       scl <=~scl;    end 

assign sda = flag ==1 ? data :1‘bz;

always @(negedge clk_400k or negedge rst_n)   begin    if(!rst_n)      begin        state     <= 0;        flag    <= 1;        data    <= 1;         temp      <= 0;        cnt        <= 0;          result    <=    0;      end        else           case (state)                0: if(scl)  //启动信号                        begin                     flag  <=1;                     state    <=1;                     data    <=0;                     temp    <=8‘b1010_0000;                          result<=0;                                  end                1: if(scl==0    &&    cnt<8) //控制信号                       begin                           flag    <=1;                           data    <=temp[7];                           cnt   <= cnt +1;                           temp  <= {temp[6:0],temp[7]};                       end                 else if(scl==0&&cnt==8)                           begin                           flag    <=0;                           state    <=2;                           cnt    <=0;                       end                 2:    if(scl==1) //ack                        begin                            if(sda==0)                            begin                            state<=3;                            temp<=8‘b0000_0000; //高字节                            end                             else                             state <=0;                        end                3: if(scl==0&&cnt<8)//高字节                   begin                     flag <=1;                     data <=temp[7];                     cnt  <= cnt+1;                     temp <={temp[6:0],temp[7]};                   end                    else if(scl ==0&&cnt==8)                    begin                     flag <=0;                     cnt  <=0;                     state<=4;                    end                 4: if(scl==1) //ack                        begin                            if(sda==0)                            begin                              state<=5;                              temp<=8‘b0000_0000; //低字节                            end                             else                             state <=0;                        end                5:  if(scl==0&&cnt<8)//低字节输入                   begin                     flag     <=1;                     data     <=temp[7];                     cnt     <= cnt+1;                     temp     <={temp[6:0],temp[7]};                   end                    else if(scl ==0&&cnt==8)                    begin                     flag <=0;                     cnt  <=0;                     state<=6;                    end                 6:  if(scl==1) //ACK                        begin                            if(sda==0)                            begin                              state<=7;                             end                             else                             state <=0;                        end                 7:    if(scl==0)  // 拉高sda 备启动信号使用  启动信号是在scl为高电平的同时, sda由高电平到低电平                        begin                     flag  <=1;                     state    <=8;                     data    <=1;                     temp    <=8‘b1010_0001;                          result<=0;                              end                 8:        if(scl)//启动信号  高到低                        begin                        data<=0;                        state<=9;                        end                9:    if(scl==0    &&        cnt<8) //控制信号                       begin                           flag    <=1;                           data    <=temp[7];                           cnt   <= cnt +1;                           temp  <= {temp[6:0],temp[7]};                       end                    else if(scl==0&&cnt==8)                           begin                           flag    <=0;                           state    <=10;                           cnt    <=0;                       end                                    10:      if(scl==1) //ACK                        begin                            if(sda==0)                            begin                              state<=11;                            end                             else                             state <=0;                        end                11:    if(scl==1&&cnt<8)//读    读的时候为高电平                        begin                      flag        <=0;                          result    <={result[6:0],sda};                      cnt        <=cnt+1;                         end                          else if(scl==0&&cnt==8)                         begin                           flag    <=1;                           cnt        <=0;                           state    <=12;                           data    <=1;                         end                 12:    if(scl==0) //NOack                        begin                            if(sda==1)                            begin                              state    <=13;                               data    <=0;                            end                             else                             state <=0;                        end                                 13:    if(scl==1)//STOP                    begin                        flag     <=1;                       data     <=1;                       state    <=13;                    end //                14:    if(scl==1)//进入非忙态//                         begin//                             flag    <=1;//                             data    <=1;//                             state    <=14;//                       end                 default :state<=0;                endcase   end endmodule

读信号时的波形图:

时间: 2024-11-20 10:01:41

FPGA学习笔记之IIC—EEPROM写和读的相关文章

TCP/IP详解学习笔记 这位仁兄写得太好了.(转载)

TCP/IP详解学习笔记   这位仁兄写得太好了 TCP/IP详解学习笔记   这位仁兄写得太好了. http://blog.csdn.net/goodboy1881/category/204448.aspx TCP/IP详解学习笔记(13)-TCP坚持定时器,TCP保活定时器 TCP/IP详解学习笔记(12)-TCP的超时与重传TCP/IP详解学习笔记(11)-TCP交互数据流,成块数据流 TCP/IP详解学习笔记(1)-基本概念 为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着

一步步写 CMOS 驱动模块 &lt;ELDD 学习笔记&gt; (最近更新,写到open release为止)

一步步写 CMOS 驱动模块 Let's implement a char driver to access the system CMOS. 首先仅仅是创建设备模块,最简单的,类似于前面hello world模块一样的东东,从最简单的框架慢慢搭 /************************************************************ code writer : EOF code date : 2014.08.15 code file : cmos_demo.c

FPGA学习笔记(二)——FPGA学习路线及开发流程

###### [该随笔部分内容转载自小梅哥]       ######### 一.FPGA学习路线 工具使用 -> 语法学习 -> 逻辑设计 -> IP使用 ->接口设计 -> 时序分析 -> 片上系统 1.工具使用 Altera:Quartus II Xlinx: Vivado 2.语法学习 Verilog HDL(FPGA设计的是电路) 3. 逻辑设计 组合逻辑:多路选择器.加法器.译码器.乘法器 ······· 时序逻辑:计数器.分频器.移位寄存器.定时器 ···

Django学习笔记(现学现写,实时更新)

说明:我是先上手做一些简单的例子,然后在尝试的过程中理解Django的原理,笔记也是按这个思路来的. 一.Django结构与基本文件介绍 1. django-admin.py 工程管理工具,主要用于创建项目和app等. 例:django-admin.py startproject project_example 会创建一个名为project_example的工程,目录结构如下: |-- project_example|    |--project_example|        |-- __in

FPGA学习笔记之Altera FPGA使用JIC文件配置固化教程(转)

很多做过单片机的朋友都知 道,我们在对MCU烧写完程序固件后,那么该程序固件就存储在了该MCU内部.即使MCU断电了再重新上电,程序也能继续运行.这是因为对MCU烧写固件 的实质就是将程序固件写入到MCU的片上程序存储器ROM中,而现代的大部分MCU这个ROM都是FLASH存储器.FLASH存储器能够掉电保持数据, 所以可以实现掉电程序不丢失.Altera或Xilinx的FPGA芯片,使用的是基于SRAM结构的查找表,而SRAM的一大特性就是掉电数据会丢失, 当我们使用JTAG将SRAM配置文件

Xilinx FPGA 学习笔记一-chipscope 无法观察信号 BUFG

今天开始试着使用chipscope,写了一个简单的流水灯的例程,开始综合布线的时候没有问题,但是加上chipscope 以后,综合就总报错. 第一种情况:用chipscope不可以直接观察全局时钟信号,即BUFG信号-----X 错误如下: ERROR:Place:1136 - This design contains a global buffer instance, <PLL_u0/clkout1_buf>, driving the net, <CLK_OUT1>, that

FPGA学习笔记之格雷码、边沿检测、门控时钟

一.格雷码 格雷码的优点主要是进位时只有一位跳变,误码率低. 1.二进制转格雷码 我们观察下表: 二进制码 格雷码 00 00 01 01 10 11 11 10 二进制码表示为B[],格雷码表示为G[],则有 G(i) = B(i),i为最高位 G(i-1) = B(i) xor B(i-1),i非最高位 用verilog可以这样写 1 reg [WIDTH-1:0] bin; 2 reg [WIDTH-1:0] gray; 3 parameter WIDTH = 8; 4 always @(

JMeter学习笔记--使用URL回写来处理用户会话

如果测试的Web应用系统使用URL回写而非Cookie来保存会话信息,那么测试人员需要做一些额外的工作来测试web站点 为了正确回应URL回写,JMeter需要解析从服务器收到的HTML,并得到唯一的会话ID.测试人员需要使用合适的HTTP URL回写修改器来完成这一点.测试人员只需简单地讲会话ID参数的名称放入修改器中,修改器就会找到会话ID,并将其放入每个请求之中.如果请求之中已经有了会话ID,那么它就会被替换掉.如果选中了“Cache Session ID?"选项,那么最近一个被找到的会话

FPGA学习笔记之FIFO IP核

FIFO总结文档 何为FIFO .? FIFO(First In First Out ) 先进先出是一个常用于数据缓存的一个数据缓冲器. fifo主要有WRREQ(写信号)WRclk(写时钟)data(写数据)wrfull(写满标志)wrempty(写空标志)wrusedw(告知里面还有多少数据) Rdreq(读信号)rdclk(读时钟)rdfull(读满标志)rdempty(读空标志)rdusedw(告知里面数据个数) 以上所有信号全是高电平有效. 为什么要用fifo? 在项目设计中,我们通常