I2C協議及Verilog實現與模擬(一)
一、協議概述
I2C匯流排是一種兩線制的序列介面,包括一根序列資料線(SDA)和一根序列時鐘(SCL)訊號。總線上連線的器件透過SDA與SCL實現資料傳輸。每個器件透過各自的地址進行區分,且都可以作為一個“傳送器”或者“接收器”(根據各自的功能決定)。從資料傳輸的角度,也可以把器件定義為主機或從機。主機是初始化匯流排的資料傳輸併產生允許傳輸的時鐘訊號的器件,此時任何被定址的器件都被認為是從機。
二、I2C術語
下面介紹I2C總線上器件角色以及它與其他器件通訊方式相關的術語:
1。 傳送器:向匯流排傳送資料的器件。初始化匯流排的資料傳輸(主機-傳送器)或者回應主機請求向匯流排傳送資料(從機-傳送器)的器件都可稱為傳送器;
2。 接收器:從總線上接收資料的器件。從總線上接收自己請求的資料(主機-接收器)或者需要回應主機請求的器件都可稱為接收器;
3。 主機:初始化傳送產生時鐘訊號和終止傳送的器件。傳送器和接收器都可以作為主機;
4。 從機:被主機定址的器件。傳送器和接收器都可以作為從機;
主從機及傳送/接收器之間的關係如下圖所示:
5。 多主機:同時有多於一個主機嘗試控制匯流排但不破壞報文;
6。 仲裁:是一個在有多個主機同時嘗試控制匯流排但只允許其中一個控制匯流排並使報文不被破壞的過程;
7。 同步:兩個或多個器件同步時鐘訊號的過程。
下面介紹一下匯流排資料傳輸過程中相關術語:
1。 START(RESTART):總線上出現START或者RESTART的情形標誌資料傳輸的起始,起始條件要求SCL時鐘線為高電平時,SDA資料線出現從高電平到低電平翻轉的情況;
2。 STOP:總線上出現STOP條件標誌資料傳輸的終止,終止條件要求SCL時鐘線為高電平時,SDA資料線出現從低電平到高電平翻轉的情況。
三、I2C資料傳輸
I2C匯流排中主機負責產生SCL時鐘以及控制資料的傳輸。從機負責傳送資料到主機,或者接收主機發送的資料。資料的應答(ACK)由接收資料的一方產生,可以是主機,也可以是從機。I2C匯流排可以透過一個仲裁機制實現多主機連線。
系統設計者需要為每一個從機定義一個專屬地址,當一個主機想和一個從機進行通訊時,主機需要在傳送起始條件後,緊跟著傳送該從機的專屬地址加上控制位(R/W),控制位決定了主機是要向從機發送資料或者是接收從機的資料。從機接收到改地址後向主機發送一個應答(ACK)脈衝。
如果主機向從機發送資料,則從機按位元組接收資料,直至主機發出終止條件;如果主機需要讀取從機資料,從機按位元組向主機發送資料,主機在接收到一個位元組後,迴應從機一個應答(ACK)脈衝,主機接收完最後一個位元組資料後發回NACK脈衝,然後發出終止條件或者發出一個起始條件與另一個從機進行通訊,通訊過程如下圖所示:
四、地址格式
I2C協議中有兩種地址格式:7-bit地址格式和10-bit地址格式。
7-bit地址格式中,在起始條件後,主機發送的第一個位元組,該位元組高7位為從機地址,最低位為R/W位-“0”表示傳送(寫),主機向從機寫資料,“1”表示請求資料(讀)主機讀取從機資料。如下圖所示:
10-bit地址格式中,使用兩個位元組來傳送10bit地址。資料傳輸的第一個位元組包含以下資料位定義:高5位標識地址格式為10-bit模式,隨後的兩位為從機地址的最高位和次高位,即地址bit[9:8],最低位為R/W位,傳送的第二個位元組為從機地址bit[7:0]。如下圖所示:
五、I2C主機的Verilog實現
opencores網站上有很多優秀的開原始碼,其中就有I2C匯流排主機的IP,該IP基於Wishbone匯流排。並且經過了ASIC驗證,可以說是非常經典的程式碼了。託管這裡就拿這個程式碼進行講解。Wishbone匯流排是一種片上匯流排,一般FPGA的設計中是用不到的,這裡託管為了大家使用方便,把IP中Wishbone介面去掉了,換成了最基本的本地介面,用於配置IP中的暫存器及監測IP的狀態。時序如下圖所示:
這樣做的另一個好處就是,大家如果用這個IP整合到APB或AXI匯流排的SOC晶片中,這個我們以後細說。
I2C主機模組的Verilog檔案層級結構如下圖所示:
結構框圖如下:
程式碼比較長,就不在這裡貼了,放在百度網盤裡,裡面有原始碼還有測試用例。大家可以去下載,下面是連結:
連結:
https://
pan。baidu。com/s/1axdJpp
59F4jWpa3JFwUPhA
提取碼:tgxd
複製這段內容後開啟百度網盤手機App,操作更方便哦——來自百度網盤超級會員V1的分享
我們先說說頂層模組,模組的介面訊號如下圖:
local interface的幾個訊號咱們上面已經說過了,比較簡單,這裡主要說一下I2C匯流排的幾個訊號:
當我們例化這個IP時,需要新增三態buffer,在FPGA中用使用下面的程式碼,綜合工具就能自動插入三態buffer:
assign
scl
=
scl_padoen_oe
?
1
’
bz
:
scl_pad_o
;
assign
sda
=
sda_padoen_oe
?
1
’
bz:
sda_pad_o
;
assign
scl_pad_i
=
scl
;
assign
sda_pad_i
=
sda
;
其中scl及sda均定義為inout型別。
下面說一下內部暫存器:
prer:時鐘預分頻暫存器,用於產生SCL時鐘訊號,由於SDA的每個bit被分解成5個狀態,相當於內部固定會有5分頻,我們用一個例子來解釋prer暫存器值計算方法,
假設系統時鐘為32MHz,要求SCL為100KHz,則prer=(32MHz/(5*100KHz))-1=63。
ctr:控制暫存器,bit7表示模組使能位,1-模組使能,0-模組禁能。bit6為中斷使能位,1-中斷使能,0-中斷禁能。其餘位保留。程式碼如下:
//——————————————————————————————————————
// decode control register
//——————————————————————————————————————
assign core_en = ctr[7];
assign ien = ctr[6];
txr:傳輸暫存器,從機地址以及資料均透過該暫存器進行配置。
rxr:接收暫存器,從總線上讀取到的資料。
cr:命令暫存器,各bit含義如下:
sr:狀態暫存器,各bit含義如下:
下面這段程式碼實現暫存器配置以及命令暫存器的解析:
//——————————————————————————————————————
// generate registers
//——————————————————————————————————————
always @(posedge clk or negedge rst_i)
begin
if (!rst_i) begin
prer <= #1 16‘hffff;
ctr <= #1 8’h0;
txr <= #1 8‘h0;
cr <= #1 8’h0;
end
else if (wr_en) begin
if (done | i2c_al)
cr[7:4] <= #1 4‘h0;
cr[2:1] <= #1 2’b0;
cr[0] <= #1 1‘b0;
case (reg_addr)
`REG_CLK_PRESCALER :
prer <= #1 ipwdata[15:0];
`REG_CTRL :
ctr <= #1 ipwdata[7:0];
`REG_TX :
txr <= #1 ipwdata[7:0];
`REG_CMD : begin
if (core_en)
cr <= #1 ipwdata[7:0];
end
endcase
end
else begin
if (done | i2c_al)
cr[7:4] <= #1 4’h0;
cr[2:1] <= #1 2‘b0;
cr[0] <= #1 1’b0;
end
end
//——————————————————————————————————————
// decode command register
//——————————————————————————————————————
assign sta = cr[7];
assign sto = cr[6];
assign rd = cr[5];
assign wr = cr[4];
assign ack = cr[3];
assign iack = cr[0];
下面的程式碼實現資料及狀態的讀取:
always @(*)
begin
case (reg_addr)
`REG_CLK_PRESCALER :
read_mux_data = {16‘h0,prer};
`REG_CTRL :
read_mux_data = {24’h0,ctr};
`REG_RX :
read_mux_data = {24‘h0,rxr};
`REG_STATUS :
read_mux_data = {24’h0,sr};
`REG_TX :
read_mux_data = {24‘h0,txr};
`REG_CMD :
read_mux_data = {24’h0,cr};
default :
read_mux_data = ‘h0;
endcase
end
// Output read data
assign iprdata = (rd_en) ? read_mux_data : {32{1’b0}};
這樣頂層模組的主要功能就解說完了,時間差不多了,要出去打羽毛球球啦。。。剩下的兩個模組以及模擬放在下一講。
大家喜歡的話請點個“贊”哦,謝謝啦