最近一段時間工作上比較忙,一直沒有抽出空來寫文章與大家分享,這兩天騰出些時間靜下心來沉淀一番。看標題大家已經知道了是來總結I2C總線,我相信大家或多或少的都接觸過I2C總線,這篇文章我們就由淺入深的仔細來研究研究,看看能不能挖掘些新東西,加深一下理解。
先說概念
I2C英文全稱Inter-Integrated Circuit,字面意思是集成電路之間,也就是我們常說的I方C總線—I2C bus。它是一種串行通訊總線,使用多主從架構,由飛利浦公司(恩智浦NXP的母公司)在80年代開發,用于主板、嵌入式系統連接周邊低速設備。
I2C由兩條雙向開漏線組成,這是一個很大的優勢,接線簡單。兩條線利用上拉電阻將電位上拉。典型電位為+3.3V或+5V。標準傳輸速率為100Kb/s,低速模式10Kb/s。
物理層
下圖為I2C總線的物理拓撲圖,大家可以看到一共只有兩條總線,一條SDA(serial data)數據線用來承載數據、一條SCL(serial clock line)時鐘線用來控制數據收發時序。所有I2C設備的SDA都接到了總線的SDA上,SCL都接到了總線的SCL上。每個設備都有自己的唯一地址,以保證設備之間訪問的準確性。
I2C總線的物理拓撲圖
I2C在物理層的連接可以說是非常簡單,這也是它最大的優勢,原理就是通過控制SDA和SCL線的高低電平時序,來產生I2C總線協議所需要的信號進行數據傳輸。在總線處于空閑狀態時SCL和SDA被上拉電阻拉高,保持高電平。
需要注意的是I2C的通訊方式為半雙工,因為只有一條數據線,某一時刻只可能單向通訊。這也說明了I2C不適合大數據量的傳輸應用。
對于主機、從機的區分很簡單,發布主要命令的就是主機,接受命令的就是從機,同一條I2C總線允許多個主機的存在。
協議層
作為基礎我們先來了解幾個重要的小概念。
1、初始狀態(即空閑狀態):SDA與SCL均為上拉電阻所致的高電平時為初始狀態;
2、開始信號:當SCL為高電平的時候,SDA被拉低,此為開始信號,表明通訊開始。
3、終止信號:當SCL為高電平的時候,SDA被拉高,此為終止信號,表明本次通訊結束。
到這里大家有沒有發現點什么?當SCL處于高電平的時候,SDA電平一旦變化就會是一種信號,要么開始要么是終止。所以在數據傳輸過程中,SCL處于高電平時,SDA必須保持狀態穩定,只有SCL處于低電平時SDA才可以變化。
4、應答信號:當發送器向接收器發送完一個字節/8位數據后,第9個時鐘周期內,接收器必須給發送器一個應答信號,這樣數據才算傳輸成功。高電平表非應答,低電平表應答。
我們了解這幾個信號狀態后,來一步一步看看數據是如何傳輸的。
1、向從機設備的某一個寄存器寫一個字節數據:開始信號+設備地址(7位)+讀/寫(1位)+等待從機應答+寄存器地址(8位)+等待從機應答+要寫的數據(8位)+等待從機應答+終止信號。下圖為24C02 EEPROM存儲器寫數據的時序圖。
2、寫我們見識了,那讀一個試試:下圖為讀取24C02當前地址一個字節數據的時序圖,是不是一目了然了。值得注意的是當讀的時候地址7位后的讀寫狀態位為1。這里說一下為什么最后是NO ACK,在“讀”這個操作下,主機為接收器,主機的NO ACK表示停止接收24C02的數據,不然24C02會繼續發。
3、我們再讀一個長一點的:下圖為讀取24C02任意地址一個字節數據的時序圖。開始信號+設備地址(7位)+寫(1位)+等待從機應答+數據地址(8位)+等待從機應答。前面這一步為假寫,目的是告訴24C02要讀哪個地址的數據。繼續,開始信號+設備地址(7位)+讀(1位)+等待從機應答+讀到的數據(8位)+等待主機(接收機)應答+終止信號。
補點干貨
1、設備的地址。I2C設備的地址為8位,但是時序操作時最后一位不屬于地址,而是讀or寫狀態位。這就是為什么arduino的SH1106庫里操作的地址不是0x7-而是0x3-,因為有用的是前7位,地址整體右移一位處理了。再一個設備地址的前四位是固定死的,是廠家用來表示設備類型的,比如接口為I2C的溫度傳感器類設備地址前四位一般為1001即9X、EEPROM存儲器地址前四位一般為1010即AX、oled屏地址前四位一般為0111即7X等等。
2、I2C接口的致命缺點就是傳輸距離近同時速度慢。大家在使用I2C總線接口的時候切記不要長線傳輸,盡量只在PCB板內傳輸,不然偶爾丟數據甚至讀不到數據會讓人崩潰,不要問我是怎么知道的,問只有眼淚。
3、關于兩線為什么設計成開漏,這個問題我記得我之前在寫《STM32單片機I/O的8種工作模式》時給大家埋下過伏筆。今天就來說一下具體原因。主要有兩點①防止短路:大家想想如果不設為開漏,而設為推挽,幾個設備連在同一條總線上,這時某一設備的某個IO輸出高電平,另有一臺設備的某一個IO輸出低電平,這時你會發現這兩個IO的VCC和GND短路了;但是開漏就不會有這個問題,如下圖示:
第二個原因是“線與”,我們想個場景:如果總線上的一個A設備將SDA拉高,這時總線上另一個B設備已將SDA拉低,這時由于1&0=0,所以A設備檢查SDA的時候會發現不是高電平而是低電平,這就表明總線上已經有其他設備占用總線了,A只好放棄,如果檢測是高電平那就可以使用。如下圖示為24C02芯片內部圖,可以看到狀態檢測腳。
總結
I2C總線作為一個常見的總線協議,是非常值得我們來仔細研究琢磨的,通透以后我們再使用任意I2C接口的設備時就可以信手拈來了。我一直覺得在學習的過程中,“會使用”不一定就是我們追求的終點,會用的同時把一些更深的東西搞懂搞透會收獲意想不到的喜悅。