0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

干貨:I2C總線最全教程講解

GReq_mcu168 ? 來(lái)源:一口Linux ? 作者:一口Linux ? 2021-02-20 15:04 ? 次閱讀

裸機(jī)操作篇

本文以三星exynos4412為例講解I2C時(shí)序,并掛載在I2C控制器mpu6050陀螺儀的數(shù)據(jù)讀取實(shí)例。通過(guò)本篇文章,讀者可以理解I2C時(shí)序,以及如何基于三星I2C控制實(shí)現(xiàn)裸機(jī)讀取從設(shè)備信息方法。

前言:

I2C(Inter-Integrated Circuit)總線(也稱 IIC 或 I2C) 是有PHILIPS公司開(kāi)發(fā)的兩線式串行總線,用于連接微控制器及外圍設(shè)備,是微電子通信控制領(lǐng)域廣泛采用的一種總線標(biāo)準(zhǔn)。它是同步通信的一種特殊形式,具有接口線少、控制方式簡(jiǎn)單、器件封裝形式小、通信速率較高等優(yōu)點(diǎn)。

b134fd3c-71b8-11eb-8b86-12bb97331649.png

一、exynos4412 i2c控制器綜述

Exynos4412精簡(jiǎn)指令集微處理器支持4個(gè)IIC總線控制器。為了能使連接在總線上的主和從設(shè)備之間傳輸數(shù)據(jù),專用的數(shù)據(jù)線SDA和時(shí)鐘信號(hào)線SCL被使用,他們都是雙向的。

如果工作在多主機(jī)的IIC總線模式,多個(gè)4412處理器將從從機(jī)那接收數(shù)據(jù)或發(fā)送數(shù)據(jù)給從機(jī)。在IIC總線上的主機(jī)端4412會(huì)啟動(dòng)或終止一個(gè)數(shù)據(jù)傳輸。4412的IIC總線控制器會(huì)用一個(gè)標(biāo)準(zhǔn)的IIC總線仲裁機(jī)制去實(shí)現(xiàn)多主機(jī)和多從機(jī)傳輸數(shù)據(jù)。

通過(guò)控制如下寄存器以實(shí)現(xiàn)IIC總線上的多主機(jī)操作:

控制寄存器: I2CCON狀態(tài)寄存器: I2CSTATTx/Rx數(shù)據(jù)偏移寄存器: I2CDS地址寄存器: I2CADD

如果I2C總線空閑,那么SCL和SDA信號(hào)線將都為高電平。在SCL為高電平期間,如果SDA有由高到低電平的跳變,那么將啟動(dòng)一個(gè)起始信號(hào),如果SDA有由低到高電平的跳變,將啟動(dòng)一個(gè)結(jié)束信號(hào)。

主機(jī)端的設(shè)備總是提供起始和停止信號(hào)的一端。在起始信號(hào)被發(fā)出后,一個(gè)數(shù)據(jù)字節(jié)的前7位被當(dāng)作地址通過(guò)SDA線被傳輸。這個(gè)地制值決定了總線上的主設(shè)備將要選擇那個(gè)從設(shè)備作為傳輸對(duì)象,bit8決定傳輸數(shù)據(jù)的方向(是讀還是寫)。

I2C總線上的數(shù)據(jù)(即在SDA上傳輸?shù)臄?shù)據(jù))都是以8位字節(jié)傳輸?shù)?,在總線上傳輸操作的過(guò)程中,對(duì)發(fā)送或接收的數(shù)據(jù)字節(jié)數(shù)是沒(méi)有限制的。I2C總線上的主/從設(shè)備發(fā)送數(shù)據(jù)總是以一個(gè)數(shù)據(jù)的最高位開(kāi)始傳輸(即MSB方式),傳輸完一個(gè)字節(jié)后,應(yīng)答信號(hào)緊接其后。

二、I2C總線接口特性

9個(gè)通道多主、從I2C總線接口。其中8個(gè)通道作為普通接口(即I2C0、I2C1.。..),1個(gè)通道作為HDMI的專用接口。

7位地址模式。串行,8位單向或雙向的數(shù)據(jù)傳輸。在標(biāo)準(zhǔn)模式中,每秒最多可以傳輸100k位,即12.5kB的數(shù)據(jù)量。在快速模式中,每秒最多可以傳輸400k位,即50kB的數(shù)據(jù)量。支持主機(jī)端發(fā)送、接收,從機(jī)端發(fā)送、接收操作。支持中斷和查詢方式。

三、框圖

b197ce26-71b8-11eb-8b86-12bb97331649.png

從上圖可以看出,4412提供4個(gè)寄存器來(lái)完成所有的IIC操作。SDA線上的數(shù)據(jù)從IICDS寄存器經(jīng)過(guò)移位寄存器發(fā)出,或通過(guò)移位寄存器傳入IICDS寄器;IICADD寄存器中保存4412當(dāng)做從機(jī)時(shí)的地址;IICCON、IICSTAT兩個(gè)寄存器用來(lái)控制或標(biāo)識(shí)各種狀態(tài),比如選擇工作工作模式,發(fā)出S信號(hào)、P信號(hào),決定是否發(fā)出ACK信號(hào),檢測(cè)是否接收到ACK信號(hào)。

四、I2C總線接口操作

針對(duì)4412處理器的I2C總線接口,具備4種操作模式:

1 -- 主機(jī)發(fā)送模式2 -- 主機(jī)接收模式3 -- 從機(jī)發(fā)送模式4 -- 從機(jī)接收模式

下面將描述這些操作模式之間的功能關(guān)系:

0、數(shù)據(jù)有效性

b1b4b626-71b8-11eb-8b86-12bb97331649.png

SDA線上的數(shù)據(jù)必須在時(shí)鐘的高電平周期保持穩(wěn)定。數(shù)據(jù)線的高或低電平狀態(tài)IIC位傳輸數(shù)據(jù)的有效性在SCL線的時(shí)鐘信號(hào)是低電平才能改變。

1. 開(kāi)始和停止條件

當(dāng)4412的I2C接口空閑時(shí),它往往工作在從機(jī)模式?;蛘哒f(shuō),4412的的i2c接口在SDA線上察覺(jué)到一個(gè)起始信號(hào)之前它應(yīng)該工作在從機(jī)模式。當(dāng)控制器改變4412的i2c接口的工作模式為主機(jī)模式后,SDA線上發(fā)起數(shù)據(jù)傳輸并且控制器會(huì)產(chǎn)生SCL時(shí)鐘信號(hào)。

開(kāi)始條件通過(guò)SDA線進(jìn)行串行的字節(jié)傳輸,一個(gè)停止信號(hào)終止數(shù)據(jù)傳輸,停止信號(hào)是指SCL在高電平器件SDA線有從低到高電平的跳變,主機(jī)端產(chǎn)生起始和停止條件。當(dāng)主、從設(shè)備產(chǎn)生一個(gè)起始信號(hào)后,I2C總線將進(jìn)入忙狀態(tài)。這里需要說(shuō)明的是上述主從設(shè)備都有可能作為主機(jī)端。

當(dāng)一個(gè)主機(jī)發(fā)送了一個(gè)起始信號(hào)后,它也應(yīng)該發(fā)送一個(gè)從機(jī)地址以通知總線上的從設(shè)備。這個(gè)地址字節(jié)的低7位表示從設(shè)備地址,最高位表示傳輸數(shù)據(jù)的方向,即主機(jī)將要進(jìn)行讀還是寫。當(dāng)最高位是0時(shí),它將發(fā)起一個(gè)寫操作(發(fā)送操作);當(dāng)最高位是1時(shí),它將發(fā)起一個(gè)讀數(shù)據(jù)的請(qǐng)求(接收操作)。

主機(jī)端發(fā)起一個(gè)結(jié)束信號(hào)以完成傳輸操作,如果主機(jī)端想在總線上繼續(xù)進(jìn)行數(shù)據(jù)的傳輸,它將發(fā)出另外一個(gè)起始信號(hào)和從設(shè)備地址。用這樣的方式,它們可以用各種各樣的格式進(jìn)行讀寫操作。

下圖為起始和停止信號(hào):

b20b0bf2-71b8-11eb-8b86-12bb97331649.png

2. 數(shù)據(jù)傳輸格式

放到SDA線上的所有字節(jié)數(shù)據(jù)的長(zhǎng)度應(yīng)該為8位,在每次傳輸數(shù)據(jù)時(shí),對(duì)傳輸數(shù)據(jù)量沒(méi)有限制。在起始信號(hào)后的第一個(gè)數(shù)據(jù)字節(jié)應(yīng)該包含地址字段,當(dāng)4412的I2C接口被設(shè)置為主模式時(shí),地址字節(jié)應(yīng)該由控制器端發(fā)出。在每個(gè)字節(jié)后,應(yīng)該有一個(gè)應(yīng)答位。

如果從機(jī)要完成一些其他功能后(例如一個(gè)內(nèi)部中斷服務(wù)程序)才能繼續(xù)接收或發(fā)送下一個(gè)字節(jié),從機(jī)可以拉低SCL迫使主機(jī)進(jìn)入等待狀態(tài)。當(dāng)從機(jī)準(zhǔn)備好接收下一個(gè)數(shù)據(jù)并釋放SCL后,數(shù)據(jù)傳輸繼續(xù)。如果主機(jī)在傳輸數(shù)據(jù)期間也需要完成一些其他功能(例如一個(gè)內(nèi)部中斷服務(wù)程序)也可以拉低SCL以占住總線。

下面的圖中將說(shuō)明數(shù)據(jù)傳輸格式:

