集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 4208|回复: 9

雾盈FPGA笔记(十一)IIC总线协议应用之一EEPROM字节写入

[复制链接]
雾盈 发表于 2016-8-4 17:59:49 | 显示全部楼层 |阅读模式
本帖最后由 雾盈 于 2016-8-28 13:41 编辑

IIC总线协议FPGA应用之一

EEPROM(24LC64)字节写入(byte write)

雾盈                          2016-8-4


雾盈FPGA笔记汇总目录


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

:以下标题1)到5)的内容是从IIC总线协议中文手册中截取的。虽然文字很多,但是我觉得想要详细了解IIC协议是怎么样一个工作原理,读一读很有必要。
1)I2C总线特点

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

2)I2C总线工作原理

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

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

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

4)I2C总线操作

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

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

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

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

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

二、        IIC协议在EEPROM 中的应用。



1)下面是EEPROM(24LC64) 数据手册中关于这八个引脚的介绍



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


 
2)I2C总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号

开始信号(start):
处理器让SCL时钟保持高电平,然后让SDA数据信号由高变低就表示一个开始信号。同时IIC总线上的设备检测到这个开始信号它就知道处理器要发送数据了。
停止信号(stop):
处理器让SCL时钟保持高电平,然后让SDA数据信号由低变高就表示一个停止信号。同时IIC总线上的设备检测到这个停止信号它就知道处理器已经结束了数据传输,我们就可以各忙各个的了,如休眠等。时序图如下:


应答信号(ACK):
接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。时序图如下:


3)数据传输
SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。时序图如下:


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

 
三、EEPROM之字节写入(byte write)

写操作分为字节写和页面写两种操作,我们今天只谈字节写,后面我会发页写和读写,毕竟我也刚学。写完这个我会用序列机写一下的。
字节写的过程如下图所示。


由图上的过程,我们是不是很容易想到这个可以用序列机(LSM)去实现,由于我们上课的时候老师要求我们用状态机(FSM)来写,所以我就说一我在写这个时候的思路,想法和遇到的问题。
我回忆一下,我在写这个程序时的思路:
首先,搞清楚我要哪些,目的是什么。
第一步:肯定先是整好工作时钟,400KHZ,我们可以写一个分频器将50M系统时钟分频为400KHZ。
  1. always @ (posedge clk or negedge rst_n)
  2. begin
  3.      if(!rst_n)
  4.          begin
  5.                     count <= 1'b0;       
  6.                         clk_400k <= 1'b0;
  7.         end
  8.      else  begin
  9.             if( count == COUNT_400K )
  10.                                 begin
  11.                                         count <= 1'b0;
  12.                                         clk_400k <= ~clk_400k;
  13.                                 end       
  14.                         else begin
  15.                                     count <= count + 1'b1;
  16.                                  end                       
  17.              end
  18. end
复制代码


第二步:分频好400KHZ后,我们发现EEPROM 的 SLC 时钟线是200KHZ ,我们也可以如第一步那样再次分频一个200KHZ,但是我们发现200KHZ是400KHZ频率的一半,于是我们用下面简单的代码就产生了一个200KHZ的时钟。
  1. always @ (negedge clk_400k or negedge rst_n)
  2. begin
  3.      if(!rst_n)
  4.         begin
  5.                   scl <= 1'b1;
  6.         end
  7.      else if( state == 10)
  8.                 begin
  9.                   scl <= 1'b1;
  10.                 end
  11.          else
  12.                 begin
  13.                   scl <= ~scl;
  14.                   end         
  15. end
复制代码

