[转帖] I2C控制问题

 2013-02-19   15:50:37

 I2C总线器件应用实例  http://wenku.baidu.com/view/98030a110b4e767f5acfce50.html 讲得通俗易懂。

I2C_Master和I2C_Slave两种控制方式。对于Slave而言,SCL是输入信号。对于Master而言,SCL是输出信号。都涉及到从器件地址、寄存器地址和数据(双向传输)。本设计中I2C_Master用于控制PCA9634芯片。

PCA9634是一款通过I2C总线控制的8位LED驱动器,该驱动器特别为红/绿/蓝/琥珀(RGBA)色的混合应用进行了优化。每个LED输出都有自己的8位分辨率(256级)固定频率的独立PWM控制器,该控制器运行在97KHz的频率下,占空比可由0%到99.6%可调,用以将LED设置到一个特定的亮度值。除此之外,该驱动器还有一个8位分辨率(256级)的组PWM控制器,该控制器的工作频率可以为固定的190Hz,也可以在24Hz和每10.73秒一次(约0.093Hz)之间调整,其占空比为0%到99.6%可调,用于使所有LED以同样的值模糊(dim)或者闪烁。


来源:http://www.cnblogs.com/yuphone/archive/2009/12/06/1617923.html I2C时序讲解

http://wenku.baidu.com/view/70e1f5a2b0717fd5360cdc26.html

I2C时钟频率定义如下:

若I2C只对寄存器的某几位有要求,其他位没说,就应该通过I2C读取该寄存口的值,然后对不需要处理的位原封不动写入,需要处理的位处理下就OK。
这里采用I2C的时钟频率用400KHZ
参考程序来自:http://wenku.baidu.com/view/d715b00cba1aa8114431d9c4.html 写得很好。
 

来源:http://www.cnblogs.com/yuphone/archive/2010/11/01/1866662.html

一般情况下SDA为双向口,为了防止输入和输出冲突,SDA和SCL总线上都会接上拉电阻来保护电路。

对于主机来说,要使得SDA作为输入口,就必须先将其拉成高阻态。而SDA作为输出口的情况下,就有一个技巧,不仔细还真有点难以捉摸:主机输出高电平,可以直接输出高电平;也可以输出高阻,由于接有上拉电阻,因此从机会接收到高电平。而主机输出想要输出低电平,为了摆脱上拉电阻的束缚,就必须直接输出低电平。

因此,当SDA为高阻态时,有可能是做为输入口,也有可能做为输出口且输出为1。因此,最好分开写以便理解。

wire I2C_SDAT=SDO?1'bz:0 ;

------------------SDO是作为输出使能信号,且高阻态是1时(因此有上拉电阻)--------

  //SLAVE ADDR 
        6'd3  : SDO=SD[31];
        6'd4  : SDO=SD[30];
        6'd5  : SDO=SD[29];

------------------SDO是作为输入使能信号,此时高阻态不变--------

  6'd20  : SDO=1'b1;//ACK

  //DATA 
  6'd21  : begin SDO=SD[15]; ACK2=I2C_SDAT; end

image

MyFPGA Forum» Altera 开发板卡应用 » DE2 » de2 demo 中I2C_Controller 问题

http://www.myfpga.org/discuz/viewthread.php?tid=133

首先,从代码上看这个代码应该是i2c主机模式的代码。你再认真看一下datasheet,i2c中,主机向从机发送任何数据(包括地址),从机在接受到之后都要有响应,而这个响应就是在数据最后一位结束后产生的。

现在解释一下响应的作用,主机在向从机发送数据后,如果从机没有返回任何信息的话,主机是不知道从机有没有接受到刚才发送的这个数据,因此为了让主机知道从机到底有没有接收到,就用这个响应信号来通知主机。即当主机发送完地址,从机在接收到这个地址(并且确认是自己的从机地址之后),从机就要发送一个响应信号来通知主机让主机知道从机已经接收到了(如果没有接收到,从机就不会发送这个响应信号)。同样的,主机发送完一个8位数据,从机在接收到之后也会有响应信号。

接下来再看这段代码,wire I2C_SDAT=SDO?1'bz:0 ;在结合源程序中,每次传输8位数据后有一个SDO=1'b1;可以知道当sdo等于一时,i2c_sdat为高阻态,这个时候i2c_sdat的值就等于输入值(因为i2c-sdat是I/O口,当i2c-sdat为高阻态时,它就是一个输入口),也就是从机发送过来的数值。然后再结合这句 6'd12  : begin SDO=SD[15]; ACK1=I2C_SDAT; end   ,于是ack1就等于之前一个周期的i2c_sdat的输入值,也就是状态‘ 11’6'd11 : SDO=1'b1;时从机发送过来的值,而这个从机发送过来的值是在主机发送完地址后发送的,也就是从机对地址的一个响应信号。于是ack2,ack3可以以此类推。

