對 IIC 總線的理解、調用函數以及常見面試問題

1、IIC 總線概述:linux

IIC 即Inter-Integrated Circuit(集成電路總線)
I2C總線是PHLIPS公司推出的一種串行總線, I2C總線只有兩根雙向信號線。一根是數據線SDA,另外一根是時鐘線SCL。
這裏寫圖片描述面試

每一個接到I2C總線上的器件都有惟一的地址。主機與其它器件間的數據傳送能夠是由主機發送數據到其它器件,這時主機即爲發送器。由總線上接收數據的器件則爲接收器。
這裏寫圖片描述算法

2、IIC 總線通訊協議:數組

要掌握IIC的通訊協議,須要掌握如下6個通訊信號:
1.起始信號
2.終止信號
3.寫數據
4.讀數據
5.應答信號
6.非應答信號安全

  • 起始和終止信號
    SCL線爲高電平期間,SDA線由高電平向低電平的變化表示起始信號;SCL線爲高電平期間,SDA線由低電平向高電平的變化表示終止信號。服務器

  • 應答信號
    IIC 總線協議規定,每傳送一個字節數據後,都要有一個應答信號以肯定數據傳送是否被對方收到。應答信號由接受設備產生,在SCL爲高電平期間,接受設備將SDA拉低爲低電平,表示數據傳輸正確,產生應答(ACK)markdown

  • 數據傳送
    I2C總線進行數據傳送時,時鐘信號爲高電平期間,數據線上的數據必須保持穩定,只有在時鐘線上的信號爲低電平期間,數據線上的高電平或低電平狀態才容許變化。網絡

根據AT24C02的芯片,可編寫如下信號函數程序:數據結構

//1.起始信號 SCL線爲高電平期間,SDA線由高電平向低電平的變化表示起始信號; void IIC_Start(void) { SDA = 1; SCL = 1; delay_us(1); //15us >> 4.7us SDA = 0; delay_us(1); SCL = 0; } //2.終止信號 SCL線爲高電平期間,SDA線由低電平向高電平的變化表示終止信號。 void IIC_Stop(void) { SDA = 0; SCL = 1; delay_us(1); //15us >> 4.7us SDA = 1; delay_us(1); SCL = 0; } void IIC_SendByte(unsigned char dat) //3.寫數據 { unsigned char i; for (i = 0; i < 8; i++) { if((dat<<i)&0x80) { SDA = 1; } else { SDA = 0; } SCL = 1; //開始讓數據維持穩定 delay_us(1); SCL = 0; delay_us(1); } SDA = 1; //釋放總線 , 發送完8位,主機置高電平 SCL = 1; delay_us(1); if (SDA) //SDA 低電平 從機回饋低電平 { ack = 0; //0 == ack 表明無ack信號, 從機不該答,發送不成功 } else { ack = 1; //從機應答,發送成功 } SCL = 0; delay_us(5); } unsigned char IIC_RecvByte(void) //4. 讀數據 { unsigned char i, temp; SDA = 1; //保險 高的 與 上低的 是低的, 線與 for (i = 0; i < 8; i++) { SCL = 0; // 告訴 數據能夠變化 SDA 脈衝線 //只有在時鐘線上的信號爲低電平期間,數據線上的高電平或低電平狀態才容許變化。 delay_us(1); SCL = 1; // 數據保持穩定, 開始讀 delay_us(1); temp<<=1; if (SDA) { temp = temp + 1; } } SCL = 0; delay_us(10); return temp; } void IIC_ACK(void) //5. 應答信號 { SDA = 0; SCL = 1; delay_us(1); SCL = 0; } void IIC_NOACK(void) //6. 非應答信號 { SDA = 1; SCL = 1; delay_us(1); SCL = 0; }

根據時序圖,可編寫24C02的讀寫函數程序:
這裏寫圖片描述架構

unsigned char AT24CXX_WriteStr(unsigned char devaddr, unsigned char romaddr, unsigned char *s, unsigned char num) { unsigned char i; IIC_Start(); IIC_SendByte(devaddr); if (0 == ack) { return 0; } IIC_SendByte(romaddr); if (0 == ack) { return 0; } for (i = 0; i < num; i++) { IIC_SendByte(*s); if (0 == ack) { return 0; } s++; } IIC_Stop(); return 1; }

這裏寫圖片描述