&#8195;
第三步:认识 input,output,reg之外的另一种数据类型 inout (输入输出三态门)
当flag为1时输入,当flag为0 时输出
在这个程序里,既是flag=1时SDA写,flag=0时SDA读。代码可以这样写:
  1. assign sda = ( flag1 == 1'b1) ? data_out : 1'bz;  
复制代码

&#8195;
第四步:这一步是核心,就是用状态机将数据传输过程的时序,严谨的表述出来。那搞清楚时序就是核心之核。我们来捋一下时序。
注明一下:IIC数据从最高位开始传输。
还是这张图。


我们将图中的过程视为状态机的各种状态。于是,写入一个字节,就必须有这些state:
0. 发送启动信号
1. 发送控制字
2. 接收并检测EEPROM发来的应答信号ACK
3. 发送高字节地址位
4. 接收并检测EEPROM发来的应答信号ACK
5. 发送低字节地址位
6. 接收并检测EEPROM发来的应答信号ACK
7. 发送8bit有效数据
8. 接收并检测EEPROM发来的应答信号ACK
9.发送停止信号

&#8195;

第五步,当我们把所有的思路捋清楚之后,就可以看着时序图敲代码了
源代码我会贴在后面。
由上图可以看出来,state3-8 都是在重复 state1-2 ,这样这个代码一下就在难度上缩小了一半,我们来详细说一下state0、state1、 state2和state9。
State0:发送启动信号,前面说的很详细了。让SCL时钟保持高电平,然后让SDA数据信号由高变低就表示一个开始信号
State9:发送停止信号。让SCL时钟保持高电平,然后让SDA数据信号由低变高就表示一个停止信号。
State1:发送8位控制字。我们知道SDA线上的数据状态仅在SCL为低电平的期间才能改变后,也能写出来了,这里说一个怎么让8位数据传给单bit信号的方法,代码在下,看一下就体会出来了。
8bit传给1bit ,左移
  1.                                 if( scl == 1'b0 && cnt < 8 )        //while scl is low ,change data
  2.                                         begin
  3.                                                 data_out <= temp[7];                //cycle 0-8
  4.                                                 flag1 <= 1'b1;
  5.                                                 temp <= { temp[6:0],temp[7] };
  6.                                                 cnt <= cnt + 1'b1;
  7.                                                 state <= 1'b1;               
  8.                                         end
复制代码


1bit 传给 8bit ,右移
  1. if( scl == 1'b1 && cnt < 8 )                // scl high and flag1=0
  2.                                         begin
  3.                                                 flag1 <= 1'b0;
  4.                                                 data_temp <= sda;
  5.                                                 temp[0] <= data_temp;
  6.                                                 temp <= { temp[0],temp[7:1] };         // remember !!!  
  7.                                                 data_rd <= temp;
  8.                                                 cnt <= cnt + 1'b1;
  9.                                                 state <= 4'd10;               
  10.                                         end
复制代码


State2:接受ack信号。应答信号(ACK):
接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。此时,SLC保持高电平,SDA 转为只读,就表示接收到了ack 信号。
其他state就是在重复啦。
再说一点,有的状态机可能会在发送stop信号状态之前多增加一个st状态,此状态就是为了给下一状态,让SDA从低拉高的上升沿信号,这样才能够发送stop信号。代码如下:
  1. 9:begin //          order to  make sda  low to  high  
  2.                                  if( scl == 1'b0 )                        // scl low and sda low ,produce "stop" signal  
  3.                                         begin
  4.                                                 data_out <= 1'b0;
  5.                                                 flag1 <= 1'b1;                                               
  6.                                                 state <= 4'd10;                       
  7.                                         end                       
  8.                           end
复制代码


所有的东西都说完了,这样一个看着貌似很难的程序,只要捋清思路,一步一步慢慢来,总能写出来的。


附源代码:
  1. //=====================================================================
  2. // module name:                iic_ctrl
  3. // function:       
  4. // create data:                from 2016-8-3 14:01:22 to   
  5. // editor:                        miao
  6. // Tool&#160;versions:         quartus 13.0
  7. //=====================================================================
  8. module                iic_ctrl(
  9.                                         // system signal
  10.                                         input                clk,  // 50MHZ
  11.                                         input                rst_n,
  12.                                         // output signal
  13.                                         output        reg scl,  // eeprom scl
  14.                                         inout                 sda             // eeprom sda                                        
  15. );       
  16. //=========================================================================================================
  17. //**************************     Define parameter and internal signals          *********************************
  18. //=========================================================================================================
  19. reg clk_400k;
  20. reg [7:0] count;
  21. parameter COUNT_400K = 62;
  22. reg flag1;  //for sda

  23. reg [3:0] state;
  24. reg [7:0] temp; // register for sda
  25. reg                  data_out; // sad = data_out
  26. reg [7:0] cnt;
  27. //=========================================================================================================
  28. //*********************************             400KHZ & scl & flag1         ***********************************
  29. //=========================================================================================================
  30. // frequency division        400KHZ , count 62 times
  31.    always @ (posedge clk or negedge rst_n)
  32. begin
  33.      if(!rst_n)
  34.          begin
  35.                     count <= 1'b0;       
  36.                         clk_400k <= 1'b0;
  37.         end
  38.      else  begin
  39.             if( count == COUNT_400K )
  40.                                 begin
  41.                                         count <= 1'b0;
  42.                                         clk_400k <= ~clk_400k;
  43.                                 end       
  44.                         else begin
  45.                                     count <= count + 1'b1;
  46.                                  end
  47.                        
  48.              end
  49. end
  50. //  400KHZ  could produce 200KHZ named scl of eeprom
  51. always @ (negedge clk_400k or negedge rst_n)
  52. begin
  53.      if(!rst_n)
  54.         begin
  55.                   scl <= 1'b1;
  56.         end
  57.      else if( state == 10)
  58.                 begin
  59.                   scl <= 1'b1;
  60.                 end
  61.          else
  62.                 begin
  63.                   scl <= ~scl;
  64.                   end
  65.          
  66. end

  67. //=========================================================================================================
  68. //*********************************                         state machine                    ***********************************
  69. //=========================================================================================================

  70. assign sda = ( flag1 == 1'b1) ? data_out : 1'bz;  

  71. always @ (posedge clk_400k or negedge rst_n)
  72. begin
  73.      if(!rst_n)
  74.         begin                                                                // initial , both scl and sda high
  75.                         state <= 4'd0;
  76.                         temp <= 8'd0;                                        // register
  77.                         data_out <= 1'b1;
  78.                         flag1 <= 1'b1;
  79.                         cnt <= 1'b0;
  80.         end
  81.      else  begin
  82.            case(state)
  83.              0:begin // send start signal
  84.                 if( scl == 1'b1 )                        //  scl high while sda low , produce  the  "start" signal
  85.                                         begin
  86.                                                 data_out <= 1'b0;
  87.                                                 flag1 <= 1'b1;
  88.                                                 temp <= 8'b1010_0000;
  89.                                                 state <= 1'b1;                                       
  90.                                         end
  91.                end
  92.              1:begin // send contrl_word        
  93.                                 if( scl == 1'b0 && cnt < 8 )        //while scl is low ,change data
  94.                                         begin
  95.                                                 data_out <= temp[7];                //cycle 0-8
  96.                                                 flag1 <= 1'b1;
  97.                                                 temp <= { temp[6:0],temp[7] };
  98.                                                 cnt <= cnt + 1'b1;
  99.                                                 state <= 1'b1;               
  100.                                         end
  101.                                 else if( scl == 1'b0 && cnt == 8 )
  102.                                          begin
  103.                                                 flag1 <= 1'b0;         // flag = 0 ,sda begin receive data
  104.                                                 state <= 4'd2;
  105.                                                 cnt <= 1'b0;
  106.                                          end
  107.                end
  108.                         2:begin // receive ack
  109.                                  if ( scl == 1'b1 )                       
  110.                                         begin
  111.                                         //        if( sda == 1'b0 )                // scl high and sda low ,receive ACK . meanwhile switch to "state3"
  112.                                                 begin                                       
  113.                                                         temp <= 8'd0000_0000;        //        put 8 bit address into the register "temp"
  114.                                                         state <= 4'd3;
  115.                                                 end       
  116.                                         /*         else begin                          //if couldn't receive ACK ,switch to "state1" and transfer contrl_word again
  117.                                                         state <= 4'd1;
  118.                                                          end */
  119.                                         end
  120.                           end
  121.                         3:begin // send high address  
  122.                                 if( scl == 1'b0 && cnt < 8 )        // scl low could change data , similar with "state1"
  123.                                         begin
  124.                                                 data_out <= temp[7];
  125.                                                 flag1 <= 1'b1;
  126.                                                 temp <= { temp[6:0],temp[7] };
  127.                                                 cnt <= cnt + 1'b1;
  128.                                                 state <= 4'd3;               
  129.                                         end
  130.                                 else if( scl == 1'b0 && cnt == 8 )
  131.                                          begin
  132.                                                 flag1 <= 1'b0;                 // sda = data_out                                               
  133.                                             state <= 4'd4;
  134.                                                 cnt <= 1'b0;
  135.                                          end
  136.                           end
  137.                         4:begin // receive ack
  138.                                  if ( scl == 1'b1 )
  139.                                         begin
  140.                                         //        if( sda = 1'b0 )
  141.                                                 begin
  142.                                                         temp <= 8'd0000_0000;
  143.                                                         state <= 4'd5;
  144.                                                 end       
  145.                                         /*         else begin
  146.                                                         state <= 4'd3;
  147.                                                          end */
  148.                                         end                               
  149.                            end
  150.                         5:begin // send low address  
  151.                                 if( scl == 1'b0 && cnt < 8 )
  152.                                         begin
  153.                                                 data_out <= temp[7];
  154.                                                 flag1 <= 1'b1;
  155.                                                 temp <= { temp[6:0],temp[7] };
  156.                                                 cnt <= cnt + 1'b1;
  157.                                                 state <= 4'd5;               
  158.                                         end
  159.                                 else if( scl == 1'b0 && cnt == 8 )
  160.                                          begin
  161.                                                 flag1 <= 1'b0;                
  162.                                                 cnt <= 1'b0;       
  163.                                             state <= 4'd6;      
  164.                                          end
  165.                          end
  166.                         6:begin // receive ack
  167.                                  if ( scl == 1'b1 )
  168.                                         begin
  169.                                         //        if( sda = 1'b0 )
  170.                                                 begin
  171.                                                         temp <= 8'd1111_1111;
  172.                                                         state <= 4'd7;
  173.                                                 end
  174.                                         /*         else begin
  175.                                                         state <= 4'd5;
  176.                                                          end */
  177.                                         end
  178.                            end
  179.                         7:begin // send data
  180.                                 if( scl == 1'b0 && cnt < 8 )
  181.                                         begin
  182.                                                 data_out <= temp[7];
  183.                                                 flag1 <= 1'b1;
  184.                                                 temp <= { temp[6:0],temp[7] };
  185.                                                 cnt <= cnt + 1'b1;
  186.                                                 state <= 4'd7;               
  187.                                         end
  188.                                 else if( scl == 1'b0 && cnt == 8 )
  189.                                          begin
  190.                                                 flag1 <= 1'b0;        
  191.                                                 cnt <= 1'b0;       
  192.                                             state <= 4'd8;      
  193.                                          end
  194.                          end
  195.                         8:begin // receive ack
  196.                                  if ( scl == 1'b1 )
  197.                                         begin
  198.                                         //        if( sda = 1'b0 )
  199.                                                 begin
  200.                                                         state <= 4'd9;
  201.                                                 end                                               
  202.                                         end
  203.                                         /*         else begin
  204.                                                         state <= 4'd7;
  205.                                                          end */
  206.                            end
  207.                         9:begin //          order to  make sda  low to  high  
  208.                                  if( scl == 1'b0 )                        // scl low and sda low ,produce "stop" signal  
  209.                                         begin
  210.                                                 data_out <= 1'b0;
  211.                                                 flag1 <= 1'b1;                                               
  212.                                                 state <= 4'd10;                       
  213.                                         end                       
  214.                           end
  215.                         10:begin        //        "stop" flag                        
  216.                                  if( scl == 1'b1)
  217.                                   begin
  218.                                         flag1 <= 1'b1;
  219.                                         data_out <= 1'b1;
  220.                                         state <= 4'd10;
  221.                                   end
  222.                            end
  223.              default: state <= 4'd0;
  224.              endcase
  225.              end
  226. end




  227. endmodule
复制代码











本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
芙蓉王 发表于 2016-8-5 14:54:36 | 显示全部楼层
                          感谢作者
回复 支持 1 反对 0

使用道具 举报

zhiweiqiang33 发表于 2016-8-5 09:52:38 | 显示全部楼层
作者对iic理解很到位 ,感谢作者的辛苦分享,
Sure 发表于 2016-8-6 11:33:42 | 显示全部楼层
                       厉害,大神
Adamancy 发表于 2016-8-9 15:18:02 | 显示全部楼层
                      可以,很强势
心有猛虎 发表于 2016-11-1 21:27:33 | 显示全部楼层
   可以,很强势
熊伟 发表于 2016-11-3 19:09:21 | 显示全部楼层
楼主,为什么将ack这个应答注释了呢?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
 楼主| 雾盈 发表于 2016-11-4 08:28:22 | 显示全部楼层
熊伟 发表于 2016-11-3 19:09
楼主,为什么将ack这个应答注释了呢?

仿真的时候注释掉了,你这个是什么字体?
熊伟 发表于 2016-11-9 21:03:20 | 显示全部楼层
哦,我一直纠结这个,我用的notepad++
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|集成电路技术分享 ( 京ICP备20003123号-1 )

GMT+8, 2024-6-29 16:32 , Processed in 0.074181 second(s), 23 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表