-
除了电源和地线外,只使用两根电线(命名为“sda”和“scl”)。 -
可以在同一总线上支持100多个设备(总线上的每个设备都有一个可单独访问的地址) -
多主机(例如,两个CPU可以轻松地共享相同的I2C设备) -
行业标准(由 飞利浦,为许多其他制造商所采用) -
到处使用(电视,个人电脑.)
-
相对较慢(100 Kbps基本速度,扩展可达3.4Mbps) -
不是即插即用
它是如何工作的
I2C波形
-
在您的FPGA/CPLD中直接使用SCL线作为时钟信号 -
使用快速时钟对SDA和SCL信号进行过采样
I2C从站:Io扩展程序,使用方法1(SCL作为FPGA/CPLD中的时钟)
module I2CslaveWith8bitsIO(SDA, SCL, IOout); inout SDA; input SCL; output [7:0] IOout;
parameter I2C_ADR = 7'h27;
// We use two wires with a combinatorial loop to detect the start and stop conditions // ... making sure these two wires don't get optimized away wire SDA_shadow /* synthesis keep = 1 */; wire start_or_stop /* synthesis keep = 1 */; assign SDA_shadow = (~SCL | start_or_stop) ? SDA : SDA_shadow; assign start_or_stop = ~SCL ? 1'b0 : (SDA ^ SDA_shadow); reg incycle; always @(negedge SCL or posedge start_or_stop) if(start_or_stop) incycle <= 1'b0; else if(~SDA) incycle <= 1'b1;
reg [3:0] bitcnt; // counts the I2C bits from 7 downto 0, plus an ACK bit wire bit_DATA = ~bitcnt[3]; // the DATA bits are the first 8 bits sent wire bit_ACK = bitcnt[3]; // the ACK bit is the 9th bit sent reg data_phase; always @(negedge SCL or negedge incycle) if(~incycle) begin bitcnt <= 4'h7; // the bit 7 is received first data_phase <= 0; end else begin if(bit_ACK) begin bitcnt <= 4'h7; data_phase <= 1; end else bitcnt <= bitcnt - 4'h1; end
wire adr_phase = ~data_phase; reg adr_match, op_read, got_ACK; // sample SDA on posedge since the I2C spec specifies as low as 0µs hold-time on negedge reg SDAr; always @(posedge SCL) SDAr<=SDA; reg [7:0] mem; wire op_write = ~op_read; always @(negedge SCL or negedge incycle) if(~incycle) begin got_ACK <= 0; adr_match <= 1; op_read <= 0; end else begin if(adr_phase & bitcnt==7 & SDAr!=I2C_ADR[6]) adr_match<=0; if(adr_phase & bitcnt==6 & SDAr!=I2C_ADR[5]) adr_match<=0; if(adr_phase & bitcnt==5 & SDAr!=I2C_ADR[4]) adr_match<=0; if(adr_phase & bitcnt==4 & SDAr!=I2C_ADR[3]) adr_match<=0; if(adr_phase & bitcnt==3 & SDAr!=I2C_ADR[2]) adr_match<=0; if(adr_phase & bitcnt==2 & SDAr!=I2C_ADR[1]) adr_match<=0; if(adr_phase & bitcnt==1 & SDAr!=I2C_ADR[0]) adr_match<=0; if(adr_phase & bitcnt==0) op_read <= SDAr; // we monitor the ACK to be able to free the bus when the master doesn't ACK during a read operation if(bit_ACK) got_ACK <= ~SDAr; if(adr_match & bit_DATA & data_phase & op_write) mem[bitcnt] <= SDAr; // memory write end
wire mem_bit_low = ~mem[bitcnt[2:0]]; wire SDA_assert_low = adr_match & bit_DATA & data_phase & op_read & mem_bit_low & got_ACK; wire SDA_assert_ACK = adr_match & bit_ACK & (adr_phase | op_write); wire SDA_low = SDA_assert_low | SDA_assert_ACK; assign SDA = SDA_low ? 1'b0 : 1'bz; assign IOout = mem; endmodule
结果是什么?
-
FPGA/CPLD采用SCL信号作为时钟。使用a 施密特触发器强烈建议在SCL输入引脚上避免异常行为(如果没有Schmitt触发器,SCL线路上的任何噪声或振铃都会引入额外的时钟周期,从而破坏功能)。 -
开始和停止条件检测逻辑使用组合反馈循环,这不是推荐的做法。在“InCycle”信号上也是一样的,它被逻辑的其余部分用作异步重置。这是为了避免使用过采样时钟而付出的代价。