寫在前面的話
IIC的通信協議和通信接口在很多工程中有廣泛的應用,如數據采集領域的串行AD,圖像處理領域的攝像頭配置,工業控制領域的X射線管配置等等。除此之外,由於IIC協議占用的IO資源特別少,連接方便,所以工程中也常選用IIC接口做為不同芯片間的通信協議。
IIC協議的完成靠的是嚴緊的時序,一個周期都不能錯,這也正是夢翼師兄設置本實驗的目的。通過IIC的學習,鍛煉大家的時序實現技巧和方法,可以說最佳的案例之一。
項目需求
設計IIC接口的驅動電路,可以實現單字節數據的讀寫。
IIC的原理分析
本實驗,我們采用的外部接口芯片為EEPROM 24LC64,其封裝圖如下:
接下來,我們梳理一下各引腳定義:
A0,A1,A2為24LC64的片選信號,由於IIC總線可以掛載多個IIC接口器件,所以每個器件都應該有自己的“身份標識”,通過對A0,A1,A2輸入不同的高低電平,就可以設置該EEPROM的片選信號。
WP為讀寫使能信號,當WP懸空或者接地,EEPROM可讀可寫,當WP接電源,EEPROM只能讀不能寫。
SCL為IIC接口的時鍾線(根據不同的配置方式,scl的頻率是可以改變的,我們采取200K)
SDA為IIC接口的數據線
得到了上面的配置信息,那么接下來,看一下我們開發板的配置原理圖
由此可以看出我們的位選信號為“000”,EEPROM可讀寫。而我們最需要關心的,就是SCL和SDA兩條線的時序關系。
原理圖分析完畢,接下來我們學習一下IIC接口的具體時序是什么。IIC讀寫時序分為隨機讀寫和頁面讀寫,也就是常說的Byte Write/Read 和Page Write/Read。
我們首先來學習Byte Write/Read時序。Byte Write時序如下:
由時序圖可以看出,如果我們要向EEPROM寫入一個字節,那么必須經過以下步驟:
發送啟動信號
發送控制字
接收並檢測EEPROM發來的應答信號ACK
發送高字節地址位
接收並檢測EEPROM發來的應答信號ACK
發送低字節地址位
接收並檢測EEPROM發來的應答信號ACK
發送8bit有效數據
接收並檢測EEPROM發來的應答信號ACK
10.發送停止信號
Byte Read時序如下:
由時序圖可以看出,如果我們要從EEPROM讀出一個字節,那么必須經過以下步驟:
1. 發送啟動信號
- 發送控制字1010_0000
- 接收並檢測EEPROM發來的應答信號ACK
- 發送高字節地址位
- 接收並檢測EEPROM發來的應答信號ACK
- 發送低字節地址位
- 接收並檢測EEPROM發來的應答信號ACK
- 發送啟動信號
- 發送控制字1010_0001
- 接收並檢測EEPROM發來的應答信號ACK
- 讀取一個字節數據
- 發送NO ACK信號
- 發送停止信號
那么現在的問題就變成了每個步驟的意義到底是什么呢?各位且聽我慢慢道來。
1.啟動信號
在SCL保持高電平期間,如果SDA出現由高到低的跳變沿,代表啟動信號
2.控制字
我們的控制字為1010_0000或1010_0001,其中1010為EEPROM的型號標識,為一組固定的序列,緊接着A2,A1,A0就是我們的片選信號,最后一位為讀寫控制位,低電平代表寫,高電平代表讀,我們這里首先需要對EEPROM寫入地址位,所以我們最后一位為0。當我們需要讀數據時,最后一位為1。
高/低位地址
由於24LC64有64Kbit的存儲空間,所以我們需要13位的地址位寬才能尋址所有的存儲空間,由於IIC協議規定只能以字節形式寫入,所以必須將13位的地址擴展為16位的地址,分為高八位和低八位,多出來的前三位填充任意數據即可,對我們的尋址地址沒有影響。
3.停止信號
在SCL保持高電平期間,如果SDA出現由低到高的跳變沿,代表停止信號
4.應答信號ACK
應答信號是由數據接收方發出的,當SCL為高電平期間,如果監測到SDA為低電平,說明有應答信號。
5.非應答信號NO ACK
非應答信號也是由數據接收方發出的,當SCL為高電平期間,如果SDA為高電平,說明有非應答信號。
由於IIC總線協議啟動和停止信號都是在SCL高電平期間發生跳變(當不發送或者接收數據的時候scl一直為高電平),這就決定了我們其他數據的改變只能發生在SCL低電平期間,在SCL為高電平期間,數據必須保持穩定。即在SCL低電平改變數據,在SCL高電平采集數據。
由於讀時序和寫時序一直到低字節地址的寫入之前都是相同的,因此我們設置IIC控制器流程圖如下:
接下來,我們來學習Page Write/Read時序
Page Write時序如下:
Page Read時序如下:
由此可以看出,頁面讀寫比隨機讀寫只是多加了幾個狀態而已,在我們前面設計的基礎上加幾個狀態就可以完成。夢翼師兄把這部分交給大家去發揮。
架構設計
根據原理分析,我們設計出架構圖如下:
模塊功能介紹
模塊名 |
功能描述 |
IIC |
iic總線的驅動 |
頂層模塊端口描述
端口名 |
端口說明 |
clk |
系統時鍾輸入 |
rst_n |
系統復位 |
key_wr |
寫信號(低電平有效) |
key_rd |
讀信號(低電平有效) |
data_in[7:0] |
輸入數據 |
scl |
iic的時鍾線 |
sda |
iic的數據線 |
data_out[7:0] |
輸出數據 |
代碼解釋
iic模塊代碼
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function: 產生iic總線的控制信號 *****************************************************/ 000 module iic( 001 clk, //外部輸入時鍾 002 rst_n,//系統復位 003 key_wr,//寫信號(低電平有效) 004 key_rd, //讀信號(低電平有效) 005 data_in, //輸入數據 006 scl, //iic的數據線 007 sda, //iic的時鍾線 008 data_out//輸出數據 009 ); 010 //系統輸入 011 input clk;//外部輸入時鍾 012 input rst_n;//系統復位 013 input key_wr;//寫信號(低電平有效) 014 input key_rd;//讀信號(低電平有效) 015 input [7:0] data_in;//輸入數據 016 //系統輸出 017 output reg scl;//iic的時鍾線 018 output reg [7:0] data_out;//輸出數據 019 020 inout sda; //iic的數據線 021 022 reg sda_buffer;//寫入數據的中間寄存器 023 reg flag;//控制系統是否占有總線控制權 024 025 assign sda = (flag) ? sda_buffer : 1'bz;//當flag為高電平時,系統擁有總線控制權 026 //並發送sda_buffer中的數據。當flag為低電平時, 027 //釋放總線。 028 029 reg [7:0] count;//計數器 030 reg clk_sys;//系統時鍾 031 //-------------------------------clk_sys 032 always @ (posedge clk or negedge rst_n) 033 begin 034 if (!rst_n) 035 begin 036 clk_sys <= 1'b0; 037 count <= 8'd0; 038 end 039 else 040 if (count < 31)//分頻成為近800K的時鍾 041 count <= count + 1; 042 else 043 begin 044 count <= 8'd0; 045 clk_sys <= ~clk_sys; 046 end 047 end 048 //------------------------------- 049 050 reg [5:0] state;//狀態寄存器 051 052 //--------------------------------scl 053 always @ (negedge clk_sys or negedge rst_n) 054 begin 055 if (!rst_n) 056 begin 057 scl <= 1'b1;//復位時,scl為高 058 end 059 else 060 begin 061 if (state > 0)//當總線忙的時候,scl為近400K的時鍾 062 scl <= ~scl; 063 else 064 scl <= 1'b1;//空閑時,scl為高 065 end 066 end 067 //---------------------------------- 068 069 reg [1:0] en;//讀寫使能中間寄存器 070 071 //----------------------------------enable 072 always @ (posedge clk or negedge rst_n) 073 begin 074 if (!rst_n) 075 begin 076 en <= 2'b00;//復位時,將中間寄存器置0; 077 end 078 else 079 begin 080 if (!key_wr)//寫有效時 081 en <= 2'b01; 082 else 083 if (!key_rd)//寫無效,讀有效時 084 en <= 2'b10; 085 end 086 end 087 //--------------------------------- 088 089 reg [3:0] cnt;//發送或者接收數據的個數 090 reg [1:0] temp;//讀寫使能的中間寄存器 091 reg [7:0] memory;//發送或者接受數據的中間寄存器 092 093 always @ (posedge clk_sys or negedge rst_n) 094 begin 095 if (!rst_n) 096 begin 097 data_out <= 8'd0; 098 flag <= 1'b1; //復位時,系統獲得總線的控制權 099 sda_buffer <= 1'b1; //向iic的數據線上發送高電平 100 state <= 0; 101 temp <= 2'b00; 102 end 103 else 104 case(state) 105 0 : begin 106 if(scl) 107 begin 108 if(en != temp)//有按鍵按下 109 begin 110 sda_buffer <= 1'b0;//發送啟動信號 111 state <= 1; 112 temp <= en;//將讀寫信號保存 113 memory <= 8'b10100000;//控制字 114 end 115 else 116 state <= 0; 117 end 118 else 119 state <= 0; 120 end 121 122 1 : begin 123 if((scl == 0) && (cnt < 8))//發送八位控制字 124 begin 125 sda_buffer <= memory[7]; 126 cnt <= cnt + 1; 127 memory = {memory[6:0],memory[7]}; 128 state <= 1; 129 end 130 else 131 begin 132 if ((scl == 0) && (cnt == 8)) 133 begin 134 cnt <= 0; 135 flag <= 0;//釋放總線控制權 136 state <= 2; 137 end 138 else 139 begin 140 state <= 1; 141 end 142 end 143 end 144 2 : begin 145 if(!sda)//檢測應答信號 146 begin 147 state <= 3; 148 memory <= 8'd0;//高字節地址 149 end 150 else 151 begin 152 state <= 0; 153 end 154 end 155 3 : begin //發送高字節地址 156 if((scl == 0) && (cnt < 8)) 157 begin 158 flag <= 1;//獲得總線控制權 159 sda_buffer <= memory[7]; 160 cnt <= cnt + 1; 161 memory = {memory[6:0],memory[7]}; 162 state <= 3; 163 end 164 else 165 begin 166 if ((scl == 0) && (cnt == 8)) 167 begin 168 cnt <= 0; 169 flag <= 0;//釋放總線控制權 170 state <= 4; 171 end 172 else 173 begin 174 state <= 3; 175 end 176 end 177 end 178 4 : begin 179 if(!sda)//檢測應答信號 180 begin 181 state <= 5; 182 memory <= 8'h00;//低字節地址 183 end 184 else 185 begin 186 state <= 0; 187 end 188 end 189 5 : begin 190 if((scl == 0) && (cnt < 8))//發送低字節地址 191 begin 192 flag <= 1;//獲得總線控制權 193 sda_buffer <= memory[7]; 194 cnt <= cnt + 1; 195 memory = {memory[6:0],memory[7]}; 196 state <= 5; 197 end 198 else 199 begin 200 if ((scl == 0) && (cnt == 8)) 201 begin 202 cnt <= 0; 203 flag <= 0;//釋放總線控制權 204 state <= 6; 205 end 206 else 207 begin 208 state <= 5; 209 end 210 end 211 end 212 6 : begin 213 if(!sda)//檢測應答信號 214 begin 215 if (temp == 2'b01)//判斷是否為寫信號 216 begin 217 state <= 7; 218 memory <= data_in[7:0];//發送數據 219 end 220 if (temp == 2'b10)//判斷是否為讀信號 221 state <= 11; 222 end 223 else 224 begin 225 state <= 0; 226 end 227 end 228 229 7 : begin 230 if((scl == 0) && (cnt < 8))//發送數據 231 begin 232 flag <= 1;//獲得總線控制權 233 sda_buffer <= memory[7]; 234 cnt <= cnt + 1; 235 memory <= {memory[6:0],memory[7]}; 236 state <= 7; 237 end 238 else 239 begin 240 if ((scl == 0) && (cnt == 8)) 241 begin 242 cnt <= 0; 243 flag <= 0;//釋放總線控制權 244 state <= 8; 245 end 246 else 247 begin 248 state <= 7; 249 end 250 end 251 end 252 8 : begin 253 if(!sda)//檢測應答信號 254 begin 255 state <= 9; 256 end 257 else 258 begin 259 state <= 0; 260 end 261 end 262 9 : begin 263 if (scl == 0) 264 begin 265 flag <= 1;//獲得總線控制權 266 sda_buffer <= 0;//拉低iic的數據線(為發送停止信號做准備) 267 state <= 10; 268 end 269 else 270 state <= 9; 271 end 272 10 : begin 273 if (scl == 1) 274 begin 275 sda_buffer <= 1;//發送停止信號 276 state <= 0; 277 end 278 else 279 state <= 10; 280 end 281 //----------------------------------------- 282 283 //------- 284 11 : begin 285 flag <= 1;//獲得總線控制權 286 sda_buffer <= 1;//拉高iic的數據線(為發送啟動信號做准備) 287 state <= 12; 288 end 289 12 : begin 290 sda_buffer <= 0;//發送啟動信號 291 state <= 13; 292 memory <= 8'b10100001; //控制字 293 end 294 13 : begin 295 if((scl == 0) && (cnt < 8))//發送八位控制字 296 begin 297 flag <= 1;//獲得總線控制權 298 sda_buffer <= memory[7]; 299 cnt <= cnt + 1; 300 memory <= {memory[6:0],memory[7]}; 301 state <= 13; 302 end 303 else 304 begin 305 if ((scl == 0) && (cnt == 8)) 306 begin 307 cnt <= 0; 308 flag <= 0;//釋放總線控制權 309 state <= 14; 310 end 311 else 312 begin 313 state <= 13; 314 end 315 end 316 end 317 14 : begin 318 if(!sda)//檢測應答信號 319 begin 320 state <= 15; 321 end 322 else 323 begin 324 state <= 0; 325 end 326 end 327 15 : begin 328 if((scl == 1) && (cnt < 8))//接收數據 329 begin 330 cnt <= cnt + 1; 331 memory <= {memory[6:0],sda}; 332 state <= 15; 333 end 334 else 335 begin 336 if ((scl == 0) && (cnt == 8)) 337 begin 338 cnt <= 0; 339 flag <= 1;//獲得總線控制權 340 state <= 16; 341 sda_buffer <= 1;//發送應答信號 342 end 343 else 344 state <= 15; 345 end 346 end 347 16 : begin 348 data_out <= memory;//輸出數據 349 state <= 17; 350 end 351 17 : begin 352 if (scl == 0) 353 begin 354 sda_buffer <= 0;//拉低iic的數據線(為發送停止信號做准備) 355 state <= 18; 356 end 357 else 358 state <= 17; 359 end 360 18 : begin //發送停止信號 361 if (scl == 1) 362 begin 363 sda_buffer <= 1; 364 state <= 0; 365 end 366 else 367 state <= 18; 368 end 369 370 default : state <= 0 ; 371 endcase 372 end 373 374 endmodule |
第112行,將讀寫信號保存起來是為了讀寫的時候只進行一次的讀寫。(按鍵按下的時間一般大於20ms,如果不進行處理,系統將重復的向同一個地址中寫入或者讀出數據)。
我們把13位地址給定了一個確定的數據,並沒有通過外部發送給系統,這樣可以降低我們理解的難度。有興趣的同學可以自己嘗試一下其他的地址控制方式。
上述代碼在下板實測時可用,仿真時則不能用。在我們的設計中,多次檢測應答信號,但是仿真過程中沒有真實的器件反饋應答信號,就會導致我們的仿真出錯。仿真時應把所有的應答信號直接跳過,如下:
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function: 產生iic總線的控制信號 *****************************************************/ 000 module iic( 001 clk, //外部輸入時鍾 002 rst_n,//系統復位 003 key_wr,//寫信號(低電平有效) 004 key_rd, //讀信號(低電平有效) 005 data_in, //輸入數據 006 scl, //iic的數據線 007 sda, //iic的時鍾線 008 data_out//輸出數據 009 ); 010 //系統輸入 011 input clk;//外部輸入時鍾 012 input rst_n;//系統復位 013 input key_wr;//寫信號(低電平有效) 014 input key_rd;//讀信號(低電平有效) 015 input [7:0] data_in;//輸入數據 016 //系統輸出 017 output reg scl;//iic的時鍾線 018 output reg [7:0] data_out;//輸出數據 019 020 inout sda; //iic的數據線 021 022 reg sda_buffer;//寫入數據的中間寄存器 023 reg flag;//控制系統是否占有總線控制權 024 025 assign sda = (flag) ? sda_buffer : 1'bz;//當flag為高電平時,系統擁有總線控制權 026 //並發送sda_buffer中的數據。當flag為低電平時, 027 //釋放總線。 028 029 reg [7:0] count;//計數器 030 reg clk_sys;//系統時鍾 031 //-------------------------------clk_sys 032 always @ (posedge clk or negedge rst_n) 033 begin 034 if (!rst_n) 035 begin 036 clk_sys <= 1'b0; 037 count <= 8'd0; 038 end 039 else 040 if (count < 31)//分頻成為近800K的時鍾 041 count <= count + 1; 042 else 043 begin 044 count <= 8'd0; 045 clk_sys <= ~clk_sys; 046 end 047 end 048 //------------------------------- 049 050 reg [5:0] state;//狀態寄存器 051 052 //--------------------------------scl 053 always @ (negedge clk_sys or negedge rst_n) 054 begin 055 if (!rst_n) 056 begin 057 scl <= 1'b1;//復位時,scl為高 058 end 059 else 060 begin 061 if (state > 0)//當總線忙的時候,scl為近400K的時鍾 062 scl <= ~scl; 063 else 064 scl <= 1'b1;//空閑時,scl為高 065 end 066 end 067 //---------------------------------- 068 069 reg [1:0] en;//讀寫使能中間寄存器 070 071 //----------------------------------enable 072 always @ (posedge clk or negedge rst_n) 073 begin 074 if (!rst_n) 075 begin 076 en <= 2'b00;//復位時,將中間寄存器置0; 077 end 078 else 079 begin 080 if (!key_wr)//寫有效時 081 en <= 2'b01; 082 else 083 if (!key_rd)//寫無效,讀有效時 084 en <= 2'b10; 085 end 086 end 087 //--------------------------------- 088 089 reg [3:0] cnt;//發送或者接收數據的個數 090 reg [1:0] temp;//讀寫使能的中間寄存器 091 reg [7:0] memory;//發送或者接受數據的中間寄存器 092 093 always @ (posedge clk_sys or negedge rst_n) 094 begin 095 if (!rst_n) begin 096 cnt <= 0; 097 data_out <= 8'd0; 098 flag <= 1'b1; //復位時,系統獲得總線的控制權 099 sda_buffer <= 1'b1; //向iic的數據線上發送高電平 100 state <= 0; 101 temp <= 2'b00; 102 end 103 else 104 case(state) 105 0 : begin 106 if(scl) 107 begin 108 if(en != temp)//有按鍵按下 109 begin 110 sda_buffer <= 1'b0;//發送啟動信號 111 state <= 1; 112 temp <= en;//將讀寫信號保存 113 memory <= 8'b10100000;//控制字 114 end 115 else 116 state <= 0; 117 end 118 else 119 state <= 0; 120 end 121 122 1 : begin 123 if((scl == 0) && (cnt < 8))//發送八位控制字 124 begin 125 sda_buffer <= memory[7]; 126 cnt <= cnt + 1; 127 memory = {memory[6:0],memory[7]}; 128 state <= 1; 129 end 130 else 131 begin 132 if ((scl == 0) && (cnt == 8)) 133 begin 134 cnt <= 0; 135 flag <= 0;//釋放總線控制權 136 state <= 2; 137 end 138 else 139 begin 140 state <= 1; 141 end 142 end 143 end 144 2 : begin 145 // if(!sda)//檢測應答信號 146 // begin 147 state <= 3; 148 memory <= 8'd0;//高字節地址 149 // end 150 // else 151 // begin 152 // state <= 0; 153 // end 154 end 155 3 : begin //發送高字節地址 156 if((scl == 0) && (cnt < 8)) 157 begin 158 flag <= 1;//獲得總線控制權 159 sda_buffer <= memory[7]; 160 cnt <= cnt + 1; 161 memory = {memory[6:0],memory[7]}; 162 state <= 3; 163 end 164 else 165 begin 166 if ((scl == 0) && (cnt == 8)) 167 begin 168 cnt <= 0; 169 flag <= 0;//釋放總線控制權 170 state <= 4; 171 end 172 else 173 begin 174 state <= 3; 175 end 176 end 177 end 178 4 : begin 179 // if(!sda)//檢測應答信號 180 // begin 181 state <= 5; 182 memory <= 8'h00;//低字節地址 183 // end 184 // else 185 // begin 186 // state <= 0; 187 // end 188 end 189 5 : begin 190 if((scl == 0) && (cnt < 8))//發送低字節地址 191 begin 192 flag <= 1;//獲得總線控制權 193 sda_buffer <= memory[7]; 194 cnt <= cnt + 1; 195 memory = {memory[6:0],memory[7]}; 196 state <= 5; 197 end 198 else 199 begin 200 if ((scl == 0) && (cnt == 8)) 201 begin 202 cnt <= 0; 203 flag <= 0;//釋放總線控制權 204 state <= 6; 205 end 206 else 207 begin 208 state <= 5; 209 end 210 end 211 end 212 6 : begin 213 // if(!sda)//檢測應答信號 214 // begin 215 if (temp == 2'b01)//判斷是否為寫信號 216 begin 217 state <= 7; 218 memory <= data_in[7:0];//發送數據 219 end 220 if (temp == 2'b10)//判斷是否為讀信號 221 state <= 11; 222 // end 223 // else 224 // begin 225 // state <= 0; 226 // end 227 end 228 229 7 : begin 230 if((scl == 0) && (cnt < 8))//發送數據 231 begin 232 flag <= 1;//獲得總線控制權 233 sda_buffer <= memory[7]; 234 cnt <= cnt + 1; 235 memory <= {memory[6:0],memory[7]}; 236 state <= 7; 237 end 238 else 239 begin 240 if ((scl == 0) && (cnt == 8)) 241 begin 242 cnt <= 0; 243 flag <= 0;//釋放總線控制權 244 state <= 8; 245 end 246 else 247 begin 248 state <= 7; 249 end 250 end 251 end 252 8 : begin 253 // if(!sda)//檢測應答信號 254 // begin 255 state <= 9; 256 // end 257 // else 258 // begin 259 // state <= 0; 260 // end 261 end 262 9 : begin 263 if (scl == 0) 264 begin 265 flag <= 1;//獲得總線控制權 266 sda_buffer <= 0;//拉低iic的數據線(為發送停止信號做准備) 267 state <= 10; 268 end 269 else 270 state <= 9; 271 end 272 10 : begin 273 if (scl == 1) 274 begin 275 sda_buffer <= 1;//發送停止信號 276 state <= 0; 277 end 278 else 279 state <= 10; 280 end 281 //----------------------------------------- 282 283 //------- 284 11 : begin 285 flag <= 1;//獲得總線控制權 286 sda_buffer <= 1;//拉高iic的數據線(為發送啟動信號做准備) 287 state <= 12; 288 end 289 12 : begin 290 sda_buffer <= 0;//發送啟動信號 291 state <= 13; 292 memory <= 8'b10100001; //控制字 293 end 294 13 : begin 295 if((scl == 0) && (cnt < 8))//發送八位控制字 296 begin 297 flag <= 1;//獲得總線控制權 298 sda_buffer <= memory[7]; 299 cnt <= cnt + 1; 300 memory <= {memory[6:0],memory[7]}; 301 state <= 13; 302 end 303 else 304 begin 305 if ((scl == 0) && (cnt == 8)) 306 begin 307 cnt <= 0; 308 flag <= 0;//釋放總線控制權 309 state <= 14; 310 end 311 else 312 begin 313 state <= 13; 314 end 315 end 316 end 317 14 : begin 318 // if(!sda)//檢測應答信號 319 // begin 320 state <= 15; 321 // end 322 // else 323 // begin 324 // state <= 0; 325 // end 326 end 327 15 : begin 328 if((scl == 1) && (cnt < 8))//接收數據 329 begin 330 cnt <= cnt + 1; 331 memory <= {memory[6:0],sda}; 332 state <= 15; 333 end 334 else 335 begin 336 if ((scl == 0) && (cnt == 8)) 337 begin 338 cnt <= 0; 339 flag <= 1;//獲得總線控制權 340 state <= 16; 341 sda_buffer <= 1;//發送應答信號 342 end 343 else 344 state <= 15; 345 end 346 end 347 16 : begin 348 data_out <= memory;//輸出數據 349 state <= 17; 350 end 351 17 : begin 352 if (scl == 0) 353 begin 354 sda_buffer <= 0;//拉低iic的數據線(為發送停止信號做准備) 355 state <= 18; 356 end 357 else 358 state <= 17; 359 end 360 18 : begin //發送停止信號 361 if (scl == 1) 362 begin 363 sda_buffer <= 1; 364 state <= 0; 365 end 366 else 367 state <= 18; 368 end 369 370 default : state <= 0 ; 371 endcase 372 end 373 374 endmodule |
仿真代碼
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function:iic測試模塊 *****************************************************/ 00 `timescale 1ns/1ps 01 02 module iic_tb; 03 //系統輸入 04 reg clk;//外部輸入時鍾 05 reg rst_n;//系統復位 06 reg key_wr;//寫信號(低電平有效) 07 reg key_rd;//讀信號(低電平有效) 08 reg [7:0] data_in;//輸入數據 09 //系統輸出 10 wire scl;//iic的時鍾線 11 wire [7:0] data_out;//輸出數據 12 wire sda; //iic的數據線 13 14 iic iic_inst( 15 .clk(clk), //外部輸入時鍾 16 .rst_n(rst_n), //系統復位 17 .key_wr(key_wr), //寫信號(低電平有效) 18 .key_rd(key_rd), //讀信號(低電平有效) 19 .scl(scl), //iic的時鍾 20 .sda(sda), //iic的數據線 21 .data_in(data_in),//輸入數據 22 .data_out(data_out)//輸出數據 23 ); 24 25 initial begin 26 clk = 1; 27 rst_n = 0; 28 key_wr = 1; 29 key_rd = 1; 30 data_in = 0; 31 #1000.1 rst_n = 1; 32 # 800 33 #8000 key_wr = 0;//寫信號有效 34 data_in = 8'h23;//輸入數據為8’h23 35 #40000 key_wr = 1;//寫信號釋放 36 37 #1000000 38 key_rd = 0;//讀信號有效 39 #40000 key_rd = 1;//讀寫號釋放 40 end 41 42 always #10 clk=~clk; //50M的時鍾 43 44 endmodule |
仿真分析
- 發送啟動信號
- 發送控制字
- 接收並檢測EEPROM發來的應答信號ACK
- 發送高字節地址位
- 接收並檢測EEPROM發來的應答信號ACK
- 發送低字節地址位
- 接收並檢測EEPROM發來的應答信號ACK
- 發送8bit有效數據
- 接收並檢測EEPROM發來的應答信號ACK
- 發送停止信號
經過一步一步的查看,我們的設計時序是正確的。
- 發送啟動信號
- 發送控制字1010_0000
- 接收並檢測EEPROM發來的應答信號ACK
- 發送高字節地址位
- 接收並檢測EEPROM發來的應答信號ACK
- 發送低字節地址位
- 接收並檢測EEPROM發來的應答信號ACK
- 發送啟動信號
- 發送控制字1010_0001
- 接收並檢測EEPROM發來的應答信號ACK
- 讀取一個字節數據
- 發送NO ACK信號
- 發送停止信號
讀寫的波形與iic原理中的讀寫時序圖一致,證明我們的設計是正確的。