I2C的基礎概念和框架
文章來源:http://blog.csdn.net/zqixiao_09/article/details/50916916
一、IIC 基礎概念
IIC(Inter-Integrated Circuit)匯流排是一種由PHILIPS公司開發的兩線式序列匯流排,用於連線微控制器及其外圍裝置。IIC匯流排產生於在80年代,最初為音訊和視訊裝置開發,如今主要在伺服器管理中使用,其中包括單個元件狀態的通訊。例如管理員可對各個元件進行查詢,以管理系統的配置或掌握元件的功能狀態,如電源和系統風扇。可隨時監控記憶體、硬碟、網路、系統溫度等多個引數,增加了系統的安全性,方便了管理。
1、 IIC匯流排的特點
IIC匯流排最主要的優點是其簡單性
2、IIC匯流排工作原理
a -- 匯流排構成
IIC匯流排是由資料線SDA和時鐘SCL構成的序列匯流排,可傳送和接收資料。在CPU與被控IC之間、IC與IC之間進行雙向傳送,最高傳送速率100kbps。各種被控制電路均並聯在這條總線上,但就像電話機一樣只有撥通各自的號碼才能工作,所以每個電路和模組都有唯一的地址
CPU發出的控制訊號分為地址碼和控制量兩部分:
1) 地址碼用來選址,即接通需要控制的電路,確定控制的種類;
2) 控制量決定該調整的類別(如對比度、亮度等)及需要調整的量。
這樣,各控制電路雖然掛在同一條總線上,卻彼此獨立,互不相關。
b -- 訊號型別
IIC匯流排在傳送資料過程中共有四種類型訊號:
開始訊號:SCL為高電平時,SDA由高電平向低電平跳變,開始傳送資料;
結束訊號:SCL為高電平時,SDA由低電平向高電平跳變,結束傳送資料;
資料傳輸訊號:
應答訊號:接收資料的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.c和i2c-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_adapter、IIC介面卡的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_adapter。i2c_driver、i2c_client屬於裝置層;i2c_algorithm、i2c_adapter屬於匯流排型。如下圖:
裝置層關係到實際的IIC裝置,匯流排層包括CPU中的IIC匯流排控制器和控制匯流排通訊的方法。值得注意的是:一個系統中可能有很多個匯流排層,也就是包含多個匯流排控制器;也可能有多個裝置層,包含不同的IIC裝置
由IIC匯流排規範可知,IIC匯流排由兩條物理線路組成,這兩條物理線路是SDA和SCL。只要連線到SDA和SCL總線上的裝置都可以叫做IIC裝置。
a -- i2c_client
一個IIC裝置由i2c_client資料結構進行描述:
- struct i2c_client
- {
- unsigned short flags; //標誌位
- unsigned short addr; //裝置的地址,低7位為晶片地址
- char name[I2C_NAME_SIZE]; //裝置的名稱,最大為20個位元組
- struct i2c_adapter *adapter; //依附的介面卡i2c_adapter,介面卡指明所屬的匯流排
- struct i2c_driver *driver; //指向裝置對應的驅動程式
- struct device dev; //裝置結構體
- int irq; //裝置申請的中斷號
- struct list_head list; //連線到總線上的所有裝置
- struct list_head detected; //已經被發現的裝置連結串列
- struct completion released; //是否已經釋放的完成量
- };
第7位是R/W位,0表示寫,2表示讀,所以I2C裝置通常有兩個地址,即讀地址和寫地址;
型別器件由中間4位組成,這是由半導體公司生產的時候就已經固化了;
自定義型別由低3位組成。由使用者自己設定;
IIC裝置還有一些重要的注意事項:
1、i2c_client資料結構是描述IIC裝置的“模板”,驅動程式的裝置結構中應包含該結構;
2、adapter指向裝置連線的匯流排介面卡,系統可能有多個匯流排介面卡。核心中靜態指標陣列adapters記錄所有已經註冊的匯流排介面卡裝置;
3、driver是指向裝置驅動程式,這個驅動程式是在系統檢測到裝置存在時賦值的;
b -- IIC裝置驅動 i2c_driver
- struct i2c_driver
- {
- int id; //驅動標識ID
- unsigned intclass; //驅動的型別
- int (*attach_adapter)(struct i2c_adapter *); //當檢測到介面卡時呼叫的函式
- int (*detach_adapter)(struct i2c_adapter*); //解除安裝介面卡時呼叫的函式
- int (*detach_client)(struct i2c_client *) __deprecated; //解除安裝裝置時呼叫的函式
- //以下是一種新型別驅動需要的函式,這些函式支援IIC裝置動態插入和拔出。如果不想支援只實現上面3個。要不實現上面3個。要麼實現下面5個。不能同時定義
- int (*probe)(struct i2c_client *,conststruct i2c_device_id *); //新型別裝置探測函式
- int (*remove)(struct i2c_client *); //新型別裝置的移除函式
- void (*shutdown)(struct i2c_client *); //關閉IIC裝置
- int (*suspend)(struct i2c_client *,pm_messge_t mesg); //掛起IIC裝置
- int (*resume)(struct i2c_client *); //恢復IIC裝置
- int (*command)(struct i2c_client *client,unsigned int cmd,void *arg); //使用命令使裝置完成特殊的功能。類似ioctl()函式
- struct devcie_driver driver; //裝置驅動結構體
- conststruct i2c_device_id *id_table; //裝置ID表
- int (*detect)(struct i2c_client *,int kind,struct i2c_board_info *); //自動探測裝置的回撥函式
- conststruct i2c_client_address_data *address_data; //裝置所在的地址範圍
- struct list_head clients; //指向驅動支援的裝置
- };
c -- i2c_adapter
IIC匯流排介面卡就是一個IIC匯流排控制器,在物理上連線若干個IIC裝置。IIC匯流排介面卡本質上是一個物理裝置,其主要功能是完成IIC匯流排控制器相關的資料通訊:
- struct i2c_adapter
- {
- struct module *owner; //模組計數
- unsigned int id; //alogorithm的型別,定義於i2c_id.h中
- unsigned intclass; //允許探測的驅動型別
- conststruct i2c_algorithm *algo; //指向介面卡的驅動程式
- void *algo_data; //指向介面卡的私有資料,根據不同的情況使用方法不同
- int (*client_register)(struct i2c_client *); //裝置client註冊時呼叫
- int (*client_unregister(struct i2c_client *); //裝置client登出時呼叫
- u8 level;
- struct mutex bus_lock; //對匯流排進行操作時,將獲得匯流排鎖
- struct mutex clist_lock ; //連結串列操作的互斥鎖
- int timeout; //超時
- int retries; //重試次數
- struct device dev; //指向 介面卡的裝置結構體
- int nr ;
- struct list_head clients; //連線總線上的裝置的連結串列
- char name[48]; //介面卡名稱
- struct completion dev_released; //用於同步的完成量
- };
d -- i2c_algorithm
每一個介面卡對應一個驅動程式,該驅動程式描述了介面卡與裝置之間的通訊方法:
- struct i2c_algorithm
- {
- int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msg, int num); //傳輸函式指標,指向實現IIC匯流排通訊協議的函式,用來確定介面卡支援那些傳輸型別
- 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
- u32 (*functionality)(struct i2c_adapter *); //返回介面卡支援的功能
- };
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也非常關鍵,呼叫驅動中的傳送接收函式需要填充該結構體
- struct i2c_msg {
- __u16 addr; /* slave address */
- __u16 flags;
- __u16 len; /* msg length */
- __u8 *buf; /* pointer to msg data */
- };
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層起到了承上啟下的作用。