unsigned char AT24CXX_ReadStr(unsigned char devaddr, unsigned char romaddr, unsigned char *s, unsigned char num) { unsigned char i; IIC_Start(); IIC_SendByte(devaddr); if (0 == ack) //無應答 返回0 失敗 { return 0; } IIC_SendByte(romaddr); if (0 == ack) { return 0; } IIC_Start(); IIC_SendByte(devaddr + 1); if (0 == ack) { return 0; } for (i = 0; i < num - 1; i++) { *s = IIC_RecvByte(); IIC_ACK(); s++; } *s = IIC_RecvByte(); IIC_NOACK(); IIC_Stop(); return 1; }

3、有關 IIC 總線常見面試題:(參考)

  • 介紹一下你瞭解的I2C?

I2C總線是飛利浦(PHLIPS)公司推出的一種串行總線,用於鏈接微控制器及其外圍設備, I2C串行總線有兩根雙向信號線。一根是數據線SDA,另外一根是時鐘線SCL。 它僅經過兩根信號線就能夠完成對全部掛載在I2C總線上的從器件進行操做。這樣的好處是能夠大大的節省咱們微處理器的IO口資源。

  • I2C到底能夠掛載多少個器件呢?

答:IIC協議規定,在啓動總線後第1字節的高7位是從節點的尋址地址,其中高四位爲器件類型識別符,接着三位爲片選,最後一位爲讀寫位,當爲1時爲讀操做,爲0時爲寫操做,因此具體掛載多少個器件由I2C地址決定,7位尋址地址減去1個廣播地址0x00不用,因此有2^7=128 - 1 = 127,那就是127個地址, 因此理論上能夠掛127個從器件。

  • I2C如何同時掛載多個同一種器件(地址相同的器件)?

答:理論上是不會這樣設計的,若是必定要這樣作的話,能夠經過硬件上設計,控制器件是否掛載總線來實現(方法可用一個開關電路切斷器件SDA或者SCL是否接入總線來實現)

  • I2C總線的主機與從機之間是如何通訊的呢?

I2C總線的主機與從機之間的通訊主要和I2C的時序有關。在通訊開始的時候SCL與SDA都置爲高電平,此時爲總線空閒時間。當SCL爲高電平期間SDA的電平被拉低,標誌這總線的啓動。當SCL爲高電平期間SDA的電平被拉高,標誌這總線的終止。在進行數據傳送時,SCL爲高電平期間,SDA上的數據必須保持穩定,只有在SCL的信號爲低電平時,SDA上的高電平才容許變化。因此只要咱們根據芯片手冊正確的寫好IIC的時序,按時序發送器件地址(不一樣的器件的地址不一樣)以及數據,就可使主機與從機之間通訊。

  • I2C總線的仲裁你知道嗎?

總線上可能掛接有多個器件,有時會發生兩個或多個主器件同時想佔用總線的狀況,這種狀況叫作總線競爭。I2C總線具備多主控能力,能夠對發生在SDA線上的總線競爭進行仲裁,其仲裁原則是這樣的:當多個主器件同時想佔用總線時,若是某個主器件發送高電平,而另外一個主器件發送低電平,則發送電平與此時SDA總線電平不符的那個器件將自動關閉其輸出級。總線競爭的仲裁是在兩個層次上進行的。首先是地址位的比較,若是主器件尋址同一個從器件,則進入數據位的比較,從而確保了競爭仲裁的可靠性。因爲是利用I2C總線上的信息進行仲裁,所以不會形成信息的丟失。

  • I2C時鐘信號(SCL)的同步問題

在I2C總線上傳送信息時的時鐘同步信號是由掛接在SCL線上的全部器件的邏輯「與」完成的。SCL線上由高電平到低電平的跳變將影響到這些器件,一旦某個器件的時鐘信號下跳爲低電平,將使SCL線一直保持低電平,使SCL線上的全部器件開始低電平期。此時,低電平週期短的器件的時鐘由低至高的跳變並不能影響SCL線的狀態,因而這些器件將進入高電平等待的狀態。當全部器件的時鐘信號都上跳爲高電平時,低電平期結束,SCL線被釋放返回高電平,即全部的器件都同時開始它們的高電平期。其後,第一個結束高電平期的器件又將SCL線拉成低電平。這樣就在SCL線上產生一個同步時鐘。可見,時鐘低電平時間由時鐘低電平期最長的器件肯定,而時鐘高電平時間由時鐘高電平期最短的器件肯定。

  • I2C總線的其餘注意點

一、進行數據傳送時,在SCL爲高電平期間,SDA線上電平必須保持穩定,只有SCL爲低時,才容許SDA線上電平改變狀態。而且每一個字節傳送時都是高位在前。
二、對於應答信號,ACK=0時爲有效應答位,說明從機已經成功接收到該字節,若爲1則說明接受不成功。
三、若是從機須要延遲下一個數據字節開始傳送的時間,能夠經過把SCL電平拉低並保持來強制主機進入等待狀態。
四、主機完成一次通訊後還想繼續佔用總線在進行一次通訊,而又不釋放總線,就要利用重啓動信號Sr。它既做爲前一次數據傳輸的結束,又做爲後一次傳輸的開始。
五、總線衝突時,按「低電平優先」的仲裁原則,把總線判給在數據線上先發送低電平的主器件。
六、在特殊狀況下,若需禁止全部發生在I2C總線上的通訊,可採用封鎖或關閉總線,具體操做爲在總線上的任一器件將SCL鎖定在低電平便可。
七、SDA仲裁和SCL時鐘同步處理過程沒有前後關係,而是同時進行的。

 

 

I2C的基礎概念和框架 

1、IIC 基礎概念

       IIC(Inter-Integrated Circuit)總線是一種由PHILIPS公司開發的兩線式串行總線,用於鏈接微控制器及其外圍設備。IIC總線產生於在80年代,最初爲音頻和視頻設備開發,現在主要在服務器管理中使用,其中包括單個組件狀態的通訊。例如管理員可對各個組件進行查詢,以管理系統的配置或掌握組件的功能狀態,如電源和系統風扇。可隨時監控內存、硬盤、網絡、系統溫度等多個參數,增長了系統的安全性,方便了管理。


一、 IIC總線的特色

       IIC總線最主要的優勢是其簡單性有效性。因爲接口直接在組件之上,所以IIC總線佔用的空間很是小,減小了電路板的空間和芯片管腳的數量,下降了互聯成本。總線的長度可高達25英尺,而且可以以10Kbps的最大傳輸速率支持40個組件。IIC總線的另外一個優勢是,它支持多主控(multimastering), 其中任何可以進行發送和接收的設備均可以成爲主總線。一個主控可以控制信號的傳輸和時鐘頻率。固然,在任什麼時候間點上只能有一個主控。


二、IIC總線工做原理

a -- 總線構成

      IIC總線是由數據線SDA和時鐘SCL構成的串行總線,可發送和接收數據。在CPU與被控IC之間、IC與IC之間進行雙向傳送,最高傳送速率100kbps。各類被控制電路均並聯在這條總線上,但就像電話機同樣只有撥通各自的號碼才能工做,因此每一個電路和模塊都有惟一的地址,在信息的傳輸過程當中,IIC總線上並接的每一模塊電路既是主控器(或被控器),又是發送器(或接收器),這取決於它所要完成的功能。

CPU發出的控制信號分爲地址碼控制量兩部分:

1) 地址碼用來選址,即接通須要控制的電路,肯定控制的種類;

