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

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

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

Linux TWI開發(fā)指南

嵌入式Linux那些事 ? 來源:嵌入式Linux那些事 ? 作者:嵌入式Linux那些事 ? 2023-03-06 10:24 ? 次閱讀

Linux TWI開發(fā)指南

1 前言

1.1 文檔簡(jiǎn)介

介紹 Sunxi 平臺(tái)上 TWI 驅(qū)動(dòng)接口與調(diào)試方法,為 TWI 模塊開發(fā)提供參考。

1.2 目標(biāo)讀者

TWI 模塊內(nèi)核層以及應(yīng)用層的開發(fā)、維護(hù)人員。

1.3 適用范圍

表1-1:適用產(chǎn)品列表

內(nèi)核版本 驅(qū)動(dòng)文件
Linux-4.9 i2c-sunxi.c
Linux-5.4 i2c-sunxi.c

2 模塊介紹

2.1 模塊功能介紹

全志公司的 twi 總線兼容 i2c 總線協(xié)議,是一種簡(jiǎn)單、雙向二線制同步串行總線。它只需要兩根線即可在連接于總線上的器件之間傳送信息。TWI 控制器支持的標(biāo)準(zhǔn)通信速率為 100kbps,最高通信速率可以達(dá)到 400kbps。全志的 twi 控制器支持一下功能:

? 支持主機(jī)模式和從機(jī)模式;

? 主機(jī)模式下支持 dma 傳輸;

? 主機(jī)模式下在多個(gè)主機(jī)的模式下支持總線仲裁;

? 主機(jī)模式下支持時(shí)鐘同步,位和字節(jié)等待;

? 從機(jī)模式下支持地址檢測(cè)中斷;

? 支持 7bit 從機(jī)地址和 10bit 從機(jī)地址;

? 支持常規(guī)的 i2c 協(xié)議模式和自定義傳輸模式;

sunxi 平臺(tái)支持多路 TWI,包含 TWI 與 S_TWI。

2.2 相關(guān)術(shù)語介紹

2.2.1 硬件術(shù)語

表 2-1: 硬件術(shù)語

相關(guān)術(shù)語 解釋說明
TWI Two Wire Interface,全志平臺(tái)兼容 I2C 標(biāo)準(zhǔn)協(xié)議的總線控制器

2.2.2 軟件術(shù)語

表 2-2: 軟件術(shù)語

相關(guān)術(shù)語 相關(guān)術(shù)語
Sunxi 全志科技使用的 linux 開發(fā)平臺(tái)
I2C_dapter linux 內(nèi)核中 I2C 總線適配器的抽象定義.IIC 總線的控制器,在物理上連接若干個(gè) I2C 設(shè)備
I2C_algorithm linux 內(nèi)核中 I2C 總線通信的抽象定義。描述 I2C 總線適配器與 I2C 設(shè)備之間的通信方法
I2C Client linux 內(nèi)核中 I2C 設(shè)備的抽象定義
I2C Driver linux 內(nèi)核中 I2C 設(shè)備驅(qū)動(dòng)的抽象定義

2.3 模塊配置介紹

在不同的 Sunxi 硬件平臺(tái)中,TWI 控制器的數(shù)目不同;但對(duì)于同一塊板子上的每一個(gè) TWI 控制器來說, 模塊配置類似,本小節(jié)展示 Sunxi 平臺(tái)上的 TWI0 控制器配置(其他 TWI 控制器配置類似)。

2.3.1 device tree 默認(rèn)配置

設(shè)備樹中存在的是該類芯片所有平臺(tái)的模塊配置,設(shè)備樹文件的路徑為:{linux-ver}/arch/arm64(32 位平臺(tái)為 arm)/boot/dts/sunxi(32 位系統(tǒng)無這目錄)/xxxx.dtsi(CHIP 為研發(fā)代號(hào),如sun50iw10p1 等), TWI 總線的設(shè)備樹配置如下所示:

twi0: twi@0x05002000{

#address-cells = <1>;

#size-cells = <0>;

compatible = "allwinner,sun50i-twi"; //具體的設(shè)備,用于驅(qū)動(dòng)和設(shè)備的綁定

device_type = "twi0"; //設(shè)備節(jié)點(diǎn)名稱,用于sys_config.fex匹配

reg = <0x0 0x05002000 0x0 0x400>; //TWI0總線寄存器配置

interrupts = ; //TWI0總線中斷號(hào)、中斷類型

clocks = <&clk_twi0>; //設(shè)備使用的時(shí)鐘

clock-frequency = <400000>; //TWI0控制器的時(shí)鐘頻率

pinctrl-names = "default", "sleep"; //TWI0控制器使用的Pin腳名稱,其中default為正常通 信時(shí)的引腳配置,sleep為睡眠時(shí)的引腳配置

pinctrl-0 = <&twi0_pins_a>; //TWI0控制器default時(shí)使用的pin腳配置

pinctrl-1 = <&twi0_pins_b>; //TWI0控制器sleep時(shí)使用的pin腳配置

twi_drv_used = <1>; //使用DMA傳輸數(shù)據(jù)

status = "disabled"; //TWI0控制器是否使能

};

在 linux-5.4 中,TWI 的配置與 linux-4.9 內(nèi)核配置有些不同,區(qū)別主要體現(xiàn)在 clock 和 dma 的配置上:

twi0: twi@0x05002000{

#address-cells = <1>;

#size-cells = <0>;

compatible = "allwinner,sun20i-twi"; //具體的設(shè)備,用于驅(qū)動(dòng)和設(shè)備的綁定

device_type = "twi0"; //設(shè)備節(jié)點(diǎn)名稱,用于sys_config.fex匹配

reg = <0x0 0x02502000 0x0 0x400>; //TWI0總線寄存器配置

interrupts-extended= <&plic0 25 IRQ_TYPE_LEVEL_HIGH>; //TWI0總線中斷號(hào)、中斷類

clocks = <&ccu CLK_BUS_I2C0>;//twi控器使用的時(shí)鐘

resets = <&ccu RST_BUS_I2C0>;//twi控器使用的reset時(shí)鐘

clock-names = "bus";

clock-frequency = <400000>; //TWI0控制器的時(shí)鐘頻率

dmas = <&dma 43>, <&dma 43>;//TWI0控制器的dma通道號(hào)

dma-names = "tx", "rx";

status = "disabled";//TWI0控制器是否使能

};

為了在 TWI 總線驅(qū)動(dòng)代碼中區(qū)分每一個(gè) TWI 控制器,需要在 Device Tree 中的 aliases 節(jié)點(diǎn)中為每一個(gè) TWI 節(jié)點(diǎn)指定別名:

aliases {

soc_twi0 = &twi0;

soc_twi1 = &twi1;

soc_twi2 = &twi2;

soc_twi3 = &twi3;

...

};

別名形式為字符串 “twi” 加連續(xù)編號(hào)的數(shù)字,在 TWI 總線驅(qū)動(dòng)程序中可以通過 of_alias_get_id()函數(shù)獲取對(duì)應(yīng) TWI 控制器的數(shù)字編號(hào),從而區(qū)別每一個(gè) TWI 控制器。

其中 twi0_pins_a, twi0_pins_b 為 TWI 的引腳配置的配置節(jié)點(diǎn)。linux4.9 中 該 配 置 的 路 徑 為 arch/arm64(32 位 平 臺(tái) 為 arm)/boot/dts/sunxi/xxxxpinctrl.dtsi(CHIP 為研發(fā)代號(hào),如 sun50iw10p1 等),具體配置如下所示:

twi0_pins_a: twi0@0 {

allwinner,pins = "PD14", "PD15"; //TWI控制器使用的引腳

allwinner,pname = "twi0_scl", "twi0_sda"; //TWI控制器的引腳功能說明

allwinner,function = "twi0"; //引腳功能描述

allwinner,muxsel = <4>; //引腳復(fù)用功能配置

allwinner,drive = <0>; //io驅(qū)動(dòng)能力

allwinner,pull = <0>; //內(nèi)部電阻狀態(tài)

};

?

twi0_pins_b: twi0@1 {

allwinner,pins = "PD14", "PD15";

allwinner,function = "io_disabled";

allwinner,muxsel = <7>;

allwinner,drive = <1>;

allwinner,pull = <0>;

};

linux-5.4 中該配置的路徑為 arch/arm64(32 位平臺(tái)為 arm)/boot/dts/sunxi/xxxx.dtsi(CHIP為研發(fā)代號(hào),如 sun50iw10p1 等),具體如下所示:

twi0_pins_a: twi0@0 {

pins = "PH0", "PH1";

function = "twi0";

drive-strength = <10>;

};

twi0_pins_b: twi0@1 {

pins = "PH0", "PH1";

function = "gpio_in";

}

另外 clk_twi0 為時(shí)鐘的配置。

在 linux-4.9 中, 路 徑 為 arch/arm64(32 位 平 臺(tái) 為 arm)/boot/dts/sunxi/XXXXclk.dtsi(CHIP 為研發(fā)代號(hào),如 sun50iw10p1 等),具體配置如下所示:

clk_twi0: twi0 {

#clock-cells = <0>;

compatible = "allwinner,periph-clock";

clock-output-names = "twi0"; //指定clock名稱,用于匹配clock配置

};

在 linux-5.4 中,無需配置。

2.3.2 board.dts 板級(jí)配置

board.dts 用于保存每一個(gè)板級(jí)平臺(tái)的設(shè)備信息(如 demo 板,perf1 板,ver1 板等等),里面的配置信息會(huì)覆蓋上面的 device tree 默認(rèn)配置信息。board.dts 的路徑為 longan/device/config/chips/IC/configs/BOARD/board.dts,

在 linux-4.9 中,對(duì)應(yīng) board.dts 里面 TWI0 的具體配置如下:

twi0_pins_a: twi0@0 {

allwinner,pins = "PA0", "PA1";

allwinner,pname = "twi0_scl", "twi0_sda";

allwinner,function = "twi0";

allwinner,muxsel = <4>;

allwinner,drive = <1>;

allwinner,pull = <0>;

};

?

twi0_pins_b: twi0@1 {

allwinner,pins = "PA0", "PA1";

allwinner,function = "io_disabled";

allwinner,muxsel = <7>;

allwinner,drive = <1>;

allwinner,pull = <0>;

};

?

twi0: twi@0x05002000{

clock-frequency = <400000>; //i2c時(shí)鐘頻率為400K

pinctrl-0 = <&twi0_pins_a>;

pinctrl-1 = <&twi0_pins_b>;

status = "okay"; //使能TWI0

};

在 linux-5.4 中,對(duì)應(yīng) board.dts 里面 TWI0 的具體配置如下:

&twi0 {

clock-frequency = <400000>;

pinctrl-0 = <&twi0_pins_a>;

pinctrl-1 = <&twi0_pins_b>;

pinctrl-names = "default", "sleep";

status = "disabled";

eeprom@50 {

compatible = "atmel,24c16";

reg = <0x50>;

status = "disabled";

};

};

其中,TWI 速率由 “clock-frequency” 屬性配置,最大支持 400K。

對(duì)于 TWI 設(shè)備,可以把設(shè)備節(jié)點(diǎn)填充作為 Device Tree 中相應(yīng) TWI 控制器的子節(jié)點(diǎn)。TWI 控制器驅(qū)動(dòng)的 probe 函數(shù)透過 of_i2c_register_devices() ,自動(dòng)展開作為其子節(jié)點(diǎn)的 TWI 設(shè)備。

對(duì)于 twi0 中引用的 pin 口,具體的配置如下:

twi0_pins_a: twi0@0 { pins = "PB10", "PB11"; /*sck sda*/ function = "twi0"; drive-strength = <10>; }; twi0_pins_b: twi0@1 { pins = "PB10", "PB11"; function = "gpio_in"; };

2.3.3 kernel menuconfig 配置

在 longan 中,linux-4.9 在 命 令 行 進(jìn) 入 內(nèi) 核 根 目 錄 (/kernel/linux-4.9), 執(zhí) 行 make ARCH=arm64 menuconfig (32 位平臺(tái)執(zhí)行:make ARCH=arm menuconfig) 進(jìn)入配置主界面,并按以下步驟操作 (linux-5.4 在根目錄中執(zhí)行./build.sh menuconfig)在 tina 中,可以直接在根目錄里面執(zhí)行 make kernel_menuconfig 進(jìn)入 menuconfig 配置界面。

? 1. 選擇 Device Drivers 選項(xiàng)進(jìn)入下一級(jí)配置,如下圖所示。

pYYBAGQFTveAAKnAAAHbr1eDpUk002.png

圖 2-1: Device Driver

? 2. 選擇 I2C support 選項(xiàng),進(jìn)入下一級(jí)配置,如下圖所示。

poYBAGQFTviAS06XAAIHNd-EQ3s627.png

圖 2-2: I2C support

? 3. 配置用戶 I2C 接口,選擇 I2C device interface,如下圖所示。

pYYBAGQFTviASwu1AAIgzatHIdc956.png

圖 2-3: I2C device interface

? 4. 選擇 I2C HardWare Bus support 選項(xiàng),進(jìn)入下一級(jí)配置,如下圖所示。

poYBAGQFTvmATAX2AAIhwDSfkmo602.png

圖 2-4: I2C HardWare Bus support

? 5. 選擇 SUNXI I2C controller 選項(xiàng),可選擇直接編譯進(jìn)內(nèi)核,也可編譯成模塊。如下圖所示。

pYYBAGQFTvqAJb1TAAJpTLeew2c779.png

圖 2-5: SUNXI I2C controller

2.4 源碼模塊結(jié)構(gòu)

I2C 總線驅(qū)動(dòng)的源代碼位于內(nèi)核在 drivers/i2c/busses 目錄下:

kernel/linux-4.9/drivers/i2c/ ├── busses │ ├── i2c-sunxi.c // Sunxi平臺(tái)的I2C控制器驅(qū)動(dòng)代碼 │ ├── i2c-sunxi.h // 為Sunxi平臺(tái)的I2C控制器驅(qū)動(dòng)定義了一些宏、數(shù)據(jù)結(jié)構(gòu) │ ├── i2c-sunxi-test.c // Sunxi平臺(tái)的i2c設(shè)備測(cè)試代碼,5.4下暫未適配 ├── i2c-core.c // I2C子系統(tǒng)核心文件,提供相關(guān)的接口函數(shù) ├── i2c-dev.c // I2C子系統(tǒng)的設(shè)備相關(guān)文件,用以注冊(cè)相關(guān)的設(shè)備文件,方便調(diào)試

2.5 驅(qū)動(dòng)框架介紹

poYBAGQFTvuAK6xeAAESfLRdhog236.png

圖 2-6: TWI 模塊結(jié)構(gòu)框圖

Linux 中 I2C 體系結(jié)構(gòu)上圖所示,圖中用分割線分成了三個(gè)層次:1. 用戶空間,包括所有使用I2C 設(shè)備的應(yīng)用程序;2. 內(nèi)核,也就是驅(qū)動(dòng)部分;3. 硬件,指實(shí)際物理設(shè)備,包括了 I2C 控制器和 I2C 外設(shè)。

其中,Linux 內(nèi)核中的 I2C 驅(qū)動(dòng)程序從邏輯上又可以分為 6 個(gè)部分:

I2C framework 提供一種 “訪問 I2C slave devices” 的方法。由于這些 slave devices 由I2C controller 控制,因而主要由 I2C controller 驅(qū)動(dòng)實(shí)現(xiàn)這一目標(biāo)。

經(jīng)過 I2C framework 的抽象,用戶可以不用關(guān)心 I2C 總線的技術(shù)細(xì)節(jié),只需要調(diào)用系統(tǒng)的接口,就可以與外部設(shè)備進(jìn)行通信。正常情況下,外部設(shè)備是位于內(nèi)核態(tài)的其它 driver(如觸摸屏,攝像頭等等)。I2C framework 也通過字符設(shè)備向用戶空間提供類似的接口,用戶空間程序可以通過該接口訪問從設(shè)備信息。

在 I2C framework 內(nèi)部,有 I2C core、I2C busses、I2C algos 和 I2C muxes 四個(gè)模塊。

I2C core 使用 I2C adapter 和 I2C algorithm 兩個(gè)子模塊抽象 I2C controller 的功能,使用 I2C client 和 I2C driver 抽象 I2C slave device 的功能(對(duì)應(yīng)設(shè)備模型中的 device 和 device driver)。另外,基于 I2C 協(xié)議,通過 smbus 模塊實(shí)現(xiàn) SMBus(System Management Bus,系統(tǒng)管理總線)的功能。

I2C busses 是各個(gè) I2C controller drivers 的集合,位于 drivers/i2c/busses/目錄下,i2c-sunxi-test.c、i2c-sunxi.c、i2c-sunxi.h。

I2C algos 包含了一些通用的 I2C algorithm,所謂的 algorithm,是指 I2C 協(xié)議的通信方法,用于實(shí)現(xiàn) I2C 的 read/write 指令,一般情況下,都是由硬件實(shí)現(xiàn),不需要特別關(guān)注該目錄。

3 模塊接口說明

3.1 i2c-core 接口

3.1.1 i2c_transfer()

函數(shù)原型:int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