b242946e-71b8-11eb-8b86-12bb97331649.png

上圖中說(shuō)明,在傳輸完每個(gè)字節(jié)數(shù)據(jù)后,都會(huì)有一個(gè)應(yīng)答信號(hào),這個(gè)應(yīng)答信號(hào)在第9個(gè)時(shí)鐘周期。具體過(guò)程如下(注意下面描述的讀寫過(guò)程都是針對(duì) 4412處理器而言,當(dāng)有具體的I2C設(shè)備與4412相連時(shí),數(shù)據(jù)表示什么需要看具體的I2C設(shè)備,4412是不知道數(shù)據(jù)的含義的):

寫過(guò)程:主機(jī)發(fā)送一個(gè)起始信號(hào)S→發(fā)送從機(jī)7位地址和1位方向,方向位表示寫→主機(jī)釋放SDA線方便從機(jī)給回應(yīng)→有從機(jī)匹配到地址,拉低SDA線作為ACK→主機(jī)重新獲得SDA傳輸8位數(shù)據(jù)→主機(jī)釋放SDA線方便從機(jī)給回應(yīng)→從機(jī)收到數(shù)據(jù)拉低SDA線作為ACK告訴主機(jī)數(shù)據(jù)接收成功→主機(jī)發(fā)出停止信號(hào)。

讀過(guò)程:主機(jī)發(fā)送一個(gè)起始信號(hào)S→發(fā)送從機(jī)7位地址和1位方向,方向位表示讀→主機(jī)釋放SDA線方便從機(jī)給回應(yīng)→有從機(jī)匹配到地址,拉低SDA線作為ACK→從機(jī)繼續(xù)占用SDA線,用SDA傳輸8位數(shù)據(jù)給主機(jī)→從機(jī)釋放SDA線(拉高)方便主機(jī)給回應(yīng)→主機(jī)接收到數(shù)據(jù)→主機(jī)獲得SDA線控制并拉低SDA線作為ACK告訴從機(jī)數(shù)據(jù)接收成功→主機(jī)發(fā)出停止信號(hào)。注意:在具體的I2C通信時(shí),要看I2C設(shè)備才能確定讀寫時(shí)序,比如下面即將描述的第七大點(diǎn)中的示例,讀寫EEPROM中就會(huì)說(shuō)道具體的數(shù)據(jù)含義,讀寫過(guò)程。

3. 應(yīng)答信號(hào)的傳輸

為了完成一個(gè)字節(jié)數(shù)據(jù)的傳輸,接收方將發(fā)送一個(gè)應(yīng)答位給發(fā)送方。應(yīng)答信號(hào)出現(xiàn)在SCL線上的時(shí)鐘周期中的第九個(gè)時(shí)鐘周期,為了發(fā)送或接收1個(gè)字節(jié)的數(shù)據(jù),主機(jī)端會(huì)產(chǎn)生8個(gè)時(shí)鐘周期,為了傳輸一個(gè)ACK位,主機(jī)端需要產(chǎn)生一個(gè)時(shí)鐘脈沖。

ACK時(shí)鐘脈沖到來(lái)之際,發(fā)送方會(huì)在SDA線上設(shè)置高電平以釋放SDA線。在ACK時(shí)鐘脈沖之間,接收方會(huì)驅(qū)動(dòng)和保持SDA線為低電平,這發(fā)生在第9個(gè)時(shí)鐘脈沖為高電平期間。應(yīng)答信號(hào)為低電平時(shí),規(guī)定為有效應(yīng)答位(ACK簡(jiǎn)稱應(yīng)答位),表示接收器已經(jīng)成功地接收了該字節(jié);應(yīng)答信號(hào)為高電平時(shí),規(guī)定為非應(yīng)答位(NACK),一般表示接收器接收該字節(jié)沒(méi)有成功。對(duì)于反饋有效應(yīng)答位ACK的要求是,接收器在第9個(gè)時(shí)鐘脈沖之前的低電平期間將SDA線拉低,并且確保在該時(shí)鐘的高電平期間為穩(wěn)定的低電平。如果接收器是主控器,則在它收到最后一個(gè)字節(jié)后,發(fā)送一個(gè)NACK信號(hào)(即不發(fā)出ACK信號(hào)),以通知被控發(fā)送器結(jié)束數(shù)據(jù)發(fā)送,并釋放SDA線,以便主控接收器發(fā)送一個(gè)停止信號(hào)P。

b282ece4-71b8-11eb-8b86-12bb97331649.png

4. 讀寫操作

當(dāng)I2C控制器在發(fā)送模式下發(fā)送數(shù)據(jù)后,I2C總線接口將等待直到移位寄存器(I2CDS)接收到一個(gè)數(shù)據(jù)。在往此寄存器寫入一個(gè)新數(shù)據(jù)前,SCL線應(yīng)該保持為低電平,寫完數(shù)據(jù)后,I2C控制器將釋放SCL線。當(dāng)前正在傳輸?shù)臄?shù)據(jù)傳輸完成后,4412會(huì)捕捉到一個(gè)中斷,然后cpu將開(kāi)始往I2CDS寄存器中寫入一個(gè)新的數(shù)據(jù)。

當(dāng)I2C控制器在接收模式下接收到數(shù)據(jù)后,I2C總線接口將等待直到I2CDS寄存器被讀。在讀到新數(shù)據(jù)之前,SCL線會(huì)被保持為低電平,讀到數(shù)據(jù)后I2C控制器將釋放掉SCL線。一個(gè)新數(shù)據(jù)接收完成后,4412將收到一個(gè)中斷,cpu收到這個(gè)中斷請(qǐng)求后,它將從I2CDS寄存器中讀取數(shù)據(jù)。

5. 總線仲裁機(jī)制

總線上可能掛接有多個(gè)器件,有時(shí)會(huì)發(fā)生兩個(gè)或多個(gè)主器件同時(shí)想占用總線的情況,這種情況叫做總線競(jìng)爭(zhēng)。I2C總線具有多主控能力,可以對(duì)發(fā)生在SDA線上的總線競(jìng)爭(zhēng)進(jìn)行仲裁,其仲裁原則是這樣的:當(dāng)多個(gè)主器件同時(shí)想占用總線時(shí),如果某個(gè)主器件發(fā)送高電平,而另一個(gè)主器件發(fā)送低電平,則發(fā)送電平與此時(shí)SDA總線電平不符的那個(gè)器件將自動(dòng)關(guān)閉其輸出級(jí)??偩€競(jìng)爭(zhēng)的仲裁是在兩個(gè)層次上進(jìn)行的。首先是地址位的比較,如果主器件尋址同一個(gè)從器件,則進(jìn)入數(shù)據(jù)位的比較,從而確保了競(jìng)爭(zhēng)仲裁的可靠性。由于是利用I2C總線上的信息進(jìn)行仲裁,因此不會(huì)造成信息的丟失。

6. 終止條件

當(dāng)一個(gè)從接收者不能識(shí)別從地址時(shí),它將保持SDA線為高電平。在這樣的情況下,主機(jī)會(huì)產(chǎn)生一個(gè)停止信號(hào)并且取消數(shù)據(jù)的傳輸。當(dāng)終止傳輸產(chǎn)生后,主機(jī)端接收器會(huì)通過(guò)取消ACK的產(chǎn)生以告訴從機(jī)端發(fā)送器結(jié)束發(fā)送操作。這將在主機(jī)端接收器接收到從機(jī)端發(fā)送器發(fā)送的最后一個(gè)字節(jié)之后發(fā)生,為了讓主機(jī)端產(chǎn)生一個(gè)停止條件,從機(jī)端發(fā)送者將釋放SDA線。

7. 配置I2C總線

如果要設(shè)置I2C總線中SCL時(shí)鐘信號(hào)的頻率,可以在I2CCON寄存器中設(shè)置4位分頻器的值。I2C總線接口地址值存放在I2C總線地址寄存器(I2CADD)中,默認(rèn)值未知。

8. 每種模式下的操作流程圖

在I2C總線上執(zhí)行任何的收發(fā)Tx/Rx操作前,應(yīng)該做如下配置:(1)在I2CADD寄存器中寫入從設(shè)備地址(2)設(shè)置I2CCON控制寄存器 a. 使能中斷 b. 定義SCL頻率(3)設(shè)置I2CSTAT寄存器以使能串行輸出

下圖為主設(shè)備發(fā)送模式

b2aa406e-71b8-11eb-8b86-12bb97331649.png

下圖為主設(shè)備接收模式

b55b86a6-71b8-11eb-8b86-12bb97331649.png

下圖為從設(shè)備發(fā)送模式

b7edb2cc-71b8-11eb-8b86-12bb97331649.png

下圖為從設(shè)備接收

模式

b830a334-71b8-11eb-8b86-12bb97331649.png

1-- I2C總線控制寄存器

IICCON寄存器用于控制是否發(fā)出ACK信號(hào)、設(shè)置發(fā)送器的時(shí)鐘、開(kāi)啟I2C中斷,并標(biāo)識(shí)中斷是否發(fā)生

ba6e5d76-71b8-11eb-8b86-12bb97331649.png

使用IICCON寄存器時(shí),有如下注意事項(xiàng)

1)、發(fā)送模式的時(shí)鐘頻率由位[6]、位[3:0]聯(lián)合決定。另外,當(dāng)IICCON[6]=0時(shí),IICCON[3:0]不能取0或1。

2)、位[4]用來(lái)標(biāo)識(shí)是否有I2C中斷發(fā)生,讀出為0時(shí)標(biāo)識(shí)沒(méi)有中斷發(fā)生,讀出為1時(shí)標(biāo)識(shí)有中斷發(fā)生。當(dāng)此位為1時(shí),SCL線被拉低,此時(shí)所以I2C傳輸停止;如果要繼續(xù)傳輸,需寫入0清除它。

