學習筆記-Verilog實現IIC匯流排協議
學習筆記-Verilog實現IIC匯流排協議
1 IIC匯流排協議原理
1.1開始和結束
IIC匯流排只有兩根線,適合低速傳輸。可工作在100kb/s,400kb/s,1Mb/s和3.4Mb/s。序列匯流排中SPI和MIPI會有更高的傳輸速率。
IIC匯流排中SCL為時鐘訊號,SDA為資料訊號。IIC匯流排的地址分為7位和10位兩種,本文用的是7位地址。由於自帶始終,工作頻率可以不準確。
IIC匯流排的SCL和SDA必須上拉。
1.2資料傳輸
所有資料都是在時鐘SCL位低電平時變化,高電平時取樣資料。
每次傳輸9位資料,其中前8位(1個位元組)位主從機傳送的資料,第9位為接收端的反饋訊號,為低電平是稱為(ACK),高電平稱為(NACK)。ACK表示繼續傳輸資料。
上圖的scl_o和sda_o產生一個佔空比和相位不同的同頻時鐘訊號,scl和sda為真實的資料傳輸波形。
上圖中表示了一個訊號的傳輸過程。第八位為讀寫訊號,高電平讀,低電平寫。ACK訊號和NACK訊號是由讀訊號的裝置發出的。接受到NACK訊號是stop或者restart。
1.3IIC匯流排仲裁
IIC匯流排可以掛很多主機和從機,一般最多不能超過400pF的負載電容。如裝置太多IIC多路選擇器擴充套件。
掛那麼多主機和從機,怎麼進行有效的通訊呢,這就需要匯流排仲裁,由於IIC匯流排都是上拉,誰先輸出0,誰贏得仲裁,輸出資料,地址對上了才能讀資料。
2 Verilog程式碼
//注意:這裡的SCL和SDA都是上拉的,如果匯流排上有多個輸出,有0輸入匯流排上邏輯為0
//由於匯流排上拉,匯流排上很多訊號都是低電平有效。但是輸出0功耗高。
//完整的IP還需要加入tx_fifo,rx_fifo,時鐘相位佔空比模組和頂層控制模組
module iic_core
(
input CLK_SDA, CLK_SCL, //時鐘訊號見上圖中的scl_o和sda_o訊號
input reset_n,
input [7:0] iic_addr, //地址輸入
input [7:0] iic_datain,
output reg [7:0] iic_dataout,
input iic_enable,
output reg iic_busy,
output reg iic_ackerror,
output reg iic_arbitlost,
input scl_i, sda_i,
output scl_o, sda_o,
output reg scl_t, sda_t //高電平輸出有效,低電平輸出無效
);
parameter [2:0] IIC_IDLE=3'h1, IIC_START=3'h2, IIC_ADDR=3'h3,
IIC_ACKA=3'h4, IIC_DATA=3'h5, IIC_ACKD=3'h6, IIC_STOP=3'h7;
reg [3:0] iic_core_state, iic_core_nextstate;
reg [7:0] addr_i, datain_i;
reg [7:0] sdain;
reg [3:0] bit_count;
reg sdaout;
always @(posedge CLK_SDA or negedge reset_n)
if (~reset_n) iic_core_state <= IIC_IDLE;
else iic_core_state <= iic_core_nextstate;
always @(*) begin
iic_core_nextstate <= IIC_IDLE;
case (iic_core_state)
IIC_IDLE : iic_core_nextstate <= iic_enable ?
IIC_START : IIC_IDLE;
IIC_START : iic_core_nextstate <= IIC_ADDR;
IIC_ADDR : iic_core_nextstate <= iic_arbitlost ?
IIC_IDLE : bit_count ? IIC_ADDR : IIC_ACKA;
IIC_ACKA : iic_core_nextstate <= iic_ackerror ?
IIC_STOP : IIC_DATA;
IIC_DATA : iic_core_nextstate <= iic_arbitlost ?
IIC_IDLE : bit_count ? IIC_DATA : IIC_ACKD;
IIC_ACKD : iic_core_nextstate <= (iic_enable & (addr_i == iic_addr)) ?
IIC_DATA : iic_enable ? IIC_IDLE : IIC_STOP;
IIC_STOP : iic_core_nextstate <= IIC_IDLE;
endcase
end
always @(posedge CLK_SDA or negedge reset_n)
if (~reset_n) begin
sda_t <= 0; sdaout <= 1;
bit_count <= 0;
iic_busy <= 0;
end else begin
sda_t <= 1; sdaout <= 1;
iic_busy <= 1;
case (iic_core_nextstate) //用iic_core_nextstate是為了使每個狀態執行的操作在同一週期上一致
IIC_IDLE : begin
sda_t <= 0;
iic_busy <= 0;
end
IIC_START : begin
sdaout <= 0;
bit_count <= 8;
end
IIC_ADDR : begin
sdaout <= iic_addr[bit_count - 1];
bit_count <= bit_count - 1;
end
IIC_ACKA : begin
sda_t <= 0;
bit_count <= 8;
end
IIC_DATA : begin
sda_t <= ~addr_i[0];
sdaout <= addr_i[0] ? 1'b1 : datain_i[bit_count - 1];
bit_count <= bit_count - 1;
end
IIC_ACKD : begin
sda_t <= (iic_enable & addr_i[0]) & (addr_i == iic_addr);
sdaout <= ~((iic_enable & addr_i[0]) & (addr_i == iic_addr));
bit_count <= 8; iic_busy <= 0;
end
IIC_STOP : begin
sdaout <= 0;
iic_busy <= 0;
end
endcase
end
//SCL用下降沿是為了在SDA穩定後在進行操作
always @(negedge CLK_SCL or negedge reset_n)
if (~reset_n) begin
sdain <= 0;
scl_t <= 0;
iic_ackerror <= 0;
iic_arbitlost <= 0;
addr_i <= 0; datain_i <= 0;
end else begin
sdain <= {sdain[6:0],sda_i};
scl_t <= 1;
case (iic_core_state)
IIC_IDLE : scl_t <= 0;
IIC_START : begin
addr_i <= iic_addr; datain_i <= iic_datain;
end
IIC_ADDR : iic_arbitlost <= (sdaout != sda_i); //匯流排仲裁訊號
IIC_ACKA : iic_ackerror <= sda_i; //是否收到ACK訊號,是否有從機的地址匹配成功(低電平表示匹配成功)
IIC_DATA : iic_arbitlost <= sda_t ? (sdaout != sda_i) : 0; //匯流排仲裁訊號
IIC_ACKD : begin
iic_ackerror <= sda_t ? sda_i : 0;
datain_i <= iic_datain;
if (addr_i[0]) iic_dataout <= sdain; //讀操作時,輸出ACK或NACK訊號
end
IIC_STOP : scl_t <= 0;
endcase
end
assign scl_o = CLK_SCL;
assign sda_o = sdaout & CLK_SDA;
//IIC匯流排輸出在頂層實現
// assign scl = scl_t ? scl_o : 1'bz;
// assign sda = sda_t ? sda_o : 1'bz;
寫操作
IDLE-START-ADDR-ACKA成功後,addr_i(0)最後最低位為0,所以進行寫操作,在DATA狀態時輸出一個位元組資料後進入ACKD狀態,判斷是否繼續寫或者換地址寫,重新寫,本程式碼寫時最簡單的IIC從機程式碼,所以很多情況沒有考慮到。
讀操作
IDLE-START-ADDR-ACKA成功後,addr_i(0)最後最低位為1,所以進行讀操作,DATA狀態時sda_t為0,sda時不輸出的,ACKD狀態輸出ACK訊號。
相關文章
- CAN匯流排協議 學習筆記協議筆記
- ARM 匯流排協議協議
- AHB匯流排協議協議
- IIC通訊協議筆記協議筆記
- 匯流排協議系列——USART協議初探協議
- 004:ZYNQ_AXI匯流排學習筆記(1)筆記
- Raft協議學習筆記Raft協議筆記
- Raft 協議學習筆記Raft協議筆記
- IP協議學習筆記協議筆記
- 學習筆記 - DNS協議筆記DNS協議
- IIC序列匯流排的組成及工作原理
- CAN匯流排原理_學習
- 序列匯流排的學習
- IIC 協議原理協議
- OAuth 2.0 協議學習筆記OAuth協議筆記
- HTTP 協議 學習筆記一HTTP協議筆記
- Internet安全協議 學習筆記協議筆記
- 《HTTPS權威指南》-協議學習筆記HTTP協議筆記
- swift學習筆記4——擴充套件、協議Swift筆記套件協議
- Object C學習筆記15-協議(protocol)Object筆記協議Protocol
- 事件匯流排的設計與實現事件
- 計組筆記第六章——匯流排筆記
- CAN匯流排分析儀工具-CAN轉USB智慧協議轉換器協議
- CAN匯流排協議簡介及其常見的應用領域協議
- arduino Uno+arduino D1 實現 阿里雲物聯網控制+IIC匯流排通訊點燈UI阿里
- 匯流排
- TCP/IP學習筆記之協議和郵件TCP筆記協議
- WebWorker與WebSocket實現前端訊息匯流排Web前端
- iOS直播技術學習筆記-流媒體協議(七)iOS筆記協議
- ZooKeeper一致性協議ZAB學習筆記協議筆記
- Java學習筆記之基於TCP協議的socketJava筆記TCP協議
- 事件匯流排事件
- 前端匯流排前端
- 【學習筆記】網路流筆記
- 網路流學習筆記筆記
- 如何在 JavaScript 中實現 Event Bus(事件匯流排)JavaScript事件
- 如何在 pyqt 中實現全域性事件匯流排QT事件
- 實現一個事件匯流排(vue.prototype.$bus)?事件Vue