2) 控制量決定該調整的類別(如對比度、亮度等)及須要調整的量。

    這樣,各控制電路雖然掛在同一條總線上,卻彼此獨立,互不相關。

b -- 信號類型

    IIC總線在傳送數據過程當中共有四種類型信號:

開始信號:SCL爲高電平時,SDA由高電平向低電平跳變,開始傳送數據;

結束信號:SCL爲高電平時,SDA由低電平向高電平跳變,結束傳送數據;

數據傳輸信號:在開始條件之後,時鐘信號SCL的高電平週期期問,當數據線穩定時,數據線SDA的狀態表示數據有效,即數據能夠被讀走,開始進行讀操做。在時鐘信號SCL的低電平週期期間,數據線上數據才容許改變。每位數據須要一個時鐘脈衝。

應答信號:接收數據的IC在接收到8bit數據後,向發送數據的IC發出特定的低電平脈衝,表示已收到數據。CPU向受控單元發出一個信號後,等待受控單元發出一個應答信號,CPU接收到應答信號後,根據實際狀況做出是否繼續傳遞信號的判斷。若未收到應答信號,由判斷爲受控單元出現故障。

 

      目前有不少半導體集成電路上都集成了IIC接口。帶有IIC接口的單片機有:CYGNAL的 C8051F0XX系列,PHILIPSP87LPC7XX系列,MICROCHIP的PIC16C6XX系列等。不少外圍器件如存儲器、監控芯片等也提供IIC接口。

三、總線基本操做

     IIC規程運用主/從雙向通信。器件發送數據到總線上,則定義爲發送器,器件接收數據則定義爲接收器。主器件和從器件均可以工做於接收和發送狀態。 總線必須由主器件(一般爲微控制器)控制,主器件產生串行時鐘(SCL)控制總線的傳輸方向,併產生起始和中止條件。SDA線上的數據狀態僅在SCL爲低電平的期間才能改變,SCL爲高電平的期間,SDA狀態的改變被用來表示起始和中止條件

a -- 控制字節

      在起始條件以後,必須是器件的控制字節,其中高四位爲器件類型識別符(不一樣的芯片類型有不一樣的定義,EEPROM通常應爲1010),接着三位爲片選,最後一位爲讀寫位,當爲1時爲讀操做,爲0時爲寫操做。

b -- 寫操做

      寫操做分爲字節寫和頁面寫兩種操做,對於頁面寫根據芯片的一次裝載的字節不一樣有所不一樣。關於頁面寫的地址、應答和數據傳送的時序。