中斷在以下3種情況下發(fā)生:

1 -- 當(dāng)發(fā)送地址信息或接收到一個(gè)從機(jī)地址并且吻合時(shí);

2 -- 當(dāng)總線仲裁失敗時(shí);

3 -- 當(dāng)發(fā)送/接收完一個(gè)字節(jié)的數(shù)據(jù)(包括響應(yīng)位)時(shí);

3)、基于SDA、SCL線上時(shí)間特性的考慮,要發(fā)送數(shù)據(jù)時(shí),先將數(shù)據(jù)寫入IICDS寄存器,然后再清除中斷。

4)、如果IICCON[5]=0,IICCON[4]將不能正常工作,所以,即使不使用I2C中斷,也要將IICCON[5]設(shè)為1.

2 -- I2C狀態(tài)寄存器

IICSTAT寄存器用于選擇I2C接口的工作模式,發(fā)出S信號(hào)、P信號(hào),使能接收/發(fā)送功能,并標(biāo)識(shí)各種狀態(tài),比如總線仲裁是否成功、作為從機(jī)時(shí)是否被尋址、是否接收到0地址、是否接收到ACK信號(hào)等。

bad280c6-71b8-11eb-8b86-12bb97331649.png

3 -- I2C數(shù)據(jù)發(fā)送/接收移位寄存器

bb1babf2-71b8-11eb-8b86-12bb97331649.png

fs4412的i2c總線上掛載了mpu6050

mpu6050每次讀取或者要寫入數(shù)據(jù)時(shí),必須先告知從設(shè)備要操作的內(nèi)部寄存器地址(RA),然后緊跟著讀取或者寫入數(shù)據(jù)(DATA),內(nèi)部寄存器的配置和讀取一次最多1個(gè)data,交互時(shí)序如下:

bb3aa084-71b8-11eb-8b86-12bb97331649.png

bb71a48a-71b8-11eb-8b86-12bb97331649.png

【注意】上述兩個(gè)時(shí)序非常重要,后續(xù)我們要編寫基于linux的驅(qū)動(dòng)編寫i2c_msg也要基于上述時(shí)序來(lái)實(shí)現(xiàn)。

bbc13ae0-71b8-11eb-8b86-12bb97331649.png

【寄存器使用規(guī)則】

下面先提前講一下具體應(yīng)用中如何啟動(dòng)和恢復(fù)IIC的傳輸

啟動(dòng)或恢復(fù)4412的I2C傳輸有以下兩種方法。

1) 當(dāng)IICCON[4]即中斷狀態(tài)位為0時(shí),通過(guò)寫IICSTAT寄存器啟動(dòng)I2C操作。有以下兩種情況。

1--在主機(jī)模式,

令I(lǐng)ICSTAT[5:4]等于0b11,將發(fā)出S信號(hào)和IICDS寄存器的數(shù)據(jù)(尋址),

令I(lǐng)ICSTAT[5:4]等于0b01,將發(fā)出P信號(hào)。

2--在從機(jī)模式,令I(lǐng)ICSTAT[4]等于1將等待其他主機(jī)發(fā)出S信號(hào)及地址信息。

2)當(dāng)IICCON[4]即中斷狀態(tài)為1時(shí),表示I2C操作被暫停。在這期間設(shè)置好其他寄存器之后,向IICCON[4]寫入0即可恢復(fù)I2C操作。所謂“設(shè)置其他寄存器”,有以下三種情況:

1--對(duì)于主機(jī)模式,可以按照上面1的方法寫IICSTAT寄存器,恢復(fù)I2C操作后即可發(fā)出S信號(hào)和IICDS寄存器的值(尋址),或發(fā)出P信號(hào)。

2--對(duì)于發(fā)送器,可以將下一個(gè)要發(fā)送的數(shù)據(jù)寫入IICDS寄存器中,恢復(fù)I2C操作后即可發(fā)出這個(gè)數(shù)據(jù)。

3--對(duì)于接收器,可以從IICDS寄存器讀出接收到的數(shù)據(jù)。最后向IICCON[4]寫入0的同時(shí),設(shè)置IICCON[7]以決定是否在接收到下一個(gè)數(shù)據(jù)后是否發(fā)出ACK信號(hào)。

【MPU6050硬件電路圖】(實(shí)際板子電路圖不一定和下面一樣,具體問(wèn)題具體分析,本例參考exynos-fs4412開(kāi)發(fā)板)

bc2574b0-71b8-11eb-8b86-12bb97331649.png

1 AD0接地的 值為 0

bc779d8a-71b8-11eb-8b86-12bb97331649.png

所以從設(shè)備地址為0x86

2 SCL、SDA連接的i2c_SCL5、i2c_SDA5

bcb117d6-71b8-11eb-8b86-12bb97331649.png

由此可得這兩個(gè)信號(hào)線復(fù)用了GPIO的GPB的2、3引腳

3 查閱exynos4412 datasheet 6.2.2 Part 1可得

bd17021c-71b8-11eb-8b86-12bb97331649.png

所以設(shè)置GPIO 的 GPB 【15:8】= 0x33 即可

下面是個(gè)IIC總線實(shí)例:

用IIC總線實(shí)現(xiàn)CPU與MPU-6050的數(shù)據(jù)查詢

具體代碼如下:

#include “exynos_4412.h”

//****************************************// MPU6050常用內(nèi)部地址,以下地址在mpu6050內(nèi)部//****************************************#define SMPLRT_DIV 0x19 //陀螺儀采樣率,典型值:0x07(125Hz)#define CONFIG 0x1A //低通濾波頻率,典型值:0x06(5Hz)#define GYRO_CONFIG 0x1B //陀螺儀自檢及測(cè)量范圍,典型值:0x18(不自檢,2000deg/s)#define ACCEL_CONFIG 0x1C //加速計(jì)自檢、測(cè)量范圍及高通濾波頻率,典型值:0x01(不自檢,2G,5Hz)#define ACCEL_XOUT_H 0x3B#define ACCEL_XOUT_L 0x3C#define ACCEL_YOUT_H 0x3D#define ACCEL_YOUT_L 0x3E#define ACCEL_ZOUT_H 0x3F#define ACCEL_ZOUT_L 0x40#define TEMP_OUT_H 0x41#define TEMP_OUT_L 0x42#define GYRO_XOUT_H 0x43#define GYRO_XOUT_L 0x44#define GYRO_YOUT_H 0x45#define GYRO_YOUT_L 0x46#define GYRO_ZOUT_H 0x47#define GYRO_ZOUT_L 0x48#define PWR_MGMT_1 0x6B //電源管理,典型值:0x00(正常啟用)#define WHO_AM_I 0x75 //IIC地址寄存器(默認(rèn)數(shù)值0x68,只讀)#define SlaveAddress 0xD0 //IIC寫入時(shí)的地址字節(jié)數(shù)據(jù),+1為讀取

void mydelay_ms(int time){int i, j;while(time--){for (i = 0; i 《 5; i++)for (j = 0; j 《 514; j++);}}/********************************************************************** * @brief iic read a byte program body * @param[in] slave_addr, addr, &data * @return None **********************************************************************/void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data){/*根據(jù)mpu6050的datasheet,要讀取數(shù)據(jù)必須先執(zhí)行寫操作:寫入一個(gè)從設(shè)備地址,

然后執(zhí)行讀操作,才能讀取到該內(nèi)部寄存器的內(nèi)容*/

I2C5.I2CDS = slave_addr; //將從機(jī)地址寫入I2CDS寄存器中I2C5.I2CCON = (1 《《 7)|(1 《《 6)|(1 《《 5); //設(shè)置時(shí)鐘并使能中斷I2C5.I2CSTAT = 0xf0; //[7:6]設(shè)置為0b11,主機(jī)發(fā)送模式;//往[5:4]位寫0b11,即產(chǎn)生啟動(dòng)信號(hào),發(fā)出IICDS寄存器中的地址

while(?。↖2C5.I2CCON & (1 《《 4))); // 等待傳輸結(jié)束,傳輸結(jié)束后,I2CCON [4]位為1,標(biāo)識(shí)有中斷發(fā)生;// 此位為1時(shí),SCL線被拉低,此時(shí)I2C傳輸停止;I2C5.I2CDS = addr; //寫命令值I2C5.I2CCON = I2C5.I2CCON & (~(1 《《 4));// I2CCON [4]位清0,繼續(xù)傳輸

while(!(I2C5.I2CCON & (1 《《 4)));// 等待傳輸結(jié)束I2C5.I2CSTAT = 0xD0; // I2CSTAT[5:4]位寫0b01,發(fā)出停止信號(hào)

I2C5.I2CDS = slave_addr | 1; //表示要讀出數(shù)據(jù)I2C5.I2CCON = (1 《《 7)|(1 《《 6) |(1 《《 5) ; //設(shè)置時(shí)鐘并使能中斷I2C5.I2CSTAT = 0xb0;//[7:6]位0b10,主機(jī)接收模式;

//往[5:4]位寫0b11,即產(chǎn)生啟動(dòng)信號(hào),發(fā)出IICDS寄存器中的地址

// I2C5.I2CCON = I2C5.I2CCON & (~(1 《《 4)); 如果強(qiáng)行關(guān)閉,將讀取不到數(shù)據(jù)

while(!(I2C5.I2CCON & (1 《《 4)));//等待傳輸結(jié)束,接收數(shù)據(jù)

I2C5.I2CCON &= ~((1《《7)|(1 《《 4));/* Resume the operation & no ack*/ // I2CCON [4]位清0,繼續(xù)傳輸,接收數(shù)據(jù),

// 主機(jī)接收器接收到最后一字節(jié)數(shù)據(jù)后,不發(fā)出應(yīng)答信號(hào) no ack // 從機(jī)發(fā)送器釋放SDA線,以允許主機(jī)發(fā)出P信號(hào),停止傳輸;

while(?。↖2C5.I2CCON & (1 《《 4)));// 等待傳輸結(jié)束

I2C5.I2CSTAT = 0x90;*data = I2C5.I2CDS;I2C5.I2CCON &= ~(1《《4); /*clean interrupt pending bit */

mydelay_ms(10);*data = I2C5.I2CDS;

}/********************************************************************** * @brief iic write a byte program body * @param[in] slave_addr, addr, data * @return None **********************************************************************/void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data){I2C5.I2CDS = slave_addr;I2C5.I2CCON = (1 《《 7)|(1 《《 6)|(1 《《 5) ;I2C5.I2CSTAT = 0xf0;while(?。↖2C5.I2CCON & (1 《《 4)));

I2C5.I2CDS = addr;I2C5.I2CCON = I2C5.I2CCON & (~(1 《《 4));while(!(I2C5.I2CCON & (1 《《 4)));

I2C5.I2CDS = data;I2C5.I2CCON = I2C5.I2CCON & (~(1 《《 4));while(?。↖2C5.I2CCON & (1 《《 4)));

I2C5.I2CSTAT = 0xd0;I2C5.I2CCON = I2C5.I2CCON & (~(1 《《 4));mydelay_ms(10);

}

void MPU6050_Init (){iic_write(SlaveAddress, PWR_MGMT_1, 0x00);iic_write(SlaveAddress, SMPLRT_DIV, 0x07);iic_write(SlaveAddress, CONFIG, 0x06);iic_write(SlaveAddress, GYRO_CONFIG, 0x18);iic_write(SlaveAddress, ACCEL_CONFIG, 0x01);}/*讀取mpu6050某個(gè)內(nèi)部寄存器的內(nèi)容*/int get_data(unsigned char addr){char data_h, data_l;iic_read(SlaveAddress, addr, &data_h);iic_read(SlaveAddress, addr+1, &data_l);return (data_h《《8)|data_l;}

/* * 裸機(jī)代碼,不同于LINUX 應(yīng)用層, 一定加循環(huán)控制 */int main(void){int data;

unsigned char zvalue;GPB.CON = (GPB.CON & ~(0xff《《8)) | 0x33《《8; // GPBCON[3], I2C_5_SCL GPBCON[2], I2C_5_SDAmydelay_ms(100);uart_init();

/*---------------------------------------------------------------------*/I2C5.I2CSTAT = 0xD0;I2C5.I2CCON &= ~(1《《4); /*clean interrupt pending bit *//*---------------------------------------------------------------------*/

mydelay_ms(100);MPU6050_Init();mydelay_ms(100);

printf(“

********** I2C test??! ***********

”);

while(1){//Turn on

data = get_data(GYRO_ZOUT_H);printf(“ GYRO --》 Z 《--- %x”, data);data = get_data(GYRO_XOUT_H);printf(“ GYRO --》 X 《--- %x”, data);printf(“

”);mydelay_ms(1000);}return 0;}實(shí)驗(yàn)結(jié)果如下:********** I2C test??! *********** GYRO --》 Z 《--- 1c GYRO --》 X 《--- feda GYRO --》 Z 《--- fefc GYRO --》 X 《--- fed6 GYRO --》 Z 《--- fefe GYRO --》 X 《--- fed6 GYRO --》 Z 《--- fefe GYRO --》 X 《--- fedc GYRO --》 Z 《--- fefe GYRO --》 X 《--- feda GYRO --》 Z 《--- fefc GYRO --》 X 《--- fed6 GYRO --》 Z 《--- fefe GYRO --》 X 《--- feda GYRO --》 Z 《--- fcf2 GYRO --》 X 《--- 202 GYRO --》 Z 《--- ec GYRO --》 X 《--- faa0 GYRO --》 Z 《--- 4c GYRO --》 X 《--- e GYRO --》 Z 《--- fe GYRO --》 X 《--- fed8 GYRO --》 Z 《--- 0 GYRO --》 X 《--- fede GYRO --》 Z 《--- 0 GYRO --》 X 《--- feda

讀寫操作代碼解析:

寫入一個(gè)數(shù)據(jù)流程

bd250966-71b8-11eb-8b86-12bb97331649.png

讀數(shù)據(jù)流程

bd3fce54-71b8-11eb-8b86-12bb97331649.png

02

驅(qū)動(dòng)篇-之基于linux的mpu6050驅(qū)動(dòng)

本文以三星exynos4412為例講解mpu6050陀螺儀的數(shù)據(jù)讀取驅(qū)動(dòng)的實(shí)現(xiàn)。通過(guò)本篇文章,讀者可以掌握基于Linux驅(qū)動(dòng)I2C編寫方法。

I2C核心(i2c_core)

I2C核心維護(hù)了i2c_bus結(jié)構(gòu)體,提供了I2C總線驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)的注冊(cè)、注銷方法,維護(hù)了I2C總線的驅(qū)動(dòng)、設(shè)備鏈表,實(shí)現(xiàn)了設(shè)備、驅(qū)動(dòng)的匹配探測(cè)。此部分代碼由Linux內(nèi)核提供。

I2C總線驅(qū)動(dòng)

I2C總線驅(qū)動(dòng)維護(hù)了I2C適配器數(shù)據(jù)結(jié)構(gòu)(i2c_adapter)和適配器的通信方法數(shù)據(jù)結(jié)構(gòu)(i2c_algorithm)。所以I2C總線驅(qū)動(dòng)可控制I2C適配器產(chǎn)生start、stop、ACK等。此部分代碼由具體的芯片廠商提供,比如Samsung、高通。

I2C設(shè)備驅(qū)動(dòng)

I2C設(shè)備驅(qū)動(dòng)主要維護(hù)兩個(gè)結(jié)構(gòu)體:i2c_driver和i2c_client,實(shí)現(xiàn)和用戶交互的文件操作集合fops、cdev等。此部分代碼就是驅(qū)動(dòng)開(kāi)發(fā)者需要完成的。

Linux內(nèi)核中描述I2C的四個(gè)核心結(jié)構(gòu)體

1)i2c_client—掛在I2C總線上的I2C從設(shè)備

每一個(gè)i2c從設(shè)備都需要用一個(gè)i2c_client結(jié)構(gòu)體來(lái)描述,i2c_client對(duì)應(yīng)真實(shí)的i2c物理設(shè)備device。

bd9e8fd4-71b8-11eb-8b86-12bb97331649.png

但是i2c_client不是我們自己寫程序去創(chuàng)建的,而是通過(guò)以下常用的方式自動(dòng)創(chuàng)建的:

方法一: 分配、設(shè)置、注冊(cè)i2c_board_info方法二: 獲取adapter調(diào)用i2c_new_device方法三: 通過(guò)設(shè)備樹(shù)(devicetree)創(chuàng)建

方法1和方法2通過(guò)platform創(chuàng)建,這兩種方法在內(nèi)核3.0版本以前使用所以在這不詳細(xì)介紹;方法3是最新的方法,3.0版本之后的內(nèi)核都是通過(guò)這種方式創(chuàng)建的,文章后面的案例就按方法3。

2)i2c_adapter

I2C總線適配器,即soc中的I2C總線控制器,硬件上每一對(duì)I2C總線都對(duì)應(yīng)一個(gè)適配器來(lái)控制它。在Linux內(nèi)核代碼中,每一個(gè)adapter提供了一個(gè)描述它的結(jié)構(gòu)(struct i2c_adapter),再通過(guò)i2c core層將i2c設(shè)備與i2c adapter關(guān)聯(lián)起來(lái)。主要用來(lái)完成i2c總線控制器相關(guān)的數(shù)據(jù)通信,此結(jié)構(gòu)體在芯片廠商提供的代碼中維護(hù)。

be05087c-71b8-11eb-8b86-12bb97331649.png

3)i2c_algorithm

I2C總線數(shù)據(jù)通信算法,通過(guò)管理I2C總線控制器,實(shí)現(xiàn)對(duì)I2C總線上數(shù)據(jù)的發(fā)送和接收等操作。亦可以理解為I2C總線控制器(適配器adapter)對(duì)應(yīng)的驅(qū)動(dòng)程序,每一個(gè)適配器對(duì)應(yīng)一個(gè)驅(qū)動(dòng)程序,用來(lái)描述適配器和設(shè)備之間的通信方法,由芯片廠商去實(shí)現(xiàn)的。

be71ee06-71b8-11eb-8b86-12bb97331649.png

4)i2c_driver

用于管理I2C的驅(qū)動(dòng)程序和i2c設(shè)備(client)的匹配探測(cè),實(shí)現(xiàn)與應(yīng)用層交互的文件操作集合fops、cdev等。

becb28e0-71b8-11eb-8b86-12bb97331649.png

填寫設(shè)備樹(shù)節(jié)點(diǎn)信息:

硬件電路圖如下:

bf2c537c-71b8-11eb-8b86-12bb97331649.png

bf5cbd6e-71b8-11eb-8b86-12bb97331649.png

由上圖所示硬件使用的是I2C通道5,

bf70f7de-71b8-11eb-8b86-12bb97331649.png

查找exnos4412的datasheet 29.6.1節(jié),對(duì)應(yīng)的基地址為0x138B0000。

bf8672c6-71b8-11eb-8b86-12bb97331649.png

由上圖可知中斷引腳復(fù)用的是GPX3_3。

參考I2C最全干貨-(1)裸機(jī)操作篇 ,mpu6050從設(shè)備地址為0x68。

綜上設(shè)備樹(shù)中描述I2C設(shè)備信息

i2c@138B0000 { 基地址是 138B0000 samsung,i2c-sda-delay = 《100》; samsung,i2c-max-bus-freq = 《20000》; pinctrl-0 =《&i2c5_bus》; 通道5 pinctrl-names = “default”; status = “okay”; mpu6050-3-asix@68 { compatible = “invensense,mpu6050”; reg= 《0x68》; 從設(shè)備地址 interrupt-parent = 《&gpx3》; 中斷父節(jié)點(diǎn) interrupts= 《3 2》; 中斷index=3,中斷觸發(fā)方式:下降沿觸發(fā) }; };其中 外面節(jié)點(diǎn) i2c@138B0000{}是i2c控制器設(shè)備樹(shù)信息,子節(jié)點(diǎn) mpu6050-3-asix@68{}是從設(shè)備mpu6050的設(shè)備樹(shù)節(jié)點(diǎn)信息。

結(jié)構(gòu)體之間關(guān)系如下:

bfa2cc32-71b8-11eb-8b86-12bb97331649.png

1. 設(shè)備樹(shù)節(jié)點(diǎn)分為控制器和從設(shè)備兩部分,控制器節(jié)點(diǎn)信息會(huì)通過(guò)platform總線與控制器驅(qū)動(dòng)匹配,

控制器驅(qū)動(dòng)已經(jīng)由內(nèi)核提供,結(jié)構(gòu)體如下:

bfe337ea-71b8-11eb-8b86-12bb97331649.png

2. 從設(shè)備節(jié)點(diǎn)信息最終會(huì)通過(guò)i2c_bus與i2c_driver匹配,i2c_driver需要由開(kāi)發(fā)者自己注冊(cè),并實(shí)現(xiàn)字符設(shè)備接口和創(chuàng)建設(shè)備節(jié)點(diǎn)/dev/mpu6050;

3. 用戶通過(guò)字符設(shè)備節(jié)點(diǎn)/dev/mpu6050調(diào)用內(nèi)核的注冊(cè)的接口函數(shù)mpu6050_read_byte、mpu6050_write_byte;

4. 內(nèi)核的i2c core模塊提供了i2c協(xié)議相關(guān)的核心函數(shù),在實(shí)現(xiàn)讀寫操作的時(shí)候,需要通過(guò)一個(gè)重要的函數(shù)i2c_transfer(),這個(gè)函數(shù)是i2c核心提供給設(shè)備驅(qū)動(dòng)的,通過(guò)它發(fā)送的數(shù)據(jù)需要被打包成i2c_msg結(jié)構(gòu),這個(gè)函數(shù)最終會(huì)回調(diào)相應(yīng)i2c_adapter-》i2c_algorithm-》master_xfer()接口將i2c_msg對(duì)象發(fā)送到i2c物理控制器。

應(yīng)用實(shí)例,實(shí)現(xiàn)mpu6050驅(qū)動(dòng),讀取溫度:

【注】實(shí)例所用soc是exynos4412,為三星公司所出品,所以i2c控制器設(shè)備樹(shù)節(jié)點(diǎn)信息可以參考linux內(nèi)核根目錄以下文件:Documentationdevicetreeindingsi2ci2c-s3c2410.txt。不同的公司設(shè)計(jì)的i2c控制器設(shè)備樹(shù)節(jié)點(diǎn)信息填寫格式不盡相同,需要根據(jù)具體產(chǎn)品填寫。

編寫驅(qū)動(dòng)代碼

分配、設(shè)置、注冊(cè)i2c_driver結(jié)構(gòu)體

bffa6050-71b8-11eb-8b86-12bb97331649.png

i2c總線驅(qū)動(dòng)模型屬于設(shè)備模型中的一類,同樣struct i2c_driver結(jié)構(gòu)體繼承于struct driver,匹配方法和設(shè)備模型中講的一樣,這里要去匹配設(shè)備樹(shù),所以必須實(shí)現(xiàn)i2c_driver結(jié)構(gòu)體中的driver成員中的of_match_table成員:

c093d956-71b8-11eb-8b86-12bb97331649.png

如果和設(shè)備樹(shù)匹配成功,那么就會(huì)調(diào)用probe函數(shù)

c0f74aa4-71b8-11eb-8b86-12bb97331649.png

實(shí)現(xiàn)文件操作集合

c111b9ac-71b8-11eb-8b86-12bb97331649.png

如何填充i2c_msg?

根據(jù)mpu6050的datasheet可知,向mpu6050寫入1個(gè)data和讀取1個(gè)值的時(shí)序分別如下圖所示。

c1308b84-71b8-11eb-8b86-12bb97331649.png

c16841fa-71b8-11eb-8b86-12bb97331649.png

基于Linux的i2c架構(gòu)編寫驅(qū)動(dòng)程序,我們需要用struct i2c_msg結(jié)構(gòu)體來(lái)表示上述所有信息。

c1d72eb2-71b8-11eb-8b86-12bb97331649.png

編寫i2c_msg信息原則如下:

1. 有幾個(gè)S信號(hào),msg數(shù)組就要有幾個(gè)元素;2. addr為從設(shè)備地址,通過(guò)i2c總線調(diào)用注冊(cè)的probe函數(shù)的參數(shù)i2c_client傳遞下來(lái);3. len的長(zhǎng)度不包括S、AD、ACK、P;4. buf為要發(fā)送或者要讀取的DATA的內(nèi)存地址。綜上所述:1. Single-Byte Write Sequence時(shí)序只需要1個(gè)i2c_msg,len值為2,buf內(nèi)容為是RA、DATA;2. Single-Byte Read Sequence時(shí)序需要2個(gè)i2c_msg,len值分別都為1,第1個(gè)msg的buf是RA,第2個(gè)msg的buf緩沖區(qū)用于存取從設(shè)備發(fā)送的DATA。

c21d51bc-71b8-11eb-8b86-12bb97331649.png

03

驅(qū)動(dòng)篇-之內(nèi)核架構(gòu)分析

通過(guò)前兩章的講解,大家對(duì)在裸機(jī)下讀寫mpu6050和基于Linux的內(nèi)核I2C框架如何編寫mpu6050驅(qū)動(dòng)都已經(jīng)有了一定了解。本文以linux3.14.0為參考, 討論Linux中的i2c控制器驅(qū)動(dòng)是的。

驅(qū)動(dòng)入口

三星的i2c控制器驅(qū)動(dòng)是基于platform總線實(shí)現(xiàn)的,struct platform_driver定義如下:

c27d1b24-71b8-11eb-8b86-12bb97331649.png

c2be871c-71b8-11eb-8b86-12bb97331649.png

當(dāng)設(shè)備樹(shù)節(jié)點(diǎn)信息的compatible信息和注冊(cè)的platform_driver.driver. of_match_table字符串會(huì)通過(guò)platform總線的macth方法進(jìn)行配對(duì),匹配成功后會(huì)調(diào)用probe函數(shù)s3c24xx_i2c_probe();

驅(qū)核心結(jié)構(gòu)

要理解i2c的內(nèi)核架構(gòu)首先必須了解一下這幾個(gè)機(jī)構(gòu)體:

s3c24xx_i2c

該結(jié)構(gòu)體是三星i2c控制器專用結(jié)構(gòu)體,描述了控制器的所有資源,包括用于等待中斷喚醒的等待隊(duì)列、傳輸i2c_msg的臨時(shí)指針、記錄與硬件通信的狀態(tài)、中斷號(hào)、控制器基地址、時(shí)鐘、i2c_adapter、設(shè)備樹(shù)信息pdata等。i2c控制器初始化的時(shí)候會(huì)為該控制器創(chuàng)建該結(jié)構(gòu)體變量,并初始化之。

i2c_adapter

對(duì)象實(shí)現(xiàn)了一組通過(guò)一個(gè)i2c控制器發(fā)送消息的所有信息, 包括時(shí)序, 地址等等, 即封裝了i2c控制器的“控制信息”。它被i2c主機(jī)驅(qū)動(dòng)創(chuàng)建, 通過(guò)clien域和i2c_client和i2c_driver相連, 這樣設(shè)備端驅(qū)動(dòng)就可以通過(guò)其中的方法以及i2c物理控制器來(lái)和一個(gè)i2c總線的物理設(shè)備進(jìn)行交互。

i2c_algorithm

描述一個(gè)i2c主機(jī)的發(fā)送時(shí)序的信息,該類的對(duì)象algo是i2c_adapter的一個(gè)域,其中注冊(cè)的函數(shù)master_xfer()最終被設(shè)備驅(qū)動(dòng)端的i2c_transfer()回調(diào)。

i2c_msg

描述一個(gè)在設(shè)備端和主機(jī)端之間進(jìn)行流動(dòng)的數(shù)據(jù), 在設(shè)備驅(qū)動(dòng)中打包并通過(guò)i2c_transfer()發(fā)送。相當(dāng)于skbuf之于網(wǎng)絡(luò)設(shè)備,urb之于USB設(shè)備。

這幾個(gè)結(jié)構(gòu)體之間關(guān)系:

c2d3b510-71b8-11eb-8b86-12bb97331649.png

i2c_client

描述一個(gè)掛接在硬件i2c總線上的設(shè)備的設(shè)備信息,即i2c設(shè)備的設(shè)備對(duì)象,與i2c_driver對(duì)象匹配成功后通過(guò)detected和i2c_driver以及i2c_adapter相連,在控制器驅(qū)動(dòng)與控制器設(shè)備匹配成功后被控制器驅(qū)動(dòng)通過(guò)i2c_new_device()創(chuàng)建。從設(shè)備所掛載的i2c控制器會(huì)在初始化的時(shí)候保存到成員adapter。

i2c_driver

描述一個(gè)掛接在硬件i2c總線上的設(shè)備的驅(qū)動(dòng)方法,即i2c設(shè)備的驅(qū)動(dòng)對(duì)象,通過(guò)i2c_bus_type和設(shè)備信息i2c_client匹配,匹配成功后通過(guò)clients和i2c_client對(duì)象以及i2c_adapter對(duì)象相連。

c3473f9e-71b8-11eb-8b86-12bb97331649.png

如上圖所示:Linux內(nèi)核維護(hù)了i2c bus總線,所有的i2c從設(shè)備信息都會(huì)轉(zhuǎn)換成i2c_client,并注冊(cè)到i2c總線,沒(méi)有設(shè)備的情況下一般填寫在一下文件中:

linux-3.14-fs4412archarmmach-s5pc100 Mach-smdkc100.c

c35a1c68-71b8-11eb-8b86-12bb97331649.png

內(nèi)核啟動(dòng)會(huì)將i2c_board_info結(jié)構(gòu)體轉(zhuǎn)換成i2c_client。

有設(shè)備樹(shù)的情況下,內(nèi)核啟動(dòng)會(huì)自動(dòng)將設(shè)備樹(shù)節(jié)點(diǎn)轉(zhuǎn)換成i2c_client。

i2c_adapter

我首先說(shuō)i2c_adapter, 并不是編寫一個(gè)i2c設(shè)備驅(qū)動(dòng)需要它, 通常我們?cè)谂渲脙?nèi)核的時(shí)候已經(jīng)將i2c控制器的設(shè)備信息和驅(qū)動(dòng)已經(jīng)編譯進(jìn)內(nèi)核了, 就是這個(gè)adapter對(duì)象已經(jīng)創(chuàng)建好了, 但是了解其中的成員對(duì)于理解i2c驅(qū)動(dòng)框架非常重要, 所有的設(shè)備驅(qū)動(dòng)都要經(jīng)過(guò)這個(gè)對(duì)象的處理才能和物理設(shè)備通信

//include/linux/i2c.h

c6a365b4-71b8-11eb-8b86-12bb97331649.png

428--》這個(gè)i2c控制器需要的控制算法, 其中最重要的成員是master_xfer()接口, 這個(gè)接口是硬件相關(guān)的, 里面的操作都是基于具體的SoC i2c寄存器的, 它將完成將數(shù)據(jù)發(fā)送到物理i2c控制器的“最后一公里”

436--》表示這個(gè)一個(gè)device, 會(huì)掛接到內(nèi)核中的鏈表中來(lái)管理, 其中的

443--》這個(gè)節(jié)點(diǎn)將一個(gè)i2c_adapter對(duì)象和它所屬的i2c_client對(duì)象以及相應(yīng)的i2c_driver對(duì)象連接到一起

下面是2個(gè)i2c-core.c提供的i2c_adapter直接相關(guān)的操作API, 通常也不需要設(shè)備驅(qū)動(dòng)開(kāi)發(fā)中使用。

adapter初始化

i2c控制器設(shè)備樹(shù)節(jié)點(diǎn)信息通過(guò)platform總線傳遞下來(lái),即參數(shù)pdev。probe函數(shù)主要功能是初始化adapter,申請(qǐng)i2c控制器需要的各種資源,同時(shí)通過(guò)設(shè)備樹(shù)節(jié)點(diǎn)初始化該控制器下的所有從設(shè)備,創(chuàng)建i2c_client結(jié)構(gòu)體。

ps3c24xx_i2c_probe

static int s3c24xx_i2c_probe(struct platform_device *pdev)

{

struct s3c24xx_i2c *i2c;//最重要的結(jié)構(gòu)體

//保存設(shè)備樹(shù)信息

struct s3c2410_platform_i2c *pdata = NULL;

struct resource *res;

int ret;

if (!pdev-》dev.of_node) {

pdata = dev_get_platdata(&pdev-》dev);

if (!pdata) {

dev_err(&pdev-》dev, “no platform data ”);

return -EINVAL;

}

}

/*為結(jié)構(gòu)體變量i2c分配內(nèi)存*/

i2c = devm_kzalloc(&pdev-》dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);

if (!i2c) {

dev_err(&pdev-》dev, “no memory for state ”);

return -ENOMEM;

}

i2c-》pdata = devm_kzalloc(&pdev-》dev, sizeof(*pdata), GFP_KERNEL);

if (!i2c-》pdata) {

dev_err(&pdev-》dev, “no memory for platform data ”);

return -ENOMEM;

}

/*i2c控制器的一些特殊行為

#define QUIRK_S3C2440 (1 《《 0)

#define QUIRK_HDMIPHY (1 《《 1)

#define QUIRK_NO_GPIO (1 《《 2)

#define QUIRK_POLL (1 《《 3)

其中bite:3如果采用輪訓(xùn)方式與底層硬件通信值為1,中斷方式值為0*/

i2c-》quirks = s3c24xx_get_device_quirks(pdev);

if (pdata)

memcpy(i2c-》pdata, pdata, sizeof(*pdata));

else

s3c24xx_i2c_parse_dt(pdev-》dev.of_node, i2c);

strlcpy(i2c-》adap.name, “s3c2410-i2c”, sizeof(i2c-》adap.name));

i2c-》adap.owner = THIS_MODULE;

/*為i2c_msg傳輸方法賦值,*/

i2c-》adap.algo = &s3c24xx_i2c_algorithm;

i2c-》adap.retries = 2;

i2c-》adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;

i2c-》tx_setup = 50;

//初始化等待隊(duì)列,該等待隊(duì)列用于喚醒讀寫數(shù)據(jù)的進(jìn)程

init_waitqueue_head(&i2c-》wait);

/* find the clock and enable it */

i2c-》dev = &pdev-》dev;

//獲取時(shí)鐘

i2c-》clk = devm_clk_get(&pdev-》dev, “i2c”);

if (IS_ERR(i2c-》clk)) {

dev_err(&pdev-》dev, “cannot get clock ”);

return -ENOENT;

}

dev_dbg(&pdev-》dev, “clock source %p ”, i2c-》clk);

/* map the registers */

//通過(guò)pdev得到i2c控制器的寄存器地址資源

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

//映射i2c控制器的物理基地址為虛擬基地址

i2c-》regs = devm_ioremap_resource(&pdev-》dev, res);

if (IS_ERR(i2c-》regs))

return PTR_ERR(i2c-》regs);

dev_dbg(&pdev-》dev, “registers %p (%p) ”,

i2c-》regs, res);

/* setup info block for the i2c core */

/*將結(jié)構(gòu)體變量i2c保存到i2c_adapter的私有變量指針algo_data,

編寫i2c設(shè)備驅(qū)動(dòng)可以通過(guò)adapter指針找到結(jié)構(gòu)體i2c*/

i2c-》adap.algo_data = i2c;

i2c-》adap.dev.parent = &pdev-》dev;

i2c-》pctrl = devm_pinctrl_get_select_default(i2c-》dev);

/* inititalise the i2c gpio lines */

//得到i2c復(fù)用的gpio引腳并初始化

if (i2c-》pdata-》cfg_gpio) {

i2c-》pdata-》cfg_gpio(to_platform_device(i2c-》dev));

} else if (IS_ERR(i2c-》pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) {

return -EINVAL;

}

/* initialise the i2c controller */

clk_prepare_enable(i2c-》clk);

/*將從設(shè)備地址寫入寄存器S3C2410_IICADD,同時(shí)初始化時(shí)鐘頻率*/

ret = s3c24xx_i2c_init(i2c);

clk_disable_unprepare(i2c-》clk);

if (ret != 0) {

dev_err(&pdev-》dev, “I2C controller init failed ”);

return ret;

}

/* find the IRQ for this unit (note, this relies on the init call to

* ensure no current IRQs pending

*/

if (?。╥2c-》quirks & QUIRK_POLL)) {

/*獲得中斷號(hào)*/

i2c-》irq = ret = platform_get_irq(pdev, 0);

if (ret 《= 0) {

dev_err(&pdev-》dev, “cannot find IRQ ”);

return ret;

}

/*注冊(cè)中斷處理函數(shù)s3c24xx_i2c_irq()*/

ret = devm_request_irq(&pdev-》dev, i2c-》irq, s3c24xx_i2c_irq, 0,

dev_name(&pdev-》dev), i2c);

if (ret != 0) {

dev_err(&pdev-》dev, “cannot claim IRQ %d ”, i2c-》irq);

return ret;

}

}

ret = s3c24xx_i2c_register_cpufreq(i2c);

if (ret 《 0) {

dev_err(&pdev-》dev, “failed to register cpufreq notifier ”);

return ret;

}

/* Note, previous versions of the driver used i2c_add_adapter()

* to add the bus at any number. We now pass the bus number via

* the platform data, so if unset it will now default to always

* being bus 0.

*/

/*保存i2c控制器的通道號(hào),本例是bus 5*/

i2c-》adap.nr = i2c-》pdata-》bus_num;

i2c-》adap.dev.of_node = pdev-》dev.of_node;

//注冊(cè)adapter

ret = i2c_add_numbered_adapter(&i2c-》adap);

if (ret 《 0) {

dev_err(&pdev-》dev, “failed to add bus to i2c core ”);

s3c24xx_i2c_deregister_cpufreq(i2c);

return ret;

}

/*保存私有變量i2c到pdev-》dev-》p-》driver_data*/

platform_set_drvdata(pdev, i2c);

pm_runtime_enable(&pdev-》dev);

pm_runtime_enable(&i2c-》adap.dev);

dev_info(&pdev-》dev, “%s: S3C I2C adapter ”, dev_name(&i2c-》adap.dev));

return 0;

}