作用:完成 I2C 總線和 I2C 設(shè)備之間的一定數(shù)目的 I2C message 交互。

參數(shù)

adap:指向所屬的 I2C 總線控制器;

msgs:i2c_msg 類型的指針;

num:表示一次需要處理幾個(gè) I2C msg

返回:

>0:已經(jīng)處理的 msg 個(gè)數(shù);

<0:失??;

3.1.2 i2c_master_recv()

函數(shù)原型:int i2c_master_recv(const struct i2c_client *client, char *buf, int count)

作用:通過封裝 i2c_transfer() 完成一次 I2c 接收操作。

參數(shù):

client:指向當(dāng)前 I2C 設(shè)備的實(shí)例;

buf:用于保存接收到的數(shù)據(jù)緩存;

count:數(shù)據(jù)緩存 buf 的長(zhǎng)度

返回:

>0:成功接收的字節(jié)數(shù);

<0:失敗;

3.1.3 i2c_master_send()

函數(shù)原型:int i2c_master_send(const struct i2c_client *client, const char *buf, int count)

作用:通過封裝 i2c_transfer() 完成一次 I2c 發(fā)送操作。

參數(shù):

client:指向當(dāng)前 I2C 從設(shè)備的實(shí)例;

buf:要發(fā)送的數(shù)據(jù);

count:要發(fā)送的數(shù)據(jù)長(zhǎng)度

返回:

>0:成功發(fā)送的字節(jié)數(shù);

<0:失??;

3.1.4 i2c_smbus_read_byte()

函數(shù)原型:s32 i2c_smbus_read_byte(const struct i2c_client *client)

作用:從 I2C 總線讀取一個(gè)字節(jié)。(內(nèi)部是通過 i2c_transfer() 實(shí)現(xiàn),以下幾個(gè)接口同。)

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備

返回:

>0:讀取到的數(shù)據(jù);

<0:失??;

3.1.5 i2c_smbus_write_byte()

函數(shù)原型:s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)

作用:從 I2C 總線寫入一個(gè)字節(jié)。

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備;

value:要寫入的數(shù)值

返回:

0:成功;

<0:失??;

3.1.6 i2c_smbus_read_byte_data()

函數(shù)原型:s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)

作用:從 I2C 設(shè)備指定偏移處讀取一個(gè)字節(jié)。

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備;

command:I2C 協(xié)議數(shù)據(jù)的第 0 字節(jié)命令碼(即偏移值);

返回:

>0:讀取到的數(shù)據(jù);

<0:失敗;

3.1.7 i2c_smbus_write_byte_data()

函數(shù)原型:s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command,u8 value)

作用:從 I2C 設(shè)備指定偏移處寫入一個(gè)字節(jié)。

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備;

command:I2C 協(xié)議數(shù)據(jù)的第 0 字節(jié)命令碼(即偏移值);

value:要寫入的數(shù)值;

返回:

0:成功;

<0:失敗;

3.1.8 i2c_smbus_read_word_data()

函數(shù)原型:s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)

作用:從 I2C 設(shè)備指定偏移處讀取一個(gè) word 數(shù)據(jù)(兩個(gè)字節(jié),適用于 I2C 設(shè)備寄存器是 16 位的情況)。

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備;

command:I2C 協(xié)議數(shù)據(jù)的第 0 字節(jié)命令碼(即偏移值);

返回:

>0:讀取到的數(shù)據(jù);

<0:失??;

3.1.9 i2c_smbus_write_word_data()

函數(shù)原型:s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,u16 value)

作用:從 I2C 設(shè)備指定偏移處寫入一個(gè) word 數(shù)據(jù)(兩個(gè)字節(jié))。

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備;

command:I2C 協(xié)議數(shù)據(jù)的第 0 字節(jié)命令碼(即偏移值);

value:要寫入的數(shù)值

返回:

0:成功;

<0:失敗;

3.1.10 i2c_smbus_read_block_data()

函數(shù)原型:s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,u8 *values)

作用:從 I2C 設(shè)備指定偏移處讀取一塊數(shù)據(jù)。

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備;

command:I2C 協(xié)議數(shù)據(jù)的第 0 字節(jié)命令碼(即偏移值);

values:用于保存讀取到的數(shù)據(jù);

返回:

>0:讀取到的數(shù)據(jù)長(zhǎng)度;

<0:失??;

3.1.11 i2c_smbus_write_block_data()

函數(shù)原型:s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command,u8 length, const u8 *values)

作用:從 I2C 設(shè)備指定偏移處寫入一塊數(shù)據(jù)(長(zhǎng)度最大 32 字節(jié))。

參數(shù):

client:指向當(dāng)前的 I2C 從設(shè)備;

command:I2C 協(xié)議數(shù)據(jù)的第 0 字節(jié)命令碼(即偏移值);

length:要寫入的數(shù)據(jù)長(zhǎng)度;

values:要寫入的數(shù)據(jù);

返回:

0:成功;

<0:失??;

3.2 i2c 用戶態(tài)調(diào)用接口

i2c 的操作在內(nèi)核中是當(dāng)做字符設(shè)備來操作的,可以通過利用文件讀寫接口(open,write,read,ioctrl)等操作內(nèi)核目錄中的/dev/i2c-* 文件來?xiàng)l用相關(guān)的接口,i2c 相關(guān)的操作定義在i2c-dev.c 里面,本節(jié)將介紹比較重要的幾個(gè)接口:

3.2.1 i2cdev_open()

函數(shù)原型:static int i2cdev_open(struct inode *inode, struct file *file)

作用:程序(C 語言等)使用 open(file) 時(shí)調(diào)用的函數(shù)。打開一個(gè) i2c 設(shè)備,可以像文件讀寫的方式往 i2c 設(shè)備中讀寫數(shù)據(jù)

參數(shù):

inode:inode 節(jié)點(diǎn);

file:file 結(jié)構(gòu)體;

