一、IIC基本概念
IIC 总线(I2C bus, Inter-IC bus)是一个双向的两线连续总线,提供集成电路(ICs)之间的通信线路。IIC总线是一种串行扩展技术,最早由Philips公司推出,广泛应用于电视,录像机和音频设备,IIC 总线的意思是“完成集成电路或功能单元之间信息交换的规范或协议”。 Philips 公司推出的 IIC 总线采用一条数据线(SDA),加一条时钟线(SCL)来完成数据的传输及外围器件的扩展。 如图1所示:
图1
二、主机和从机的概念
主机就是负责整个系统的任务协调与分配,从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。
三、传输速率
IIC 总线数据传输速率在标准模式下可达 100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。IIC总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输,IIC协议为半双工协议。
四、器件地址的概念
每个 IIC 器件都有一个器件地址,有的器件地址在出厂时地址就设置好了,用户不可以更改(例如 OV7670 器件地址为固定的 0x42),有的确定了几位,剩下几位由硬件确定(比如常见的 IIC 接口的 EEPROM 存储器,留有 3 个控制地
址的引脚,由用户自己在硬件设计时确定)。
五、总体时序简介
图2
在IIC器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原 因处于高电平状态,此时IIC总线处于空闲状态。如果主机(此处指FPGA)想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数 据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生 是在SCL为高电平时, SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。 IiC 整体时序如图2所示,起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传 输状态,主机可以向从机写数据, 也可以读取从机输出的数据,数据的传输由双向数据线( SDA) 完成。停止信号产生后,总线再次处于空闲状态。
由于IIC总线协议启动和停止信号都是在SCL高电平期间发生跳变的(当不发送或接收数据时,SCL一直处于高电平),这就决定了我们其他数据的改变只能发生在SCL低电平期间,在SCL为高电平期间,数据必须保持稳定。即在SCL低电平时改变数据,在SCL高电平时采集数据。
六、三态电路设计
主机(FPGA)给从机发送信号时,如传输启动信号,停止信号,数据位时,sda为输出;从机给主机发送信号,如ACK,读取数据时,sda充当输入,如图3所示;
图3
七、单字节写时序
图4 单字节写时序
由时序图可以看出,如果要向EEPROM 24C64中写入1字节,那么必须经过以下步骤。
(1)发送启动信号;
(2)发送器件地址(CONTROL BYTE);
(3)接收并检测EEPROM发送过来的应答信号(ACK);
(4)发送高字节地址位(ADDRESS HIGH BYTE);
(5)接收并检测EEPROM发送过来的应答信号(ACK);
(6)发送低字节地址位(ADDRESS LOW BYTE);
(7)接收并检测EEPROM发送过来的应答信号(ACK);
(8)发送8bit有效数据(DATA);
(9)接收并检测EEPROM发送过来的应答信号(ACK);
(10)发送停止信号(STOP);
八、单字节读时序
图5 单字节读时序
由时序图可以看出,如果要向EEPROM 24C64中读出1字节,那么必须经过以下步骤。
(1)发送启动信号;
(2)发送器件地址1010_0000(CONTROL BYTE);
(3)接收并检测EEPROM发送过来的应答信号(ACK);
(4)发送高字节地址位(ADDRESS HIGH BYTE);
(5)接收并检测EEPROM发送过来的应答信号(ACK);
(6)发送低字节地址位(ADDRESS LOW BYTE);
(7)接收并检测EEPROM发送过来的应答信号(ACK);
(8)发送启动信号;
(9)发送器件地址1010_0001(CONTROL BYTE);
(10)接收并检测EEPROM发送过来的应答信号(ACK);
(11)读取1字节的数据(DATA);
(12)发送NO_ACK信号;
(13)发送停止信号(STOP);
九、时序设计
(1)启动信号:在SCL为高电平期间,sda出现从高到低的跳变沿,代表启动信号;
(2)器件地址:EEPROM 24C64 的写时序时的器件地址为:8‘b1010_0000,读时序时的器件地址为:8‘b1010_0001;
(3)高、低位的字地址:由于24C64有64bit的存储空间,所以我们需要13位的地址位宽才能寻址所有的存储空间,由于IIC协议规定只能以字节形式写入,所以必须将13位的地址扩展为16位的地址,分为高8位和低8位,多出来的前三位填充任意数据即可。
(4)停止信号:在SCL为高电平期间,sda出现从低到高的跳变沿,代表停止信号;
(5)应答信号:应答信号是由数据接收方发出的,当SCL为高电平期间,如果监测到SDA为低电平,则说明有应答信号;
(6)非应答信号:非应答信号也是由数据接收方发出的,当SCL为高电平时,如果SDA为高电平,则说明有非应答信号;
九、代码设计(代码还未上板验证)
功能描述:按键读写EEPROM 24C64
1 module iic_test( 2 clk, 3 rst_n, 4 5 scl, 6 sda, 7 8 key_rd, 9 key_wr, 10 11 //data_in, 12 data_out 13 ); 14 15 input clk; 16 input rst_n; 17 18 input key_rd; 19 input key_wr; 20 //input[7:0] data_in; 21 22 output scl; 23 output reg[7:0] data_out; 24 25 inout sda; 26 27 wire flag; 28 reg sda_buffer; 29 reg scl_r; 30 31 reg[11:0] current_state,next_state; 32 33 parameter W_IDLE = 12‘d0; 34 parameter W_START = 12‘d1; 35 parameter W_DEVICE_ADD = 12‘d2; 36 parameter W_ACK1 = 12‘d3; 37 parameter W_WORD_H_ADD = 12‘d4; 38 parameter W_ACK2 = 12‘d5; 39 parameter W_WORD_L_ADD = 12‘d6; 40 parameter W_ACK3 = 12‘d7; 41 parameter W_DATA = 12‘d8; 42 parameter W_ACK4 = 12‘d9; 43 parameter W_STOP = 12‘d10; 44 45 parameter R_START = 12‘d11; 46 parameter R_DEVICE_ADD = 12‘d12; 47 parameter R_ACK1 = 12‘d13; 48 parameter R_DATA = 12‘d14; 49 parameter R_NO_ACK = 12‘d15; 50 parameter R_STOP = 12‘d16; 51 52 reg[1:0] en; 53 always@(posedge clk or negedge rst_n) // en信号设计 54 if(!rst_n) 55 en <= 2‘b00; 56 else begin 57 if(!key_wr) 58 en <= 2‘b01; 59 else begin 60 if(!key_rd) 61 en <= 2‘b10; 62 end 63 end 64 65 parameter SYS_CLOCK = 50_000_000; 66 parameter SCL_CLOCK = 400_000; 67 localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK; 68 69 reg[11:0] cnt; 70 always@(posedge clk or negedge rst_n) begin 71 if(!rst_n) 72 cnt <= 12‘d0; 73 else if(cnt == SCL_CNT_M - 1) 74 cnt <= 12‘d0; 75 else 76 cnt <= cnt + 1‘b1; 77 end 78 79 always@(posedge clk or negedge rst_n) begin 80 if(!rst_n) 81 scl_r <= 1‘b1; 82 else if(cnt == SCL_CNT_M >> 1) 83 scl_r <= 1‘b0; 84 else if(cnt == 12‘d0) 85 scl_r <= 1‘b1; 86 else 87 scl_r <= scl_r; 88 end 89 90 wire scl_l; 91 wire scl_h; 92 assign scl_h = (cnt == SCL_CNT_M >> 2) ? 1‘b1 : 1‘b0; // scl 高电平的中间 93 94 assign scl_l = (cnt == (SCL_CNT_M >> 1) + (SCL_CNT_M >> 2)) ? 1‘b1 : 1‘b0; // scl低电平的中间 95 96 reg[4:0] count; 97 reg[7:0] memory; 98 always@(posedge clk or negedge rst_n) // 同步时序,次态寄存器迁移到现态寄存器 99 if(!rst_n) 100 current_state <= W_IDLE; 101 else if(scl_l) 102 current_state <= next_state; 103 104 always@(*) begin // 状态转移条件判断 105 next_state = W_IDLE; 106 case(current_state) 107 W_IDLE:begin 108 if(en != 2‘b00) 109 next_state = W_START; 110 else 111 next_state = W_IDLE; 112 end 113 114 W_START:begin 115 if(scl_l) 116 next_state = W_DEVICE_ADD; 117 else 118 next_state = W_START; 119 end 120 121 W_DEVICE_ADD:begin 122 if((count == 5‘d8) && (scl_l)) 123 next_state = W_ACK1; 124 else 125 next_state = W_DEVICE_ADD; 126 end 127 128 W_ACK1:begin 129 if(scl_l) 130 next_state = W_WORD_H_ADD; 131 else 132 next_state = W_ACK1; 133 end 134 135 W_WORD_H_ADD:begin 136 if((count == 5‘d8) && (scl_l)) 137 next_state = W_ACK2; 138 else 139 next_state = W_WORD_H_ADD; 140 end 141 142 W_ACK2:begin 143 if(scl_l) 144 next_state = W_WORD_L_ADD; 145 else 146 next_state = W_ACK2; 147 end 148 149 W_WORD_L_ADD:begin 150 if((count == 5‘d8) && (scl_l)) 151 next_state = W_ACK3; 152 else 153 next_state = W_WORD_L_ADD; 154 end 155 156 W_ACK3:begin 157 if(scl_l) begin 158 if(en == 2‘b01) 159 next_state = W_DATA; 160 else if(en == 2‘b10) 161 next_state = R_START; 162 end 163 else 164 next_state = W_ACK3; 165 end 166 167 W_DATA:begin 168 if((count == 5‘d8) && (scl_l)) 169 next_state = W_ACK4; 170 else 171 next_state = W_DATA; 172 end 173 174 W_ACK4:begin 175 if(scl_l) 176 next_state = W_STOP; 177 else 178 next_state = W_ACK4; 179 end 180 181 W_STOP:begin 182 if(scl_l) 183 next_state = W_IDLE; 184 else 185 next_state = W_STOP; 186 end 187 188 R_START:begin 189 if(scl_l) 190 next_state = R_DEVICE_ADD; 191 else 192 next_state = R_START; 193 end 194 195 R_DEVICE_ADD:begin 196 if((count == 5‘d8) && (scl_l)) 197 next_state = R_ACK1; 198 else 199 next_state = R_DEVICE_ADD; 200 end 201 202 R_ACK1:begin 203 if(scl_l) 204 next_state = R_DATA; 205 else 206 next_state = R_ACK1; 207 end 208 209 R_DATA:begin 210 if((count == 5‘d8) && (scl_l)) 211 next_state = R_NO_ACK; 212 else 213 next_state = R_DATA; 214 end 215 216 R_NO_ACK:begin 217 if(scl_l) 218 next_state = R_STOP; 219 else 220 next_state = R_NO_ACK; 221 end 222 223 R_STOP:begin 224 if(scl_l) 225 next_state = W_IDLE; 226 else 227 next_state = R_STOP; 228 end 229 230 default:next_state = W_IDLE; 231 endcase 232 end 233 234 always@(posedge clk or negedge rst_n) // 状态输出 235 if(!rst_n) begin 236 sda_buffer <= 1‘b1; 237 count <= 5‘d0; 238 //data_out <= 8‘b00000000; 239 memory <= 8‘b00000000; 240 end 241 else if(scl_l) begin 242 case(next_state) 243 W_IDLE:begin 244 sda_buffer <= 1‘b1; 245 end 246 247 W_START:begin 248 sda_buffer <= 1‘b0; //起始位 249 count <= 5‘d0; 250 memory <= 8‘b10100000; // 写器件地址 251 end 252 253 W_DEVICE_ADD:begin 254 count <= count + 1‘b1; 255 sda_buffer <= memory[3‘d7 - count]; 256 end 257 258 W_ACK1:begin 259 count <= 5‘d0; 260 memory <= 8‘b00000000; // 高八位字地址 261 end 262 263 W_WORD_H_ADD:begin 264 count <= count + 1‘b1; 265 sda_buffer <= memory[3‘d7 - count]; 266 end 267 268 W_ACK2:begin 269 count <= 5‘d0; 270 memory <= 8‘b01010011; // 低八位的字地址 271 end 272 273 W_WORD_L_ADD:begin 274 count <= count + 1‘b1; 275 sda_buffer <= memory[3‘d7 - count]; 276 end 277 278 W_ACK3:begin 279 count <= 5‘d0; 280 memory <= 8‘b11110000; // 写数据 281 end 282 283 W_DATA:begin 284 count <= count + 1‘b1; 285 sda_buffer <= memory[3‘d7 - count]; 286 end 287 288 W_ACK4:begin 289 count <= 5‘d0; 290 end 291 292 W_STOP:begin 293 sda_buffer <= 1‘b1; 294 end 295 296 R_START:begin 297 sda_buffer <= 1‘b0; 298 memory <= 8‘b10100001; // 读器件地址 299 count <= 5‘d0; 300 end 301 302 R_DEVICE_ADD:begin 303 count <= count + 1‘b1; 304 sda_buffer <= memory[3‘d7 - count]; 305 end 306 307 R_ACK1:begin 308 count <= 5‘d0; 309 end 310 311 R_DATA:begin 312 count <= count + 1‘b1; 313 //data_out <= {data_out[6:0],sda}; //memory[5‘d7 - count] <= sda; // data_out[5‘d7 - count] <= sda; data_out[5‘d7 - count] <= sda; 314 end 315 316 R_NO_ACK:begin 317 sda_buffer <= 1‘b1; 318 end 319 320 R_STOP:begin 321 sda_buffer <= 1‘b0; 322 end 323 324 default:begin 325 count <= 5‘d0; 326 sda_buffer <= 1‘b1; 327 memory <= 8‘b00000000; 328 end 329 endcase 330 end 331 else begin 332 sda_buffer <= sda_buffer; 333 count <= count; 334 end 335 336 always@(posedge clk or negedge rst_n) 337 if(!rst_n) 338 data_out <= 8‘b00000000; 339 else if(scl_h) begin 340 case(next_state) 341 R_DATA:data_out[3‘d7 - count] <= sda; 342 default:; 343 endcase 344 end 345 else 346 data_out <= data_out; 347 348 assign scl = ((current_state >= W_DEVICE_ADD && current_state <= W_ACK4) || 349 (current_state >= R_DEVICE_ADD && current_state <= R_NO_ACK)) ? scl_r : 1‘b1; 350 351 assign flag =((current_state == W_ACK1)|| 352 (current_state == W_ACK2) || 353 (current_state == W_ACK3) || 354 (current_state == W_ACK4) || 355 (current_state == R_ACK1) || 356 (current_state == R_DATA) 357 358 ) ? 1‘b0 : 1‘b1; 359 360 assign sda = flag ? sda_buffer : 1‘bz; 361 362 endmodule
测试代码
1 `timescale 1s/1ps 2 module iic_test_tb; 3 reg clk; 4 reg rst_n; 5 6 reg key_rd; // 低电平有效 7 reg key_wr; // 低电平有效 8 9 wire[7:0] data_out; 10 wire scl; 11 wire sda; 12 13 iic_test u0( 14 .clk(clk), 15 .rst_n(rst_n), 16 17 .scl(scl), 18 .sda(sda), 19 20 .key_wr(key_wr), 21 .key_rd(key_rd), 22 .data_out(data_out) 23 ); 24 25 initial 26 clk = 1‘b0; 27 always #10 clk = ~clk; 28 29 initial 30 begin 31 rst_n = 1‘b0; 32 key_rd = 1‘b1; 33 key_wr = 1‘b1; 34 #1000; 35 rst_n = 1‘b1; 36 37 #800; 38 #8000 key_wr = 1‘b0; 39 #40000; 40 key_wr = 1‘b1; 41 42 #1000000; 43 key_rd = 0; 44 #40000; 45 key_rd = 1‘b1; 46 47 #1000000; 48 $stop; 49 end 50 endmodule 51 52
仿真结果
(1)写时序
写时序存在的一些问题,在停止信号上没有完全符合在SCL的高电平时,sda由0变为1。解决办法是在停止信号之前,再加一个状态,在这个状态中,令scl处于低电平的中间时,sda_buffer为0,这样在停止信号时,就可以由0变1.
(2)读时序
读时序也存在一些问题,停止信号出现了和写时序时同样的问题,没有完全符合预期,数据接收部分全为高阻状态。
还有一种IIC驱动的写法,和调用仿真模型的来测试iic驱动程序,留待更新。
注:
(1)文字叙述方面参考了小梅哥,正点原子,至芯科技的相关文章;
(2)代码设计参考了小梅哥,CrazyBingo的设计思路;
原文地址:https://www.cnblogs.com/571328401-/p/12639976.html