来源:http://groups.google.com/group/de2-dev/msg/c50c75a1f31d047f?hl=zh-TW

以下是D5M中的de2_camera中的i2c_controller.v源文件:
module I2C_Controller (
        CLOCK,
        I2C_SCLK,//I2C CLOCK
        I2C_SDAT,//I2C DATA
        I2C_DATA,//DATA:[SLAVE_ADDR,SUB_ADDR,DATA]
        GO,      //GO transfor
        END,     //END transfor

        ACK,      //ACK
        RESET
);
        input  CLOCK;
        input  [31:0]I2C_DATA;
        input  GO;
        input  RESET;
        inout  I2C_SDAT;
        output I2C_SCLK;
        output END;
        output ACK;

reg SDO;
reg SCLK;
reg END;
reg [31:0]SD;
reg [6:0]SD_COUNTER;

wire I2C_SCLK=SCLK | ( ((SD_COUNTER >= 4) & (SD_COUNTER <=39))?
~CLOCK :0 );
wire I2C_SDAT=SDO?1'bz:0 ;

reg ACK1,ACK2,ACK3,ACK4;
wire ACK=ACK1 | ACK2 |ACK3 |ACK4;

//--I2C COUNTER
always @(negedge RESET or posedge CLOCK ) begin
if (!RESET) SD_COUNTER=6'b111111;
else begin
if (GO==0)
        SD_COUNTER=0;
        else
        if (SD_COUNTER < 41) SD_COUNTER=SD_COUNTER+1;
end
end
//----

always @(negedge RESET or  posedge CLOCK ) begin
if (!RESET) begin SCLK=1;SDO=1; ACK1=0;ACK2=0;ACK3=0;ACK4=0; END=1;
end
else
case (SD_COUNTER)
        6'd0  : begin ACK1=0 ;ACK2=0 ;ACK3=0 ;ACK4=0 ; END=0; SDO=1;
SCLK=1;end
        //start
        6'd1  : begin SD=I2C_DATA;SDO=0;end
        6'd2  : SCLK=0;
        //SLAVE ADDR
        6'd3  : SDO=SD[31];
        6'd4  : SDO=SD[30];
        6'd5  : SDO=SD[29];
        6'd6  : SDO=SD[28];
        6'd7  : SDO=SD[27];
        6'd8  : SDO=SD[26];
        6'd9  : SDO=SD[25];
        6'd10 : SDO=SD[24];
        6'd11 : SDO=1'b1;//ACK

        //SUB ADDR
        6'd12  : begin SDO=SD[23]; ACK1=I2C_SDAT; end
        6'd13  : SDO=SD[22];
        6'd14  : SDO=SD[21];
        6'd15  : SDO=SD[20];
        6'd16  : SDO=SD[19];
        6'd17  : SDO=SD[18];
        6'd18  : SDO=SD[17];
        6'd19  : SDO=SD[16];
        6'd20  : SDO=1'b1;//ACK

        //DATA
        6'd21  : begin SDO=SD[15]; ACK2=I2C_SDAT; end
        6'd22  : SDO=SD[14];
        6'd23  : SDO=SD[13];
        6'd24  : SDO=SD[12];
        6'd25  : SDO=SD[11];
        6'd26  : SDO=SD[10];
        6'd27  : SDO=SD[9];
        6'd28  : SDO=SD[8];
        6'd29  : SDO=1'b1;//ACK

        //DATA
        6'd30  : begin SDO=SD[7]; ACK3=I2C_SDAT; end
        6'd31  : SDO=SD[6];
        6'd32  : SDO=SD[5];
        6'd33  : SDO=SD[4];
        6'd34  : SDO=SD[3];
        6'd35  : SDO=SD[2];
        6'd36  : SDO=SD[1];
        6'd37  : SDO=SD[0];
        6'd38  : SDO=1'b1;//ACK

        //stop
    6'd39 : begin SDO=1'b0;     SCLK=1'b0; ACK4=I2C_SDAT; end
    6'd40 : SCLK=1'b1;
    6'd41 : begin SDO=1'b1; END=1; end

endcase
end
endmodule
在此想问一下wire I2C_SDAT=SDO?1'bz:0 ;语句的运行效果。
我的疑问是SD0是要输出的数据位,如果是1就要输出z,如果是0输出为0.这里的z究竟是什么状态?为什么一定要有这个语句呢?

原文地址:https://www.cnblogs.com/zlh840/p/2596935.html