日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

I2C 总线原理与架构

發(fā)布時間:2025/7/14 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 I2C 总线原理与架构 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、I2C總線原理

?

????????I2C是一種常用的串行總線,由串行數(shù)據(jù)線SDA 和串行時鐘線SCL組成。I2C是一種多主機(jī)控制總線,它和USB總線不同,USB是基于master-slave機(jī)制,任何設(shè)備的通信必須由主機(jī)發(fā)起才可以,而?I2C 是基于multi master機(jī)制,一條總線上可允許多個master。

? ?? ??系統(tǒng)的I2C模塊分為I2C總線控制器和I2C設(shè)備。I2C總線控制器是CPU提供的控制I2C總線接口,它控制I2C總線的協(xié)議、仲裁、時序。I2C設(shè)備是指通過I2C總線與CPU相連的設(shè)備,如EEPROM。 使用I2C通信時必須指定主從設(shè)備。 一般來說,I2C總線控制器被配置成主設(shè)備,與總線相連的I2C設(shè)備如AT24C02作為從設(shè)備。

1.1、IIC讀寫原理

????????IIC總線的開始/停止信號如圖1所示。開始信號為:時鐘信號線SCL為高電平,數(shù)據(jù)線SDA從高變低。停止信號為:時鐘信號線SCL為高電平,數(shù)據(jù)線SDA從低變高。

1.2、IIC總線Byte Write

????????IIC總線寫數(shù)據(jù)分幾種格式,如字節(jié)寫和頁寫。

????????字節(jié)寫傳送格式如圖2所示。開始信號之后,總線開始發(fā)數(shù)據(jù),第一個Byte是IIC的設(shè)備地址,第二個Byte是設(shè)備內(nèi)的地址(如EEPROM中具體的某個物理地址),然后就是要傳送的真正的數(shù)據(jù)DATA。

????????NOTE:IIC總線在傳送每個Byte后,都會從IIC總線上的接收設(shè)備得到一個ACK信號來確認(rèn)接收到了數(shù)據(jù)。其中,第一個Byte的設(shè)備地址中,前7位是地址碼,第8位是方向位(“0”為發(fā)送,“1”為接收)。IIC的中斷信號有:ACK,Start,Stop。

?????????Write功能的實(shí)際實(shí)現(xiàn)原理如圖3所示:

????????(1)設(shè)置GPIO的相關(guān)引腳為IIC輸出;

????????(2)設(shè)置IIC(打開ACK,打開IIC中斷,設(shè)置CLK等);

????????(3)設(shè)備地址賦給IICDS ,并設(shè)置IICSTAT,啟動IIC發(fā)送設(shè)備地址出去;從而找到相應(yīng)的設(shè)備即IIC總線上的設(shè)備。

????????(4)第一個Byte的設(shè)備地址發(fā)送后,從EEPROM得到ACK信號,此信號觸發(fā)中斷;

????????(5)在中斷處理函數(shù)中把第二個Byte(設(shè)備內(nèi)地址)發(fā)送出去;發(fā)送之后,接收到ACK又觸發(fā)中斷;

????????(6)中斷處理函數(shù)把第三個Byte(真正的數(shù)據(jù))發(fā)送到設(shè)備中。

????????(7)發(fā)送之后同樣接收到ACK并觸發(fā)中斷,中斷處理函數(shù)判斷,發(fā)現(xiàn)數(shù)據(jù)傳送完畢。

????????(8)IIC Stop信號,關(guān)IIC中斷,置位各寄存器。

????????NOTE:對于EEPROM,IICDS寄存器發(fā)送的數(shù)據(jù)會先放在Ring buffer中,當(dāng)其收到stop信號時,開始實(shí)際寫入EEPROM中。在實(shí)際寫的過程中,EEPROM不響應(yīng)從CPU來的信號,直到寫完才會響應(yīng),因而有一段延遲代碼。在page write時,注意一定要有延時!

????????NOTE:數(shù)據(jù)先寫到EEPROM的ring buffer中,收到Stop信號時,開始實(shí)際地把數(shù)據(jù)寫入EEPROM,這時不響應(yīng)任何輸入。即這時Write函數(shù)中后面的延時中,向其發(fā)slvaddr時,不會得到ACK,直到數(shù)據(jù)寫完時,才會收到ACK。

1.3、IIC總線Random Read

????????IIC總線讀數(shù)據(jù)為Current Address Read,Random Read,Sequential Read

????????IIC總線Random Read傳送格式如圖4所示。開始信號后,CPU開始寫第一個Byte(IIC的設(shè)備地址),第二個Byte是設(shè)備內(nèi)的地址(此地址保存在設(shè)備中)。然后開始讀過程:發(fā)送設(shè)備地址找到IIC設(shè)備,然后就開始讀數(shù)據(jù)。類似寫過程,CPU讀一個byte的實(shí)際數(shù)據(jù)后,CPU向IIC的EEPROM發(fā)ACK,ACK觸發(fā)中斷。讀數(shù)據(jù)也在中斷程序中進(jìn)行。




圖4 IIC Random Read Operation

二、I2C架構(gòu)概述

????????在linux中,I2C驅(qū)動架構(gòu)如下所示:



圖5?I2C驅(qū)動架構(gòu)1

????????Linux中I2C體系結(jié)構(gòu)如下圖所示(圖片來源于網(wǎng)絡(luò))。圖中用分割線分成了三個層次:用戶空間(也就是應(yīng)用程序),內(nèi)核(也就是驅(qū)動部分)和硬件(也就是實(shí)際物理設(shè)備)。我們現(xiàn)在就是要研究中間那一層。

