1. 程式人生 > >I2C的基礎概念和框架

I2C的基礎概念和框架

文章來源:http://blog.csdn.net/zqixiao_09/article/details/50916916

一、IIC 基礎概念

       IIC(Inter-Integrated Circuit)匯流排是一種由PHILIPS公司開發的兩線式序列匯流排,用於連線微控制器及其外圍裝置。IIC匯流排產生於在80年代,最初為音訊和視訊裝置開發,如今主要在伺服器管理中使用,其中包括單個元件狀態的通訊。例如管理員可對各個元件進行查詢,以管理系統的配置或掌握元件的功能狀態,如電源和系統風扇。可隨時監控記憶體、硬碟、網路、系統溫度等多個引數,增加了系統的安全性,方便了管理。


1、 IIC匯流排的特點

       IIC匯流排最主要的優點是其簡單性

有效性。由於介面直接在元件之上,因此IIC匯流排佔用的空間非常小,減少了電路板的空間和晶片管腳的數量,降低了互聯成本。匯流排的長度可高達25英尺,並且能夠以10Kbps的最大傳輸速率支援40個元件。IIC匯流排的另一個優點是,它支援多主控(multimastering), 其中任何能夠進行傳送和接收的裝置都可以成為主匯流排。一個主控能夠控制訊號的傳輸和時鐘頻率。當然,在任何時間點上只能有一個主控。


2、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介面。

3、匯流排基本操作

     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)重複起始條件和停止條件。


4、特性總結

      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裝置.

二、 Linux下IIC驅動架構

         Linux定義了系統的IIC驅動體系結構,在Linux系統中,IIC驅動由3部分組成,即IIC核心IIC匯流排驅動IIC裝置驅動。這3部分相互協作,形成了非常通用、可適應性很強的IIC框架。


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

       那麼我們如何編寫特定i2c介面器件的驅動程式?就是說上述架構中的那些部分需要我們完成,而哪些是linux核心已經完善的或者是晶片提供商已經提供的

1、架構層次分類

   第一層:提供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相干的就需要驅動工程師來實現了

2、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裝置驅動程式,在驅動中完成對特定的資料格式的解釋以及實現一些專用的功能。

3、重要的結構體

      因為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裝置還有一些重要的注意事項:

1、i2c_client資料結構是描述IIC裝置的“模板”,驅動程式的裝置結構中應包含該結構;

2、adapter指向裝置連線的匯流排介面卡,系統可能有多個匯流排介面卡。核心中靜態指標陣列adapters記錄所有已經註冊的匯流排介面卡裝置;

3、driver是指向裝置驅動程式,這個驅動程式是在系統檢測到裝置存在時賦值的

b -- IIC裝置驅動     i2c_driver

  1. struct  i2c_driver  
  2. {  
  3.     int id;                         //驅動標識ID
  4.     unsigned intclass;               //驅動的型別
  5.     int (*attach_adapter)(struct i2c_adapter *);             //當檢測到介面卡時呼叫的函式
  6.     int (*detach_adapter)(struct i2c_adapter*);              //解除安裝介面卡時呼叫的函式
  7.     int (*detach_client)(struct i2c_client *)   __deprecated;             //解除安裝裝置時呼叫的函式
  8.      //以下是一種新型別驅動需要的函式,這些函式支援IIC裝置動態插入和拔出。如果不想支援只實現上面3個。要不實現上面3個。要麼實現下面5個。不能同時定義
  9.     int  (*probe)(struct i2c_client *,conststruct  i2c_device_id *);              //新型別裝置探測函式
  10.     int (*remove)(struct i2c_client *);                   //新型別裝置的移除函式
  11.     void (*shutdown)(struct i2c_client *);              //關閉IIC裝置
  12.     int (*suspend)(struct  i2c_client *,pm_messge_t mesg);           //掛起IIC裝置
  13.         int (*resume)(struct  i2c_client *);                               //恢復IIC裝置
  14.     int (*command)(struct i2c_client *client,unsigned int cmd,void *arg);        //使用命令使裝置完成特殊的功能。類似ioctl()函式
  15.     struct devcie_driver  driver;                         //裝置驅動結構體
  16.     conststruct  i2c_device_id *id_table;                       //裝置ID表
  17.     int (*detect)(struct i2c_client *,int  kind,struct  i2c_board_info *);          //自動探測裝置的回撥函式
  18.     conststruct i2c_client_address_data          *address_data;                 //裝置所在的地址範圍
  19.     struct  list_head    clients;                    //指向驅動支援的裝置
  20. };  
結構體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   intclass;                           //允許探測的驅動型別
  6.     conststruct 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. };  

      IIC裝置驅動程式大致可以分為裝置層和匯流排層。裝置層包括一個重要的資料結構,i2c_client。匯流排層包括兩個重要的資料結構,分別是i2c_adapter和i2c_algorithm。一個i2c_algorithm結構表示介面卡對應的傳輸資料方法。3個數據結構關係:

IIC裝置層次結構較為簡單,但是寫IIC裝置驅動程式卻相當複雜。

IIC裝置驅動程式的步驟:

4、各結構體的作用與它們之間的關係

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層起到了承上啟下的作用。