基於FPGA的IIC驅動設計


一、IIC基本概念

    IIC 總線(I2C busInter-IC bus)是一個雙向的兩線連續總線,提供集成電路(ICs)之間的通信線路。IIC總線是一種串行擴展技術,最早由Philips公司推出,廣泛應用於電視,錄像機和音頻設備,IIC 的意思是完成集成電路或功能單元之間信息交換的規范或協議Philips 公司推出的 IIC 總線采用一條數據線(SDA),加一條時鍾線(SCL)來完成數據的傳輸及外圍器件的擴展。 如圖1所示:

圖1

二、主機和從機的概念

    主機就是負責整個系統的任務協調與分配,從機一般是通過接收主機的指令從而完成某些特定的任務,主機和從機之間通過總線連接,進行數據通訊。

 

三、傳輸速率

    IIC 總線數據傳輸速率在標准模式下可達 100kbit/s,快速模式下可達400kbit/s,高速模式下可達3.4Mbit/sIIC總線上的主設備與從設備之間以字節(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
View Code

    測試代碼

 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      
View Code

    仿真結果

    (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   
View Code

 

測試代碼

 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
View Code

仿真結果:

    (1)寫時序

 

     (2)讀時序

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM