精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

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

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

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

Linux應(yīng)用開發(fā)【第十二章】I2C編程應(yīng)用開發(fā)

weidongshan ? 來(lái)源:weidongshan ? 作者:weidongshan ? 2021-12-10 19:28 ? 次閱讀

文章目錄

12 I2C編程應(yīng)用開發(fā)

12.1 I2C協(xié)議

12.1.1 概述

12.2.2 物理層

1) 特性1:半雙工(非全雙工)

2) 特性2:地址和角色可配置

3) 特性3:多主機(jī)

4) 特性4:傳輸速率

5) 特性5:負(fù)載和距離

12.2.3 協(xié)議層

1) 數(shù)據(jù)有效性

2) 起始和結(jié)束條件

3) 應(yīng)答

4) 數(shù)據(jù)幀格式

12.2 在linux系統(tǒng)下操作I2C總線的外設(shè)

12.2.1 概述

12.2.2 簡(jiǎn)述I2C的linux驅(qū)動(dòng)

1) I2C核心層:

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

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

12.3 在linux應(yīng)用層使用I2C

12.3.1 如何使用I2C tools測(cè)試I2C外設(shè)

1) I2C tools概述:

2) 下載I2C tools源碼:

3) 編譯I2C tools源碼:

4) 介紹I2C tools各功能之—i2cdetect

5) 介紹I2C tools各功能之—i2cget

6) 介紹I2C tools各功能之—i2cdump

7) 介紹I2C tools各功能之—i2cset

8) 介紹I2C tools各功能之—i2ctransfer

12.3.2 在linux應(yīng)用程序中讀寫I2C外設(shè)

1) 確定I2C適配器的設(shè)備文件節(jié)點(diǎn)

2) 打開適配器對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)

3) IOCTL控制

4) 使用I2C協(xié)議和設(shè)備進(jìn)行通信

5) 用read和write讀寫I2C設(shè)備

6) 用數(shù)據(jù)包的方式操作I2C設(shè)備

12.3.3 簡(jiǎn)介I2C的調(diào)試方式

1) 概述I2C通信中完成正常通信的常見元素:

12.4 總結(jié)I2C在嵌入式項(xiàng)目開發(fā)的應(yīng)用優(yōu)缺點(diǎn)

1) I2C時(shí)鐘信號(hào)(SCL)的同步問(wèn)題

2) 總線驅(qū)動(dòng)能力

12 I2C編程應(yīng)用開發(fā)

I2C(Inter-Integrated Circuit BUS)是I2C BUS簡(jiǎn)稱,中文為集成電路總線,是目前應(yīng)用最廣泛的總線之一。和IMX6ULL有些相關(guān)的是,剛好該總線是NXP前身的PHILIPS設(shè)計(jì)。

12.1 I2C協(xié)議

12.1.1 概述

I2C是一種串行通信總線,使用多主從架構(gòu),最初設(shè)計(jì)目的為了讓主板、嵌入式系統(tǒng)手機(jī)用來(lái)連接低速周邊設(shè)備。多用于小數(shù)據(jù)量的場(chǎng)合,有傳輸距離短,任意時(shí)刻只能有一個(gè)主機(jī)等特性。嚴(yán)格意義上講,I2C應(yīng)該是軟硬件結(jié)合體,所以我們將分物理層和協(xié)議層來(lái)介紹該總線。

I2C總線結(jié)構(gòu)如下圖:

poYBAGGzOeGADtqaAACdT-MAjaM701.png

傳輸數(shù)據(jù)時(shí),我們需要發(fā)數(shù)據(jù),從主設(shè)備發(fā)送到從設(shè)備上去;也需要把數(shù)據(jù)從從設(shè)備傳送到主設(shè)備上去,數(shù)據(jù)涉及到雙向傳輸。

對(duì)于I2C通信的過(guò)程,下面使用一個(gè)形象的生活例子進(jìn)行類比。

poYBAGGzOeeAQC4jAABC-2-JuT4353.png

體育老師:可以把球發(fā)給學(xué)生,也可以把球從學(xué)生中接過(guò)來(lái)。

① 發(fā)球:

a. 老師說(shuō):注意了(start);

b. 老師對(duì)A學(xué)生說(shuō),我要球發(fā)給你(A就是地址);

c. 老師就把球發(fā)出去了(傳輸);

d. A收到球之后,應(yīng)該告訴老師一聲(回應(yīng));

e. 老師說(shuō)下課(停止)。

② 接球:

a. 老師說(shuō)注意了(start);

b. 老師說(shuō):B把球發(fā)給我(B是地址);

c. B就把球發(fā)給老師(傳輸);

d. 老師收到球之后,給B說(shuō)一聲,表示收到球了(回應(yīng));

e. 老師說(shuō)下課(停止)。

我們就使用這個(gè)簡(jiǎn)單的例子,來(lái)解釋一下I2C的傳輸協(xié)議:

① 老師說(shuō)注意了,表示開始信號(hào)(start)

② 老師告訴某個(gè)學(xué)生,表示發(fā)送地址(address)

③ 老師發(fā)球/接球,表示數(shù)據(jù)的傳輸

④ 老師/學(xué)生收到球,回應(yīng)表示:回應(yīng)信號(hào)(ACK)

⑤ 老師說(shuō)下課,表示I2C傳輸接受§

12.2.2 物理層

1) 特性1:半雙工(非全雙工)

I2C總線中只使用兩條線路:SDA、SCL。

① SDA(串行數(shù)據(jù)線):

芯片通過(guò)一根SDA線既可以把數(shù)據(jù)發(fā)給從設(shè)備,也可以從SDA上讀取數(shù)據(jù)。在I2C設(shè)備內(nèi)部有兩個(gè)引腳(發(fā)送引腳/接受引腳),它們都連接到外部的SDA線上,具體可以參考下圖device端里面的I2Cn_SDA(output/input)。

② SCL(串行時(shí)鐘線):

I2C主設(shè)備發(fā)出時(shí)鐘,從設(shè)備接收時(shí)鐘。

SDA和SCL引腳的內(nèi)部電路結(jié)構(gòu)一致,引腳的輸出驅(qū)動(dòng)與輸入緩沖連在一起。其中輸出為漏極開路的場(chǎng)效應(yīng)管、輸入緩沖為一只高輸入阻抗的同相器。這樣結(jié)構(gòu)有如下特性:

a. 由于 SDA、SCL 為漏極開路結(jié)構(gòu),借助于外部的上拉電阻實(shí)現(xiàn)了信號(hào)的“線與”邏輯;

b. 引腳在輸出信號(hào)的同時(shí)還作用輸入信號(hào)供內(nèi)部進(jìn)行檢測(cè),當(dāng)輸出與輸入不一致時(shí),就表示有問(wèn)題發(fā)生了。這為 “時(shí)鐘同步”和“總線仲裁”提供硬件基礎(chǔ)。

SDA和CLK連接線上連有兩個(gè)上拉電阻,當(dāng)總線空閑時(shí),兩根線均為高電平。連到總線上的任一器件輸出的低電平,都將使總線的信號(hào)變低。

物理層連接如下圖所示:

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-kyu89PWq-1639020220827)(http://photos.100ask.net/NewHomeSite/IIC_Image003.png)]

2) 特性2:地址和角色可配置

每個(gè)連接到總線的器件都可以通過(guò)唯一的地址和其它器件通信,主機(jī)/從機(jī)角色和地址可配置,主機(jī)可以作為主機(jī)發(fā)送器和主機(jī)接收器

3) 特性3:多主機(jī)

I2C是真正的多主機(jī)總線,I2C設(shè)備可以在通訊過(guò)程轉(zhuǎn)變成主機(jī)。如果兩個(gè)或更多的主機(jī)同時(shí)請(qǐng)求總線,可以通過(guò)沖突檢測(cè)和仲裁防止總線數(shù)據(jù)被破壞。

4) 特性4:傳輸速率

傳輸速率在標(biāo)準(zhǔn)模式下可以達(dá)到100kb/s,快速模式下可以達(dá)到400kb/s。

5) 特性5:負(fù)載和距離

節(jié)點(diǎn)的最大數(shù)量受限于地址空間以及總線電容決定,另外總電容也限制了實(shí)際通信距離只有幾米。

12.2.3 協(xié)議層

1) 數(shù)據(jù)有效性

I2C協(xié)議的數(shù)據(jù)有效性是靠時(shí)鐘來(lái)保證的,在時(shí)鐘的高電平周期內(nèi),SDA線上的數(shù)據(jù)必須保持穩(wěn)定。數(shù)據(jù)線僅可以在時(shí)鐘SCL為低電平時(shí)改變。

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-WQhqSPjW-1639020220828)(http://photos.100ask.net/NewHomeSite/IIC_Image004.png)]

2) 起始和結(jié)束條件

**起始條件:**當(dāng)SCL為高電平的時(shí)候,SDA線上由高到低的跳變被定義為起始條件。

**結(jié)束條件:**當(dāng)SCL為高電平的時(shí)候,SDA線上由低到高的跳變被定義為停止條件。

要注意起始和終止信號(hào)都是由主機(jī)發(fā)出的,連接到I2C總線上的器件,若具有I2C總線的硬件接口,則很容易檢測(cè)到起始和終止信號(hào)。

pYYBAGGzOe2AHbX_AABpxVOmaDc729.png

總線在起始條件之后,視為忙狀態(tài),在停止條件之后被視為空閑狀態(tài)。

3) 應(yīng)答

每當(dāng)主機(jī)向從機(jī)發(fā)送完一個(gè)字節(jié)的數(shù)據(jù),主機(jī)總是需要等待從機(jī)給出一個(gè)應(yīng)答信號(hào),以確認(rèn)從機(jī)是否成功接收到了數(shù)據(jù),從機(jī)應(yīng)答主機(jī)所需要的時(shí)鐘仍是主機(jī)提供的,應(yīng)答出現(xiàn)在每一次主機(jī)完成8個(gè)數(shù)據(jù)位傳輸后緊跟著的時(shí)鐘周期,低電平0表示應(yīng)答,1表示非應(yīng)答。

4) 數(shù)據(jù)幀格式

SDA線上每個(gè)字節(jié)必須是8位長(zhǎng),在每個(gè)傳輸(transfer)中所傳輸字節(jié)數(shù)沒(méi)有限制,每個(gè)字節(jié)后面必須跟一個(gè)ACK。8位數(shù)據(jù)中,先傳輸最高有效位(MSB)傳輸。

poYBAGGzOfOAXkr6AAITyTV70iw469.png

12.2 在linux系統(tǒng)下操作I2C總線的外設(shè)

12.2.1 概述

下圖是在linux系統(tǒng)環(huán)境里操作i2c總線上的外設(shè)流程框圖。我們按照從下向上的順序研究一下該流程中各個(gè)角色的功能。

在硬件層中,I2C硬件總線只有兩條線路,上面可以掛載多個(gè)I2C-device,這些I2C-device有的在I2C總線里充當(dāng)主機(jī)的角色,一般情況該主機(jī)為板子上的主cpu中的I2C控制器,比如我們用的100ask_imx6UL板子,這個(gè)I2C主機(jī)就是imx6中的I2C控制器模塊;其他的I2C-device在I2C總線里充當(dāng)從機(jī)的角色,通常這些從機(jī)是板子上完成特定功能的傳感器外設(shè),只不過(guò)該外設(shè)與主控cpu的通信方式是只需要兩條線路的I2C總線,比如在我們的100ask_imx6UL板子中就有eeprom和AP3216兩個(gè)外設(shè),它們?cè)贗2C總線中充當(dāng)?shù)亩际荌2C從機(jī)的角色,它們和主控芯片imx6中的I2C控制器1都是以并聯(lián)的方式掛在這個(gè)I2C總線上。

在內(nèi)核中,驅(qū)動(dòng)程序?qū)ο乱瓿蒊2C總線上的I2C通信協(xié)議,收集硬件傳感器的I2C數(shù)據(jù)并封裝成標(biāo)準(zhǔn)的linux操作接口供用戶空間的應(yīng)用程序操作。對(duì)上要實(shí)現(xiàn)可以通過(guò)linux程序把數(shù)據(jù)流組織成I2C協(xié)議下發(fā)到硬件層的相應(yīng)的外設(shè)傳感器中。

在用戶空間的應(yīng)用程序中,應(yīng)用工程師完全可以不必理會(huì)I2C協(xié)議的詳細(xì)規(guī)定。只需要按照驅(qū)動(dòng)層提供給我們的操作I2C外設(shè)的操作接口函數(shù)就可以像操作linux中其他普通設(shè)備文件那樣輕松的操作I2C外設(shè)了。

pYYBAGGzOfOAQmUlAABTj-QkGj4884.png

12.2.2 簡(jiǎn)述I2C的linux驅(qū)動(dòng)

I2C在linux內(nèi)核層的驅(qū)動(dòng)框架主要由三部分組成:

1) I2C核心層:

I2C核心提供了I2C總線驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)的注冊(cè)、注銷方法,I2C通信方法(algorithm)的上層部分,并且還提供了一系列與具體硬件平臺(tái)無(wú)關(guān)的接口函數(shù)以及探測(cè)設(shè)備,檢測(cè)設(shè)備地址的上層代碼等。它位于內(nèi)核源碼目錄下的drivers/i2c/i2c-core.c文件中,是I2C總線驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)之間依賴于I2C核心作為紐帶。

I2C核心中的主要函數(shù)包括:

增加/刪除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adap); int i2c_del_adapter(struct i2c_adapter *adap);

增加/刪除i2c_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver); int i2c_del_driver(struct i2c_driver *driver); inline int i2c_add_driver(struct i2c_driver *driver);

i2c_client依附/脫離

int i2c_attach_client(struct i2c_client *client); int i2c_detach_client(struct i2c_client *client);

i2c傳輸、發(fā)送和接收

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);

用于進(jìn)行I2C適配器和I2C設(shè)備之間的一組消息交互。其本身不具備驅(qū)動(dòng)適配器物理硬件完成消息交互的能力,它只是尋找到i2c_adapter對(duì)應(yīng)的i2c_algorithm,并使用i2c_algorithm的master_xfer()函數(shù)真正驅(qū)動(dòng)硬件流程。

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_master_send()和i2c_master_recv()函數(shù)內(nèi)部會(huì)調(diào)用i2c_transfer()函數(shù)分別完成一條寫消息和一條讀消息。

a) I2C控制命令分派

下面函數(shù)有助于將發(fā)給I2C適配器設(shè)備文件ioctl的命令分派給對(duì)應(yīng)適配器的algorithm的algo_control()函數(shù)或i2c_driver的command()函數(shù):

int i2c_control(struct i2c_client *client, unsigned int cmd, unsigned long arg); void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg);

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

I2C總線驅(qū)動(dòng)是對(duì)I2C硬件體系結(jié)構(gòu)中適配器端的實(shí)現(xiàn),適配器可由CPU控制,甚至可以直接集成在CPU內(nèi)部。

它主要完成的功能有:

a) 初始化I2C適配器所使用的硬件資源,申請(qǐng)I/O地址、中斷號(hào)等。

b) 通過(guò)i2c_add_adapter()添加i2c_adapter的數(shù)據(jù)結(jié)構(gòu),當(dāng)然這個(gè)i2c_adapter數(shù)據(jù)結(jié)構(gòu)的成員已經(jīng)被xxx適配器的相應(yīng)函數(shù)指針?biāo)跏蓟?/p>

c) 釋放I2C適配器所使用的硬件資源,釋放I/O地址、中斷號(hào)等。

d) 通過(guò)i2c_del_adapter()刪除i2c_adapter的數(shù)據(jù)結(jié)構(gòu)。

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

I2C設(shè)備驅(qū)動(dòng)(也稱為客戶驅(qū)動(dòng))是對(duì)I2C硬件體系結(jié)構(gòu)中設(shè)備端的實(shí)現(xiàn),設(shè)備一般掛接在受CPU控制的I2C適配器上,通過(guò)I2C適配器與CPU交換數(shù)據(jù)。I2C設(shè)備驅(qū)動(dòng)模塊加載函數(shù)通用的方法是在I2C設(shè)備驅(qū)動(dòng)模塊加載函數(shù)中完成兩件事:通過(guò)register_chrdev()函數(shù)將I2C設(shè)備注冊(cè)為一個(gè)字符設(shè)備。通過(guò)I2C核心的i2c_add_driver()函數(shù)添加i2c_driver。

12.3 在linux應(yīng)用層使用I2C

前面我們講解了I2C的協(xié)議及在linux驅(qū)動(dòng)框架,那么當(dāng)你拿到開發(fā)板或者是從公司的硬件同事拿到一個(gè)帶有I2C外設(shè)的板子,我們應(yīng)該如何最快速的使用起來(lái)這個(gè)I2C設(shè)備呢?既然我們總是說(shuō)這個(gè)I2C總線在嵌入式開發(fā)中被廣泛的使用,那么是否有現(xiàn)成的測(cè)試工具幫我們完成這個(gè)快速使用板子的I2C設(shè)備呢?答案是有的,而且這個(gè)測(cè)試工具的代碼還是開源的,它被廣泛的應(yīng)用在linux應(yīng)用層來(lái)快速驗(yàn)證I2C外設(shè)是否可用,為我們測(cè)試I2C設(shè)備提供了很好的捷徑。

12.3.1 如何使用I2C tools測(cè)試I2C外設(shè)

1) I2C tools概述:

I2C tools包含一套用于Linux應(yīng)用層測(cè)試各種各樣I2C功能的工具。它的主要功能包括:總線探測(cè)工具、SMBus訪問(wèn)幫助程序、EEPROM解碼腳本、EEPROM編程工具和用于SMBus訪問(wèn)的python模塊。只要你所使用的內(nèi)核中包含I2C設(shè)備驅(qū)動(dòng),那么就可以在你的板子中正常使用這個(gè)測(cè)試工具。

2) 下載I2C tools源碼:

前面我們已經(jīng)說(shuō)過(guò)了這個(gè)I2C tools工具是開源的,那么這個(gè)源碼在哪里可以找到呢?

下載方法一:直接在內(nèi)核的網(wǎng)站https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/下載I2C tools代碼的壓縮包。

下載方法二:利用git管理工具下載這個(gè)I2C tools的源代碼,命令為git clone git://git.kernel.org/pub/scm/utils/i2c-tools/i2c-tools.git強(qiáng)烈建議讀者采用第二種方法下載這個(gè)代碼,因?yàn)槟憧梢酝ㄟ^(guò)git快速地了解這個(gè)開源代碼的不同版本的功能改進(jìn)及bug修復(fù),而且使用git開發(fā)也是作為一名優(yōu)秀的開發(fā)人員必備的一項(xiàng)技能。

3) 編譯I2C tools源碼:

進(jìn)入剛才利用git下載好的iic-tools源碼目錄,修改編譯工具為你當(dāng)前使用的交叉編譯工具:

26 CC ?= arm-linux-gnueabihf-gcc 27 AR ?= arm-linux-gnueabihf-ar

編譯源碼:如果你想編譯靜態(tài)版本,你可以輸入命令:make USE_STATIC_LIB=1;如果使用動(dòng)態(tài)庫(kù)的話,可以直接輸入make進(jìn)行編譯。安裝命令為:make install,如果你想要讓最后生成的二進(jìn)制文件最小的話,可以在“make install”之前運(yùn)行“make strip”。但是,這將不能生成任何調(diào)試庫(kù),也就不能嘗試進(jìn)一步調(diào)試。然后將tools目錄下的5個(gè)可執(zhí)行文件i2cdetect,i2cdump,i2cget,i2cset和i2ctransfer復(fù)制到板子的/usr/sbin/中;將lib目錄下的libi2c.so.0.1.1文件復(fù)制到板子的/usr/lib/libi2c.so.0。之后別忘了將上面的文件修改為可執(zhí)行的權(quán)限。

4) 介紹I2C tools各功能之—i2cdetect

i2cdetect的主要功能就是I2C設(shè)備查詢,它用于掃描I2C總線上的設(shè)備。它輸出一個(gè)表,其中包含指定總線上檢測(cè)到的設(shè)備的列表。

該命令的常用格式為:i2cdetect [-y] [-a] [-q|-r] i2cbus [first last]。具體參數(shù)的含義如下:

-y 取消交互模式。默認(rèn)情況下,i2cdetect將等待用戶的確認(rèn),
當(dāng)使用此標(biāo)志時(shí),它將直接執(zhí)行操作。
-a 強(qiáng)制掃描非規(guī)則地址。一般不推薦
-q 使用SMBus“快速寫入”命令進(jìn)行探測(cè)。一般不推薦。
-r 使用SMBus“接收字節(jié)”命令進(jìn)行探測(cè)。一般不推薦。
-F 顯示適配器實(shí)現(xiàn)的功能列表并退出。
-V 顯示I2C工具的版本并推出。
-l 顯示已經(jīng)在系統(tǒng)中使用的I2C總線。
i2cbus 表示要掃描的I2C總線的編號(hào)或名稱。
first last 表示要掃描的從設(shè)備地址范圍。

該功能的常用方式:

第一,先通過(guò)i2cdetect -l查看當(dāng)前系統(tǒng)中的I2C的總線情況:

poYBAGGzOfOAbZS7AAAw8Dtf9jo756.png

第二,若總線上掛載I2C從設(shè)備,可通過(guò)i2cdetect掃描某個(gè)I2C總線上的所有設(shè)備。可通過(guò)控制臺(tái)輸入i2cdetect -y 1:(其中"–"表示地址被探測(cè)到了,但沒(méi)有芯片應(yīng)答; "UU"因?yàn)檫@個(gè)地址目前正在被一個(gè)驅(qū)動(dòng)程序使用,探測(cè)被省略;而16進(jìn)制的地址號(hào)60,1e和50則表示發(fā)現(xiàn)了一個(gè)外部片選從地址為0x60,0x1e(AP3216)和0x50(eeprom)的外設(shè)芯片。

pYYBAGGzOfSAV2iSAAA7Pa1T9-8924.png

poYBAGGzOfSAD-Y9AAA5-zOvYSg140.png

第三,查詢I2C總線1 (I2C -1)的功能,命令為i2cdetect -F 1:

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-pCijYzNc-1639020220832)(http://photos.100ask.net/NewHomeSite/IIC_Image011.png)]

5) 介紹I2C tools各功能之—i2cget

i2cget的主要功能是獲取I2C外設(shè)某一寄存器的內(nèi)容。該命令的常用格式為:

i2cget [-f] [-y] [-a] i2cbus chip-address [data-address [mode]]。具體參數(shù)的含義如下:

-f 強(qiáng)制訪問(wèn)設(shè)備,即使它已經(jīng)很忙。 默認(rèn)情況下,i2cget將拒絕訪問(wèn)
已經(jīng)在內(nèi)核驅(qū)動(dòng)程序控制下的設(shè)備。
-y 取消交互模式。默認(rèn)情況下,i2cdetect將等待用戶的確認(rèn),當(dāng)使用此
標(biāo)志時(shí),它將直接執(zhí)行操作。
-a 允許在0x00 - 0x07和0x78 - 0x7f之間使用地址。一般不推薦。
i2cbus 表示要掃描的I2C總線的編號(hào)或名稱。這個(gè)數(shù)字應(yīng)該與i2cdetect -l列出
的總線之一相對(duì)應(yīng)。
chip-address 要操作的外設(shè)從地址。
data-address 被查看外設(shè)的寄存器地址。
mode 顯示數(shù)據(jù)的方式: b (read byte data, default) w (read word data)
c (write byte/read byte)

下面是完成讀取0總線上從地址為0x50的外設(shè)的0x10寄存器的數(shù)據(jù),命令為:

i2cget -y -f 0 0x50 0x10

pYYBAGGzOfWAFBKzAAAn4qdShQk846.png

6) 介紹I2C tools各功能之—i2cdump

i2cdump的主要功能查看I2C從設(shè)備器件所有寄存器的值。 該命令的常用格式為:i2cdump [-f] [-r first-last] [-y] [-a] i2cbus address [mode [bank [bankreg]]]。具體參數(shù)的含義如下:

-f 強(qiáng)制訪問(wèn)設(shè)備,即使它已經(jīng)很忙。 默認(rèn)情況下,i2cget將拒絕訪問(wèn)已經(jīng)在
內(nèi)核驅(qū)動(dòng)程序控制下的設(shè)備。
-r 限制正在訪問(wèn)的寄存器范圍。 此選項(xiàng)僅在模式b,w,c和W中可用。對(duì)于
模式W,first必須是偶數(shù),last必須是奇數(shù)。
-y 取消交互模式。默認(rèn)情況下,i2cdetect將等待用戶的確認(rèn),當(dāng)使用此標(biāo)志
時(shí),它將直接執(zhí)行操作。
-V 顯示I2C工具的版本并推出。
i2cbus 表示要掃描的I2C總線的編號(hào)或名稱。這個(gè)數(shù)字應(yīng)該對(duì)應(yīng)于i2cdetect -l列
出的總線之一。
first last 表示要掃描的從設(shè)備地址范圍。
mode b: 單個(gè)字節(jié) w:16位字 s:SMBus模塊 i:I2C模塊的讀取大小 c: 連續(xù)讀
取所有字節(jié),對(duì)于具有地址自動(dòng)遞增功能的芯片(如EEPROM)非常有用。
W與 w類似,只是讀命令只能在偶數(shù)寄存器地址上發(fā)出;這也是主要用于EEPROM的。

下面是完成讀取0總線上從地址為0x50的eeprom的數(shù)據(jù),命令為:

i2cdump -f -y 0 0x50

poYBAGGzOfWAOBXwAABhGBGu_qY200.png

7) 介紹I2C tools各功能之—i2cset

i2cset的主要功能是通過(guò)I2C總線設(shè)置設(shè)備中某寄存器的值。該命令的常用格式為:

i2cset [-f] [-y] [-m mask] [-r] i2cbus chip-address data-address [value] …[mode]

具體參數(shù)的含義如下:

-f 強(qiáng)制訪問(wèn)設(shè)備,即使它已經(jīng)很忙。 默認(rèn)情況下,i2cget將拒絕訪問(wèn)已
經(jīng)在內(nèi)核驅(qū)動(dòng)程序控制下的設(shè)備。
-r 在寫入值之后立即讀取它,并將結(jié)果與寫入的值進(jìn)行比較。
-y 取消交互模式。默認(rèn)情況下,i2cdetect將等待用戶的確認(rèn),當(dāng)使用此標(biāo)
志時(shí),它將直接執(zhí)行操作。
-V 顯示I2C工具的版本并推出。
i2cbus 表示要掃描的I2C總線的編號(hào)或名稱。這個(gè)數(shù)字應(yīng)該對(duì)應(yīng)于i2cdetect -l列
出的總線之一。
-m mask 如果指定mask參數(shù),那么描述哪些value位將是實(shí)際寫入data-addres的。
掩碼中設(shè)置為1的位將從值中取出,而設(shè)置為0的位將從數(shù)據(jù)地址中讀取,從
而由操作保存。
mode b: 單個(gè)字節(jié) w:16位字 s:SMBus模塊 i:I2C模塊的讀取大小 c: 連續(xù)讀
取所有字節(jié),對(duì)于具有地址自動(dòng)遞增功能的芯片(如EEPROM)非常有用。
W與 w類似,只是讀命令只能在偶數(shù)寄存器地址上發(fā)出;這也是主要用于
EEPROM的。

下面是完成向0總線上從地址為0x50的eeprom的0x10寄存器寫入0x55,命令為:

i2cset -y -f 0 0x50 0x10 0x55

然后用i2cget讀取0總線上從地址為0x50的eeprom的0x10寄存器的數(shù)據(jù),命令為:i2cget -y -f 0 0x50 0x10

poYBAGGzOfWAeq1AAAD4A6k_Sl8733.png

8) 介紹I2C tools各功能之—i2ctransfer

i2ctransfer的主要功能是在一次傳輸中發(fā)送用戶定義的I2C消息。i2ctransfer是一個(gè)創(chuàng)建I2C消息并將其合并為一個(gè)傳輸發(fā)送的程序。對(duì)于讀消息,接收緩沖區(qū)的內(nèi)容被打印到stdout,每個(gè)讀消息一行。

該命令的常用格式為:i2ctransfer [-f] [-y] [-v] [-a] i2cbus desc [data] [desc [data]]

具體參數(shù)的含義如下:

-f 強(qiáng)制訪問(wèn)設(shè)備,即使它已經(jīng)很忙。 默認(rèn)情況下,i2cget將拒絕訪問(wèn)已
經(jīng)在內(nèi)核驅(qū)動(dòng)程序控制下的設(shè)備。
-y 取消交互模式。默認(rèn)情況下,i2cdetect將等待用戶的確認(rèn),當(dāng)使用此
標(biāo)志時(shí),它將直接執(zhí)行操作。
-v 啟用詳細(xì)輸出。它將打印所有信息發(fā)送,即不僅為讀消息,也為寫消息。
-V 顯示I2C工具的版本并推出。
-a 允許在0x00 - 0x02和0x78 - 0x7f之間使用地址。一般不推薦。
i2cbus 表示要掃描的I2C總線的編號(hào)或名稱。這個(gè)數(shù)字應(yīng)該對(duì)應(yīng)于i2cdetect -l
列出的總線之一。

下面是完成向0總線上從地址為0x50的eeprom的0x20開始的4個(gè)寄存器寫入0x01,0x02,0x03,0x04命令為:i2ctransfer -f -y 0 w5@0x50 0x20 0x01 0x02 0x03 0x04然后再通過(guò)命令i2ctransfer -f -y 0 w1@0x50 0x20 r4將0x20地址的4個(gè)寄存器數(shù)據(jù)讀出來(lái),見下圖:

pYYBAGGzOfaAAJr3AAFGP75Lbbc358.png

12.3.2 在linux應(yīng)用程序中讀寫I2C外設(shè)

首先通過(guò)前面的介紹,我們已經(jīng)知道站在cpu的角度來(lái)看,操作I2C外設(shè)實(shí)際上就是通過(guò)控制cpu中掛載該I2C外設(shè)的I2C控制器,而這個(gè)I2C控制器在linux系統(tǒng)中被稱為“I2C適配器”,這個(gè)已經(jīng)在驅(qū)動(dòng)簡(jiǎn)介中介紹過(guò)了。而且眾所周知,在linux系統(tǒng)中,每一個(gè)設(shè)備都是以文件的形式存在的,所以在linux中操作I2C外設(shè)就變成了操作I2C適配器設(shè)備文件。Linux系統(tǒng)(也就是內(nèi)核)為每個(gè)I2C適配器生成了一個(gè)主設(shè)備號(hào)為89的設(shè)備節(jié)點(diǎn)(次設(shè)備號(hào)為0-255),它并沒(méi)有針對(duì)特定的I2C外設(shè)而設(shè)計(jì),只是提供了通用的read(),write(),和ioctl()等文件操作接口,在用戶空間的應(yīng)用層就可以借用這些接口訪問(wèn)掛接在適配器上的I2C設(shè)備的存儲(chǔ)空間或寄存器,并控制I2C設(shè)備的工作方式。

操作流程:

1) 確定I2C適配器的設(shè)備文件節(jié)點(diǎn)

i2c適配器的設(shè)備節(jié)點(diǎn)是/dev/i2c-x,其中x是數(shù)字。由于適配器編號(hào)是動(dòng)態(tài)分配的(和注冊(cè)次序有關(guān)),所以想了解哪一個(gè)適配器對(duì)應(yīng)什么編號(hào),可以查看/sys/class/i2c-dev/目錄下的文件內(nèi)容(在這里筆者強(qiáng)烈建議讀者好好利用好sys文件系統(tǒng)):

cat /sys/class/i2c-dev/i2c-0/name cat /sys/class/i2c-dev/i2c-1/name

poYBAGGzOfeAFBETAAA6JvR9RFg260.png

然后查看硬件原理圖中eeprom是掛在cpu的i2c1控制器中了,然后查看IMX6UL芯片手冊(cè)中I2C1的寄存器地址為21A_0000。

pYYBAGGzOfiAV9OOAANavKGdvAA774.png

比對(duì)后,我們就很容易知道eeprom外設(shè)對(duì)應(yīng)的I2C控制器的設(shè)備節(jié)點(diǎn)為:/dev/i2c-0。

2) 打開適配器對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)

當(dāng)用戶打開適配器設(shè)備節(jié)點(diǎn)的時(shí)候,Kernel中的i2c-dev代碼為其建立一個(gè)i2c_client,但是這個(gè)i2c_client并不加到i2c_adapter的client鏈表當(dāng)中。當(dāng)用戶關(guān)閉設(shè)備節(jié)點(diǎn)時(shí),它自動(dòng)被釋放。

3) IOCTL控制

這個(gè)可以參考內(nèi)核源碼中的include/linux/i2c-dev.h文件。下面舉例說(shuō)明主要的IOCTL命令:

I2C_SLAVE_FORCE 設(shè)置I2C從設(shè)備地址(只有在該地址空閑的情況下成功)
I2C_SLAVE_FORCE 強(qiáng)制設(shè)置I2C從設(shè)備地址(無(wú)論內(nèi)核中是否已有驅(qū)動(dòng)在使用
這個(gè)地址都會(huì)成功)
I2C_TENBIT 選擇地址位長(zhǎng): 0 表示是7bit地址 ; 不等于0 就是10 bit的
地址。只有適配器支持I2C_FUNC_10BIT_ADDR,這個(gè)請(qǐng)求才是有效的。
I2C_FUNCS 獲取適配器支持的功能,詳細(xì)的可以參考文件include/linux/i2c.h
I2C_RDWR 設(shè)置為可讀寫
I2C_RETRIES 設(shè)置收不到ACK時(shí)的重試次數(shù)
I2C_TIMEOUT 設(shè)置超時(shí)的時(shí)限

4) 使用I2C協(xié)議和設(shè)備進(jìn)行通信

代碼為:ioctl(file,I2C_RDWR,(struct i2c_rdwr_ioctl_data *)msgset); 它可以進(jìn)行連續(xù)的讀寫,中間沒(méi)有間歇。只有當(dāng)適配器支持I2C_FUNC_I2C此命令才有效。參數(shù)msgset是一個(gè)指針,指向一個(gè)i2c_rdwr_ioctl_data類型的結(jié)構(gòu)體,該結(jié)構(gòu)體的功能就是讓應(yīng)用程序可以向內(nèi)核傳遞消息,其成員包括:struct i2c_msg __ user *msgs; 和表示i2c_msgs 個(gè)數(shù)的 __u32 nmsgs,它也決定了在硬件I2C總線的硬件通信中有多少個(gè)開始信號(hào)。由于I2C適配器與外設(shè)通信是以消息為單位的,所以struct i2c_msg對(duì)我們來(lái)說(shuō)是非常重要的,它可以包含多條消息,而一條消息有可能包含多個(gè)數(shù)據(jù),比如對(duì)于eeprom頁(yè)寫就包含多個(gè)數(shù)據(jù)。下面就介紹一下這個(gè)結(jié)構(gòu)體的內(nèi)容:

__u16 addr; 從設(shè)備地址
__u16 flags; 標(biāo)志(讀/寫)
I2C_M_TEN 這是一個(gè)10位芯片地址
I2C_M_RD 從設(shè)備到適配器讀數(shù)據(jù)
I2C_M_NOSTART 不發(fā)送起始位
I2C_M_REV_DIR_ADDR 翻轉(zhuǎn)讀寫標(biāo)志
I2C_M_IGNORE_NAK 忽略I2C的NACK信號(hào)
I2C_M_NO_RD_ACK 讀操作的時(shí)候不發(fā)ACK信號(hào)
I2C_M_RECV_LEN 第一次接收數(shù)據(jù)的長(zhǎng)度
__u16 len; 寫入或者讀出數(shù)據(jù)的個(gè)數(shù)(字節(jié))
__u8 *buf; 寫入或者讀出數(shù)據(jù)的地址 buf[0]。 注意:千萬(wàn)不要忘記給 2c_rdwr_ioctl_data結(jié)構(gòu)體中的最重要的結(jié)構(gòu)i2c_msg中的buf分配內(nèi)存。

5) 用read和write讀寫I2C設(shè)備

當(dāng)然你可以使用read()/write()來(lái)與I2C設(shè)備進(jìn)行通信,代碼如下(以eeprom為例簡(jiǎn)要概述操作過(guò)程):

第一,打開I2C控制器文件節(jié)點(diǎn): fd =open(“/dev/i2c-0”, O_RDWR);

第二,設(shè)置eeprom的設(shè)備地址:ioctl(fd,I2C_SLAVE, 0x50);

第三,向eeprom寫數(shù)據(jù):

首先將要操作的eeprom的第一個(gè)寄存器地址賦給寫buf的第0個(gè)元素wr_buf[0] = 0x10;

然后把要寫入的數(shù)據(jù)寫入到后面的buf中for(i=1;i<13;i++) wr_buf[i]=i;

最后通過(guò)write函數(shù)完成向eeprom寫數(shù)據(jù)的功能:write(fd, wr_buf, 13);

最后延遲1秒,讓后面的操作與上面的寫操作分開。

第四,從eeprom讀數(shù)據(jù):
首先和寫操作一樣,將要操作的寄存器首地址0x10發(fā)給eeprom:write(fd, wr_buf, 1);
從0x10寄存器地址處讀取12個(gè)字節(jié)的數(shù)據(jù):ret=read(fd, rd_buf, 12);

你會(huì)發(fā)現(xiàn),用read和write一次只能進(jìn)行一個(gè)方向的傳輸:或者是讀外設(shè)操作,或者就是寫操作傳輸。

代碼如下:

01 #include 02 #include 03 #include 04 #include 05 #include 06 #include 07 08 /* eeprom所對(duì)應(yīng)的I2C控制器的設(shè)備節(jié)點(diǎn) */ 09 #define EEPROM_DEVICE "/dev/i2c-0" 10 11 /* eeprom的I2C設(shè)備地址 */ 12 #define EEPROM_ADDR 0x50 13 14 15 int main() 16 { 17 int fd,i,ret=0; 18 unsigned char w_add=0x10; 19 20 /* 將要讀取的數(shù)據(jù)buf*/ 21 unsigned char rd_buf[13] = {0x10}; 22 23 /* 要寫的數(shù)據(jù)buf,第0個(gè)元素是要操作eeprom的寄存器地址*/ 24 unsigned char wr_buf[13] = {0}; 25 26 printf("hello,this is read_write i2c test n"); 27 28 /* 打開eeprom對(duì)應(yīng)的I2C控制器文件 */ 29 fd =open(EEPROM_DEVICE, O_RDWR); 30 if (fd< 0) 31 { 32 printf("open"EEPROM_DEVICE"failed n"); 33 } 34 35 /*設(shè)置eeprom的I2C設(shè)備地址*/ 36 if (ioctl(fd,I2C_SLAVE_FORCE, EEPROM_ADDR) < 0) 37 { 38 printf("set slave address failed n"); 39 } 40 41 /* 將要操作的寄存器首地址賦給wr_buf[0] */ 42 wr_buf[0] = w_add; 43 44 /* 把要寫入的數(shù)據(jù)寫入到后面的buf中 */ 45 for(i=1;i<13;i++) 46 wr_buf[i]=i; 47 48 /* 通過(guò)write函數(shù)完成向eeprom寫數(shù)據(jù)的功能 */ 49 write(fd, wr_buf, 13); 50 51 /* 延遲一段時(shí)間 */ 52 sleep(1); 53 54 /*重新開始下一個(gè)操作,先寫寄存器的首地址*/ 55 write(fd, wr_buf, 1); 56 57 /* 從wr_buf[0] = w_add的寄存器地址開始讀取12個(gè)字節(jié)的數(shù)據(jù) */ 58 ret=read(fd, rd_buf, 12); 59 printf("ret is %d rn",ret); 60 61 for(i=0;i<12;i++) 62 { 63 printf("rd_buf is :%dn",rd_buf[i]); 64 } 65 66 /* 完成操作后,關(guān)閉eeprom對(duì)應(yīng)的I2C控制器的設(shè)備文件 */ 67 close(fd); 68 69 return 0; 70 }

