原创 fpga学习日记15,实现IIC通信

2013-8-30 13:26 7294 19 19 分类: FPGA/CPLD 文集: 数电,FPGA学习

第一部分IIC介绍 

第二部分 verilog实现写24c16

IIC总线在嵌入式开发中是常用的总线之一

无论是实时时钟,温度传感器还是EEPROM等芯片都有采用IIC通信的

IIC简单的说具有以下特点

1,省IO只需要SDA  SCK两根线即可完成通信

2,速度快  最快可达到4Mbit/s

3,一条总线可挂载多个设备实现主从通信

 虽然现在的单片机和arm都内置了iic接口简单的配置下寄存器和调用API函数即可完成通信

但是研究其时序的实现对iic的加强理解还是有益的

 

时序相关

1 .数据的有效性:

SDA 线上的数据必须在时钟的高电平周期保持稳定数据线的高或低电平状态只有在 SCL线的时钟信号是低电平时才能改变。

2,基本信号为 空闲 起始信号  应答信号  结束信号

 

20130829203751790.jpg

空闲:SDA和SCL两条信号线都处于高电平,即总线上所有的器件都释放总线,两条信号线各自的上拉电阻把电平拉高;

起始信号:当SCL线是高电平时,SDA线从高电平向低电平切换,这个情况表示起始条件。

20130829203825971.jpg

应答信号:发送到SDA线上的数据必须是8位的。每次传输可以发送的数据不受限制。每个字节后必须在时钟的第9个脉冲期间释放数据总线(SDA为高),由接收器发送一个ACK(把数据总线的电平拉低)来表示数据成功接收。

20130829204434868.jpg

停止:当SCL线是高电平时,SDA线由低电平向高电平切换表示停止条件

3,首先传输的是数据的最高位(MSB)。如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟SCL保持低电平迫使主机进入等待状态。当从机准备好接收下一个数据字节并释放时钟线SCL后,数据传输继续。

 

地址与数据

地址格式

20130829203932745.jpg

在起始条件之后,发送一个7位的从机地址,其中高四位A6-A3是从机器件的固定编址,出厂时就已给定;A2-A0是从机器件的引脚地址,通过接地接电源来形成地址。例如:4位LED的驱动控制器只用到两位A1,A0,所以同样的器件最多可以挂4个在IIC总线上;AT24C02(EEPROM)通过器件地址输入端A0、A1和A2可以实现将最多8个AT24C02器件连接到总线上。紧接着第8位是数据方向(R/W),0-表示发送数据(写),1-表示接收数据(读)。数据传输一般由主机产生的停止位(P)终止。

但是如果主机仍希望在总线上通讯,它可以产生重复起始条件(Sr),和寻址另一个从机,而不是首先产生一个停止条件。在这种传输中,可能有不同的读/写格式结合。

 

24C02写入操作

进行写操作时,首先发送该器件的7位地址码和写方向位”0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为响应,单片机收到应答后就可以传送数据了。传送数据时,单片机首先发送一个字节的被写入存储器的首地址,收到存储器器件的应答后,单片机就逐个发送数据字节,但每发送一个字节后都要等待应答。AT24C系列片内地址在接收到每一个数据字节地址后自动加1,在芯片的“一次装载字节数”限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。  

20130829204645686.jpg

读取时序

先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。      

 

然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。   

20130829204705625.jpg

20130829204722556.jpg

 第二部分 verilog实现写eeprom 24cxx

 1,设计思想

难点在于

a,IIC时钟频率  频率与起始 结束信号的产生 以及数据在时钟低电平变化的控制 

b,如何找到IIC 时钟的低电平  进行数据变化操作

c,如何产生开始和结束信号

 

24CXX在3,3v时最高速度100k

开发板晶振50为了简单设计速度为50k  计数1000次产生一个时钟

对50khz进行2倍频 4倍频即可找到50khz时钟高低电平的中点

在产生起始 结束信号时 接过50khz时钟的权限

强制控制时钟电平和数据信号 产生开始和结束信号

然后按照数据手册上写的时序进行设计即可

2,verilog代码实现

50khz时钟产生

// counter for gen a clk_50k : need count to 1000, for 50M/1000 = 50K hz
always @(posedge sys_clk or negedge sys_rst_n) begin
       if (sys_rst_n ==1'b0)
           counter_div <= 10'b0;
       else if (counter_div >= 10'd999)
           counter_div <= 10'b0;
       else
           counter_div <= counter_div + 10'b1;
end

// gen a clk_50k use counter_div :  not use counter_div 0 - 500 is for i2c bus request start timing
always @(posedge sys_clk or negedge sys_rst_n) begin
       if (sys_rst_n ==1'b0)  
           clk_50k <= 10'b0;
       else  if ((counter_div >= 375) && (counter_div < 875))    
           clk_50k <= 10'b1;
       else
           clk_50k <= 10'b0;
end

 

我们对50khz进行4倍频以便于找到50khz时钟高低电平中点

 

// gen a 200K CLK for work counter count
always @(posedge sys_clk or negedge sys_rst_n) begin
       if (sys_rst_n ==1'b0)
           clk_200k <= 10'b0;
       else  if ((counter_div >= 0  ) && (counter_div < 125))
           clk_200k <= 10'b0;
       else  if ((counter_div >= 125) && (counter_div < 250))
           clk_200k <= 10'b1;  
       else  if ((counter_div >= 250) && (counter_div < 375))
           clk_200k <= 10'b0;                        
       else  if ((counter_div >= 375) && (counter_div < 500))
           clk_200k <= 10'b1;
       else  if ((counter_div >= 500) && (counter_div < 625))
           clk_200k <= 10'b0;
       else  if ((counter_div >= 625) && (counter_div < 750))
           clk_200k <= 10'b1;
       else  if ((counter_div >= 750) && (counter_div < 875))
           clk_200k <= 10'b0;                        
       else  if ((counter_div >= 875) && (counter_div < 1000))
           clk_200k <= 10'b1;
       else ;
end

产生开始和结束信号时强制时钟为高而不是50khz的那个时钟来源

//generate real clk for SCLK ,when the i2c bus is idle, make the clk wire high level
always @(*) begin
       if ( counter >= 2 && counter <= 118 )
           clk_sclk = clk_50k;
       else
           clk_sclk = 1'b1;
end

 

最后在每个50khz的低电平中点变换要发送的数据

always @(*) begin
   case (counter)
        0 : sda = 1'b1 ;
        1 : sda = 1'b1 ;
        2 : sda = 1'b1 ;
        3 : sda = 1'b0 ;
        4 : sda = 1'b0 ;              //Start

        5 : sda = 1'b1 ;              // 1
        9 : sda = 1'b0 ;              //0
       13 : sda = 1'b1 ;               //1
       17 : sda = 1'b0 ;               //0
       21 : sda = device_addr[2] ;
       25 : sda = device_addr[1] ;
       29 : sda = device_addr[0] ;

       33 : sda = 1'b0 ;                   // write ,bit[0] =  0  

       33 + 4 : sda = 1'bz ;                 //ACK ,sda should be input
       
//---------重复以上数据发送------------------- 
      109 + 4: sda = 1'b0 ;
      111 + 4: sda = 1'b1 ;                      //End

      default :  sda = 1'b1 ;
  endcase
end

其他参考资料

24C02通信详解

http://blog.csdn.net/drivermonkey/article/details/7695547

与非网IIC协议文章

http://www.eefocus.com/article/08-07/48416s.html
IIC总线协议中文版

http://wenku.baidu.com/view/08ef1c4e767f5acfa1c7cdfc.html

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
19
关闭 站长推荐上一条 /3 下一条