一、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)讀時序
讀時序也存在一些問題,停止信號出現了和寫時序時同樣的問題,沒有完全符合預期,數據接收部分全為高阻狀態。
注:
(1)文字敘述方面參考了小梅哥,正點原子,至芯科技的相關文章;
(2)代碼設計參考了小梅哥,CrazyBingo的設計思路;
下面的代碼為至芯科技的源碼(最初的代碼無法仿真成功,下面是我做了小小修改的代碼)

1 // Time : 2020.03.19 22:59 2 // Describe : iic_driver 3 module iic_driver( 4 clk, 5 rst_n, 6 7 iic_scl, 8 iic_sda, 9 10 key_wr, 11 key_rd, 12 13 data_in, 14 data_out 15 ); 16 17 input clk; 18 input rst_n; 19 20 input key_rd; 21 input key_wr; 22 23 input[7:0] data_in; 24 25 inout iic_sda; 26 27 output reg iic_scl; 28 output reg[7:0] data_out; 29 30 reg[7:0] sda_buffer; 31 reg flag; 32 33 assign iic_sda = (flag) ? sda_buffer : 1'bz; 34 35 reg[7:0] count; 36 reg clk_sys; 37 38 always@(posedge clk or negedge rst_n) // 分頻為800KHz的時鍾 39 if(!rst_n) begin 40 clk_sys <= 1'b0; 41 count <= 8'd0; 42 end 43 else begin 44 if(count < 31) 45 count <= count + 1'b1; 46 else begin 47 count <= 8'd0; 48 clk_sys <= ~clk_sys; 49 end 50 end 51 52 reg[5:0] state; // 狀態寄存器 53 54 always@(posedge clk_sys or negedge rst_n) // iic_scl設計 55 if(!rst_n) 56 iic_scl <= 1'b1; 57 else begin 58 if(state > 0) // 當總線忙的時候,iic_scl為近400kHz的時鍾 59 iic_scl <= ~iic_scl; 60 else 61 iic_scl <= 1'b1; // 空閑時為高電平 62 end 63 64 reg[1:0] en; 65 always@(posedge clk or negedge rst_n) // en信號設計 66 if(!rst_n) 67 en <= 2'b00; 68 else begin 69 if(!key_wr) 70 en <= 2'b01; 71 else begin 72 if(!key_rd) 73 en <= 2'b10; 74 end 75 end 76 77 reg[3:0] cnt; // 發送或接收數據的個數 78 reg[1:0] temp; // 讀/寫使能中間寄存器 79 reg[7:0] memory; // 發送或接收數據的中間寄存器 80 81 always@(posedge clk_sys or negedge rst_n) 82 if(!rst_n) begin 83 data_out <= 8'd0; 84 flag <= 1'b1; //復位時,系統獲得總線的控制權,作為輸出端口 85 sda_buffer <= 1'b1; //向 iic_scl 發送高電平 86 state <= 0; 87 temp <= 2'b0; 88 end 89 else 90 case(state) 91 0:begin 92 if(iic_scl) begin 93 if(en != temp) begin // 有按鍵按下 94 sda_buffer <= 1'b0; // 發送啟動信號 95 state <= 1; 96 temp <= en; 97 cnt <= 0; 98 memory <= 8'b10100000; 99 end 100 else 101 state <= 0; 102 end 103 else 104 state <= 0; 105 end 106 107 1:begin 108 if((iic_scl == 0) && (cnt < 8)) begin // 發送8位控制字,器件地址 109 sda_buffer <= memory[7]; 110 cnt <= cnt + 1'b1; 111 memory <= {memory[6:0],memory[7]}; 112 state <= 1; 113 end 114 else begin 115 if((iic_scl == 0) && (cnt == 8)) begin 116 cnt <= 0; 117 flag <= 0; // 釋放總線控制權 118 state <= 2; 119 end 120 else 121 state <= 1; 122 end 123 end 124 125 2:begin 126 if(!iic_scl) begin // 檢測應答信號 127 state <= 3; 128 memory <= 8'd0; // 高字節地址 129 end 130 else 131 state <= 2; 132 end 133 134 3:begin // 發送高字節地址 135 if((iic_scl == 0) && (cnt < 8 )) begin 136 flag <= 1'b1; // 系統獲得總線控制權 137 sda_buffer <= memory[7]; 138 cnt <= cnt + 1'b1; 139 memory ={memory[6:0],memory[7]}; 140 state <= 3; 141 end 142 else begin 143 if((iic_scl == 0) && (cnt == 8)) begin 144 cnt <= 0; 145 flag <= 0; // 釋放總線控制權 146 state <= 4; 147 end 148 else 149 state <= 3; 150 end 151 end 152 153 4:begin 154 if(!iic_scl) begin // 檢測應答信號 155 state <= 5; 156 memory <= 8'b00110101; // 低字節地址 157 end 158 else begin 159 state <= 4; 160 end 161 end 162 163 5:begin 164 if((iic_scl == 0) && (cnt < 8)) begin //發送低字節地址 165 flag <= 1'b1; // 獲得總線控制權 166 sda_buffer <= memory[7]; 167 cnt <= cnt + 1'b1; 168 memory = {memory[6:0],memory[7]}; 169 state <= 5; 170 end 171 else begin 172 if((iic_scl == 0) && (cnt == 8)) begin 173 cnt <= 0; 174 flag <= 0; // 釋放總線控制權 175 state <= 6; 176 end 177 else 178 state <= 5; 179 end 180 end 181 182 6:begin 183 if(!iic_scl) begin // 檢測應答信號 184 if(temp == 2'b01) begin // 判斷是否為寫信號 185 state <= 7; 186 memory = data_in[7:0]; // 發送數據 187 end 188 if(temp == 2'b10) // 判斷是否為讀信號 189 state <= 11; 190 end 191 else 192 state <= 6; 193 end 194 195 7:begin 196 if((iic_scl == 0) && (cnt < 8)) begin // 發送數據 197 flag <= 1'b1; // 獲得總線控制權 198 sda_buffer <= memory[7]; 199 cnt <= cnt + 1'b1; 200 memory = {memory[6:0],memory[7]}; 201 state <= 7; 202 end 203 else begin 204 if((iic_scl == 0) && (cnt == 8)) begin 205 cnt <= 0; 206 flag <= 0; // 釋放總線控制權 207 state <= 8; 208 end 209 else 210 state <= 7; 211 end 212 end 213 214 8:begin 215 if(!iic_scl) //檢測應答信號 216 state <= 9; 217 else 218 state <= 8; 219 end 220 221 9:begin 222 if(iic_scl == 0) begin 223 flag <= 1; //獲得總線控制權 224 sda_buffer <= 0; //拉低IIC的數據線,為發送停止信號做准備 225 state <= 10; 226 end 227 else 228 state <= 9; 229 end 230 231 10:begin 232 if(iic_scl == 1) begin 233 sda_buffer <= 1; // 發送停止信號 234 state <= 0; 235 end 236 else 237 state <= 10; 238 end 239 //--------------------------------------// 240 11:begin 241 flag <= 1; //獲得總線控制權 242 sda_buffer <= 1; // 拉高IIC控制線(為發送啟動信號做准備) 243 state <= 12; 244 end 245 246 12:begin 247 sda_buffer <= 0; // 發送啟動信號 248 state <= 13; 249 memory <= 8'b10100001; 250 end 251 252 13:begin 253 if((iic_scl == 0) && (cnt < 8)) begin // 發送8位控制字 254 flag <= 1; // 獲得總線控制權 255 sda_buffer <= memory[7]; 256 cnt <= cnt + 1'b1; 257 memory <= {memory[6:0],memory[7]}; 258 state <= 13; 259 end 260 else begin 261 if((iic_scl == 0) && (cnt == 8)) begin 262 cnt <= 0; 263 flag <= 0; // 釋放總線控制權 264 state <= 14; 265 end 266 else 267 state <= 13; 268 end 269 end 270 271 14:begin 272 if(!iic_scl) // 檢測應答信號 273 state <= 15; 274 else 275 state <= 14; 276 end 277 278 15:begin 279 if((iic_scl == 1) && (cnt < 8)) begin // 接收數據 280 cnt <= cnt + 1'b1; 281 memory <= {memory[6:0],iic_sda}; 282 end 283 else begin 284 if((iic_scl == 0) && (cnt == 8)) begin 285 cnt <= 0; 286 flag <= 1; // 獲得總線控制權 287 state <= 16; 288 sda_buffer <= 1; // 發送應答信號 289 end 290 else 291 state <= 15; 292 end 293 end 294 295 16:begin 296 data_out <= memory; // 發送數據 297 state <= 17; 298 end 299 300 17:begin 301 if(iic_scl == 0) begin 302 sda_buffer <= 0; //拉低IIC的數據線,為發送停止信號做准備 303 state <= 18; 304 end 305 else 306 state <= 17; 307 end 308 309 18:begin // 發送停止信號 310 if(iic_scl == 1) begin 311 sda_buffer <= 1; 312 state <= 0; 313 end 314 else 315 state <= 18; 316 end 317 318 default: state <= 0; 319 endcase 320 321 endmodule 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
測試代碼