6) 用數(shù)據(jù)包的方式操作I2C設(shè)備

構(gòu)建數(shù)據(jù)包結(jié)構(gòu)體:
首先是struct i2c_rdwr_ioctl_data data; 應(yīng)用程序通過(guò)該結(jié)構(gòu)體來(lái)給內(nèi)核傳遞消息。該結(jié)構(gòu)體包含兩個(gè)成員struct i2c_msg __ user * msgs;和 __ u32 nmsgs;其中*msgs指向表示通信方法傳輸為消息的結(jié)構(gòu)體。而nmsgs則決定了該數(shù)據(jù)包有多少個(gè)這樣的通信消息,在I2C通信協(xié)議上來(lái)看就代表了有多少個(gè)開始信號(hào)。
接著就是struct i2c_msg; 它可以包含多條消息,而一條消息有可能包含多個(gè)數(shù)據(jù)。其成員包括:“代表I2C設(shè)備從地址的 __ u16 addr; 表示本次消息的標(biāo)志位的 __ u16 flags; 表示數(shù)據(jù)長(zhǎng)度的 __ u16 len; 表示數(shù)據(jù)緩沖區(qū)的指針 __u8 *buf”
然后把要和I2C從設(shè)備通信的數(shù)據(jù)與上面兩個(gè)結(jié)構(gòu)體建立起相應(yīng)的聯(lián)系。
最后調(diào)用I2C_RDWR進(jìn)入驅(qū)動(dòng)程序執(zhí)行讀寫組合的I2C數(shù)據(jù)傳輸。
代碼如下:

01 #include 02 #include 03 #include 04 #include 05 #include 06 #include 07 #include 08 09 /* eeprom所對(duì)應(yīng)的I2C控制器的設(shè)備節(jié)點(diǎn) */ 10 #define EEPROM_DEVICE "/dev/i2c-0" 11 12 /* eeprom的I2C設(shè)備地址 */ 13 #define EEPROM_ADDR 0x50 14 15 /*函數(shù)名:eeprom_write 16 **功能:向eeprom寫數(shù)據(jù) 17 **參數(shù):fd:eeprom對(duì)應(yīng)I2C控制器設(shè)備節(jié)點(diǎn)的文件名 18 ** dev_addr:eeprom的I2C從設(shè)備地址 19 ** reg_addr:eeprom的寄存器地址 20 ** data_buf:要向eeprom寫數(shù)據(jù)的數(shù)據(jù)buf 21 ** len:要寫多少個(gè)字節(jié)。本例中當(dāng)前最大支持為8個(gè)字節(jié) 22 **返回值:負(fù)數(shù)表示操作失敗,其他為成功 23 */ 24 int eeprom_write(int fd, unsigned char dev_addr, unsigned char reg_addr, unsigned char * data_buf,int len) 25 { 26 int ret; 27 28 unsigned char msg_buf[9]; 29 struct i2c_rdwr_ioctl_data data; 30 31 struct i2c_msg messages; 32 33 34 /* 1. 構(gòu)建msg_buf*/ 35 /* 1.1. 將要操作的寄存器首地址賦給要進(jìn)行I2C數(shù)據(jù)通信的首字節(jié)數(shù)據(jù) */ 36 msg_buf[0] = reg_addr; 37 38 /* 1.2. 將要向eeprom寫數(shù)據(jù)的數(shù)據(jù)buf賦在I2C數(shù)據(jù)通信中eeprom寄存器的后面 */ 39 if (len < 9) { /* 本demo最大支持一次向eeprom寫一頁(yè)大小的8個(gè)字節(jié)數(shù)據(jù) */ 40 memcpy((void *) &msg_buf[1], data_buf, len); //第1位之后是數(shù)據(jù) 41 } else { 42 printf("This function supports up to 8 bytes at a time !!!n"); 43 return -1; 44 } 45 46 /* 2. 構(gòu)建 struct i2c_msg messages */ 47 /* 2.1. 賦值eeprom的I2C從設(shè)備地址 */ 48 messages.addr = dev_addr; 49 50 /* 2.2. 賦值flags為本次I2C通信完成寫功能 */ 51 messages.flags = 0; 52 53 /* 2.3. 賦值len為數(shù)據(jù)buf的長(zhǎng)度 + eeprom寄存器地址的數(shù)據(jù)長(zhǎng)度 */ 54 messages.len = len+1; 55 56 /* 2.4. 構(gòu)建消息包的數(shù)據(jù)buf*/ 57 messages.buf = msg_buf; 58 59 /* 3. 構(gòu)建struct i2c_rdwr_ioctl_data data */ 60 /* 3.1. 將準(zhǔn)備好的消息包賦值給i2c_rdwr_ioctl_data中的msgs消息*/ 61 data.msgs = &messages; 62 63 /* 3.2. 由于本次I2C通信只有寫動(dòng)作,所以消息數(shù)為1次 */ 64 data.nmsgs = 1; 65 66 /* 4. 調(diào)用驅(qū)動(dòng)層的讀寫組合的I2C數(shù)據(jù)傳輸 */ 67 if(ioctl(fd, I2C_RDWR, &data) < 0) 68 { 69 printf("I2C_RDWR err n"); 70 return -1; 71 } 72 73 /* 5. 等待I2C總線寫入完成 */ 74 sleep(1); 75 76 return 0; 77 } 78 79 /*函數(shù)名:eeprom_read 80 **功能:從eeprom讀數(shù)據(jù) 81 **參數(shù):fd:eeprom對(duì)應(yīng)I2C控制器設(shè)備節(jié)點(diǎn)的文件名 82 ** dev_addr:eeprom的I2C從設(shè)備地址 83 ** reg_addr:eeprom的寄存器地址 84 ** data_buf:存放從eeprom讀數(shù)據(jù)的buf 85 ** len:要讀多少個(gè)字節(jié)。 86 **返回值:負(fù)數(shù)表示操作失敗,其他為成功 87 */ 88 int eeprom_read(int fd, unsigned char dev_addr, unsigned char reg_addr, unsigned char * data_buf,int len) 89 { 90 int ret; 91 92 unsigned char msg_buf[9]; 93 struct i2c_rdwr_ioctl_data data; 94 95 struct i2c_msg messages[2]; 96 97 /* 1. 構(gòu)建 struct i2c_msg messages */ 98 /* 1.1. 構(gòu)建第一條消息 messages[0] */ 99 /* 1.1.1. 賦值eeprom的I2C從設(shè)備地址 */ 100 messages[0].addr = dev_addr; 101 102 /* 1.1.2. 賦值flags為本次I2C通信完成寫動(dòng)作 */ 103 messages[0].flags = 0; 104 105 /* 1.1.3. 賦值len為eeprom寄存器地址的數(shù)據(jù)長(zhǎng)度是1 */ 106 messages[0].len = 1; 107 108 /* 1.1.4. 本次寫動(dòng)作的數(shù)據(jù)是要讀取eeprom的寄存器首地址*/ 109 messages[0].buf = ?_addr; 110 111 /* 1.2. 構(gòu)建第二條消息 messages[1] */ 112 /* 1.2.1. 賦值eeprom的I2C從設(shè)備地址 */ 113 messages[1].addr = dev_addr; 114 115 /* 1.1.2. 賦值flags為本次I2C通信完成讀動(dòng)作 */ 116 messages[1].flags = I2C_M_RD; 117 118 /* 1.1.3. 賦值len為要讀取eeprom寄存器數(shù)據(jù)長(zhǎng)度len */ 119 messages[1].len = len; 120 121 /* 1.1.4. 本次讀動(dòng)作的數(shù)據(jù)要存放的buf位置*/ 122 messages[1].buf = data_buf; 123 124 /* 2. 構(gòu)建struct i2c_rdwr_ioctl_data data */ 125 /* 2.1. 將準(zhǔn)備好的消息包賦值給i2c_rdwr_ioctl_data中的msgs消息*/ 126 data.msgs = messages; 127 128 /* 2.2. 由于本次I2C通信既有寫動(dòng)作也有讀動(dòng)作,所以消息數(shù)為2次 */ 129 data.nmsgs = 2; 130 131 /* 3. 調(diào)用驅(qū)動(dòng)層的讀寫組合的I2C數(shù)據(jù)傳輸 */ 132 if(ioctl(fd, I2C_RDWR, &data) < 0) 133 { 134 printf("I2C_RDWR err n"); 135 return -1; 136 } 137 138 /* 4. 等待I2C總線讀取完成 */ 139 sleep(1); 140 141 return 0; 142 } 143 144 int main() 145 { 146 int fd,i,ret=0; 147 unsigned char w_add=0x10; 148 149 /* 將要讀取的數(shù)據(jù)buf*/ 150 unsigned char rd_buf[8] = {0}; 151 152 /* 要寫的數(shù)據(jù)buf*/ 153 unsigned char wr_buf[8] = {0}; 154 155 printf("hello,this is I2C_RDWR i2c test n"); 156 157 /* 打開eeprom對(duì)應(yīng)的I2C控制器文件 */ 158 fd =open(EEPROM_DEVICE, O_RDWR); 159 if (fd< 0) 160 { 161 printf("open"EEPROM_DEVICE"failed n"); 162 } 163 164 /* 把要寫入的數(shù)據(jù)寫入到后面的buf中 */ 165 for(i=0;i<8;i++) 166 wr_buf[i]=i; 167 168 /* 通過(guò)I2C_RDWR完成向eeprom讀數(shù)據(jù)的功能 */ 169 eeprom_write(fd,EEPROM_ADDR,w_add,wr_buf,8); 170 171 172 /* 通過(guò)I2C_RDWR完成向eeprom寫數(shù)據(jù)的功能 */ 173 eeprom_read(fd,EEPROM_ADDR,w_add,rd_buf,8); 174 175 for(i=0;i<8;i++) 176 { 177 printf("rd_buf is :%dn",rd_buf[i]); 178 } 179 180 /* 完成操作后,關(guān)閉eeprom對(duì)應(yīng)的I2C控制器的設(shè)備文件 */ 181 close(fd); 182 183 return 0; 184 } 185 186

12.3.3 簡(jiǎn)介I2C的調(diào)試方式

1) 概述I2C通信中完成正常通信的常見元素:

第一,先檢查I2C總線上的所有設(shè)備是否都經(jīng)上拉電阻到電源,并檢查供電是否穩(wěn)定。

第二,數(shù)據(jù)線和時(shí)鐘信號(hào)線是否有接反的情況。

第三,I2C的通信速率是否超過(guò)了設(shè)備所支持的最高速度。

第四,檢查外部I2C設(shè)備與操作的I2C控制器是否掛在了同一條I2C總線上。

第五,檢查操作的I2C外設(shè)地址是否正確。

第六,檢查I2C總線上是否有多個(gè)相同設(shè)備地址的從機(jī)設(shè)備,導(dǎo)致通信沖突。

第七,操作的I2C外設(shè)是否處于寫保護(hù)狀態(tài),寫保護(hù)狀態(tài)是無(wú)法寫入數(shù)據(jù)的。

第八,檢查I2C通信時(shí)序是否滿足I2C通信協(xié)議。

第九,檢查在沒(méi)有開始運(yùn)行I2C通信程序的時(shí)候,I2C總線上的電平信號(hào)是否干凈穩(wěn)定的保持高電平,是否出現(xiàn)過(guò)主機(jī)誤把SDA拉低的情況,導(dǎo)致I2C總線出現(xiàn)“忙碌”狀態(tài)。

第十,檢查I2C通信過(guò)程中是否出現(xiàn)SDA或者SCL被長(zhǎng)時(shí)間一直拉低的狀態(tài)。比如I2C外設(shè)從機(jī)由于異常在發(fā)送完ACK信號(hào)后沒(méi)有釋放SDA。另一種情況是cpu在做從機(jī)的時(shí)候,沒(méi)有及時(shí)完成將讀取的主機(jī)數(shù)據(jù)進(jìn)行處理,導(dǎo)致長(zhǎng)時(shí)間將SCL拉低,破壞了I2C通信流程,因此我們?cè)趯慖2C通信的時(shí)候最好盡快在I2C接收數(shù)據(jù)中斷服務(wù)函數(shù)中完成數(shù)據(jù)處理工作并授權(quán)I2C控制器讓其正常工作。

由于I2C總線的協(xié)議特性,如果總線上有任何一個(gè)I2C設(shè)備將SCL或者SDA的信號(hào)拉低,其他的I2C設(shè)備都將看到這個(gè)低電平,并且都無(wú)法拉高他們。這也就是說(shuō),如果有設(shè)備不釋放總線,一直把總線的電平拉低,那么整個(gè)I2C總線將會(huì)出現(xiàn)暫停掛死的狀態(tài),將無(wú)法按照I2C協(xié)議進(jìn)行正常通信。

如果負(fù)責(zé)I2C總線主機(jī)cpu的I2C控制器出現(xiàn)上述長(zhǎng)時(shí)間拉低I2C總線的電平,理論上我們可以通過(guò)調(diào)試代碼找出I2C總線死機(jī)的原因,并修改代碼重新初始化該I2C控制器來(lái)復(fù)位它,讓其重新進(jìn)行I2C通信。如果通過(guò)調(diào)試發(fā)現(xiàn)導(dǎo)致I2C總線死機(jī)的原因是由I2C外設(shè)導(dǎo)致的,那么我們可以復(fù)位該外設(shè)芯片。但是在實(shí)際的項(xiàng)目開發(fā)中,可能復(fù)位I2C總線上的元件也無(wú)法恢復(fù)正常的I2C通信,這個(gè)時(shí)候就要設(shè)計(jì)I2C總線的主機(jī)程序?qū)2C控制器引腳設(shè)置為GPIO功能并模擬I2C協(xié)議完成一次完整的I2C通信,再將I2C控制器設(shè)置設(shè)置為I2C功能。

12.4 總結(jié)I2C在嵌入式項(xiàng)目開發(fā)的應(yīng)用優(yōu)缺點(diǎn)

優(yōu)點(diǎn):只使用兩根線,支持多個(gè)主控制器和多個(gè)從設(shè)備,I2C具有非常廣泛使用的協(xié)議。

缺點(diǎn):數(shù)據(jù)傳輸速率比SPI慢,數(shù)據(jù)幀的大小限制為8位,實(shí)現(xiàn)比SPI更復(fù)雜的硬件。而且I2C通信需要注意下面的使用問(wèn)題:

1) I2C時(shí)鐘信號(hào)(SCL)的同步問(wèn)題

在I2C總線上傳送信息時(shí)的時(shí)鐘同步信號(hào)是由掛接在SCL線上的所有器件的邏輯“與”完成的。SCL線上由高電平到低電平的跳變將影響到這些器件,一旦某個(gè)器件的時(shí)鐘信號(hào)下跳為低電平,將使SCL線一直保持低電平,使SCL線上的所有器件開始低電平期。此時(shí),低電平周期短的器件的時(shí)鐘由低至高的跳變并不能影響SCL線的狀態(tài),于是這些器件將進(jìn)入高電平等待的狀態(tài)。當(dāng)所有器件的時(shí)鐘信號(hào)都上跳為高電平時(shí),低電平期結(jié)束,SCL線被釋放返回高電平,即所有的器件都同時(shí)開始它們的高電平期。其后,第一個(gè)結(jié)束高電平期的器件又將SCL線拉成低電平。這樣就在SCL線上產(chǎn)生一個(gè)同步時(shí)鐘。可見,時(shí)鐘低電平時(shí)間由時(shí)鐘低電平期最長(zhǎng)的器件確定,而時(shí)鐘高電平時(shí)間由時(shí)鐘高電平期最短的器件確定。

2) 總線驅(qū)動(dòng)能力

上拉電阻和負(fù)載電容決定了總線在某一速率下的穩(wěn)定性。當(dāng)輸出為高時(shí),電流通過(guò)上拉電阻對(duì)負(fù)載電容充電。上拉越大,電容越大,所需要的時(shí)間就越長(zhǎng),如果超過(guò)了通信周期的10%,那么這個(gè)上升沿就太緩了,相應(yīng)的建立時(shí)間會(huì)受到影響,I2C規(guī)范的最大負(fù)載電容是400pF,快速模式下是100pF。如果輸出為低,電流通過(guò)上拉電阻被I2C master器件吸取,(注意根據(jù)I2C規(guī)范,最小只有3毫安的吸取電流)那么這個(gè)吸取電流在上拉電阻上的壓降就決定了輸出低電平能達(dá)到的范圍,如果不能達(dá)到0.3VDD以下,就會(huì)有誤采樣。有人說(shuō)加大上拉電阻是不妥當(dāng)?shù)模唧w分析吸取電流、負(fù)載電容、上拉電平和通信速率才能決定(普通模式和快速模式是不一樣的)。

雖然速度不是特別快,但是信號(hào)線上如果有加電容的話,切記不要加大的,一定要小,否則信號(hào)還沒(méi)到從設(shè)備呢,就被電容吃了。

審核編輯黃昊宇

聲明:本文內(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)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11232

    瀏覽量

    208939
  • I2C
    I2C
    +關(guān)注

    關(guān)注

    28

    文章

    1482

    瀏覽量

    123326
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    I2C通信開發(fā)障礙以及總結(jié)

    本文主要講解的是I2C開發(fā)過(guò)程中會(huì)遇到的坑以及I2C的個(gè)人總結(jié)經(jīng)驗(yàn),很實(shí)用,可以i幫助開發(fā)人員更好的理解
    的頭像 發(fā)表于 04-17 16:35 ?1358次閱讀
    <b class='flag-5'>I2C</b>通信<b class='flag-5'>開發(fā)</b>障礙以及總結(jié)

    數(shù)據(jù)采集光盤實(shí)例第六第十二章

    數(shù)據(jù)采集光盤實(shí)例第六第十二章
    發(fā)表于 06-28 21:17

    明德?lián)P視頻分享點(diǎn)撥FPGA課程---第十二章??學(xué)習(xí)自檢方法

    第十二章學(xué)習(xí)自檢方法1. 波形對(duì)比方法http://yunpan.cn/cjZTiDA9pY56x訪問(wèn)密碼 c359
    發(fā)表于 11-13 11:01

    好消息!昇潤(rùn)科技推出藍(lán)牙4.2開發(fā)視頻教程啦

    端口第十一章 SNV掉電存儲(chǔ)應(yīng)用第十二章 UTC實(shí)時(shí)時(shí)鐘第十三章 ADC模數(shù)轉(zhuǎn)換器第十四章 從機(jī)片內(nèi)OAD第十五章 從機(jī)片外OAD
    發(fā)表于 05-22 16:14

    UNIX線程控制

    UNIX環(huán)境高級(jí)編程(3) 第十二章
    發(fā)表于 10-12 10:02

    「正點(diǎn)原子Linux連載」第十二章官方SDK移植試驗(yàn)

    ,獲取更多資料:正點(diǎn)原子第十二章官方SDK移植試驗(yàn) 在上一中,我們參考ST官方給STM32編寫的stm32f10x.h來(lái)自行編寫I.MX6U的寄存器定義文件。自己編寫這些寄存器定義不僅費(fèi)時(shí)費(fèi)力,沒(méi)有
    發(fā)表于 12-20 15:22

    迅為iMX6ULL開發(fā)板使用手冊(cè)資料下載

    按鍵例程第十三章 GPT定時(shí)器延時(shí)例程第十四章 串口通信例程第十五章 串口格式化函數(shù)例程第十六章 LCD屏幕顯示例程第十七章 SRTC實(shí)時(shí)時(shí)
    發(fā)表于 12-02 14:13

    I.MX6U嵌入式的相關(guān)資料推薦

    :http://www.openedv.com/thread-300792-1-1.html3)對(duì)正點(diǎn)原子Linux感興趣的同學(xué)可以加群討論:9354467414)關(guān)注正點(diǎn)原子公眾號(hào),獲取最新資料更新第十二章官方SDK移植試驗(yàn)在上一
    發(fā)表于 12-03 07:52

    【傾心力作!】i.MX8MM嵌入式linux開發(fā)指南+全覆蓋開發(fā)資料

    搭建第四部分 開發(fā)板固件編譯第八 Android編譯環(huán)境搭建第九 Android固件編譯第十章 Linux固件編譯
    發(fā)表于 02-11 10:27

    【迅為資料上新】iTOP-3568開發(fā)板指南手冊(cè)!

    中斷實(shí)驗(yàn)第五十八章 中斷下文之tasklet第五十九章 等待隊(duì)列第六十章 中斷下文之工作隊(duì)列第六十一章 Linux內(nèi)核定時(shí)器第六十二章 定時(shí)器按鍵消抖實(shí)驗(yàn)第六十三章 輸入子系統(tǒng)實(shí)驗(yàn)第六十四章 Linux
    發(fā)表于 04-26 15:03

    Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》第15LinuxI2C核心、總線與設(shè)備驅(qū)動(dòng)

    Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》第15LinuxI2C核心、總線與設(shè)備驅(qū)動(dòng)
    發(fā)表于 10-27 11:19 ?8次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動(dòng)<b class='flag-5'>開發(fā)</b>詳解》第15<b class='flag-5'>章</b>、<b class='flag-5'>Linux</b>的<b class='flag-5'>I2C</b>核心、總線與設(shè)備驅(qū)動(dòng)

    單片機(jī)應(yīng)用開發(fā)技術(shù)基于Proteus單片機(jī)仿真和C語(yǔ)言編程第二版教學(xué)課件

    ,第四 單片機(jī)C語(yǔ)言編程,第五 鍵盤和顯示,第六 定時(shí)器和中斷,第七 單片機(jī)串行通信,第
    發(fā)表于 05-28 08:00 ?0次下載
    單片機(jī)應(yīng)用<b class='flag-5'>開發(fā)</b>技術(shù)基于Proteus單片機(jī)仿真和<b class='flag-5'>C</b>語(yǔ)言<b class='flag-5'>編程</b>第二版教學(xué)課件

    嵌入式Linux開發(fā)——I2C總線接口

    嵌入式Linux開發(fā)——I2C總線接口
    發(fā)表于 11-02 11:06 ?15次下載
    嵌入式<b class='flag-5'>Linux</b><b class='flag-5'>開發(fā)</b>——<b class='flag-5'>I2C</b>總線接口

    第十二屆藍(lán)橋杯單片機(jī)設(shè)計(jì)與開發(fā)項(xiàng)目省賽

    第十二屆藍(lán)橋杯單片機(jī)設(shè)計(jì)與開發(fā)項(xiàng)目省賽客觀試題程序設(shè)計(jì)試題客觀試題程序設(shè)計(jì)試題
    發(fā)表于 11-04 17:36 ?15次下載
    <b class='flag-5'>第十二</b>屆藍(lán)橋杯單片機(jī)設(shè)計(jì)與<b class='flag-5'>開發(fā)</b>項(xiàng)目省賽

    【正點(diǎn)原子Linux連載】第十二章官方SDK移植試驗(yàn)-摘自【正點(diǎn)原子】I.MX6U嵌入式Linux驅(qū)動(dòng)開發(fā)指南V1.0

    【正點(diǎn)原子Linux連載】第十二章官方SDK移植試驗(yàn)-摘自【正點(diǎn)原子】I.MX6U嵌入式Linux驅(qū)動(dòng)開發(fā)指南V1.0
    發(fā)表于 11-23 17:51 ?11次下載
    【正點(diǎn)原子<b class='flag-5'>Linux</b>連載】<b class='flag-5'>第十二章</b>官方SDK移植試驗(yàn)-摘自【正點(diǎn)原子】<b class='flag-5'>I</b>.MX6U嵌入式<b class='flag-5'>Linux</b>驅(qū)動(dòng)<b class='flag-5'>開發(fā)</b>指南V1.0