2.1 IIC協議的FPGA實現
2.1.2 IIC協議的FPGA實現
圖2 13 IIC模塊的建模圖
圖2 13是 IIC 儲存模塊的建模圖,左邊是頂層信號,右邊則是溝通用的問答信號,寫入地址 iAddr,寫入數據 iData,還有讀出數據 oData。 Call/Done 有兩位,即表示該模塊有讀功能還有些功能。具體內容,我們還是來看代碼吧:
代碼2 1 IIC代碼聲明
1. parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31;
2. parameter THIGH = 10'd30, TLOW = 10'd65, TR = 10'd15, TF = 10'd15;
3. parameter THD_STA = 10'd30, TSU_STA = 10'd30, TSU_STO = 10'd30;
如代碼2 1 所示, FCLK 表示 400Khz 的週期, FHALF 表示 1/2 週期, FQUARTER 表示 1/4 週期。
圖2 14 起始位
首先讓我們先瞧瞧起始位這枚拼圖。如圖2 14所示,左圖是起始位的理想時序,右圖是起始位的物理時序。 IIC 總線的起始位也就類似串口或者 PS/2 等傳輸協議的起始位,然而不同的是, IIC 總線的起始位是 SCL 拉高 TR + TSU_STA + THD_STA + TF 之久,換之 SDA 則是拉高 TR + THIGH 然後拉低 TF + TLOW。起始位總和所用掉的時間,恰恰好有一個速率的週期。對此, Verilog 則可以這樣描述,結果如下所示:
代碼2 2 IIC起始位產生代碼
1. begin
2. isQ = 1;
3. rSCL <= 1'b1;
4. if( C1 == 0 ) rSDA <= 1'b1;
5. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0;
6. if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end
7. else C1 <= C1 + 1'b1;
8. end
如代碼2 2所示,第 2 行的 isQ = 1 表示設置 SDA 爲輸出狀態(即時結果),第 3 行則表示 SCL 一直持續拉高狀態,第 4~5 行表示 C1 爲 0 的時候 SDA 拉高,直到 C1 爲TR+THIGH 才拉低 SDA。第 6~7 行表示一個步驟所逗留的時間。
圖2 15 結束位
圖2 15是結束位的時序圖, IIC 設備的操作好壞一般都取決結束位。保險起見, SCL 與SDA 都事先拉低 1/4 週期,緊接着 SCL 會拉高 TR+TSU_STO(或者 1/2 週期),最後又保持高電平 1/2 週期。反之, SDA 會拉低 1/2 週期,隨之拉高 TR+THIGH(或者 1/2週期)。對此, Verilog 可以這樣表示,結果如代碼2 3所示:
代碼2 3 IIC結束位代碼實現
1. begin
2. isQ = 1'b1;
3.
4. if( C1 == 0 ) rSCL <= 1'b0;
5. else if( C1 == FQUARTER ) rSCL <= 1'b1;
6.
7. if( C1 == 0 ) rSDA <= 1'b0;
8. else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 1'b1;
9.
10. if( C1 == (FQUARTER + FCLK) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
11. else C1 <= C1 + 1'b1;
12. end
如代碼2 3所示,第 2 行表示 SDA 爲輸出狀態(即時),第 3~4 行表示 C1 爲 0 拉高SCL, C1 爲 1/4 週期就拉高。第 5~6 行表示, C1 爲 0 拉低 SDA, C1 爲 1/4 週期 + TR +TSU_STO 就拉高 SDA。第 7~8 行表示該步驟所逗留的時間。
圖2 16 釋放總線
此外,結束位還有 Bus Free Time 這個時序參數,IIC 總線在閒置的狀態下 SCL 與 SDA等信號都持續高電平。主機發送結束位以示結束操作,然而主機持續拉高 SCL 信號與SDA 信號 TBUF 以示總線釋放。 TBUF 的有效時間從 SCL 信號與 SDA 信號拉高那一刻開始算起
根據表2 2 所示, TBUF 是 65 個時鐘,結果如圖 16.6 所示, SDA 信號拉高之後, SCL與 SDA 信號只要持續保持 1/2 週期(即 62 個時),基本上就能滿足 TBUF。如果筆者是一位緊密控時狂人,可能無法接受這樣的結果,因爲滿足 TBUF 少了 3 個時鐘,爲此代碼2 3需要更動一下:
代碼 2 4 IIC結束位代碼修改
1. if( C1 == ( FQUARTER + FCLK + 3) -1 )
2. begin C1 <= 10'd0; i <= i + 1'b1; end
3. else C1 <= C1 + 1'b1;
如代碼 2 4所示,筆者爲第 1 行寫下 +3 表示該步驟多逗留 3 個時鐘,以致滿足 TBUF。
不管對象是設備地址,數據地址,寫入數據,讀出數據,還是應答位,大夥都視爲數據位。 IIC 總線類似其他傳輸協議,它有時鐘信號也有上升沿與下降沿。如圖 16.7 所示,SCL 信號的下降沿導致設備設置(更新)數據,上升沿則是鎖存(讀取)數據。期間,TF+TLOW 表示時鐘信號的前半週期, TR+THIGH 則表示後半週期。此外,爲了確保數據成功打入寄存器,數據被上升沿鎖存哪一刻起, TSU_DAT 還有 THD_DAT 必須得到滿足。
圖2 17 數據位更新有效
除此之外,爲了確保數據有效被更新,也必須確保 TAA 得到滿足,結果如圖2 17所示。理解完畢以後,就可以開始學習,寫一字節數據與讀一字節數據,還有應答位。
圖2 18 寫一字節
IIC 總線一般都是一個字節一個字節讀寫數據,如圖2 18 所示,那是寫一字節的理想時序圖,一字節數據是從最高位開始寫起。對此, Verilog 可以這樣描述,結果如代碼2 5所示:
代碼2 5 IIC 總線寫一個字節
1. 7,8,9,10,11,12,13,14:
2. begin
3. isQ = 1'b1;
4. rSDA <= D1[14-i];
5. if( C1 == 0 ) rSCL <= 1'b0;
6. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1;
7. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
8. else C1 <= C1 + 1'b1;
9. end
如代碼2 5 所示,第 1 行有 8 個步驟,表示寫一個字節。第 3 行 isQ 爲 1 表示 SDA 爲輸出狀態。第 4 行表示從最高位開始更新 SDA 的數據位。第 5~6 行表示, C1 爲 0 拉低SCL, C1 爲 TF+TLOW 則拉高 SCL。第 7~8 行表示該步驟逗留一個週期的時間。
圖2 19 應答位
應答位是從機給予主機的回答, 0 爲是,1 爲否。然而,從旁觀看,讀取應答位也是讀取一位數據位。當主機完成寫入一個字節或者讀取一個字節數據的時候,從機都會產生應答位。主機拉低 SCL 那刻,從機便會發送應答位,然後主機會藉由上升沿讀取應答位。如圖2 19 所示,上升沿會產生在 TF + TLOW 之後,也是 1/2 週期。對此, Verilog 可以這樣表示,結果如代碼2 6所示:
代碼2 6 IIC應答位
1. begin
2. isQ = 1'b0;
3.
4. if( C1 == FHALF ) isAck <= SDA;
5.
6. if( C1 == 0 ) rSCL <= 1'b0;
7. else if( C1 == FHALF ) rSCL <= 1'b1;
8.
9. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
10. else C1 <= C1 + 1'b1;
11. end
如代碼2 6所示,第 2 行表示 SDA 爲輸入狀態。第 4~5 行表示, C1 爲 0 拉低 SCL,C1 爲 1/2 週期則拉高 SCL。第 3 行表示, C1 爲 1/2 週期的時候讀取應答位。第 6~7 行表示該步驟逗留 1 個週期的時間。
圖2 20 讀一字節
所謂讀一字節數據就是重複讀取 8 次應答位。如圖2 20所示, SCL 的下降沿導致從機更新數據,然後主機在 SCL 的上升沿讀取數據。此外,從機也會由高至低更新數據位。至於 Verilog 則可以這樣表示,結果如代碼2 7所示:
代碼2 7 IIC讀一字節
1. 19,20,21,22,23,24,25,26: // Read
2. begin
3. isQ = 1'b0;
4. if( C1 == FHALF ) D1[26-i] <= SDA;
5.
6. if( C1 == 0 ) rSCL <= 1'b0;
7. else if( C1 == FHALF ) rSCL <= 1'b1;
8.
9. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
10. else C1 <= C1 + 1'b1;
11. end
如代碼2 7所示,第 1 行表示讀取一字節。第 3 行表示 SDA 爲輸入狀態,第 5~6 行表示, C1 爲 0 拉低 SCL, C1 爲 1/2 週期則拉高 SCL。第 4 行表示, C1 爲 1/2 週期的時候讀取數據,而且數據位由高至低存入 D1。第 7~8 行表示該步驟逗留一個週期的時間。
圖2 21 第二次起始位
知道主機向從機讀取數據的時候,它必須改變設備地址的方向,因此讀操作又第二次起始位。如圖2 21所示,感覺上第二次起始位也是第一次起始位,不過爲了促使改變方向成功,第二次起始位相較第一次起始位的前後都拉低 1/4 週期。對此, Verilog 可以這樣表示,結果如代碼2 8所示:
代碼2 8 IIC第二次起始位
1. begin
2. isQ = 1'b1;
3. if( C1 == 0 ) rSCL <= 1'b0;
4. else if( C1 == FQUARTER ) rSCL <= 1'b1;
5. else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF) ) rSCL <= 1'b0;
6.
7. if( C1 == 0 ) rSDA <= 1'b0;
8. else if( C1 == FQUARTER ) rSDA <= 1'b1;
9. else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0;
10.
11. if( C1 == (FQUARTER + FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
12. else C1 <= C1 + 1'b1;
13. end
如代碼2 8 所示,第 2 行表示 SDA 爲輸出狀態。第 3~5 行表示, C1 爲 0 拉低 SCL,C1 爲 1/4 週期拉高 SCL, C1 爲 1/4 週期 + TR + TSU_STA + THD_STA + TF 便拉低SCL。第 7~9 行表示, C1 爲 0 拉低 SDA, C1 爲 1/4 週期拉高 SDA, C1 爲 1/4 週期 + TR+ THIGH 便拉低 SDA。第 11~12 行表示該步驟停留一個週期的時間。
接下來是仿真驗證,結果如下:
圖 2 22 IIC總線仿真時序圖
結合上述仿真波形圖和程序可以看出:
起始位:SCLK爲高電平時,SDAT由高到低,指示IIC總線傳輸數據的開始;
之後,傳送一個字節的數據,即4A,爲從機的地址,隨後,跟了一個高電平,爲應答位;
之後,傳送一個字節的數據,即01,爲從機地址的子地址,隨後,跟了一個高電平,爲應答位;
之後,傳送一個字節的數據,即08,爲上面子地址寄存器配置的數據,隨後,跟了一個高電平,爲應答位;
最後,爲停止位,SCLK爲高電平時,SDAT由低到高,指示該次IIC總線傳輸數據的結束。
由仿真結果可知,當傳送完一個字節後,SDAT爲一個脈衝的高電平,而不是從器件先將SDAT拉低再拉高,這樣也是可以的。