i2c_add_numbered_adapter

老版本的注冊(cè)函數(shù)為i2c_add_adapter()新的版本對(duì)該函數(shù)做了封裝,將i2c控制的通道號(hào)做了注冊(cè),默認(rèn)情況下nr值為0.

i2c_add_numbered_adapter-》__i2c_add_numbered_adapter-》 i2c_register_adapter

int i2c_add_numbered_adapter(struct i2c_adapter *adap)

{

if (adap-》nr == -1) /* -1 means dynamically assign bus id */

return i2c_add_adapter(adap);

return __i2c_add_numbered_adapter(adap);

}

i2c_add_adapter

static int i2c_register_adapter(struct i2c_adapter *adap)

{

int res = 0;

/* Can‘t register until after driver model init */

if (unlikely(WARN_ON(!i2c_bus_type.p))) {

res = -EAGAIN;

goto out_list;

}

/* Sanity checks */

if (unlikely(adap-》name[0] == ’‘)) {

pr_err(“i2c-core: Attempt to register an adapter with ”

“no name! ”);

return -EINVAL;

}

if (unlikely(!adap-》algo)) {

pr_err(“i2c-core: Attempt to register adapter ’%s‘ with ”

“no algo! ”, adap-》name);

return -EINVAL;

}

rt_mutex_init(&adap-》bus_lock);

mutex_init(&adap-》userspace_clients_lock);

INIT_LIST_HEAD(&adap-》userspace_clients);

/* Set default timeout to 1 second if not already set */

if (adap-》timeout == 0)

adap-》timeout = HZ;

//設(shè)置adapter名字,本例注冊(cè)后會(huì)生成以下節(jié)點(diǎn)/dev/i2c-5

dev_set_name(&adap-》dev, “i2c-%d”, adap-》nr);

adap-》dev.bus = &i2c_bus_type;

adap-》dev.type = &i2c_adapter_type;

res = device_register(&adap-》dev);

if (res)

goto out_list;

dev_dbg(&adap-》dev, “adapter [%s] registered ”, adap-》name);

#ifdef CONFIG_I2C_COMPAT

res = class_compat_create_link(i2c_adapter_compat_class, &adap-》dev,

adap-》dev.parent);

if (res)

dev_warn(&adap-》dev,

“Failed to create compatibility class link ”);

#endif

/* bus recovery specific initialization */

/*初始化sda、scl,通常這兩個(gè)引腳會(huì)復(fù)用gpio引腳*/

if (adap-》bus_recovery_info) {

struct i2c_bus_recovery_info *bri = adap-》bus_recovery_info;

if (!bri-》recover_bus) {

dev_err(&adap-》dev, “No recover_bus() found, not using recovery ”);

adap-》bus_recovery_info = NULL;

goto exit_recovery;

}

/* Generic GPIO recovery */

if (bri-》recover_bus == i2c_generic_gpio_recovery) {

if (!gpio_is_valid(bri-》scl_gpio)) {

dev_err(&adap-》dev, “Invalid SCL gpio, not using recovery ”);

adap-》bus_recovery_info = NULL;

goto exit_recovery;

}

if (gpio_is_valid(bri-》sda_gpio))

bri-》get_sda = get_sda_gpio_value;

else

bri-》get_sda = NULL;

bri-》get_scl = get_scl_gpio_value;

bri-》set_scl = set_scl_gpio_value;

} else if (!bri-》set_scl || !bri-》get_scl) {

/* Generic SCL recovery */

dev_err(&adap-》dev, “No {get|set}_gpio() found, not using recovery ”);

adap-》bus_recovery_info = NULL;

}

}

exit_recovery:

/* create pre-declared device nodes */

/*通過(guò)設(shè)備樹(shù)節(jié)點(diǎn)注冊(cè)所有該控制器下的所有從設(shè)備*/

of_i2c_register_devices(adap);

acpi_i2c_register_devices(adap);

/*與動(dòng)態(tài)分配的總線號(hào)相關(guān),動(dòng)態(tài)分配的總線號(hào)應(yīng)該是從已經(jīng)現(xiàn)有最大總線號(hào)基礎(chǔ)上+1的,

這樣能夠保證動(dòng)態(tài)分配出的總線號(hào)與板級(jí)總線號(hào)不會(huì)產(chǎn)生沖突

在沒(méi)有設(shè)備樹(shù)情況下,會(huì)基于隊(duì)列__i2c_board_list, 創(chuàng)建i2c_client

其中節(jié)點(diǎn)struct i2c_board_info手動(dòng)填寫*/

if (adap-》nr 《 __i2c_first_dynamic_bus_num)

i2c_scan_static_board_info(adap);

/* Notify drivers */

mutex_lock(&core_lock);

bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);

mutex_unlock(&core_lock);

return 0;

out_list:

mutex_lock(&core_lock);

idr_remove(&i2c_adapter_idr, adap-》nr);

mutex_unlock(&core_lock);

return res;

}

of_i2c_register_devices

該函數(shù)用于將從設(shè)備節(jié)點(diǎn)轉(zhuǎn)換成i2c_client,并注冊(cè)到i2c總線上。

static void of_i2c_register_devices(struct i2c_adapter *adap)

{

void *result;

struct device_node *node;

/* Only register child devices if the adapter has a node pointer set */

if (!adap-》dev.of_node)

return;

dev_dbg(&adap-》dev, “of_i2c: walking child nodes ”);

for_each_available_child_of_node(adap-》dev.of_node, node) {

struct i2c_board_info info = {};

struct dev_archdata dev_ad = {};

const __be32 *addr;

int len;

dev_dbg(&adap-》dev, “of_i2c: register %s ”, node-》full_name);

if (of_modalias_node(node, info.type, sizeof(info.type)) 《 0) {

dev_err(&adap-》dev, “of_i2c: modalias failure on %s ”,

node-》full_name);

continue;

}

/*獲取從設(shè)備的地址*/

addr = of_get_property(node, “reg”, &len);

if (!addr || (len 《 sizeof(int))) {

dev_err(&adap-》dev, “of_i2c: invalid reg on %s ”,

node-》full_name);

continue;

}

/*存儲(chǔ)從設(shè)備地址*/

info.addr = be32_to_cpup(addr);

if (info.addr 》 (1 《《 10) - 1) {

dev_err(&adap-》dev, “of_i2c: invalid addr=%x on %s ”,

info.addr, node-》full_name);

continue;

}

/*獲取中斷號(hào)*/

info.irq = irq_of_parse_and_map(node, 0);

info.of_node = of_node_get(node);

info.archdata = &dev_ad;

/*獲取設(shè)備樹(shù)節(jié)點(diǎn)wakeup-source信息*/

if (of_get_property(node, “wakeup-source”, NULL))

info.flags |= I2C_CLIENT_WAKE;

request_module(“%s%s”, I2C_MODULE_PREFIX, info.type);

/*將i2c_board_info轉(zhuǎn)換成i2c_client并注冊(cè)到i2c總線*/

result = i2c_new_device(adap, &info);

if (result == NULL) {

dev_err(&adap-》dev, “of_i2c: Failure registering %s ”,

node-》full_name);

of_node_put(node);

irq_dispose_mapping(info.irq);

continue;

}

}

}

i2c_new_device ( )

將i2c_board_info轉(zhuǎn)換成i2c_client并注冊(cè)到Linux核心。

{

struct i2c_client *client;

int status;

/*給i2c_client分配內(nèi)存*/

client = kzalloc(sizeof *client, GFP_KERNEL);

if (!client)

return NULL;

/*將adapter的地址保存到i2c_client-》adapter,

在驅(qū)動(dòng)函數(shù)中可以通過(guò)i2c_client找到adapter*/

client-》adapter = adap;

client-》dev.platform_data = info-》platform_data;

if (info-》archdata)

client-》dev.archdata = *info-》archdata;

/*保存從設(shè)備地址類型*/

client-》flags = info-》flags;

/*保存從設(shè)備地址*/

client-》addr = info-》addr;

/*保存從設(shè)備中斷號(hào)*/

client-》irq = info-》irq;

strlcpy(client-》name, info-》type, sizeof(client-》name));

/* Check for address validity */

/*檢測(cè)從設(shè)備地址是否合法*/

status = i2c_check_client_addr_validity(client);

if (status) {

dev_err(&adap-》dev, “Invalid %d-bit I2C address 0x%02hx ”,

client-》flags & I2C_CLIENT_TEN ? 10 : 7, client-》addr);

goto out_err_silent;

}

/* Check for address business */

/*檢測(cè)從設(shè)備地址是否被占用*/

status = i2c_check_addr_busy(adap, client-》addr);

if (status)

goto out_err;

/*建立從設(shè)備與適配器的父子關(guān)系*/

client-》dev.parent = &client-》adapter-》dev;

client-》dev.bus = &i2c_bus_type;

client-》dev.type = &i2c_client_type;

client-》dev.of_node = info-》of_node;

ACPI_COMPANION_SET(&client-》dev, info-》acpi_node.companion);

i2c_dev_set_name(adap, client);

/*注冊(cè)到Linux核心*/

status = device_register(&client-》dev);

if (status)

goto out_err;

dev_dbg(&adap-》dev, “client [%s] registered with bus id %s ”,

client-》name, dev_name(&client-》dev));

return client;

out_err:

dev_err(&adap-》dev, “Failed to register i2c client %s at 0x%02x ”

“(%d) ”, client-》name, client-》addr, status);

out_err_silent:

kfree(client);

return NULL;

}

