摘要:總線空閑和均為高電平協(xié)議起始位為高電平時,出現(xiàn)下降沿協(xié)議終止位為高電平時,出現(xiàn)上升沿。主設(shè)備產(chǎn)生所有時鐘脈沖,包括確認位第九個時鐘脈沖。當在第個時鐘脈沖期間保持高時,這被定義為非應答信號。
??I2C總線是由Philips公司開發(fā)的一種簡單、雙向二線制同步串行總線。它只需要兩根線即可在連接于總線上的器件之間傳送信息。
??主器件用于啟動總線傳送數(shù)據(jù),并產(chǎn)生時鐘以開放傳送的器件,此時任何被尋址的器件均被認為是從器件.在總線上主和從、發(fā)和收的關(guān)系不是恒定的,而取決于此時數(shù)據(jù)傳送方向。如果主機要發(fā)送數(shù)據(jù)給從器件,則主機首先尋址從器件,然后主動發(fā)送數(shù)據(jù)至從器件,最后由主機終止數(shù)據(jù)傳送;如果主機要接收從器件的數(shù)據(jù),首先由主器件尋址從器件.然后主機接收從器件發(fā)送的數(shù)據(jù),最后由主機終止接收過程。在這種情況下.主機負責產(chǎn)生定時時鐘和終止數(shù)據(jù)傳送。
??I2C總線是公認的世界標準,由50多家公司生產(chǎn)超過1000個不同的地方實施的集成電路。此外,通用的i2c總線用于各種控制體系結(jié)構(gòu),如系統(tǒng)管理總線(SMBus),電源管理總線(PMBus),智能平臺管理接口(IPMI),顯示器數(shù)據(jù)通道(DDC)和高級電信計算架構(gòu)(ATCA)。
??I2C總線的原理和細節(jié)參考這篇文章,感謝作者的整理,受益匪淺。
串行數(shù)據(jù)線(SDA)和串行時鐘線(SCL)
??SDA和SCL都是雙向的線路,通過電流源或上拉電阻連接到一個正的供應電壓。連接到總線的設(shè)備的輸出級必須有一個開路漏極或開路集電極來執(zhí)行線和功能。I2C-bus上的數(shù)據(jù)可以在標準模式下以高達100kbit /s的速率傳輸,在快速模式下以高達400kbit /s的速率傳輸,在快速模式+下為1 Mbit/s,在高速模式下最高為3.4 Mbit/s。總線電容限制連接到總線的接口數(shù)量。
數(shù)據(jù)有效性
??SDA線上的數(shù)據(jù)必須在SCL時鐘線時鐘高電平期間保持穩(wěn)定,在時鐘低電平期間改變(見下圖)。因此我們在設(shè)計信號時,最佳情況就是在時鐘線SCL為低電平中間時SDA數(shù)據(jù)線上的數(shù)據(jù)改變,在時鐘高電平中間時獲取SDA數(shù)據(jù)線上的數(shù)據(jù)。
起始和終止位
??所有數(shù)據(jù)的傳輸都以START ( S )開始,以STOP ( P )結(jié)束(見下圖)。
??I2C總線空閑:SDA和SCL均為高電平;
??I2C協(xié)議起始位:SCL為高電平時,SDA出現(xiàn)下降沿;
??I2C協(xié)議終止位:SCL為高電平時,SDA出現(xiàn)上升沿。
??啟動和停止條件總是由主設(shè)備生成。在啟動條件后,總線被認為是忙碌的。該總線在停止條件后的某一段時間內(nèi)再次空閑。如果生成了重復啟動(Sr)而不是停止條件,則總線將保持忙碌狀態(tài)。在這方面,啟動(S)和重復啟動(Sr)條件在功能上是相同的。
傳輸1字節(jié)格式
??在SDA數(shù)據(jù)線上傳輸?shù)拿總€字節(jié)長度必須是8位。每次傳輸可以傳輸?shù)淖止?jié)數(shù)是不受限制的。每個字節(jié)后面必須跟著一個應答位(ACK)。數(shù)據(jù)從字節(jié)最高位(MSB)開始傳輸(見下圖)。如果一個從設(shè)備無法接收或發(fā)送另一個完整的字節(jié)的數(shù)據(jù),直到執(zhí)行一些其他功能,例如服務內(nèi)部中斷,它可以維持時鐘線scl為低強迫主設(shè)備進入等待狀態(tài)。當從設(shè)備準備好另一個字節(jié)的數(shù)據(jù)后繼續(xù)傳輸數(shù)據(jù)并釋放時鐘線SCL。
應答(ACK)與非應答(NACK)
??應答發(fā)生在每個字節(jié)之后。應答位響應,表明該字節(jié)已成功接收,并且可以發(fā)送另一個字節(jié)。主設(shè)備產(chǎn)生所有時鐘脈沖,包括確認位第九個時鐘脈沖。應答信號定義如下:在響應時鐘脈沖期間,發(fā)射機釋放SDA線,接收機可以把SDA線拉低,,并且在時鐘高電平期間穩(wěn)定保持低電平。
??當SDA在第9個時鐘脈沖期間保持高時,這被定義為非應答信號。然后,主設(shè)備可以生成終止傳輸 的停止條件,或者生成啟動新傳輸?shù)闹貜蛦訔l件。NACK的產(chǎn)生有五個條件:
??- 總線上沒有帶有所傳輸?shù)刂返慕邮照?,因此沒有設(shè)備以應答。
??- 接收器無法接收或發(fā)送,因為它正在執(zhí)行一些實時功能,并沒有準備好開始與主控通信。
??- 在傳輸過程中,接收方獲取它不理解的數(shù)據(jù)或命令。
??- 在傳輸過程中,接收器無法接收到更多的數(shù)據(jù)字節(jié)。
??- 主控接收機必須將傳輸結(jié)束的信號發(fā)送給從發(fā)射機。
??從設(shè)備器件地址通常是由固定為和可變位組合而成的。所謂固定位就是器件本就確定無法更改的,認為不能讓控制的的,而可變位通常是器件的硬件,可供用戶進行硬件連接,按照用戶的硬件連接確定,例如下圖中7位器件地址中,前四位1010是出廠時就已經(jīng)固定了的,而后三位是器件硬件引腳可供用戶改變的。
??對不同的器件,I2C傳輸格式略有不同,對于存儲設(shè)備,還具有存儲器的地址號,在主設(shè)備發(fā)送器件地址,從設(shè)備存儲器響應后,主設(shè)備要再發(fā)8或16位存儲器地址數(shù)據(jù),來選擇存儲器的地址,等待從設(shè)備響應后,主設(shè)備在發(fā)送數(shù)據(jù)到存儲器地址或從存儲器地址讀取數(shù)據(jù)。
??I2C接口具有嚴格的讀寫時序,包括寫數(shù)據(jù)時序、讀數(shù)據(jù)時序、連續(xù)寫數(shù)據(jù)以及連續(xù)讀時序。本次實驗僅實現(xiàn)寫數(shù)據(jù)時序和讀數(shù)據(jù)時序。I2C數(shù)據(jù)傳輸都是高位優(yōu)先。
??根據(jù)I2C的讀寫時序圖,我們可以發(fā)現(xiàn),讀寫時序傳輸順序:
??所以可以據(jù)此畫出狀態(tài)機圖:
廢話不多說,上代碼:
module IICModule( input CLK, // 50MHz時鐘頻率 input reset, input start, // 啟動信號,注意?。。有盘柛唠娖匠掷m(xù)時間最好在一個scl高/低周期左右,不可以很長 input WR_OR_RE, // 讀/寫控制,0=寫,1=讀 input [6:0]DeviecAddr, // 從設(shè)備器件地址,7bit input [7:0] RegisterAddr, // 寄存器地址 input [7:0] WriteData, // 要寫入的數(shù)據(jù) output reg scl, inout sda, output reg done, output reg [7:0] readData // 從從設(shè)備讀取的8bit數(shù)據(jù));parameter CLK_FREQ = 50_000_000 , SCL_FREQ = 100_000; // SCL的頻率設(shè)置為100kHzlocalparam SCL_CNT = CLK_FREQ/(2*SCL_FREQ); // 為使signalTap能夠觀察足夠長的窗口時間,我提高了頻率,實際不需要除2// 狀態(tài)機的狀態(tài)標識parameter IDLE =4"d0 ,START =4"d1 ,WR_ADDR =4"d2 ,WR_REGISTER =4"d3 , WR_DATA =4"d4 ,RE_START =4"d5 ,RE_ADDR =4"d6 ,RE_DATA =4"d7 ,STOP =4"d8 ;reg [7:0] state = IDLE , next_state = IDLE;// 設(shè)備地址// parameter DeviecAddr = 7"b0011101;reg [7:0] DeviecAddr_wr;reg [7:0] DeviecAddr_re;always@(*)begin DeviecAddr_wr = {DeviecAddr , 1"b0}; DeviecAddr_re = {DeviecAddr , 1"b1};end// scl & scl_cnt ---- 50MHz時鐘生成100kHzscl時鐘reg [7:0] scl_cnt;always@(posedge CLK)begin if(reset)begin scl <= 1"b1; scl_cnt <= 8"d0; end else begin if(scl_cnt == (SCL_CNT/2-1))begin scl <= ~scl; scl_cnt <= 8"d0; end else begin scl_cnt <= scl_cnt + 1"b1; end endend// scl_midHigh ---- 當處于scl高電平段的中間時,置1reg scl_midHigh;always@(posedge CLK)begin if(reset)begin scl_midHigh <= 1"b0; end else begin if((scl_cnt == (SCL_CNT/4-1)) & (scl == 1"b1))begin scl_midHigh <= 1"b1; end else begin scl_midHigh <= 1"b0; end endend// scl_midLow ---- 當處于scl低電平段的中間時,置1reg scl_midLow;always@(posedge CLK)begin if(reset)begin scl_midLow <= 1"b0; end else begin if((scl_cnt == (SCL_CNT/4-1)) & (scl == 1"b0))begin scl_midLow <= 1"b1; end else begin scl_midLow <= 1"b0; end endend// bit_cnt ---- 寫器件地址、寄存器、數(shù)據(jù)等都是一字節(jié)(8bit)// 在scl_midLow和scl_midHigh時均計數(shù)(8bit數(shù)據(jù)位 + 1bit應答位,所以計數(shù)范圍為0~17)reg [4:0] bit_cnt;always@(posedge CLK)begin if(reset)begin bit_cnt <= 5"d0; end else begin case(state) IDLE: bit_cnt <= 5"d0; START: bit_cnt <= 5"d0; WR_ADDR: begin if(scl_midHigh | scl_midLow)begin bit_cnt <= (bit_cnt == 5"d17 & scl_midLow)?5"d0:(bit_cnt + 1"b1); end else begin bit_cnt <= bit_cnt + 1"b0; end end WR_REGISTER: begin if(scl_midHigh | scl_midLow)begin bit_cnt <= (bit_cnt == 5"d17 & scl_midLow)?5"d0:(bit_cnt + 1"b1); end else begin bit_cnt <= bit_cnt + 1"b0; end end WR_DATA: begin if(scl_midHigh | scl_midLow)begin bit_cnt <= (bit_cnt == 5"d17 & scl_midLow)?5"d0:(bit_cnt + 1"b1); end else begin bit_cnt <= bit_cnt + 1"b0; end end RE_START: bit_cnt <= 5"d0; RE_ADDR: begin if(scl_midHigh | scl_midLow)begin bit_cnt <= (bit_cnt == 5"d17 & scl_midLow)?5"d0:(bit_cnt + 1"b1); end else begin bit_cnt <= bit_cnt + 1"b0; end end RE_DATA: begin if(scl_midHigh | scl_midLow)begin bit_cnt <= (bit_cnt == 5"d17 & scl_midLow)?5"d0:(bit_cnt + 1"b1); end else begin bit_cnt <= bit_cnt + 1"b0; end end STOP: bit_cnt <= 5"d0; default: bit_cnt <= 5"d0; endcase endend// link ---- 控制sda(inout信號)的控制權(quán),link == 0 時從設(shè)備控制, link == 1 時主設(shè)備控制reg link;assign sda = link?sda_reg:1"bz; // 1"bz表示由從設(shè)備控制數(shù)據(jù)總線always@(posedge CLK)begin if(reset)begin link <= 1"b1; end else begin case(state) IDLE: link <= 1"b1; START: link <= 1"b1; WR_ADDR: begin if(bit_cnt < 5"d16)begin link <= 1"b1; end else begin link <= 1"b0; end end WR_REGISTER: begin if(bit_cnt < 5"d16)begin link <= 1"b1; end else begin link <= 1"b0; end end WR_DATA: begin if(bit_cnt < 5"d16)begin link <= 1"b1; end else begin link <= 1"b0; end end RE_START: link <= 1"b1; RE_ADDR: begin if(bit_cnt < 5"d16)begin link <= 1"b1; end else begin link <= 1"b0; end end RE_DATA: begin if(bit_cnt < 5"d16)begin link <= 1"b0; end else begin link <= 1"b1; // 此時主設(shè)備不會產(chǎn)生應答信號,但是也要將控制權(quán)交給主設(shè)備 end end STOP: link <= 1"b1; default: link <= 1"b1; endcase endend// ack ---- 當主設(shè)備發(fā)送8bit數(shù)據(jù)后,將sda控制權(quán)釋放,從設(shè)備將sda拉低產(chǎn)生應答位reg ack;always@(posedge CLK)begin if(reset)begin ack <= 1"d0; end else begin case(state) IDLE: ack <= 1"d0; START: ack <= 1"d0; WR_ADDR: begin if(bit_cnt < 5"d16)begin ack <= 1"b0; end else if(bit_cnt == 5"d16 & scl_midHigh & ~sda)begin ack <= 1"b1; end else ack <= ack + 1"b0; end WR_REGISTER: begin if(bit_cnt < 5"d16)begin ack <= 1"b0; end else if(bit_cnt == 5"d16 & scl_midHigh & ~sda)begin ack <= 1"b1; end else ack <= ack + 1"b0; end WR_DATA: begin if(bit_cnt < 5"d16)begin ack <= 1"b0; end else if(bit_cnt == 5"d16 & scl_midHigh & ~sda)begin ack <= 1"b1; end else ack <= ack + 1"b0; end RE_START: ack <= 1"b0; RE_ADDR: begin if(bit_cnt < 5"d16)begin ack <= 1"b0; end else if(bit_cnt == 5"d16 & scl_midHigh & ~sda)begin ack <= 1"b1; end else ack <= ack + 1"b0; end RE_DATA: begin if(bit_cnt < 5"d16)begin ack <= 1"b0; end else if(bit_cnt == 5"d16 & scl_midHigh & ~sda)begin ack <= 1"b1; end else ack <= ack + 1"b0; end STOP: ack <= 1"b0; default: ack <= 1"b0; endcase endend// sda_reg_cnt ---- 8bit數(shù)據(jù)計數(shù),用作數(shù)據(jù)數(shù)組索引reg [3:0] sda_reg_cnt;always@(posedge CLK)begin if(reset)begin sda_reg_cnt <= 4"d7; end else begin case(state) IDLE: sda_reg_cnt <= 4"d7; START: sda_reg_cnt <= 4"d7; WR_ADDR: begin if(bit_cnt < 5"d16 & scl_midLow )begin sda_reg_cnt <= (sda_reg_cnt == 4"d0)?4"d7:(sda_reg_cnt - 1"b1); end else begin sda_reg_cnt <= sda_reg_cnt + 1"b0; end end WR_REGISTER: begin if(bit_cnt < 5"d16 & scl_midLow )begin sda_reg_cnt <= (sda_reg_cnt == 4"d0)?4"d7:(sda_reg_cnt - 1"b1); end else begin sda_reg_cnt <= sda_reg_cnt + 1"b0; end end WR_DATA: begin if(bit_cnt < 5"d16 & scl_midLow )begin sda_reg_cnt <= (sda_reg_cnt == 4"d0)?4"d7:(sda_reg_cnt - 1"b1); end else begin sda_reg_cnt <= sda_reg_cnt + 1"b0; end end RE_START: sda_reg_cnt <= 4"d7; RE_ADDR: begin if(bit_cnt < 5"d16 & scl_midLow )begin sda_reg_cnt <= (sda_reg_cnt == 4"d0)?4"d7:(sda_reg_cnt - 1"b1); end else begin sda_reg_cnt <= sda_reg_cnt + 1"b0; end end RE_DATA: sda_reg_cnt <= 4"d7; STOP: sda_reg_cnt <= 4"d7; default: sda_reg_cnt <= 4"d7; endcase endend// sda_reg ---- 在不同state時sda輸出不同的值,使用sda_reg來控制reg sda_reg;always@(posedge CLK)begin if(reset)begin sda_reg <= 1"b1; end else begin case(state) IDLE: sda_reg <= 1"b1; START: sda_reg <= 1"b0; WR_ADDR: begin if(bit_cnt < 5"d16)begin sda_reg <= DeviecAddr_wr[sda_reg_cnt]; end else begin sda_reg <= 1"b1; end end WR_REGISTER: begin if(bit_cnt < 5"d16)begin sda_reg <= RegisterAddr[sda_reg_cnt]; end else begin sda_reg <= 1"b1; // 這里sda_reg <= 1"b1是為了state==RE_START時方便產(chǎn)生start信號 end end WR_DATA: begin if(bit_cnt < 5"d16)begin sda_reg <= WriteData[sda_reg_cnt]; end else begin sda_reg <= 1"b0; // 這里sda_reg <= 1"b0是為了state==STOP時方便產(chǎn)生stop信號 end end RE_START: begin if(scl_midHigh & scl)begin sda_reg <= 1"b0; end else begin sda_reg <= sda_reg; end end RE_ADDR: sda_reg <= DeviecAddr_re[sda_reg_cnt]; RE_DATA: sda_reg <= 1"b0; // 這里sda_reg <= 1"b0是為了state==STOP時方便產(chǎn)生stop信號 STOP: begin // 產(chǎn)生stop信號 if(scl_midHigh & scl) sda_reg <= 1"b1; else sda_reg <= sda_reg + 1"b0; end default: sda_reg <= 1"b1; endcase endend// readData ---- 存儲從設(shè)備發(fā)回的數(shù)據(jù)always@(posedge CLK)begin if(reset)begin readData <= 8"d0; end else begin case(state) RE_DATA: begin if(bit_cnt < 5"d16 & scl_midHigh)begin readData <= {readData[6:0],sda}; //readData[sda_reg_cnt] <= sda; end else begin readData <= readData + 1"b0; end end default: readData <= readData + 1"b0; endcase endend// done ---- 一次讀/寫數(shù)據(jù)完成后置1always@(posedge CLK)begin if(reset)begin done <= 1"b0; end else begin if(state == STOP) done <= 1"b1; else done <= 1"b0; endend// 狀態(tài)機第一段always@(posedge CLK)begin if(reset)begin state <= IDLE; end else begin state <= next_state; endend// 狀態(tài)機第二段always@(*)begin case(state) IDLE: next_state = (start&scl)?START:IDLE; START: next_state = (scl_midLow&~scl)?WR_ADDR:START; WR_ADDR: begin if(bit_cnt == 5"d17 && scl_midLow && ack) next_state = WR_REGISTER
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/123012.html
摘要:如圖所示在了解起始條件和停止條件后,我們再來看看在這個過程中數(shù)據(jù)的傳輸是如何進行的。四參考資料通過接口實現(xiàn)溫濕度的采集硬件和軟件區(qū)別 stm32通過I2C接口實現(xiàn)...
摘要:使用庫讀寫環(huán)境光照度傳感器本文將教大家如何快速使用庫讀取光照度數(shù)據(jù)。五實驗樣機測試展示通過之前配置好的面板,通過涂鴉智能進行配網(wǎng)實時采集光照度傳感器的數(shù)據(jù)。 使用STM32 HAL庫讀寫環(huán)境光照度傳感器(BH1750) 本文將教大家如何快速使用STM32HAL庫讀取光照度數(shù)據(jù)。 實現(xiàn)功能:通...
摘要:總線掛載的外設(shè)有等。外設(shè)地址映射片上外設(shè)區(qū)分為三條總線,根據(jù)外設(shè)速度的不同,不同總線掛載著不同的外設(shè),掛載低速外設(shè),和掛載高速外設(shè)。 第二章 STM32資源介紹 2...
摘要:背景開發(fā)板具有接口,本文對該接口的使用方法做一介紹。同時,與設(shè)備要共地連接。例如向號總線上,設(shè)備地址,寄存器地址開始,讀取長度為個字節(jié)的數(shù)據(jù)。 0 背景 Jetson 開發(fā)板具有 I2C 接口,本文對該接口的使用方法做一介紹。以 Jetson TX2 為例,其它設(shè)備的方法類似,主要是硬件接口...
摘要:因為操作系統(tǒng)一直被看做是計算機軟件的基石。本系列是我學習操作系統(tǒng)的筆記,操作系統(tǒng)是以為例子。其他的操作系統(tǒng)也是差不多。將設(shè)備驅(qū)動一共分為個級別,每個級別的驅(qū)動初始化聲明宏定義及其在系統(tǒng)啟動過程中的啟動順序如下表所示。 老板說我技術(shù)需要有長進,不能只做一個crud boy。 于是我選來選去,...
閱讀 1017·2023-04-25 22:27
閱讀 871·2021-11-22 14:56
閱讀 984·2021-11-11 16:54
閱讀 1678·2019-08-30 15:54
閱讀 3499·2019-08-30 13:20
閱讀 1213·2019-08-30 10:55
閱讀 2080·2019-08-26 13:34
閱讀 3281·2019-08-26 11:53