返回:文件描述符

3.2.2 i2cdev_read()

函數(shù)原型:static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,loff_t *offset)

作用:程序(C 語言等)調(diào)用 read() 時(shí)調(diào)用的函數(shù)。像往文件里面讀數(shù)據(jù)一樣從 i2c 設(shè)備中讀數(shù)據(jù)。底層調(diào)用 i2c_xfer 傳輸數(shù)據(jù)

參數(shù):

file:file 結(jié)構(gòu)體;

buf,寫數(shù)據(jù) buf;

offset, 文件偏移。

返回:

非空:返回讀取的字節(jié)數(shù);

<0:失敗;

3.2.3 i2cdev_write()

函數(shù)原型:static ssize_t i2cdev_write(struct file *file, const char __user *buf,size_t count, loff_t *offset)

作用:程序(C 語言等)調(diào)用 write() 時(shí)調(diào)用的函數(shù)。像往文件里面寫數(shù)據(jù)一樣往 i2c 設(shè)備中寫數(shù)據(jù)。底層調(diào)用 i2c_xfer 傳輸數(shù)據(jù)

參數(shù):

file:file 結(jié)構(gòu)體;

buf:讀數(shù)據(jù) buf;

offset, 文件偏移。

返回:

0:成功;

<0:失??;

3.2.4 i2cdev_ioctl()

函數(shù)原型:static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

作用:程序(C 語言等)調(diào)用 ioctl() 時(shí)調(diào)用的函數(shù)。像對(duì)文件管理 i/o 一樣對(duì) i2c 設(shè)備管理。該功能比較強(qiáng)大,可以修改 i2c 設(shè)備的地址,往 i2 設(shè)備里面讀寫數(shù)據(jù),使用 smbus 等等,詳細(xì)的可以查閱該函數(shù)。

參數(shù):

file:file 結(jié)構(gòu)體;

cmd:指令;

arg:其他參數(shù)。

返回:

0:成功;

<0:失?。?/p>

4 模塊使用范例

4.1 利用 i2c-core 接口讀寫 TWI 設(shè)備