c -- 讀操做

      讀操做有三種基本操做:當前地址讀、隨機讀和順序讀。圖4給出的是順序讀的時序圖。應當注意的是:最後一個讀操做的第9個時鐘週期不是「不關心」。爲告終束讀操做,主機必須在第9個週期間發出中止條件或者在第9個時鐘週期內保持SDA爲高電平、而後發出中止條件。

d -- 總線仲裁

       主機只能在總線空閒的時候啓動傳輸。兩個或多個主機可能在起始條件的最小持續內產生一個起始條件,結果在總線上產生一個規定的起始條件。

       當SCL線是高電平時,仲裁在SDA線發生這樣,在其餘主機發送低電平時,發送高電平的主機將斷開它的數據輸出級,由於總線上的電平和它本身的電平不一樣。

      仲裁能夠持續多位。從地址位開始,同一個器件的話接着就是數據位(若是主機-發送器),或者比較相應位(若是主機-接收器)。IIC總線的地址和數據信息由贏得仲裁的主機決定,在這個過程當中不會丟失信息。

仲裁不能在下面狀況之間進行:

1)重複起始條件和數據位;
2)中止條件和數據位;
3)重複起始條件和中止條件。


四、特性總結

      IIC確定是2線的(不算地線)IIC協議確實很科學,比3/4線的SPI要好,固然線多通信速率相對就快了

IIC的原則是

a -- 在SCL=1(高電平)時,SDA千萬別忽悠!!!不然,SDA下跳則"判罰"爲"起始信號S",SDA上跳則"判罰"爲"中止信號P".

b -- 在SCL=0(低電平)時,SDA隨便忽悠!!!(可別忽悠過火到SCL跳高)

c -- 每一個字節後應該由對方回送一個應答信號ACK作爲對方在線的標誌.非應答信號通常在全部字節的最後一個字節後.通常要由雙方協議簽訂.

d -- SCL必須由主機發送,不然天下大亂

e -- 首字節是"片選信號",即7位從機地址加1位方向(讀寫)控制.從機收到(聽到)本身的地址才能發送應答信號(必須應答!!!)表示本身在線.其餘地址的從機不容許忽悠!!!(固然羣呼能夠忽悠但只能聽不準說話)

f -- 讀寫是站在主機的立場上定義的."讀"是主機接收從機數據,"寫"是主機發送數據給從機.

g-- 重複位主要用於主機從發送模式到接收模式的轉換"信號",因爲只有2線,因此收發轉換確定要比SPI複雜,由於SPI可用不一樣的邊沿來收發數據,而IIC不行.

h -- 在硬件IIC模塊,特別是MCU/ARM/DSP等每一個階段都會獲得一個準確的狀態碼,根據這個狀態碼能夠很容易知道如今在什麼狀態和什麼出錯信息.

i -- 7位IIC總線能夠掛接127個不一樣地址的IIC設備,0號"設備"做爲羣呼地址.10位IIC總線能夠掛接更多的10位IIC設備.

 

2、 Linux下IIC驅動架構

         Linux定義了系統的IIC驅動體系結構,在Linux系統中,IIC驅動由3部分組成,即IIC核心IIC總線驅動IIC設備驅動。這3部分相互協做,造成了很是通用、可適應性很強的IIC框架。

 


 

       上圖完整的描述了linux i2c驅動架構,雖然I2C硬件體系結構比較簡單,可是i2c體系結構在linux中的實現卻至關複雜。

       那麼咱們如何編寫特定i2c接口器件的驅動程序?就是說上述架構中的那些部分須要咱們完成,而哪些是linux內核已經完善的或者是芯片提供商已經提供的?

 

一、架構層次分類

   第一層:提供i2c adapter的硬件驅動,探測、初始化i2c adapter(如申請i2c的io地址和中斷號),驅動soc控制的i2c adapter在硬件上產生信號(start、stop、ack)以及處理i2c中斷。覆蓋圖中的硬件實現層

   第二層:提供i2c adapter的algorithm,用具體適配器的xxx_xferf()函數來填充i2c_algorithm的master_xfer函數指針,並把賦值後的i2c_algorithm再賦值給i2c_adapter的algo指針。覆蓋圖中的訪問抽象層、i2c核心層

   第三層:實現i2c設備驅動中的i2c_driver接口,用具體的i2c device設備的attach_adapter()、detach_adapter()方法賦值給i2c_driver的成員函數指針。實現設備device與總線(或者叫adapter)的掛接覆蓋圖中的driver驅動層

  第四層:實現i2c設備所對應的具體device的驅動,i2c_driver只是實現設備與總線的掛接,而掛接在總線上的設備則是千差萬別的,因此要實現具體設備device的write()、read()、ioctl()等方法,賦值給file_operations,而後註冊字符設備(多數是字符設備)。覆蓋圖中的driver驅動層

 

     第一層和第二層又叫i2c總線驅動(bus),第三第四屬於i2c設備驅動(device driver)

     在linux驅動架構中,幾乎不須要驅動開發人員再添加bus,由於linux內核幾乎集成全部總線bus,如usb、pci、i2c等等。而且總線bus中的(與特定硬件相關的代碼)已由芯片提供商編寫完成,例如三星的s3c-2440平臺i2c總線bus爲/drivers/i2c/buses/i2c-s3c2410.c

      第三第四層與特定device相干的就須要驅動工程師來實現了

 

