原创 [Craftor原创]基于Verilog的I2C总线驱动设计

2010-10-4 18:48 3811 12 15 分类: FPGA/CPLD

摘要:


此版本的设计中,笔者将协议里对总线的操作细分为4个,即起始(Start)、写(Write)、读(Read)、停止(Stop),并给对应的操作编码:起始(1000)、写(0100)、读(0010)、停止(0001)。每次读写操作中也包括了一次应答操作。上层模块需要操作总线时,仅需要按照芯片操作步骤给出命令,即可以对任意支持I2C总线的外设进行操作。


此版本中主机不具有应答能力,在对24C02芯片进行操作时,可以进行任意随机读写,但不可进行连续读,这个问题将会在下一版中解决。


设计步骤:


1、双向I/O口的设计


I2C总线的SCL和SDA信号都是双向端口,在操作的时候与一般的单向端口有一定差别,在设计时需要特别注意。


I/O的定义如下:





1inout wire scl


2inout wire sda

当需要进行输出操作时,需将使能信号scl_en/sda_en拉高,并给scl_out/sda_out赋相应的值;输入操作时,拉低scl_en/sda_en,读取scl_in/sda_in信号即可。





01reg scl_en, scl_r, scl_out;


02reg sda_en, sda_r, sda_out;


03  


04wire scl_in;


05wire sda_in;


06  


07assign scl = scl_r;


08assign sda = sda_r;


09assign scl_in = scl;


10assign sda_in = sda;


11  


12always@(scl_en or scl_out) begin


13  if (scl_en)


14    scl_r <= scl_out;


15  else


16    scl_r <= 1'bZ;


17end


18  


19always@(sda_en or sda_out) begin


20  if (sda_en)


21    sda_r <= sda_out;


22  else


23    sda_r <= 1'bZ;


24end

2、时钟的设计


此设计中采用1MHz的输入时钟,进行4分频,总线工作的250KHz的频率下。


在1MHz的输入时钟下,对一个2bit的计数器进行循环计数,产生0、1、2、3四个节拍,在对SCL和SDA进行操作时都基于这4个节拍,如果需要在SCL和SDA间产生相位差,就相当容易。因为在写操作时,需要先送数据,再送时钟,而读操作时,需要写给时钟,再读数据。





1reg [1:0] bit = 0;


2always@(posedge sys_clk) bit <= bit + 1’b1;

3、起始信号(Start, 4’b1000)的产生


在SCL高电平时,SDA上产生一个下降沿,即是起始信号。





01always@(posedge sys_clk) begin


02  case (start_state)


03  0:


04    begin