在內(nèi)核源碼中有現(xiàn)成的 i2c 設(shè)備驅(qū)動(dòng)實(shí)例:tina/lichee/kernel/linux-5.4/drivers/misc/eeprom/at24.c, 這是一個(gè) EEPROM 的 I2C 設(shè)備驅(qū)動(dòng),為了驗(yàn)證 I2C 總線驅(qū)動(dòng),所以其中通過 sysfs 節(jié)點(diǎn)實(shí)現(xiàn)讀寫訪問。下面對(duì)這個(gè)文件的一些關(guān)鍵點(diǎn)進(jìn)行展示介紹:

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EEPROM_ATTR(_name) { .attr = { .name = #_name,.mode = 0444 }, .show = _name_00_show, } struct i2c_client *this_client; static const struct i2c_device_id at24_ids[] = { { "24c16", 0 }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, at24_ids); static int eeprom_i2c_rxdata(char *rxdata, int length) { int ret; struct i2c_msg msgs[] = { { .addr = this_client->addr, .flags = 0, .len = 1, .buf = &rxdata[0], }, { .addr = this_client->addr, .flags = I2C_M_RD, .len = length, .buf = &rxdata[1], }, }; ret = i2c_transfer(this_client->adapter, msgs, 2); if (ret < 0) ? ? pr_info("%s i2c read eeprom error: %dn", __func__, ret); ? ? ? ?return ret; } ? ? static int eeprom_i2c_txdata(char *txdata, int length) { ? ?int ret; ? ?struct i2c_msg msg[] = { ? ? ? ?{ ? ? ? ? ? ?.addr = this_client->addr, .flags = 0, .len = length, .buf = txdata, }, }; ret = i2c_transfer(this_client->adapter, msg, 1); if (ret < 0) ? ?pr_err("%s i2c write eeprom error: %dn", __func__, ret); ? ? ? ?return 0; } static ssize_t read_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { ? ?int i; ? ?u8 rxdata[4]; ? ?rxdata[0] = 0x1; ? ?eeprom_i2c_rxdata(rxdata, 3); ? ? ? ?for(i=0;i<4;i++) ? ?printk("rxdata[%d]: 0x%xn", i, rxdata[i]); ? ? ? ?return sprintf(buf, "%sn", "read end!"); } static ssize_t write_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { ? ?int i; ? ?static u8 txdata[4] = {0x1, 0xAA, 0xBB, 0xCC}; ? ?for(i=0;i<4;i++) ? ? printk("txdata[%d]: 0x%xn", i, txdata[i]); ? ? ? ?eeprom_i2c_txdata(txdata,4); ? ? ? ?txdata[1]++; ? ?txdata[2]++; ? ?txdata[3]++; ? ? ? ?return sprintf(buf, "%sn", "write end!"); } static struct kobj_attribute read = EEPROM_ATTR(read); static struct kobj_attribute write = EEPROM_ATTR(write); static const struct attribute *test_attrs[] = { ? ?&read.attr, ? ?&write.attr, ? ?NULL, }; static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { ? ?int err; ? ?this_client = client; ? ?printk("1..at24_probe n"); ? ?err = sysfs_create_files(&client->dev.kobj,test_attrs); printk("2..at24_probe n"); if(err){ printk("sysfs_create_files failedn"); } printk("3..at24_probe n"); return 0; } static int at24_remove(struct i2c_client *client) { return 0; } static struct i2c_driver at24_driver = { .driver = { .name = "at24", .owner = THIS_MODULE, }, .probe = at24_probe, .remove = at24_remove, .id_table = at24_ids, }; static int __init at24_init(void) { printk("%s %dn", __func__, __LINE__); return i2c_add_driver(&at24_driver); } module_init(at24_init); static void __exit at24_exit(void) { printk("%s()%d - n", __func__, __LINE__); i2c_del_driver(&at24_driver); } module_exit(at24_exit);

4.2 利用用戶態(tài)接口讀寫 TWI 設(shè)備

如果配置了 i2c devices interface,可以直接利用文件讀寫函數(shù)來操作 I2C 設(shè)備。下面這個(gè)程序直接讀取 /dev/i2c-* 來讀寫 i2c 設(shè)備:

#include #include #include #include #define CHIP "/dev/i2c-1" #define CHIP_ADDR 0x50 int main() { unsigned char rddata; unsigned char rdaddr[2] = {0, 0}; /* 將要讀取的數(shù)據(jù)在芯片中的偏移量 */ unsigned char wrbuf[3] = {0, 0, 0x3c}; /* 要寫的數(shù)據(jù),頭兩字節(jié)為偏移量 */ printf("hello, this is i2c testern"); int fd = open(CHIP, O_RDWR); if (fd < 0) ? ?{ ? ? ? ?printf("open "CHIP"failedn"); ? ? ? ?goto exit; ? ?} ? ?if (ioctl(fd, I2C_SLAVE_FORCE, CHIP_ADDR) < 0) ? ?{ /* 設(shè)置芯片地址 */ ? ? ? ?printf("oictl:set slave address failedn"); ? ? ? ?goto close; ? ?} ? ?printf("input a char you want to write to E2PROMn"); ? ?wrbuf[2] = getchar(); ? ?printf("write return:%d, write data:%xn", write(fd, wrbuf, 3), wrbuf[2]); ? ?sleep(1); ? ?printf("write address return: %dn",write(fd, rdaddr, 2)); /* 讀取之前首先設(shè)置讀取的偏移量 */ ? ?printf("read data return:%dn", read(fd, &rddata, 1)); ? ?printf("rddata: %cn", rddata); ? ?close(fd); ? ? exit: ? ?return 0; }

5 FAQ

5.1 調(diào)試方法

5.1.1 調(diào)試工具

5.1.1.1 i2c-tools 調(diào)試工具

i2c-tools 是一個(gè)開源工具,專門用來調(diào)試 I2C 設(shè)備??梢杂?i2c-tools 來獲取 i2c 設(shè)備的相關(guān)信息(默認(rèn)集成在內(nèi)核里面),并且讀寫相關(guān)的 i2c 設(shè)備的數(shù)據(jù)。i2c-tools 主要是通過讀寫/dev/i2c-* 文件獲取 I2C 設(shè)備,所以需要在 kernel/linux-4.9 的 menuconfig 里面把 I2C的 device interface 節(jié)點(diǎn)打開,具體的 i2c-tools 使用方法如下。

i2cdetect -l //獲取i2c設(shè)備信息 i2cdump -y i2c-number i2c-reg //把相關(guān)的i2c設(shè)備數(shù)據(jù)dump出來,如i2cdump -y 1 0x50 i2cget -y i2c-number i2c-reg data_rege //讀取i2c設(shè)備某個(gè)地址的數(shù)據(jù),如i2cget -y 1 0x50 1 i2cset -y i2c-number i2c-reg data_rege data //往i2c設(shè)備某個(gè)地址寫數(shù)據(jù),如i2cset -y 1 0x50 1 1

5.1.2 調(diào)試節(jié)點(diǎn)

5.1.2.1 /sys/module/i2c_sunxi/parameters/transfer_debug

此節(jié)點(diǎn)文件的功能是打開某個(gè) TWI 通道通信過程的調(diào)試信息。缺省值是-1,不會(huì)打印任何通道的通信調(diào)試信息。

打開通道 x 通信過程調(diào)試信息的方法:

echo x > /sys/module/i2c_sunxi/parameters/transfer_debug

關(guān)閉通信過程調(diào)試信息的方法:

echo -1 > /sys/module/i2c_sunxi/parameters/transfer_debug

5.1.2.2 /sys/devices/soc.2/1c2ac00.twi.0/info

此節(jié)點(diǎn)文件可以打印出當(dāng)前 TWI 通道的一些硬件資源信息。

cat /sys/devices/soc.2/1c2ac00.twi.0/info

5.1.2.3 /sys/devices/soc.2/1c2ac00.twi/status

此節(jié)點(diǎn)文件可以打印出當(dāng)前 TWI 通道的一些運(yùn)行狀態(tài)信息,包括控制器的各寄存器值。

cat /sys/devices/soc.2/1c2ac00.twi/status

5.2 常見問題

5.2.1 TWI 數(shù)據(jù)未完全發(fā)送

問題現(xiàn)象:incomplete xfer。具體的 log 如下所示:

[ 1658.926643] sunxi_i2c_do_xfer()1936 - [i2c0] incomplete xfer (status: 0x20, dev addr: 0x50) [ 1658.926643] sunxi_i2c_do_xfer()1936 - [i2c0] incomplete xfer (status: 0x48, dev addr: 0x50)

問題分析:此錯(cuò)誤表示主控已經(jīng)發(fā)送了數(shù)據(jù)(status 值為 0x20 時(shí),表示發(fā)送了 SLAVE ADDR + WRITE;status 值為 0x48 時(shí),表示發(fā)送了 SLAVE ADDR + READ),但是設(shè)備沒有回ACK,這表明設(shè)備無響應(yīng),應(yīng)該檢查是否未接設(shè)備、接觸不良、設(shè)備損壞和上電時(shí)序不正確導(dǎo)致的設(shè)備未就緒等問題。

問題排查步驟

? 步驟 1:通過設(shè)備樹里面的配置信息,核對(duì)引腳配置是否正確。每組 TWI 都有好幾組引腳配置。

? 步驟 2:更換 TWI 總線下的設(shè)備為 at24c16,用 i2ctools 讀寫 at24c16 看看是否成功,成功則表明總線工作正常。

? 步驟 3:排查設(shè)備是否可以正常工作以及設(shè)備與 I2C 之間的硬件接口是否完好。

? 步驟 4:詳細(xì)了解當(dāng)前需要操作的設(shè)備的初始化方法,工作時(shí)序,使用方法,排查因初始化設(shè)備不正確導(dǎo)致通訊失敗。

? 步驟 5:用示波器檢查 TWI 引腳輸出波形,查看波形是否匹配。

5.2.2 TWI 起始信號(hào)無法發(fā)送

問題現(xiàn)象:START can’t sendout!。具體的 log 如下所示:

sunxi_i2c_do_xfer()1865 - [i2c1] START can't sendout!

問題分析:此錯(cuò)誤表示 TWI 無法發(fā)送起始信號(hào),一般跟 TWI 總線的引腳配置以及時(shí)鐘配置有關(guān)。應(yīng)該檢查引腳配置是否正確,時(shí)鐘配置是否正確,引腳是否存在上拉電阻等等。

問題排查步驟

? 步驟 1:重新啟動(dòng)內(nèi)核,通過查看 log,分析 TWI 是否成功初始化,如若存在引腳配置問題,應(yīng)核對(duì)引腳信息是否正確。

? 步驟 2:根據(jù)原理圖,查看 TWI-SCK 和 TWI-SDA 是否經(jīng)過合適的上拉電阻接到 3.3v 電壓。

? 步驟 3:用萬用表量 SDA 與 SCL 初始電壓,看電壓是否在 3.3V 附近(斷開此 TWI 控制器所有外設(shè)硬件連接與軟件通訊進(jìn)程)。

? 步驟 4:核查引腳配置以及 clk 配置是否進(jìn)行正確設(shè)置。

? 步驟 5:測(cè)試 PIN 的功能是否正常,利用寄存器讀寫的方式,將 PIN 功能直接設(shè)為 INPUT 功能(echo [reg] [val] > /sys/class/sunxi_dump/write),然后將 PIN 上拉和接地改變 PIN狀態(tài),讀 PIN 的狀態(tài) (echo [reg,reg] > /sys/class/sunxi_dump/dump;cat dump),看是否匹配。

? 步驟 6:測(cè)試 CLK 的功能是否正常,利用寄存器讀寫的方式,將 TWI 的 CLK gating 等打開,(echo [reg] [val] > /sys/class/sunxi_dump/write),然后讀取相應(yīng) TWI 的寄存器信息,讀 TWI 寄存器的數(shù)據(jù)(echo [reg] ,[len]> /sys/class/sunxi_dump/dump),查看寄存器數(shù)據(jù)是否正常。

5.2.3 TWI 終止信號(hào)無法發(fā)送

問題現(xiàn)象:STOP can’t sendout。具體的 log 如下所示:

twi_stop()511 - [i2c4] STOP can't sendout! sunxi_i2c_core_process()1726 - [i2c4] STOP failed!

問題分析:此錯(cuò)誤表示 TWI 無法發(fā)送終止信號(hào),一般跟 TWI 總線的引腳配置。應(yīng)該檢查引腳配置是否正確,引腳電壓是否穩(wěn)定等等。

問題排查步驟

? 步驟 1:根據(jù)原理圖,查看 TWI-SCK 和 TWI-SDA 是否經(jīng)過合適的上拉電阻接到 3.3v 電壓。

? 步驟 2:用萬用表量 SDA 與 SCL 初始電壓,看電壓是否在 3.3V 附近(斷開此 TWI 控制器所有外設(shè)硬件連接與軟件通訊進(jìn)程)。

? 步驟 3:測(cè)試 PIN 的功能是否正常,利用寄存器讀寫的方式,將 PIN 功能直接設(shè)為 INPUT 功能(echo [reg] [val] > /sys/class/sunxi_dump/write),然后將 PIN 上拉和接地改變 PIN狀態(tài),讀 PIN 的狀態(tài) (echo [reg,reg] > /sys/class/sunxi_dump/dump;cat dump),看是否匹配。

? 步驟 4: 查看設(shè)備樹配置,把其他用到 SCK/SDA 引腳的節(jié)點(diǎn)關(guān)閉,重新測(cè)試 I2C 通信功能。

5.2.4 TWI 傳送超時(shí)

問題現(xiàn)象:xfer timeout。具體的 log 如下所示:

[123.681219] sunxi_i2c_do_xfer()1914 - [i2c3] xfer timeout (dev addr:0x50)

問題分析: 此錯(cuò)誤表示主控已經(jīng)發(fā)送完起始信號(hào),但是在與設(shè)備通信的過程中無法正常完成數(shù)據(jù)發(fā)送與接收,導(dǎo)致最終沒有發(fā)出終止信號(hào)來結(jié)束 I2C 傳輸,導(dǎo)致的傳輸超時(shí)問題。應(yīng)該檢查引腳配置是否正常,CLK 配置是否正常,TWI 寄存器數(shù)據(jù)是否正常,是否有其他設(shè)備干擾,中斷是否正常等問題。

問題排查步驟

? 步驟 1:核實(shí) TWI 控制器配置是否正確。

? 步驟 2:根據(jù)原理圖,查看 TWI-SCK 和 TWI-SDA 是否經(jīng)過合適的上拉電阻接到 3.3v 電壓。

? 步驟 3:用萬用表量 SDA 與 SCL 初始電壓,看電壓是否在 3.3V 附近(斷開此 TWI 控制器所有外設(shè)硬件連接與軟件通訊進(jìn)程)。

? 步驟 4:關(guān)閉其他 TWI 設(shè)備,重新進(jìn)行燒錄測(cè)試 TWI 功能是否正常。

? 步驟 4:測(cè)試 PIN 的功能是否正常,利用寄存器讀寫的方式,將 PIN 功能直接設(shè)為 INPUT 功能(echo [reg] [val] > /sys/class/sunxi_dump/write),然后將 PIN 上拉和接地改變 PIN狀態(tài),讀 PIN 的狀態(tài) (echo [reg,reg] > /sys/class/sunxi_dump/dump;cat dump),看是否匹配。

? 步驟 5:測(cè)試 CLK 的功能是否正常,利用寄存器讀寫的方式,將 TWI 的 CLK gating 等打開,(echo [reg] [val] > /sys/class/sunxi_dump/write),然后讀取相應(yīng) TWI 的寄存器信息,讀 TWI 寄存器的數(shù)據(jù)(echo [reg] ,[len]> /sys/class/sunxi_dump/dump),查看寄存器數(shù)據(jù)是否正常。

? 步驟 7:根據(jù)相關(guān)的 LOG 跟蹤 TWI 代碼執(zhí)行流程,分析報(bào)錯(cuò)原因。

審核編輯:湯梓紅

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

    關(guān)注

    33

    文章

    8451

    瀏覽量

    150733
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11212

    瀏覽量

    208724
  • 調(diào)試
    +關(guān)注

    關(guān)注

    7

    文章

    571

    瀏覽量

    33875
  • TWI
    TWI
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    8053
  • 開發(fā)指南
    +關(guān)注

    關(guān)注

    0

    文章

    34

    瀏覽量

    7522
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    EAC0945 linux開發(fā)指南

    EAC0945 linux開發(fā)指南
    發(fā)表于 09-28 12:40

    EAC0945 linux開發(fā)指南

    `EAC0945 linux開發(fā)指南`
    發(fā)表于 10-31 12:18

    Rockchip Linux SDK uboot logo開發(fā)指南

    arm嵌入式vs-rk3399 板卡uboot logo 開發(fā)指南概述:本文檔主要介紹 rockchip linux sdk uboot logo 顯示的相關(guān)功能、配置以及開發(fā)過程中的注意事項(xiàng)。適用于 rockhip
    發(fā)表于 10-09 08:12

    CPLD FPGA高級(jí)應(yīng)用開發(fā)指南

    CPLD FPGA高級(jí)應(yīng)用開發(fā)指南
    發(fā)表于 04-15 10:56 ?58次下載
    CPLD FPGA高級(jí)應(yīng)用<b class='flag-5'>開發(fā)指南</b>

    Tiny6410 Linux開發(fā)指南詳解

    Tiny6410 Linux 開發(fā)指南
    發(fā)表于 07-08 17:12 ?210次下載
    Tiny6410 <b class='flag-5'>Linux</b><b class='flag-5'>開發(fā)指南</b>詳解

    A64開發(fā)板LCD開發(fā)指南

    A64開發(fā)板LCD開發(fā)指南,驅(qū)動(dòng)開發(fā)指南
    發(fā)表于 06-21 17:02 ?0次下載

    彩光燈開發(fā)指南

    彩光燈開發(fā)指南
    發(fā)表于 12-29 20:15 ?0次下載

    Linux的平臺(tái)下Mini210S裸機(jī)程序開發(fā)指南

    Linux的平臺(tái)下Mini210S裸機(jī)程序開發(fā)指南
    發(fā)表于 10-29 10:52 ?59次下載
    <b class='flag-5'>Linux</b>的平臺(tái)下Mini210S裸機(jī)程序<b class='flag-5'>開發(fā)指南</b>

    Rockchip Linux SDK的開發(fā)指南的詳細(xì)資料說明

    本文檔的主要內(nèi)容詳細(xì)介紹的是Rockchip Linux SDK的開發(fā)指南的詳細(xì)資料說明。
    發(fā)表于 01-10 17:17 ?74次下載
    Rockchip <b class='flag-5'>Linux</b> SDK的<b class='flag-5'>開發(fā)指南</b>的詳細(xì)資料說明

    迅為RK3399開發(fā)板嵌入式linux開發(fā)指南

    迅為RK3399開發(fā)板嵌入式linux開發(fā)指南迅為RK3399開發(fā)板發(fā)布《北京迅為嵌入式linux開發(fā)指
    發(fā)表于 11-01 16:58 ?76次下載
    迅為RK3399<b class='flag-5'>開發(fā)</b>板嵌入式<b class='flag-5'>linux</b><b class='flag-5'>開發(fā)指南</b>

    nRF52832開發(fā)指南-上冊(cè)

    nRF52832開發(fā)指南-上冊(cè)
    發(fā)表于 06-16 14:15 ?75次下載

    Tina_Linux_系統(tǒng)軟件開發(fā)指南

    Tina_Linux_系統(tǒng)軟件開發(fā)指南
    的頭像 發(fā)表于 03-02 15:25 ?1747次閱讀
    Tina_<b class='flag-5'>Linux</b>_系統(tǒng)軟件<b class='flag-5'>開發(fā)指南</b>

    Tina Linux配置開發(fā)指南

    Tina Linux配置開發(fā)指南
    的頭像 發(fā)表于 03-02 15:28 ?1.6w次閱讀
    Tina <b class='flag-5'>Linux</b>配置<b class='flag-5'>開發(fā)指南</b>

    Linux NOR開發(fā)指南

    Linux NOR開發(fā)指南
    的頭像 發(fā)表于 03-06 09:55 ?908次閱讀
    <b class='flag-5'>Linux</b> NOR<b class='flag-5'>開發(fā)指南</b>

    【北京迅為】itop-龍芯2k1000開發(fā)指南Linux基礎(chǔ)入門vim 編輯器

    【北京迅為】itop-龍芯2k1000開發(fā)指南Linux基礎(chǔ)入門vim 編輯器
    的頭像 發(fā)表于 10-25 14:56 ?230次閱讀
    【北京迅為】itop-龍芯2k1000<b class='flag-5'>開發(fā)指南</b><b class='flag-5'>Linux</b>基礎(chǔ)入門vim 編輯器