二、Linux下I2C驅動體系結構三部分詳細分析

a -- IIC核心

       IIC 核心提供了IIC總線驅動和設備驅動的註冊、註銷方法,IIC通訊方法(即「algorithm」,筆者認爲直譯爲「運算方法」並不合適,爲免引發誤解, 下文將直接使用「algorithm」)上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。

     在咱們的Linux驅動的i2c文件夾下有algos,busses,chips三個文件夾,另外還有i2c-core.ci2c-dev.c兩個文件。

     i2c-core.c文件實現了I2Ccore框架,是Linux內核用來維護和管理的I2C的核心部分,其中維護了兩個靜態的List,分別記錄系統中的I2Cdriver結構和I2Cadapter結構。I2Ccore提供接口函數,容許一個I2Cadatper,I2Cdriver和I2Cclient初始化時在I2Ccore中進行註冊,以及退出時進行註銷。同時還提供了I2C總線讀寫訪問的通常接口,主要應用在I2C設備驅動中。

 

b -- IIC總線驅動

       IIC總線驅動是對IIC硬件體系結構中適配器端的實現,適配器可由CPU控制,甚至直接集成在CPU內部。總線驅動的職責,是爲系統中每一個I2C總線增長相應的讀寫方法。可是總線驅動自己並不會進行任何的通信,它只是存在那裏,等待設備驅動調用其函數。

     IIC總線驅動主要包含了IIC適配器數據結構i2c_adapterIIC適配器的algorithm數據結構i2c_algorithm控制IIC適配器產生通訊信號的函數。經由IIC總線驅動的代碼,咱們能夠控制IIC適配器以主控方式產生開始位、中止位、讀寫週期,以及以從設備方式被讀寫、產生ACK等。

 Busses文件夾下的i2c-mpc.c文件實現了PowerPC下I2C總線適配器驅動,定義描述了具體的I2C總線適配器的i2c_adapter數據結構,實現比較底層的對I2C總線訪問的具體方法。I2Cadapter 構造一個對I2Ccore層接口的數據結構,並經過接口函數向I2Ccore註冊一個控制器。I2Cadapter主要實現對I2C總線訪問的算法,iic_xfer() 函數就是I2Cadapter底層對I2C總線讀寫方法的實現。同時I2Cadpter 中還實現了對I2C控制器中斷的處理函數。

 

c -- IIC設備驅動

      IIC設備驅動是對IIC硬件體系結構中設備端的實現,設備通常掛接在受CPU控制的IIC適配器上,經過IIC適配器與CPU交換數據。設備驅動則是與掛在I2C總線上的具體的設備通信的驅動。經過I2C總線驅動提供的函數,設備驅動能夠忽略不一樣總線控制器的差別,不考慮其實現細節地與硬件設備通信。

      IIC設備驅動主要包含了數據結構i2c_driver和i2c_client,咱們須要根據具體設備實現其中的成員函數。

     i2c-dev.c文件中實現了I2Cdriver,提供了一個通用的I2C設備的驅動程序,實現了字符類型設備的訪問接口,實現了對用戶應用層的接口,提供用戶程序訪問I2C設備的接口,包括實現open,release,read,write以及最重要的ioctl等標準文件操做的接口函數。咱們能夠經過open函數打開 I2C的設備文件,經過ioctl函數設定要訪問從設備的地址,而後就能夠經過 read和write函數完成對I2C設備的讀寫操做。

    經過I2Cdriver提供的通用方法能夠訪問任何一個I2C的設備,可是其中實現的read,write及ioctl等功能徹底是基於通常設備的實現,全部的操做數據都是基於字節流,沒有明確的格式和意義。爲了更方便和有效地使用I2C設備,咱們能夠爲一個具體的I2C設備開發特定的I2C設備驅動程序,在驅動中完成對特定的數據格式的解釋以及實現一些專用的功能。

 

