這是第三篇博文,本來是打算一天更一篇的,但是由於最近要上后摩爾時代的課程,爆肝三天去學了一下量子力學和量子計算機的入門原理,也就沒來得及更新。我是微電子專業的學生,雖然說今年這一屆已經把固體物理的課程取消了,但個人感覺還是要在固體物理以及半導體物理上下一番功夫,而這兩門物理的基礎就是量子力學(好像還要學數學物理方法),而量子力學又少不了近世代數和復變函數這兩項工具去把握要領,看來讀工科專業真是頭禿的料,早知道就去讀文科了(x)。
言歸正傳,對於I2C的使用,通常來說有兩種實現方法,一種是利用單片機自帶的硬件實現I2C,另外一個就是根據I2C協議和GPIO的輸出,來實現軟件I2C。雖然我沒有實操過I2C,但是我的很多朋友(巨佬)都和我說硬件I2C巨坑,會有很多玄學問題,相比之下軟件I2C會靠譜許多,於是就決定把軟件I2C給辦了。至於硬件I2C嘛.。。。嘛。。。有空再說!
其實想要用好I2C很簡單,只需要上CSDN上扒一些代碼移植移植就完事了。但是要是想真正用好I2C,個人認為理論基礎也必須要過關,所以上代碼之前,我們先來過一遍I2C的原理。
硬件的電氣連接:
I2C通信中,主機SDA端與從機SDA端相連,主機SCL端與從機SCL端相連,且必須共地,這樣就完成了我們的電氣連接。在I2C總線空閑時,SDA和SCL應該處於高電平,我們上百度一搜索就能發現參考電路有一個上拉電阻。但是不是意味着我們也要接一個上拉電阻呢,個人認為未必。因為接上拉電阻的前提是單片機的GPIO輸出是開漏輸出,但是對於MSP432來說,似乎根本沒有在STM32中開漏輸出和推挽輸出的概念(至少我查文獻是查不到的),因此我們連這個輸出是不是開漏輸出都搞不清楚,直接接上一個上拉電阻恐怕並非良策。針對該問題的解決方案是,輸出低電平的時候使用單片機的輸出模式輸出低電平,輸出高電平的時候使用單片機的輸入上拉模式讓總線處於高電平。有的人可能會問你這不是脫褲子放屁嘛,明明我可以只靠輸出模式來拉高電平,搞那么復雜干什么?似乎很對,但是我們想一下I2C通信中,從機應答的時候是要主機交出SDA線的控制權的,那么這個時候主機在這根線輸出高電平,從機在這根線輸出低電平會導致什么后果?(我也不知道什么后果)亦或者說總線上有兩個主機,那在一個主機空閑的時候用輸出把電平拉高了,那另外一個主機想發起通信的時候他還能把電平拉低嘛?這種聽起來就很危險的實驗我沒有做過,所以無從得知是否會有不良后果,但是如果我們可以通過上拉輸入的方式來輸出高電平,那是不是上面的矛盾都完全解決了呢?所以嘛,何樂而不為。
我們看到電氣連接圖中有時鍾信號線,這意味着I2C通信是一個同步通信,有無時鍾信號線是同步通信與異步通信的最主要的區別。
為了理解時鍾線在信息傳輸中的作用,我舉一個比較簡單的例子:假設你是主機,我是從機,你的手上有一面紅旗,我們編碼:舉起紅旗代表1,放下紅旗代表0,我們兩個的位置相距很遠無法交談以至於你要用紅旗來給我傳輸信息,那么,當你要給我傳輸信息"1111"時你會怎么做呢?有的人會說,那就連續舉起四次紅旗啊。但其實這樣的想法是錯誤的,因為"舉起"的動作前提必然是"放下",如果你連續舉起了四次紅旗,在我看來,你是"舉起-放下-舉起-放下-舉起-放下-舉起",按照編碼規則,我認為你傳輸的信息應該是"1010101",這樣一來信息的傳遞就出現了誤差。你一看,欸,多了三個零,那你一直舉着紅旗不就行了?這樣一來你給我發送的信息必定只有1而沒有0。但問題又來了,你一直舉着紅旗,我怎么知道你要給我傳輸的數據是"1111111"(7個1)還是"11"(2個1)還是其他位數的"11111…"呢?在你發出同樣信息的前提下,我對你發出的信息的采樣起始時間,采樣頻率決定了我接收到的信息是什么樣的。
那如何確定信息采樣的起始時間和采樣頻率,才能讓我接收到的信息恰恰就是你想表達的信息呢?這里有兩個解決方案:
方案1:
我和你都去買一塊10塊錢的電子手表,我們兩個手表的時間都是一樣地轉動,我和你約定,在你作出某一個特定動作(隨便什么都行)的一瞬間,我和你同時開始掐表計時,約定每隔5秒就對信息進行一次采樣。那如果你要給我傳輸的第一個信息是0,你就在第一個5秒前放下紅旗,到了第一個5秒的時刻,我一看,發現你是放下紅旗,那么我就記錄下一個0;如果你要給我傳輸的第二個信息是1,那就在第二個5秒前舉起紅旗,我一看,就能記錄下一個1,以此類推。這里要注意的是,既然以及約定好了采樣的時間點,那么在兩個相鄰時間點之間,我是沒有必要看你做了什么動作的(可能是因為我比較懶x),你在這段時間可以干任何事情(比如說放松下手臂),只要在我采樣的時間點你能夠給我你想要給的信息就行。換句話說,信息只在某一個特定的時間點才進行傳輸,而不允許在相鄰兩個時間點之間進行傳輸,就算你心血來潮想要在這段時間內多傳輸幾個比特的信息,也沒有用,因為我根本不會看(就算看了我也不知道你是在休息還是在傳遞信息)。
方案2:
你去買多一面藍旗,我們約定,當你舉起藍旗的瞬間,我就去看一下你拿紅旗的手是舉起還是放下,從而記錄下1或0,這樣也能夠接收到准確的信息。
如果你以前對單片機的幾種通信比較了解,那么你一定能夠看出,我所描述的第一種通信方式就是異步通信,第二種通信方式就是同步通信。
就我個人理解,異步通信和同步通信都需要時鍾,只不過異步通信的時鍾是同時存在於主機和從機的"北京時間",而同步通信的時鍾是由主機單方面控制的"紅綠燈"。
好,有了同步和異步通信的概念,我們就能夠通過約定一系列的通信規則,來准確傳輸信息,這也就是我們所說的通信協議。I2C通信就是一種同步通信,而研究現有的同步通信的通信協議,是絕對少不了"時序圖"這一概念的,所以在開始學習I2C協議前,我們先來了解一下兩個時序圖:(以下部分借用了網絡圖片,如有侵權請與我聯系)
1.方波時序圖
這樣的時序圖代表着信號線的電平在0和1之間反復橫跳,按這種規律變化的信號線常常被人們用來傳輸時鍾信號(類似於前面舉起藍旗又放下的例子)。由於實際器件是無法做到瞬時改變電壓值的,所以在跳變沿的地方有時也會是一個斜線而不是一條垂直線。
2.Either or (非此即彼)信號時序圖
我們可以看到,第二條信號的時序圖有很多交叉,在同一時間點電平又上又下的,令人迷惑。但其實它的意思是,在"又上又下"的地方,你可以置高電平,或者也可以置低電平(不存在又高又低的電平,這里不是量子力學),而這個電平就代表着你想傳輸出去的信息。
好,理解了這兩個基本的時序圖,我們就足以閱讀I2C的時序圖了。
首先我們來看信號的起始與終止的時序圖:
觀察時序圖可以發現以下規律:
信號的起始:SCL高電平時,SDA產生一個下降沿
信號的終止:SCL高電平時,SDA產生一個上升沿
另外要記得,在I2C通訊的大部分過程中,SDA和SCL線的控制權都在主機手上(后面會提到從機控制的情況)。
接下來,我們看看數據是如何同步傳輸的:
SDA線置為高電平代表1,置為低電平代表0,在從機進行數據采樣之前,SCL被置於低電位,而在采樣時,SCL線拉高,此時SDA線就不允許切換電平了,它處在什么電平就決定了從機讀到的數據是0還是1。而當SCL線被拉回低電位的時候,SDA線既可以切換電平,也可以保持不變,為下一比特信息的傳輸作准備。
好,我們來看數據傳輸的字節格式:
對於I2C通信而言,可以無限制地傳輸字節,但是每一個字節的長度必須是8bit。我們知道I2C通信一種總線通信,一個總線上可能會掛有許多部從機,但每一個從機都不一定是同樣功能的,我們希望一個主機調遣目標從機時不會對其他的從機產生影響,因此,主機在發起通信后,必須先發送地址位進行尋址,只有地址與主機輸出相符的從機才會有響應,這個地址位一般是占用7bit。另外,主機還需要告訴從機自己是想對它進行讀還是寫的操作,因此還要再加多1bit存放該信息,其中"寫"代表"0","讀"代表"1"。這樣一來,主機發送的第一字在發送完8位數據之后,就確定了應答的從機是哪一個、以及它的工作模式。
當完成一個字節的發送之后,需要有一個來自從機的響應告訴主機已經收到了信息。因為即使主機聯系了從機要他去干活,但沒准別人正在忙別的沒看微信,也不知道你發了消息過來,如果從機沒有收到主機的報文,那么后面主機的數據發送將毫無意義。因此,從機響應是必須的。具體的方法是,當8位數據傳輸完以后,主機繼續控制SCL線,但釋放SDA線(也就是用上拉輸入置於高電平),SDA線的控制權交給從機,這時從機如果收到了信號,便會把SDA線拉低。SCL置高時,主機發現SDA變低電平了,就能夠得知從機已經收到了數據,於是就可以繼續發送下一個字節的信息了。如果從機沒有響應,那主機只好重新發起通信。
呼~ 終於把I2C的原理給好好捋一遍了,學校自習室也趕人關門了,就先寫到這里吧。具體的代碼實現與實際應用我會在下一篇博客中進行講解。