2.1、I2C驅(qū)動概述

????????Linux的I2C驅(qū)動結(jié)構(gòu)可分為3個部分:

????????a、 ?I2C核心

????????I2C 核心提供了I2C總線驅(qū)動和設(shè)備驅(qū)動的注冊、注銷方法,I2C通信方法(即“algorithm”),與具體適配器無關(guān)的代碼以及探測設(shè)備、檢測設(shè)備地址等。i2c-core.c中的核心驅(qū)動程序可管理多個I2C總線適配器(控制器)和多個I2C從設(shè)備。每個I2C從設(shè)備驅(qū)動都能找到和它相連的I2C總線適配器。

????????b、 I2C總線驅(qū)動

????????I2C總線驅(qū)動主要包括I2C適配器結(jié)構(gòu)i2c_adapter和I2C適配器的algorithm數(shù)據(jù)結(jié)構(gòu)。

????????通過I2C總線驅(qū)動的代碼,可控制I2C適配器以主控方式產(chǎn)生開始位、停止位、讀寫周期,以及以從設(shè)備方式被讀寫、產(chǎn)生ACK等。

????c、 I2C設(shè)備驅(qū)動

????????I2C設(shè)備驅(qū)動是對I2C設(shè)備端的實(shí)現(xiàn),設(shè)備一般掛接在受CPU控制的I2C適配器上,通過I2C適配器與CPU交換數(shù)據(jù)。I2C設(shè)備驅(qū)動主要包括數(shù)據(jù)結(jié)構(gòu)i2c_driver和i2c_client。


圖6?I2C驅(qū)動架構(gòu)2

????????如上圖所示,每一條I2C總線對應(yīng)一個adapter。在kernel中,每一個adapter提供了一個描述的結(jié)構(gòu)(struct i2c_adapter),也定義了adapter支持的操作。再通過i2c core層將i2c設(shè)備與i2c adapter關(guān)聯(lián)起來。

三、I2C代碼在內(nèi)核中的結(jié)構(gòu)

3.1? I2C驅(qū)動調(diào)用關(guān)系 ??????

?

????????內(nèi)核中對于I2C定義了4種結(jié)構(gòu):
????????1)i2c_adapter—I2C總線適配器。 即為CPU中的I2C總線控制器。
????????2)i2c_algorithm—I2C總線通信傳輸算法,管理I2C總線控制器,實(shí)現(xiàn)I2C總線上數(shù)據(jù)的發(fā)送和接收等操作。
????????3)i2c_client—掛載在I2C總線上的I2C設(shè)備的驅(qū)動程序。
????????4)i2c_driver—用于管理I2C的驅(qū)動程序,它對應(yīng)I2C的設(shè)備節(jié)點(diǎn)。
????????這4種結(jié)構(gòu)的定義見include/linux/i2c.h文件。
????????對于i2c_driver和i2c_client,i2c_driver對應(yīng)一套驅(qū)動方法,是純粹的用于輔助作用的數(shù)據(jù)結(jié)構(gòu),它不對應(yīng)于任何的物理實(shí)體。????????
????????i2c_client對應(yīng)于真實(shí)的物理設(shè)備,每個I2C設(shè)備都需要一個i2c_client來描述。i2c_client一般被包含在i2c字符設(shè)備的私有信息結(jié)構(gòu)體中。 i2c_driver 與i2c_client發(fā)生關(guān)聯(lián)的時刻在i2c_driver的attach_adapter()函數(shù)被運(yùn)行時。attach_adapter()會探測物理設(shè)備,當(dāng)確定一個client存在時,把該client使用的i2c_client數(shù)據(jù)結(jié)構(gòu)的adapter指針指向?qū)?yīng)的i2c_adapter, driver指針指向該i2c_driver,并會調(diào)用i2c_adapter的client_register()函數(shù)。相反的過程發(fā)生在 i2c_driver 的detach_client()函數(shù)被調(diào)用的時候。
????????對于i2c_adpater 與i2c_client,與I2C硬件體系中適配器和設(shè)備的關(guān)系一致,即i2c_client依附于i2c_adpater。由于一個適配器上可以連接多個I2C設(shè)備,所以一個i2c_adpater也可以被多個i2c_client依附,i2c_adpater中包括依附于它的i2c_client的鏈表。
????????i2c.h文件中除定義上述4個重要結(jié)構(gòu)之外,還定義了一個非常重要的結(jié)構(gòu)體:i2c_msg,其定義如下:

點(diǎn)擊(此處)折疊或打開

  • struct i2c_msg?{
  • ???????__u16 addr;?/*?slave address*/
  • ???????__u16 flags;
  • #define I2C_M_TEN 0x0010?/*?this?is?a ten bit chip address?*/
  • #define I2C_M_RD 0x0001?/*?read data,?from slave?to?master?*/
  • #define I2C_M_NOSTART 0x4000?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/
  • #define I2C_M_REV_DIR_ADDR 0x2000?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/
  • #define I2C_M_IGNORE_NAK 0x1000?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/
  • #define I2C_M_NO_RD_ACK 0x0800?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/
  • #define I2C_M_RECV_LEN 0x0400?/*?length will be first received byte?*/
  • ???????__u16?len;?/*?msg length?*/
  • ???????__u8?*buf;?/*?pointer?to?msg data?*/
  • };
  • ??????? 它是實(shí)際傳輸?shù)臄?shù)據(jù),其中包括了slave address、數(shù)據(jù)長度和實(shí)際的數(shù)據(jù)。

    3.2? 內(nèi)核中的I2C驅(qū)動

    ????????Linux內(nèi)核源碼的drivers目錄下有個i2c目錄,其中包含如下文件和文件夾:

    ????????a、i2c-core.c

    ????????這個文件實(shí)現(xiàn)了I2C核心的功能以及/proc/bus/i2c*接口。

    ????????b、 i2c-dev.c

    ????????實(shí)現(xiàn)了I2C適配器設(shè)備文件的功能,每一個I2C適配器都被分配一個設(shè)備。通過適配器訪問設(shè)備時的主設(shè)備號都為89,次設(shè)備號為0~255。應(yīng)用程序通過 “i2c-%d” (i2c-0, i2c-1, ..., i2c-10, ...)文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等來訪問這個設(shè)備。

    ????????i2c-dev.c并沒有針對特定的設(shè)備而設(shè)計,只是提供了通用的read()、write()和ioctl()等接口,應(yīng)用層可以借用這些接口訪問掛接在適配器上I2C設(shè)備的存儲空間或寄存器,并控制I2C設(shè)備的工作方式。

    ????????c、chips文件夾

    ????????此目錄中包含了一些特定的I2C設(shè)備驅(qū)動,如RTC實(shí)時鐘芯片驅(qū)動和I2C接口的EEPROM驅(qū)動等。

    ????????d、busses文件夾

    ????????此目錄中包含了一些I2C總線的驅(qū)動,如S3C2410的I2C控制器驅(qū)動為i2c-s3c2410.c。

    ????????e、algos文件夾

    ????????實(shí)現(xiàn)了一些I2C總線適配器的algorithm。

    ????????i2c-core.c文件不需要修改,其主要實(shí)現(xiàn)的函數(shù)有:

    ????????1)adapter和client相關(guān)操作

    ?

    點(diǎn)擊(此處)折疊或打開

  • int?i2c_add_adapter(struct i2c_adapter?*adap);?//增加adapter
  • int?i2c_del_adapter(struct i2c_adapter?*adap);
  • int?i2c_register_driver(struct module?*,?struct i2c_driver?*);?//增加驅(qū)動?(i2c_add_driver)
  • int?i2c_del_driver(struct i2c_driver?*driver);
  • int?i2c_attach_client(struct i2c_client?*client);?//增加client
  • int?i2c_detach_client(struct i2c_client?*client);
  • ????????2)I2C傳輸,發(fā)送和接收

    點(diǎn)擊(此處)折疊或打開

  • int?i2c_transfer(struct i2c_adapter?*?adap,?struct i2c_msg?*msgs,?int?num);
  • int?i2c_master_send(struct i2c_client?*client,const?char?*buf?,int?count);
  • int?i2c_master_recv(struct i2c_client?*client,?char?*buf?,int?count);
  • ???????? i2c_transfer函數(shù)用于進(jìn)行I2C適配器和I2C設(shè)備之間的一組消息交互。i2c_master_send函數(shù)和i2c_master_recv函數(shù)調(diào)用i2c_transfer函數(shù)分別完成一條寫消息和一條讀消息。而i2c_transfer函數(shù)實(shí)現(xiàn)中使用這句話adap->algo->master_xfer(adap,msgs,num);來調(diào)用i2c_algorithm中注冊的master_xfer函數(shù)。?????????????????i2c_algorithm如下定義:

    點(diǎn)擊(此處)折疊或打開

  • struct i2c_algorithm?{
  • ????int?(*master_xfer)(struct i2c_adapter?*adap,?struct i2c_msg?*msgs,
  • ????????????????????????????int?num);
  • ????int?(*smbus_xfer)?(struct i2c_adapter?*adap,?u16 addr,
  • ????????????????????????????unsigned short flags,?char read_write,
  • ????????????????????????????u8 command,?int?size,?union i2c_smbus_data?*data);
  • ????u32?(*functionality)?(struct i2c_adapter?*);
  • ????}
  • ? ?? ? 根據(jù)定義主要要實(shí)現(xiàn)i2c_algorithm的master_xfer()函數(shù)和functionality()函數(shù)。

    四、Algorithm中的傳輸函數(shù)master_xfer

    ????????圖6只是提供了一個大概的框架。在下面的代碼分析中,從Algorithm中的傳輸函數(shù)master_xfer來開始分析整個結(jié)構(gòu)。以下的代碼分析是基于linux 3.0.4。分析的代碼基本位于: linux-3.0.4/drivers/i2c/位置。

    ??????? 博文以一款CPU的I2C模塊作為例子。

    ??????? 分析一個Linux驅(qū)動代碼,一般都是從module_init()開始,分析一個不帶操作系統(tǒng)的程序,一般從main函數(shù)開始,此處我們分析I2C的總線驅(qū)動,從設(shè)備調(diào)用I2C總線驅(qū)動的入口處開始分析。在i2c-core.c中的i2c_transfer函數(shù)中,會有語句:ret = adap->algo->master_xfer(adap, msgs, num);來實(shí)現(xiàn)數(shù)據(jù)傳遞,實(shí)際此處就是I2C總線驅(qū)動執(zhí)行的入口,相應(yīng)算法結(jié)構(gòu)體函數(shù)的賦值會在總線驅(qū)動的探測函數(shù)中執(zhí)行,后面會講述。

    ??????? 算法結(jié)構(gòu)體賦值如下:

    點(diǎn)擊(此處)折疊或打開

  • static struct i2c_algorithm i2c_gsc_algo?=?{
  • ????.master_xfer?=?i2c_gsc_xfer,
  • ????.functionality?=?i2c_gsc_func,
  • };
  • ????????i2c_gsc_func()函數(shù)實(shí)現(xiàn)的就是總線驅(qū)動支持的操作,程序如下:

    點(diǎn)擊(此處)折疊或打開

  • static u32 i2c_gsc_func(struct i2c_adapter?*adap)
  • {
  • ????return I2C_FUNC_I2C?|
  • ????????I2C_FUNC_10BIT_ADDR?|
  • ????????I2C_FUNC_SMBUS_BYTE?|
  • ????????I2C_FUNC_SMBUS_BYTE_DATA?|
  • ????????I2C_FUNC_SMBUS_WORD_DATA?|
  • ????????I2C_FUNC_SMBUS_I2C_BLOCK;
  • }
  • ????????i2c_gsc_xfer()函數(shù)實(shí)現(xiàn)開始傳輸I2C數(shù)據(jù),程序如下:

    點(diǎn)擊(此處)折疊或打開

  • static?int?i2c_gsc_xfer(struct i2c_adapter?*adap,?struct i2c_msg msgs[],?int?num)
  • {
  • ????struct gsc_i2c_dev?*dev?=?i2c_get_adapdata(adap);?//獲取總線設(shè)備結(jié)構(gòu)體,設(shè)置在probe函數(shù)中
  • ????int?ret;
  • ????dev_dbg(dev->dev,?"%s: msgs: %d\n",?__func__,?num);
  • ????//開始初始化變量,準(zhǔn)備開始傳輸
  • ????mutex_lock(&dev->lock);
  • ????INIT_COMPLETION(dev->cmd_complete);
  • ????dev->msgs?=?msgs;
  • ????dev->msgs_num?=?num;
  • ????dev->cmd_err?=?0;
  • ????dev->msg_write_idx?=?0;?//此變量用來標(biāo)識傳輸?shù)降趲讉€dev->msgs,dev->msgs_num標(biāo)識總共有幾個msgs
  • ????dev->msg_read_idx?=?0;
  • ????dev->msg_err?=?0;
  • ????dev->status?=?STATUS_IDLE;
  • ????dev->abort_source?=?0;
  • ????ret?=?i2c_gsc_wait_bus_not_busy(dev);?//查詢總線是否空閑,只有空閑才開始傳輸?
  • ????if?(ret?<?0)
  • ????????goto done;
  • ????/*?start the transfers?*/
  • ????i2c_gsc_xfer_init(dev);?//設(shè)置傳輸模式,開啟中斷?
  • ????/*?wait?for?tx?to?complete?*/
  • ????ret?=?wait_for_completion_interruptible_timeout(&dev->cmd_complete,?HZ);?//等待傳輸完成,中斷中會設(shè)置
  • ????if?(ret?==?0)?{
  • ????????dev_err(dev->dev,?"controller timed out\n");
  • ????????i2c_gsc_init(dev);
  • ????????ret?=?-ETIMEDOUT;
  • ????????goto done;
  • ????}?else?if?(ret?<?0)
  • ????????goto done;
  • ????if?(dev->msg_err)?{
  • ????????ret?=?dev->msg_err;
  • ????????goto done;
  • ????}
  • ????/*?no?error?*/
  • ????if?(likely(!dev->cmd_err))?{
  • ????????/*?Disable the adapter?*/
  • ????????writel(0,?dev->base?+?GSC_IC_ENABLE);
  • ????????ret?=?num;
  • ????????goto done;
  • ????}
  • ????/*?We have an?error?*/
  • ????if?(dev->cmd_err?==?GSC_IC_ERR_TX_ABRT)?{
  • ????????ret?=?i2c_gsc_handle_tx_abort(dev);
  • ????????goto done;
  • ????}
  • ????ret?=?-EIO;
  • done:
  • ????mutex_unlock(&dev->lock);
  • ????return ret;
  • }
  • ????????從以上函數(shù)看出,當(dāng)執(zhí)行完此函數(shù)后,中斷打開,實(shí)際的傳輸在中斷中完成。

    ??????? 中斷號和申請中斷函數(shù)在總線驅(qū)動的probe函數(shù)中完成,最后會講述。接下來就看下中斷函數(shù)i2c_gsc_isr:

    點(diǎn)擊(此處)折疊或打開

  • static irqreturn_t i2c_gsc_isr(int?this_irq,?void?*dev_id)
  • {
  • ????struct gsc_i2c_dev?*dev?=?dev_id;
  • ??? u32 stat;
  • ????stat?=?i2c_gsc_read_clear_intrbits(dev);?//清除中斷標(biāo)志位?
  • ????dev_dbg(dev->dev,?"%s: stat=0x%x\n",?__func__,?stat);
  • ????if?(stat?&?GSC_IC_INTR_TX_ABRT)?{
  • ????????dev->cmd_err?|=?GSC_IC_ERR_TX_ABRT;
  • ????????dev->status?=?STATUS_IDLE;
  • ????????/*
  • ?????????*?Anytime TX_ABRT?is?set,?the contents of the tx/rx
  • ?????????*?buffers are flushed.?Make sure?to?skip them.
  • ?????????*/
  • ????????writel(0,?dev->base?+?GSC_IC_INTR_MASK);?//如果是傳輸終止則清除所有中斷?
  • ????????goto tx_aborted;
  • ????}
  • ????if?(stat?&?GSC_IC_INTR_RX_FULL)
  • ????????i2c_gsc_read(dev);?//接收fifo滿中斷,讀取數(shù)據(jù)
  • ????if?(stat?&?GSC_IC_INTR_TX_EMPTY)
  • ????????i2c_gsc_xfer_msg(dev);?//發(fā)送fifo空中斷,發(fā)送數(shù)據(jù)?
  • ????/*
  • ?????*?No need?to?modify?or?disable the interrupt mask here.
  • ?????*?i2c_gsc_xfer_msg()?will take care of it according?to
  • ?????*?the current transmit status.
  • ?????*/
  • tx_aborted:
  • ????if?((stat?&?(GSC_IC_INTR_TX_ABRT?|?GSC_IC_INTR_STOP_DET))?||?dev->msg_err)
  • ????????complete(&dev->cmd_complete);?//發(fā)送錯誤或者發(fā)送終止,完成事件,對應(yīng)上面的wait_for_completion_interruptible_timeout(&dev->cmd_complete,?HZ);
  • ????return IRQ_HANDLED;
  • }
  • ??????? 接下來看下:接收fifo滿中斷,讀取數(shù)據(jù)函數(shù):i2c_gsc_read()

    點(diǎn)擊(此處)折疊或打開

  • static void i2c_gsc_read(struct gsc_i2c_dev?*dev)
  • {
  • ????struct i2c_msg?*msgs?=?dev->msgs;
  • ????int?rx_valid;
  • ????for?(;?dev->msg_read_idx?<?dev->msgs_num;?dev->msg_read_idx++)?{
  • ????????u32?len;
  • ????????u8?*buf;
  • ????????if?(!(msgs[dev->msg_read_idx].flags?&?I2C_M_RD))
  • ????????????continue;
  • ????????if?(!(dev->status?&?STATUS_READ_IN_PROGRESS))?{
  • ????????????//第一次開始讀,設(shè)置長度和存儲數(shù)組地址
  • ????????????len?=?msgs[dev->msg_read_idx].len;
  • ????????????buf?=?msgs[dev->msg_read_idx].buf;
  • ????????}?else?{
  • ????????????/*?注意此處,如果是第一次開始讀,讀的長度和存儲數(shù)組都放在結(jié)構(gòu)體dev->msgs中,如果不是
  • ????????????第一次讀,長度和存儲數(shù)組放在dev->rx_buf_len和dev->rx_buf中,在本函數(shù)最后會判斷一次是否能夠
  • ????????????讀完全,如果不完全,則更新dev->rx_buf_len和dev->rx_buf。*/
  • ????????????len?=?dev->rx_buf_len;
  • ????????????buf?=?dev->rx_buf;
  • ????????}
  • ????????rx_valid?=?readl(dev->base?+?GSC_IC_RXFLR);?//讀取接收fifo里數(shù)據(jù)長度?
  • ????????for?(;?len?>?0?&&?rx_valid?>?0;?len--,?rx_valid--)
  • ????????????*buf++?=?readl(dev->base?+?GSC_IC_DATA_CMD);?//讀取數(shù)據(jù)
  • ????????if?(len?>?0)?{
  • ????????????//如果沒有讀取完成,設(shè)置狀態(tài)位,更新變量,和上面紅色的呼應(yīng)
  • ????????????dev->status?|=?STATUS _READ_IN_PROGRESS;
  • ????????????dev->rx_buf_len?=?len;
  • ????????????dev->rx_buf?=?buf;
  • ????????????return;
  • ????????}?else
  • ????????????dev->status?&=?~STATUS_READ_IN_PROGRESS;?//一次讀取完成
  • ????}
  • }
  • ????????發(fā)送fifo空中斷,發(fā)送數(shù)據(jù)函數(shù)i2c_gsc_xfer_msg:

    點(diǎn)擊(此處)折疊或打開

  • static void i2c_gsc_xfer_msg(struct gsc_i2c_dev?*dev)
  • {
  • ????struct i2c_msg?*msgs?=?dev->msgs;
  • ????u32 intr_mask;
  • ????int?tx_limit,?rx_limit;
  • ????u32 addr?=?msgs[dev->msg_write_idx].addr;
  • ????u32 buf_len?=?dev->tx_buf_len;
  • ????u8?*buf?=?dev->tx_buf;
  • ????intr_mask?=?GSC_IC_INTR_DEFAULT_MASK;?//設(shè)置默認(rèn)屏蔽位
  • ????//使用dev->msg_write_idx標(biāo)識傳輸?shù)趲讉€msgs
  • ????for?(;?dev->msg_write_idx?<?dev->msgs_num;?dev->msg_write_idx++)?{
  • ????????/*
  • ?????????*?if?target address has changed,?we need?to
  • ?????????*?reprogram the target address?in?the i2c
  • ?????????*?adapter when we are done with this transfer
  • ?????????*/
  • ????????//兩次傳輸?shù)刂凡灰粯?#xff0c;退出
  • ????????if?(msgs[dev->msg_write_idx].addr?!=?addr)?{
  • ????????????dev_err(dev->dev,
  • ????????????????"%s: invalid target address\n",?__func__);
  • ????????????dev->msg_err?=?-EINVAL;
  • ????????????break;
  • ????????}
  • ????????//傳輸長度為0,退出
  • ????????if?(msgs[dev->msg_write_idx].len?==?0)?{
  • ????????????dev_err(dev->dev,
  • ????????????????"%s: invalid message length\n",?__func__);
  • ????????????dev->msg_err?=?-EINVAL;
  • ????????????break;
  • ????????}
  • ????????//如果是第一次傳輸,設(shè)置傳輸長度和數(shù)組地址
  • ????????if?(!(dev->status?&?STATUS_WRITE_IN_PROGRESS))?{
  • ????????????/*?new i2c_msg?*/
  • ????????????buf?=?msgs[dev->msg_write_idx].buf;
  • ????????????buf_len?=?msgs[dev->msg_write_idx].len;
  • ????????}
  • ????????tx_limit?=?dev->tx_fifo_depth?-?readl(dev->base?+?GSC_IC_TXFLR);?//計算可以往寄存器里寫幾個數(shù)據(jù)?
  • ????????rx_limit?=?dev->rx_fifo_depth?-?readl(dev->base?+?GSC_IC_RXFLR);?//計算可以從寄存器里讀幾個數(shù)據(jù)
  • ????????while?(buf_len?>?0?&&?tx_limit?>?0?&&?rx_limit?>?0)?{
  • ????????????u32 cmd?=?0;
  • ????????????if((dev->msg_write_idx?==?dev->msgs_num-1)?&&?buf_len?==?1)
  • ????????????????cmd?|=?0x200;?//最后一次傳輸,設(shè)置寄存器發(fā)送stop信號
  • ????????????if?(msgs[dev->msg_write_idx].flags?&?I2C_M_RD)?{
  • ????????????????writel(cmd|0x100,?dev->base?+?GSC_IC_DATA_CMD);?//寫命令,此處為讀
  • ????????????????rx_limit--;
  • ????????????}?else
  • ????????????????writel(cmd|*buf++,?dev->base?+?GSC_IC_DATA_CMD);?//寫數(shù)據(jù)
  • ????????????tx_limit--;?buf_len--;
  • ????????}
  • ????????//更新變量
  • ????????dev->tx_buf?=?buf;
  • ????????dev->tx_buf_len?=?buf_len;
  • ????????if?(buf_len?>?0)?{
  • ????????????/*?more bytes?to?be written?*/
  • ????????????dev->status?|=?STATUS_WRITE_IN_PROGRESS;
  • ????????????break;
  • ????????}?else
  • ????????????dev->status?&=?~STATUS_WRITE_IN_PROGRESS;?//讀寫完成?
  • ????}
  • ????/*
  • ?????*?If?i2c_msg index search?is?completed,?we don't need TX_EMPTY
  • ?????*?interrupt any more.
  • ?????*/
  • ????if?(dev->msg_write_idx?==?dev->msgs_num)
  • ????????intr_mask?&=?~GSC_IC_INTR_TX_EMPTY;?//如果寫完成,屏蔽發(fā)送中斷
  • ????if?(dev->msg_err)
  • ????????intr_mask?=?0;?//如果出現(xiàn)錯誤,屏蔽所有中斷
  • ????writel(intr_mask,?dev->base?+?GSC_IC_INTR_MASK);?//寫屏蔽寄存器
  • }
  • ??????? 到這里就講述完成了I2C數(shù)據(jù)傳輸中總線驅(qū)動部分,接下來講述總線驅(qū)動中的注冊和探測函數(shù)。

    五、總線驅(qū)動注冊和探測函數(shù)

    ??????? 和其他總線驅(qū)動類似,I2C總線驅(qū)動注冊成平臺設(shè)備,所以首先需要定義平臺設(shè)備,包括寄存器的起始地址和大小,中斷信息等。

    ??????? 接下來就是總線驅(qū)動模塊的注冊和移除了,如下:

    點(diǎn)擊(此處)折疊或打開

  • static?int?__init gsc_i2c_init_driver(void)
  • {
  • ????return platform_driver_probe(&gsc_i2c_driver,?gsc_i2c_probe);
  • }
  • static void __exit gsc_i2c_exit_driver(void)
  • {
  • ????platform_driver_unregister(&gsc_i2c_driver);
  • }
  • module_init(gsc_i2c_init_driver);
  • module_exit(gsc_i2c_exit_driver);
  • ??????? 平臺設(shè)備驅(qū)動的結(jié)構(gòu)體如下:

    點(diǎn)擊(此處)折疊或打開

  • static struct platform_driver gsc_i2c_driver?=?{
  • ????.remove?=?__devexit_p(gsc_i2c_remove),
  • ????.driver?=?{
  • ????????.name?=?"XXXX-i2c",
  • ????????.owner?=?THIS_MODULE,
  • ????},
  • };
  • ??????? 接下來就看下I2C總線驅(qū)動的探測函數(shù)gsc_i2c_probe:

    點(diǎn)擊(此處)折疊或打開

  • static?int?__devinit gsc_i2c_probe(struct platform_device?*pdev)
  • {
  • ????struct gsc_i2c_dev?*dev;
  • ????struct i2c_adapter?*adap;
  • ????struct resource?*mem,?*ioarea;
  • ????int?irq,?r;
  • ????//申請設(shè)備資源
  • ????/*?NOTE:?driver uses the static register mapping?*/
  • ????mem?=?platform_get_resource(pdev,?IORESOURCE_MEM,?0);
  • ????if?(!mem)?{
  • ????????dev_err(&pdev->dev,?"no mem resource?\n");
  • ????????return?-EINVAL;
  • ????}
  • ????irq?=?platform_get_irq(pdev,?0);
  • ????if?(irq?<?0)?{
  • ????????dev_err(&pdev->dev,?"no irq resource?\n");
  • ????????return irq;?/*?-ENXIO?*/
  • ????}
  • ????ioarea?=?request_mem_region(mem->start,?resource_size(mem),
  • ????????????pdev->name);
  • ????if?(!ioarea)?{
  • ????????dev_err(&pdev->dev,?"I2C region already claimed\n");
  • ????????return?-EBUSY;
  • ????}
  • ????//申請總線結(jié)構(gòu)體變量
  • ????dev?=?kzalloc(sizeof(struct gsc_i2c_dev),?GFP_KERNEL);
  • ????if?(!dev)?{
  • ????????r?=?-ENOMEM;
  • ????????goto err_release_region;
  • ????}
  • ????//初始化變量
  • ????init_completion(&dev->cmd_complete);
  • ????mutex_init(&dev->lock);
  • ????dev->dev?=?get_device(&pdev->dev);
  • ????dev->irq?=?irq;
  • ????platform_set_drvdata(pdev,?dev);
  • ????dev->clk?=?clk_get(&pdev->dev,?"i2c");
  • ????if?(IS_ERR(dev->clk))?{
  • ????????r?=?-ENODEV;
  • ????????goto err_free_mem;
  • ????}
  • ????clk_enable(dev->clk);
  • ????dev->base?=?ioremap(mem->start,?resource_size(mem));
  • ????if?(dev->base?==?NULL)?{
  • ????????dev_err(&pdev->dev,?"failure mapping io resources\n");
  • ????????r?=?-EBUSY;
  • ????????goto err_unuse_clocks;
  • ????}
  • ????//設(shè)置發(fā)送和接收fifo深度
  • ????dev->tx_fifo_depth?=?8;
  • ????dev->rx_fifo_depth?=?8;
  • ????i2c_gsc_init(dev);?//初始化I2C總線時鐘
  • ????writel(0,?dev->base?+?GSC_IC_INTR_MASK);?/*?disable IRQ?*/
  • ????r?=?request_irq(dev->irq,?i2c_gsc_isr,?IRQF_DISABLED,?pdev->name,?dev);?//申請中斷函數(shù),上面已經(jīng)講述
  • ????if?(r)?{
  • ????????dev_err(&pdev->dev,?"failure requesting irq %i\n",?dev->irq);
  • ????????goto err_iounmap;
  • ????}
  • ????//設(shè)置I2C的adap
  • ????adap?=?&dev->adapter;
  • ????i2c_set_adapdata(adap,?dev);
  • ????adap->owner?=?THIS_MODULE;
  • ????adap->class?=?I2C_CLASS_HWMON;
  • ????strlcpy(adap->name,?"BLX GSC3280 I2C adapter",
  • ????????????sizeof(adap->name));
  • ????adap->algo?=?&i2c_gsc_algo;?//設(shè)置adap的算法,包括傳輸函數(shù)和支持的操作函數(shù),本文 開始已經(jīng)講述
  • ????adap->dev.parent?=?&pdev->dev;
  • ????adap->nr?=?pdev->id;
  • ????r?=?i2c_add_numbered_adapter(adap);?//增加適配器計數(shù),后面講述
  • ????if?(r)?{
  • ????????dev_err(&pdev->dev,?"failure adding adapter\n");
  • ????????goto err_free_irq;
  • ????}
  • ????return 0;
  • ????//中途退出分支
  • err_free_irq:
  • ????free_irq(dev->irq,?dev);
  • err_iounmap:
  • ????iounmap(dev->base);
  • err_unuse_clocks:
  • ????clk_disable(dev->clk);
  • ????clk_put(dev->clk);
  • ????dev->clk?=?NULL;
  • err_free_mem:
  • ????platform_set_drvdata(pdev,?NULL);
  • ????put_device(&pdev->dev);
  • ????kfree(dev);
  • err_release_region:
  • ????release_mem_region(mem->start,?resource_size(mem));
  • ????return r;
  • }
  • ????????在kernel中提供了兩個adapter注冊接口,分別為i2c_add_adapter()和 i2c_add_numbered_adapter()。由于在系統(tǒng)中可能存在多個adapter,因此將每一條I2C總線對應(yīng)一個編號,下文中稱為 I2C總線號。這個總線號與PCI中的總線號不同。它和硬件無關(guān),只是軟件上便于區(qū)分而已。對于實(shí)際的設(shè)備,一條I2C總線就意味著CPU的一個I2C控制器,也對應(yīng)著一個adapter結(jié)構(gòu)體。

    ????????對于i2c_add_adapter()而言,它使用的是動態(tài)總線號,即由系統(tǒng)給其分配一個總線號,而i2c_add_numbered_adapter()則是自己指定總線號,如果這個總線號非法或者是被占用,就會注冊失敗。

    點(diǎn)擊(此處)折疊或打開

  • int?i2c_add_adapter(struct i2c_adapter?*adapter)
  • {
  • ????int?id,?res?=?0;
  • retry:
  • ????if?(idr_pre_get(&i2c_adapter_idr,?GFP_KERNEL)?==?0)
  • ????????return?-ENOMEM;
  • ????mutex_lock(&core_lock);
  • ????/*?"above"?here means?"above or equal to",?sigh?*/
  • ????res?=?idr_get_new_above(&i2c_adapter_idr,?adapter,
  • ????????????????__i2c_first_dynamic_bus_num,?&id);
  • ????mutex_unlock(&core_lock);
  • ????if?(res?<?0)?{
  • ????????if?(res?==?-EAGAIN)
  • ????????????goto retry;
  • ????????return res;
  • ????}
  • ????adapter->nr?=?id;
  • ????return i2c_register_adapter(adapter);
  • }
  • ?????????在這里涉及到一個idr結(jié)構(gòu)。idr結(jié)構(gòu)本來是為了配合page cache中的radix tree而設(shè)計的.在這里我們只需要知道,它是一種高效的搜索樹,且這個樹預(yù)先存放了一些內(nèi)存。避免在內(nèi)存不夠的時候出現(xiàn)問題。所以,在往idr中插入結(jié)構(gòu)的時候,首先要調(diào)用idr_pre_get()為它預(yù)留足夠的空閑內(nèi)存,然后再調(diào)用idr_get_new_above()將結(jié)構(gòu)插入idr中,該函數(shù)以參數(shù)的形式返回一個id。以后憑這個id就可以在idr中找到相對應(yīng)的結(jié)構(gòu)了。

    ????????注意一下 idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)參數(shù)的含義,它是將adapter結(jié)構(gòu)插入到i2c_adapter_idr中,存放位置的id必須要大于或者等于 __i2c_first_dynamic_bus_num,然后將對應(yīng)的id號存放在adapter->nr中。調(diào)用i2c_register_adapter(adapter)對這個adapter進(jìn)一步注冊。

    點(diǎn)擊(此處)折疊或打開

  • int?i2c_add_numbered_adapter(struct i2c_adapter?*adap)
  • {
  • ????int?id;
  • ????int?status;
  • ????if?(adap->nr?&?~MAX_ID_MASK)
  • ????????return?-EINVAL;
  • retry:
  • ????if?(idr_pre_get(&i2c_adapter_idr,?GFP_KERNEL)?==?0)
  • ????????return?-ENOMEM;
  • ????mutex_lock(&core_lock);
  • ????/*?"above"?here means?"above or equal to",?sigh;
  • ?????*?we need the?"equal to"?result?to?force the result
  • ?????*/
  • ????status?=?idr_get_new_above(&i2c_adapter_idr,?adap,?adap->nr,?&id);
  • ????if?(status?==?0?&&?id?!=?adap->nr)?{
  • ????????status?=?-EBUSY;
  • ????????idr_remove(&i2c_adapter_idr,?id);
  • ????}
  • ????mutex_unlock(&core_lock);
  • ????if?(status?==?-EAGAIN)
  • ????????goto retry;
  • ????if?(status?==?0)
  • ????????status?=?i2c_register_adapter(adap);
  • ????return status;
  • }
  • ????????對比一下就知道差別了,在這里它已經(jīng)指定好了adapter->nr了。如果分配的id不和指定的相等,便返回錯誤。本文使用的注冊函數(shù)即為i2c_add_numbered_adapter。

    ????????i2c_register_adapter()代碼如下:

    點(diǎn)擊(此處)折疊或打開

  • static?int?i2c_register_adapter(struct i2c_adapter?*adap)
  • {
  • ????int?res?=?0,?dummy;
  • ????mutex_init(&adap->bus_lock);
  • ????mutex_init(&adap->clist_lock);
  • ????INIT_LIST_HEAD(&adap->clients);
  • ????mutex_lock(&core_lock);
  • ????/*?Add the adapter?to?the driver core.
  • ????*?If?the parent pointer?is?not?set?up,
  • ????*?we add this adapter?to?the host bus.
  • ????*/
  • ????if?(adap->dev.parent?==?NULL)?{
  • ????????adap->dev.parent?=?&platform_bus;
  • ????????pr_debug("I2C adapter driver [%s] forgot to specify "
  • ????????????"physical device/n",?adap->name);
  • ????}
  • ????sprintf(adap->dev.bus_id,?"i2c-%d",?adap->nr);
  • ????adap->dev.release?=?&i2c_adapter_dev_release;
  • ????adap->dev.class?=?&i2c_adapter_class;
  • ????res?=?device_register(&adap->dev);
  • ????if?(res)
  • ????????goto out_list;
  • ????dev_dbg(&adap->dev,?"adapter [%s] registered/n",?adap->name);
  • ????/*?create pre-declared device nodes?for?new-style drivers?*/
  • ????if?(adap->nr?<?__i2c_first_dynamic_bus_num)
  • ????????i2c_scan_static_board_info(adap);?//板級設(shè)備靜態(tài)掃描,第二部分會講述
  • ????/*?let?legacy drivers scan this bus?for?matching devices?*/
  • ????dummy?=?bus_for_each_drv(&i2c_bus_type,?NULL,?adap,
  • ????????????????i2c_do_add_adapter);
  • out_unlock:
  • ????mutex_unlock(&core_lock);
  • ????return res;
  • out_list:
  • ????idr_remove(&i2c_adapter_idr,?adap->nr);
  • ????goto out_unlock;
  • }
  • ????????首先對adapter和adapter中內(nèi)嵌的struct device結(jié)構(gòu)進(jìn)行必須的初始化,之后注冊adapter內(nèi)嵌的struct device。在這里注意一下adapter->dev的初始化,它的類別為i2c_adapter_class,如果沒有父結(jié)點(diǎn),則將其父結(jié)點(diǎn)設(shè)為platform_bus.adapter->dev的名字,為i2c + 總線號。

    ?

    文章轉(zhuǎn)自:輝輝308 ? ? ?https://blog.csdn.net/apple_guet/article/details/21379425

    轉(zhuǎn)載于:https://www.cnblogs.com/isAndyWu/p/10292649.html

    《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的I2C 总线原理与架构的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。