三、重要的結構體

      由於IIC設備種類太多,若是每個IIC設備寫一個驅動程序,那麼顯得內核很是大。不符合軟件工程代碼複用,因此對其層次話:

     這裏簡單的將IIC設備驅動分爲設備層總線層。理解這兩個層次的重點是理解4個數據結構,這4個數據結構是i2c_driver、i2c_client、i2c_algorithm、i2c_adapteri2c_driver、i2c_client屬於設備層;i2c_algorithm、i2c_adapter屬於總線型。以下圖:

    設備層關係到實際的IIC設備,總線層包括CPU中的IIC總線控制器和控制總線通訊的方法。值得注意的是:一個系統中可能有不少個總線層,也就是包含多個總線控制器;也可能有多個設備層,包含不一樣的IIC設備

        由IIC總線規範可知,IIC總線由兩條物理線路組成,這兩條物理線路是SDA和SCL。只要鏈接到SDA和SCL總線上的設備均可以叫作IIC設備。

a -- i2c_client 

       一個IIC設備由i2c_client數據結構進行描述:

  1. struct  i2c_client  
  2. {  
  3.     unsigned short  flags;                          //標誌位  
  4.     unsigned short  addr;                //設備的地址,低7位爲芯片地址  
  5.     char name[I2C_NAME_SIZE];             //設備的名稱,最大爲20個字節  
  6.     struct  i2c_adapter *adapter;           //依附的適配器i2c_adapter,適配器指明所屬的總線  
  7.     struct  i2c_driver *driver;             //指向設備對應的驅動程序  
  8.     struct device  dev;                 //設備結構體  
  9.     int irq;                       //設備申請的中斷號  
  10.     struct list_head  list;                //鏈接到總線上的全部設備  
  11.     struct list_head   detected;           //已經被發現的設備鏈表  
  12.     struct completion  released;           //是否已經釋放的完成量  
  13. };  

設備結構體i2c_client中addr的低8位表示設備地址。設備地址由讀寫位、器件類型和自定義地址組成,以下圖:

第7位是R/W位,0表示寫,2表示讀,因此I2C設備一般有兩個地址,即讀地址和寫地址;

類型器件由中間4位組成,這是由半導體公司生產的時候就已經固化了;

自定義類型由低3位組成。由用戶本身設置;

 

     IIC設備還有一些重要的注意事項:

一、i2c_client數據結構是描述IIC設備的「模板」,驅動程序的設備結構中應包含該結構;

二、adapter指向設備鏈接的總線適配器,系統可能有多個總線適配器。內核中靜態指針數組adapters記錄全部已經註冊的總線適配器設備;

三、driver是指向設備驅動程序,這個驅動程序是在系統檢測到設備存在時賦值的;

 

b -- IIC設備驅動     i2c_driver

  1. struct  i2c_driver  
  2. {  
  3.     int id;                         //驅動標識ID  
  4.     unsigned int class;               //驅動的類型  
  5.     int (*attach_adapter)(struct i2c_adapter *);             //當檢測到適配器時調用的函數  
  6.     int (*detach_adapter)(struct i2c_adapter*);              //卸載適配器時調用的函數  
  7.     int (*detach_client)(struct i2c_client *)   __deprecated;             //卸載設備時調用的函數  
  8.     
  9.      //如下是一種新類型驅動須要的函數,這些函數支持IIC設備動態插入和拔出。若是不想支持只實現上面3個。要不實現上面3個。要麼實現下面5個。不能同時定義  
  10.     int  (*probe)(struct i2c_client *,const struct  i2c_device_id *);              //新類型設備探測函數  
  11.     int (*remove)(struct i2c_client *);                   //新類型設備的移除函數  
  12.     void (*shutdown)(struct i2c_client *);              //關閉IIC設備  
  13.     int (*suspend)(struct  i2c_client *,pm_messge_t mesg);           //掛起IIC設備  
  14.         int (*resume)(struct  i2c_client *);                               //恢復IIC設備  
  15.     int (*command)(struct i2c_client *client,unsigned int cmd,void *arg);        //使用命令使設備完成特殊的功能。相似ioctl()函數  
  16.     struct devcie_driver  driver;                         //設備驅動結構體  
  17.     const struct  i2c_device_id *id_table;                       //設備ID表  
  18.     int (*detect)(struct i2c_client *,int  kind,struct  i2c_board_info *);          //自動探測設備的回調函數  
  19.   
  20.     const  struct i2c_client_address_data          *address_data;                 //設備所在的地址範圍  
  21.     struct  list_head    clients;                    //指向驅動支持的設備  
  22. };  

結構體i2c_driver和i2c_client的關係較爲簡單,其中i2c_driver表示一個IIC設備驅動,i2c_client表示一個IIC設備。關係以下圖:

c -- i2c_adapter

      IIC總線適配器就是一個IIC總線控制器,在物理上鍊接若干個IIC設備。IIC總線適配器本質上是一個物理設備,其主要功能是完成IIC總線控制器相關的數據通訊:

  1. struct i2c_adapter  
  2. {  
  3.     struct module *owner;                        //模塊計數  
  4.         unsigned  int id;                                  //alogorithm的類型,定義於i2c_id.h中  
  5.         unsigned   int  class;                           //容許探測的驅動類型  
  6.     const struct i2c_algorithm *algo;         //指向適配器的驅動程序  
  7.         void *algo_data;                                  //指向適配器的私有數據,根據不一樣的狀況使用方法不一樣  
  8.         int (*client_register)(struct  i2c_client *);          //設備client註冊時調用  
  9.         int (*client_unregister(struct  i2c_client *);       //設備client註銷時調用  
  10.         u8 level;                                                           
  11.     struct  mutex  bus_lock;                             //對總線進行操做時,將得到總線鎖  
  12.         struct  mutex  clist_lock ;                            //鏈表操做的互斥鎖  
  13.         int timeout;                                                  //超時  
  14.     int retries;                                                     //重試次數  
  15.     struct device dev;                                          //指向 適配器的設備結構體  
  16.     int  nr ;                                                            
  17.     struct  list_head      clients;                            //鏈接總線上的設備的鏈表  
  18.     char name[48];                                              //適配器名稱  
  19.     struct completion     dev_released;               //用於同步的完成量  
  20. };  


d -- i2c_algorithm

       每個適配器對應一個驅動程序,該驅動程序描述了適配器與設備之間的通訊方法:

  1. struct  i2c_algorithm  
  2. {  
  3.     int  (*master_xfer)(struct  i2c_adapter *adap,  struct  i2c_msg *msg, int num);              //傳輸函數指針,指向實現IIC總線通訊協議的函數,用來肯定適配器支持那些傳輸類型  
  4.     int  (*smbus_xfer)(struct  i2c_adapter *adap, u16  addr, unsigned  short flags, char  read_write, u8 command, int size, union  i2c_smbus_data  *data);    //smbus方式傳輸函數指針,指向實現SMBus總線通訊協議的函數。SMBus和IIC之間能夠經過軟件方式兼容,因此這裏提供了一個函數,可是通常都賦值爲NULL  
  5.     u32  (*functionality)(struct  i2c_adapter *);                   //返回適配器支持的功能  
  6.   
  7. };  


      IIC設備驅動程序大體能夠分爲設備層和總線層。設備層包括一個重要的數據結構,i2c_client。總線層包括兩個重要的數據結構,分別是i2c_adapter和i2c_algorithm。一個i2c_algorithm結構表示適配器對應的傳輸數據方法。3個數據結構關係:

IIC設備層次結構較爲簡單,可是寫IIC設備驅動程序卻至關複雜。

IIC設備驅動程序的步驟:

 

 

四、各結構體的做用與它們之間的關係

a -- i2c_adapter與i2c_algorithm

  i2c_adapter對應與物理上的一個適配器,而i2c_algorithm對應一套通訊方法,一個i2c適配器須要i2c_algorithm中提供的(i2c_algorithm中的又是更下層與硬件相關的代碼提供)通訊函數來控制適配器上產生特定的訪問週期。缺乏i2c_algorithm的i2c_adapter什麼也作不了,所以i2c_adapter中包含其使用i2c_algorithm的指針。

        i2c_algorithm中的關鍵函數master_xfer()用於產生i2c訪問週期須要的start stop ack信號,以i2c_msg(即i2c消息)爲單位發送和接收通訊數據。

  i2c_msg也很是關鍵,調用驅動中的發送接收函數須要填充該結構體

  1. struct i2c_msg {    
  2.     __u16 addr; /* slave address            */    
  3.     __u16 flags;            
  4.     __u16 len;      /* msg length               */    
  5.     __u8 *buf;      /* pointer to msg data          */    
  6. };    


b --i2c_driver和i2c_client

      i2c_driver對應一套驅動方法,其主要函數是attach_adapter()和detach_client()

      i2c_client對應真實的i2c物理設備device,每一個i2c設備都須要一個i2c_client來描述

      2c_driver與i2c_client的關係是一對多。一個i2c_driver上能夠支持多個同等類型的i2c_client.



c -- i2c_adapter和i2c_client

  i2c_adapter和i2c_client的關係與i2c硬件體系中適配器和設備的關係一致,即i2c_client依附於i2c_adapter,因爲一個適配器上能夠鏈接多個i2c設備,因此i2c_adapter中包含依附於它的i2c_client的鏈表。

 

 從i2c驅動架構圖中能夠看出,linux內核對i2c架構抽象了一個叫核心層core的中間件,它分離了設備驅動device driver和硬件控制的實現細節(如操做i2c的寄存器),core層不但爲上面的設備驅動提供封裝後的內核註冊函數,並且還爲小面的硬件事件提供註冊接口(也就是i2c總線註冊接口),能夠說core層起到了承上啓下的做用。

 

 

面試你該如何回答---->IIC總線協議?

面試的時候主要會遇到的IIC問題以下

  • 介紹一下你瞭解的I2C?

I2C總線是飛利浦(PHLIPS)公司推出的一種串行總線,用於鏈接微控制器及其外圍設備, I2C串行總線有兩根雙向信號線。一根是數據線SDA,另外一根是時鐘線SCL。 它僅經過兩根信號線就能夠完成對全部掛載在I2C總線上的從器件進行操做。這樣的好處是能夠大大的節省咱們微處理器的IO口資源。

  • I2C到底能夠掛載多少個器件呢?

答:IIC協議規定,在啓動總線後第1字節的高7位是從節點的尋址地址,其中高四位爲器件類型識別符,接着三位爲片選,最後一位爲讀寫位,當爲1時爲讀操做,爲0時爲寫操做,因此具體掛載多少個器件由I2C地址決定,7位尋址地址減去1個廣播地址0x00不用,因此有2^7=128 - 1 = 127,那就是127個地址, 因此理論上能夠掛127個從器件。

  • I2C如何同時掛載多個同一種器件(地址相同的器件)?

答:理論上是不會這樣設計的,若是必定要這樣作的話,能夠經過硬件上設計,控制器件是否掛載總線來實現(方法可用一個開關電路切斷器件SDA或者SCL是否接入總線來實現)

  • I2C總線的主機與從機之間是如何通訊的呢?

I2C總線的主機與從機之間的通訊主要和I2C的時序有關。在通訊開始的時候SCL與SDA都置爲高電平,此時爲總線空閒時間。當SCL爲高電平期間SDA的電平被拉低,標誌這總線的啓動。當SCL爲高電平期間SDA的電平被拉高,標誌這總線的終止。在進行數據傳送時,SCL爲高電平期間,SDA上的數據必須保持穩定,只有在SCL的信號爲低電平時,SDA上的高電平才容許變化。因此只要咱們根據芯片手冊正確的寫好IIC的時序,按時序發送器件地址(不一樣的器件的地址不一樣)以及數據,就可使主機與從機之間通訊。

  • I2C總線的仲裁你知道嗎?

總線上可能掛接有多個器件,有時會發生兩個或多個主器件同時想佔用總線的狀況,這種狀況叫作總線競爭。I2C總線具備多主控能力,能夠對發生在SDA線上的總線競爭進行仲裁,其仲裁原則是這樣的:當多個主器件同時想佔用總線時,若是某個主器件發送高電平,而另外一個主器件發送低電平,則發送電平與此時SDA總線電平不符的那個器件將自動關閉其輸出級。總線競爭的仲裁是在兩個層次上進行的。首先是地址位的比較,若是主器件尋址同一個從器件,則進入數據位的比較,從而確保了競爭仲裁的可靠性。因爲是利用I2C總線上的信息進行仲裁,所以不會形成信息的丟失。

  • I2C時鐘信號(SCL)的同步問題

在I2C總線上傳送信息時的時鐘同步信號是由掛接在SCL線上的全部器件的邏輯「與」完成的。SCL線上由高電平到低電平的跳變將影響到這些器件,一旦某個器件的時鐘信號下跳爲低電平,將使SCL線一直保持低電平,使SCL線上的全部器件開始低電平期。此時,低電平週期短的器件的時鐘由低至高的跳變並不能影響SCL線的狀態,因而這些器件將進入高電平等待的狀態。當全部器件的時鐘信號都上跳爲高電平時,低電平期結束,SCL線被釋放返回高電平,即全部的器件都同時開始它們的高電平期。其後,第一個結束高電平期的器件又將SCL線拉成低電平。這樣就在SCL線上產生一個同步時鐘。可見,時鐘低電平時間由時鐘低電平期最長的器件肯定,而時鐘高電平時間由時鐘高電平期最短的器件肯定。

  • I2C總線的其餘注意點

一、進行數據傳送時,在SCL爲高電平期間,SDA線上電平必須保持穩定,只有SCL爲低時,才容許SDA線上電平改變狀態。而且每一個字節傳送時都是高位在前。


二、對於應答信號,ACK=0時爲有效應答位,說明從機已經成功接收到該字節,若爲1則說明接受不成功。


三、若是從機須要延遲下一個數據字節開始傳送的時間,能夠經過把SCL電平拉低並保持來強制主機進入等待狀態。


四、主機完成一次通訊後還想繼續佔用總線在進行一次通訊,而又不釋放總線,就要利用重啓動信號Sr。它既做爲前一次數據傳輸的結束,又做爲後一次傳輸的開始。


五、總線衝突時,按「低電平優先」的仲裁原則,把總線判給在數據線上先發送低電平的主器件。


六、在特殊狀況下,若需禁止全部發生在I2C總線上的通訊,可採用封鎖或關閉總線,具體操做爲在總線上的任一器件將SCL鎖定在低電平便可。


七、SDA仲裁和SCL時鐘同步處理過程沒有前後關係,而是同時進行的。

相關文章
相關標籤/搜索