05      if ((cmd == 4'b1000) && (!cmd_busy))


06        begin


07          scl_en <= 1;


08          scl_out <= 1;


09          sda_en <= 1;


10          sda_out <= 1;


11          start_state <= 1;


12          start_busy <= 1;


13        end


14      else


15        start_busy <= 0;


16    end


17  1: if (bit == 3) start_state <= 2;


18  2:


19    begin


20      case (bit)


21      0: sda_out <= 0;


22      1: scl_out <= 0;


23      3: 


24        begin


25          start_state <= 0;


26          start_busy <= 0;


27        end


28      endcase


29    end


30  endcase


31end

4、停止信号(Stop, 4’b0001)的产生


在SCL高电平时,SDA上产生一个上升沿,即是停止信号。





01always@(posedge sys_clk) begin


02  case (stop_state)


03  0:


04    begin


05      if ((cmd == 4'b0001) && (!cmd_busy))


06        begin


07          scl_en <= 1;


08          scl_out <= 1;


09          sda_en <= 1;


10          sda_out <= 0;


11          stop_state <= 1;


12          stop_busy <= 1;


13        end


14      else


15        stop_busy <= 0;


16    end


17  1: if (bit ==3 ) stop_state <= 2;


18  2:


19    case (bit)


20    0: sda_out <= 1;


21    3: 


22      begin


23        stop_state <= 0;


24        stop_busy <= 0;


25        scl_en <= 0;


26        scl_out <= 1'bZ;


27        sda_en <= 0;


28        sda_out <= 1'bZ;


29      end


30    endcase


31  endcase


32end

5、写操作(Write, 4’b0100)的产生





01always@(posedge sys_clk) begin


02  case (wr_state)


03  0:


04    begin


05      if ((cmd == 4'b0100) && (!cmd_busy))


06        begin


07          wr_cnt <= 0;


08          wr_state <= 1;


09          sda_en <= 1;


10          sda_out <= dout[7];


11          scl_en <= 1;


12          scl_out <= 0;


13          wr_busy <= 1;


14        end


15      else


16        wr_busy <= 0;


17    end


18  1: if (bit == 3) wr_state <= 2;


19  2:


20    begin


21      case (bit)


22      0:


23        case(wr_cnt)


24        0,1,2,3,4,5,6,7:


25          begin


26            sda_out <= dout[7-wr_cnt];


27            wr_cnt <= wr_cnt + 1;


28          end


29        endcase


30      1: scl_out <= ~scl_out;


31      3:


32        begin


33          scl_out <= ~scl_out;


34          if (wr_cnt == 8)


35            begin


36              wr_cnt <= 0;


37              wr_state <= 3;


38              sda_en <= 0;


39              wr_busy <= 0;


40              wr_ack <= 1;


41            end


42        end


43      endcase


44    end


45  3:


46    if (bit == 3)


47      begin


48        wr_ack <= 2;


49        wr_state <= 0;


50        sda_en <= 0;


51        sda_out <= 1'bZ;


52      end


53  endcase


54end

6、读操作(Read, 4’b0010)的产生





01always@(posedge sys_clk) begin


02  case (rd_state)


03  0:


04    begin


05      if ((cmd == 4'b0010) && (!cmd_busy))


06        begin


07          rd_cnt <= 0;


08          sda_en <= 0;


09          rd_state <= 1;


10          scl_en <= 1;


11          scl_out <= 0;


12          rd_busy <= 1;


13        end


14      else


15        rd_busy <= 0;


16    end


17  1: if (bit == 3) rd_state <= 2;


18  2:


19    begin


20      case (bit)


21      0,2: scl_out <= ~scl_out;


22      1:


23        begin


24          case (rd_cnt)


25          0,1,2,3,4,5,6,7:


26            begin


27              din[7-rd_cnt] <= sda_in;


28              rd_cnt <= rd_cnt + 1;


29            end


30          endcase


31        end


32      3:


33        if (rd_cnt == 8)


34          begin


35            rd_cnt <= 0;


36            rd_state <= 3;


37            rd_busy <= 0;


38            rd_ack <= 1;


39          end


40      endcase


41    end


42  3:


43    if (bit == 3)


44      begin


45        rd_ack <= 0;


46        rd_state <= 0;


47      end


48  endcase


49end

7、应答信号(Acknowledge)接收


应答信号会在读写进程里自动接收,不需要另外操作。





1always@(posedge sys_clk)


2if ((bit == 1)&&(ack_en == 1)) ack <= sda_in;

8、设计整合


很显然,如果简单地将上面各个操作的代码放在一个Verilog文件里,肯定是综合不过去的,因为每个进程都要对SCL和SDA进行操作。所以笔者又将各操作整合到了一个状态机里,这样就不会出现对SCL和SDA多重操作的情况。


->完整的代码见附件


9、仿真与测试


仿真:起始、写、读、停止信号的产生,都在如下的时序图上。


8f155e1d-1e7c-48d4-99c2-79d63ca33481.JPG


测试:在Altera的EP2C8Q208C8上测试通过。


10、附件:


I2C总线驱动,V1.0


http://myfpga.googlecode.com/files/i2c_interface_v1.v


对24C02读写的测试程序


http://myfpga.googlecode.com/files/ctrl_24C02.v


用到的分频程序


http://myfpga.googlecode.com/files/clk_div.v


 


写在最后:


文中及文中提到的代码和设计全为Craftor原创,转载请保留作者信息。


文中代码仅供交流和学习使用,不得用于任何商业用途。


 

文章评论3条评论)

登录后参与讨论

用户124183 2011-11-2 11:58

IIC的连续读写,可以参考24C02的手册。

用户544061 2011-10-22 23:55

楼主,请问能否提供I2C总线驱动V1.0 的modelsim 仿真测试文件?

用户1374706 2011-9-7 21:59

Craftor您好,FPGA IIC连续读写的操作能否讲解一下呢,看了你的文章收货很大,非常感谢。

用户533944 2009-3-14 12:01

求图! 很想看看你的电路图,参考学习一下! 谢谢!

用户143781 2008-3-25 21:22

楼主啊,我想看看你的电路图,但找不到啊,想想办法呗
相关推荐阅读
用户124183 2010-09-03 10:42
4/8/16/32/64位乘法器的设计
4/8/16/32/64位乘法器的设计,单个时钟周期运算出结果。思路如下:4位乘法器a,b输入,y输出。使用case语句,对于输入a,y输出是b输入的16种可能。单个周期内可以输出结果。8位乘法器a,...
用户124183 2010-09-02 15:32
除法器的设计与仿真(Verilog&VHDL)
最近在做算法,要用到除法。本来想使用除法器的IP核,但发现Xilinx的除法器IP核是流水线的,如果是批量的数做除法,自然是很快,也很方便。而我的算法中需要将前一次的结果算出来之后,再到下一次运算里做...
用户124183 2010-08-06 09:58
Modelsim中添加Xilinx仿真库
不少朋友在刚接触Xilinx的FPGA时,对仿真库的编译和使用不是很了解,而官方的说明也不是很详细,而且看起来有些费劲,这里Craftor给出在Modelsim中编译和使用Xilinx库的详细教程,P...
用户124183 2010-08-02 22:51
周末写了个51单片机软核
为了弥补这段时间的罪过,这个周末决定做宅男,写程序。其实酝酿51软核已经有好几个月时间了,之前是因为在一个地方卡住了,中间停止了。上周五上班的时候突然来了灵感,一下子想通了,呵呵代码不长,1000多行...
用户124183 2010-06-19 15:41
深入研究Modelsim之使用do文件仿真(1)
Modelsim的仿真功能非常强大,本文介绍使用do脚本仿真单个Verilog或者VHDL文件的操作过程,希望对大家有用!以带时钟和复位信号的计数器为例,代码如下:module counter (  ...
我要评论
3
12
关闭 站长推荐上一条 /2 下一条