1 `timescale 1ns/1ps 2 module iic_driver_tb; 3 reg clk; 4 reg rst_n; 5 6 reg key_rd; 7 reg key_wr; 8 9 reg[7:0] data_in; 10 11 wire iic_sda; 12 13 wire iic_scl; 14 wire[7:0] data_out; 15 16 iic_driver u0( 17 .clk(clk), 18 .rst_n(rst_n), 19 20 .iic_scl(iic_scl), 21 .iic_sda(iic_sda), 22 23 .key_wr(key_wr), 24 .key_rd(key_rd), 25 26 .data_in(data_in), 27 .data_out(data_out) 28 ); 29 30 initial 31 clk = 0; 32 always #10 clk = ~clk; 33 34 initial 35 begin 36 rst_n = 1'b0; 37 key_rd = 1'b1; 38 key_wr = 1'b1; 39 data_in = 1'b0; 40 41 #1000; 42 rst_n = 1'b1; 43 44 #800; 45 46 #8000 key_wr = 1'b0; 47 data_in = 8'h23; 48 #4000 key_wr = 1'b1; 49 50 #1000000; 51 key_rd = 1'b0; 52 #40000; 53 key_rd = 1'b1; 54 55 #10000000; 56 $stop; 57 end 58 endmodule
仿真結果:
(1)寫時序
(2)讀時序