i2c_msg如何傳遞?

核心方法i2c_transfer

l i2c_transfer()是i2c核心提供給設(shè)備驅(qū)動(dòng)的發(fā)送方法, 通過(guò)它發(fā)送的數(shù)據(jù)需要被打包成i2c_msg, 這個(gè)函數(shù)最終會(huì)回調(diào)相應(yīng)i2c_adapter-》i2c_algorithm-》master_xfer()接口將i2c_msg對(duì)象發(fā)送到i2c物理控制器,

i2c_adapte-》algo在函數(shù)s3c24xx_i2c_probe()中賦值:

c6fdff38-71b8-11eb-8b86-12bb97331649.png

該變量定義如下:

c72e91ca-71b8-11eb-8b86-12bb97331649.png

i2c_transfer()最終會(huì)調(diào)用函數(shù)s3c24xx_i2c_xfer();

i2c_msg中斷傳輸

以下是一次i2c_msg傳輸?shù)闹袛嗄J降拇蟾挪襟E:

c77ebc40-71b8-11eb-8b86-12bb97331649.png

1. i2c_transfer()首先通過(guò)函數(shù)i2c_trylock_adapter()嘗試獲得adapter的控制權(quán)。如果adapter正在忙則返回錯(cuò)誤信息;

2. __i2c_transfer()通過(guò)調(diào)用方法adap-》algo-》master_xfer(adap, msgs, num)傳輸i2c_msg,如果失敗會(huì)嘗試重新傳送,重傳次數(shù)最多adap-》retries;

3. adap-》algo-》master_xfer()就是函數(shù)s3c24xx_i2c_xfer(),該函數(shù)最終調(diào)用 s3c24xx_i2c_doxfer(i2c, msgs, num)傳輸信息;

4. s3c24xx_i2c_doxfer()通過(guò)函數(shù)s3c24xx_i2c_message_start(i2c, msgs)產(chǎn)生S和AD+W的信號(hào),然后通過(guò)函數(shù)wait_event_timeout( )阻塞在等待隊(duì)列i2c-》wait上;

5. 右上角時(shí)序mpu6050的寫和讀的時(shí)序,從設(shè)備回復(fù)ACK和DATA都會(huì)發(fā)送中斷信號(hào)給CPU。每次中斷都會(huì)調(diào)用s3c24xx_i2c_irq-》i2c_s3c_irq_nextbyte,

6. 最后一次中斷,所有數(shù)據(jù)發(fā)送或讀取完畢會(huì)調(diào)用s3c24xx_i2c_stop-》s3c24xx_i2c_master_complete,通過(guò)wake_up喚醒阻塞在等待隊(duì)列i2c-》wait上的任務(wù)。

詳細(xì)的代碼流程如下:

c7c13278-71b8-11eb-8b86-12bb97331649.png

對(duì)著可以根據(jù)上圖代碼行號(hào)一步步去跟代碼,涉及到寄存器設(shè)置可以參考

原文標(biāo)題:超硬干貨:I2C最全教程,絕對(duì)不負(fù)期望?。ㄈf(wàn)字長(zhǎng)文,建議收藏)

文章出處:【微信公眾號(hào):玩轉(zhuǎn)單片機(jī)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • I2C
    I2C
    +關(guān)注

    關(guān)注

    28

    文章

    1477

    瀏覽量

    123064

原文標(biāo)題:超硬干貨:I2C最全教程,絕對(duì)不負(fù)期望?。ㄈf(wàn)字長(zhǎng)文,建議收藏)

文章出處:【微信號(hào):mcu168,微信公眾號(hào):硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    詳解I2C總線與SPI總線的區(qū)別

    I2C(Inter-Integrated Circuit)表示集成電路互連,是一種用于線路板內(nèi)部芯片之間通信的總線。
    的頭像 發(fā)表于 10-16 15:16 ?1497次閱讀
    詳解<b class='flag-5'>I2C</b><b class='flag-5'>總線</b>與SPI<b class='flag-5'>總線</b>的區(qū)別

    了解I2C總線

    電子發(fā)燒友網(wǎng)站提供《了解I2C總線.pdf》資料免費(fèi)下載
    發(fā)表于 10-08 11:13 ?1次下載
    了解<b class='flag-5'>I2C</b><b class='flag-5'>總線</b>

    I2C總線上拉電阻計(jì)算

    電子發(fā)燒友網(wǎng)站提供《I2C總線上拉電阻計(jì)算.pdf》資料免費(fèi)下載
    發(fā)表于 10-08 09:54 ?1次下載
    <b class='flag-5'>I2C</b><b class='flag-5'>總線</b>上拉電阻計(jì)算

    優(yōu)秀實(shí)踐:I3C共享總線上的I2C器件

    電子發(fā)燒友網(wǎng)站提供《優(yōu)秀實(shí)踐:I3C共享總線上的I2C器件.pdf》資料免費(fèi)下載
    發(fā)表于 09-06 09:49 ?0次下載
    優(yōu)秀實(shí)踐:<b class='flag-5'>I3C</b>共享<b class='flag-5'>總線</b>上的<b class='flag-5'>I2C</b>器件

    I2C總線協(xié)議的工作原理和尋址格式

    I2C(Inter-Integrated Circuit)總線協(xié)議,即集成電路總線協(xié)議,是一種用于連接微控制器及其外圍設(shè)備的串行總線協(xié)議。I2C
    的頭像 發(fā)表于 05-27 15:47 ?1483次閱讀

    什么是I2C協(xié)議 I2C總線的控制邏輯

    在實(shí)際使用過(guò)程中,I2C比較容易出現(xiàn)的一個(gè)問(wèn)題就是死鎖 ,死鎖在I2C中主要表現(xiàn)為:I2C死鎖時(shí)表現(xiàn)為SCL為高,SDA一直為低。
    發(fā)表于 03-12 09:17 ?885次閱讀
    什么是<b class='flag-5'>I2C</b>協(xié)議 <b class='flag-5'>I2C</b><b class='flag-5'>總線</b>的控制邏輯

    藍(lán)牙串口通訊總線——I2C/SPI/UART

    嵌入式工程師在做串口通信調(diào)試工作的時(shí)候,會(huì)經(jīng)常用到I2C、SPI、UART這3條總線,I2C、SPI、UART這三種通信總線在嵌入式領(lǐng)域很常見(jiàn),目前主流的SOC芯片都內(nèi)置了這三種
    的頭像 發(fā)表于 01-22 09:41 ?1391次閱讀
    藍(lán)牙串口通訊<b class='flag-5'>總線</b>——<b class='flag-5'>I2C</b>/SPI/UART

    關(guān)于I2C總線的6個(gè)問(wèn)題分析

    I2C總線上的上拉電阻范圍是多少? 回答: 總線電容是走線部分、連接部分、管腳部分的電容的總和。總線電容限制了上拉電阻(Rp) 的最大值,因?yàn)?b class='flag-5'>
    發(fā)表于 12-25 09:19 ?1373次閱讀

    I2C總線簡(jiǎn)介和主要參數(shù)講解

    I2C總線為串行同步通訊總線,可以進(jìn)行多設(shè)備通訊。由飛利浦半導(dǎo)體(現(xiàn)已被恩智浦半導(dǎo)體收購(gòu))開(kāi)發(fā)的雙向兩線總線,用來(lái)進(jìn)行IC芯片之間的通訊。總線
    的頭像 發(fā)表于 12-04 14:02 ?1778次閱讀
    <b class='flag-5'>I2C</b><b class='flag-5'>總線</b>簡(jiǎn)介和主要參數(shù)<b class='flag-5'>講解</b>

    I2C總線信號(hào)與測(cè)試案例(二)

    前面一章講解I2C總線的基本原理,那么在電路設(shè)計(jì)完成打板回來(lái),肯定要做信號(hào)測(cè)試來(lái)驗(yàn)證設(shè)計(jì)
    的頭像 發(fā)表于 11-20 15:49 ?3138次閱讀
    <b class='flag-5'>I2C</b><b class='flag-5'>總線</b>信號(hào)與測(cè)試案例(二)

    I2C總線信號(hào)與測(cè)試案例(一)

    I2C bus是Inter-IC bus的縮寫,意思是IC器件之間的通訊總線I2C 總線的特點(diǎn)如下
    的頭像 發(fā)表于 11-20 15:45 ?1797次閱讀
    <b class='flag-5'>I2C</b><b class='flag-5'>總線</b>信號(hào)與測(cè)試案例(一)

    分享I2C總線規(guī)范

    電子發(fā)燒友網(wǎng)站提供《分享I2C總線規(guī)范.pdf》資料免費(fèi)下載
    發(fā)表于 11-18 10:49 ?0次下載
    分享<b class='flag-5'>I2C</b><b class='flag-5'>總線</b>規(guī)范

    I2C串行總線組成及工作原理

    電子發(fā)燒友網(wǎng)站提供《I2C串行總線組成及工作原理.ppt》資料免費(fèi)下載
    發(fā)表于 11-16 09:23 ?1次下載
    <b class='flag-5'>I2C</b>串行<b class='flag-5'>總線</b>組成及工作原理