I2C驱动原理

1. I2C驱动整体框架

1.1 I2C硬件框架

  • 在一个芯片(SoC)内部,有一个或多个I2C控制器

  • 在一个I2C控制器上,可以连接一个或多个I2C设备

  • I2C总线只需要2条线:时钟线SCL、数据线SDA

  • 在I2C总线的SCL、SDA线上,都有上拉电阻

1.2 I2C软件框架

 

以I2C接口的存储设备AT24C02为例:

  • APP:

    • 提出要求:把字符串"www.100ask.net"写入AT24C02地址16开始的地方

    • 它是大爷,不关心底层实现的细节

    • 它只需要调用设备驱动程序提供的接口

  • AT24C02驱动:

    • 它知道AT24C02要求的地址、数据格式

    • 它知道发出什么信号才能让AT24C02执行擦除、烧写工作

    • 它知道怎么判断数据是否烧写成功

    • 它构造好一系列的数据,发给I2C控制器

  • I2C控制器驱动

    • 它根据I2C协议发出各类信号:I2C设备地址、I2C存储地址、数据

    • 它根据I2C协议判断

1.3 构建驱动程序步骤

1.3.1 对于Linux

从上到下:

  • 先讲I2C协议

  • APP可以通过两类驱动程序访问设备

    • I2C设备自己的驱动程序

    • 内核自带的i2c-dev.c驱动程序,它是i2c控制器驱动程序暴露给用户空间的驱动程序(i2c-dev.c)

  • I2C Device Driver

    • I2C设备自己的驱动程序

    • 内核自带的i2c-dev.c驱动程序,它是i2c控制器驱动程序暴露给用户空间的驱动程序(i2c-dev.c)

  • I2C Controller Driver

    • 芯片I2C控制器的驱动程序(称为adapter)

    • 使用GPIO模拟的I2C控制器驱动程序(i2c-gpio.c)

1.3.2 对于单片机/裸机

 

从上到下:

  • 先讲I2C协议

  • APP

  • I2C Device Driver

  • I2C Controller Driver(也被称为adapter)

2. I2C协议 

2.1 硬件连接

I2C在硬件上的接法如下所示,主控芯片引出两条线SCL,SDA线,在一条I2C总线上可以接很多I2C设备,我们还会放一个上拉电阻(放一个上拉电阻的原因以后我们再说)。

2.2 传输数据类比

怎么通过I2C传输数据,我们需要把数据从主设备发送到从设备上去,也需要把数据从从设备传送到主设备上去,数据涉及到双向传输。

举个例子:

体育老师:可以把球发给学生,也可以把球从学生中接过来。

* 发球:
  * 老师:开始了(start)
  * 老师:A!我要发球给你!(地址/方向)
  * 学生A:到!(回应)
  * 老师把球发出去(传输)
  * A收到球之后,应该告诉老师一声(回应)
  * 老师:结束(停止)

* 接球:
  * 老师:开始了(start)
  * 老师:B!把球发给我!(地址/方向)
  * 学生B:到!
  * B把球发给老师(传输)
  * 老师收到球之后,给B说一声,表示收到球了(回应)
  * 老师:结束(停止)

我们就使用这个简单的例子,来解释一下IIC的传输协议:

* 老师说开始了,表示开始信号(start)
* 老师提醒某个学生要发球,表示发送地址和方向(address/read/write)
* 老师发球/接球,表示数据的传输
* 收到球要回应:回应信号(ACK)
* 老师说结束,表示IIC传输结束(P)

 2.3 IIC传输数据的格式

2.3.1 写操作

流程如下:

* 主芯片要发出一个start信号
* 然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0表示写,1表示读)
* 从设备回应(用来确定这个设备是否存在),然后就可以传输数据
* 主设备发送一个字节数据给从设备,并等待回应
* 每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据。
* 数据发送完之后,主芯片就会发送一个停止信号。

下图:白色背景表示"主→从",灰色背景表示"从→主"

2.3.2 读操作

流程如下:

* 主芯片要发出一个start信号
* 然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0表示写,1表示读)
* 从设备回应(用来确定这个设备是否存在),然后就可以传输数据

* 从设备发送一个字节数据给主设备,并等待回应
* 每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据。
* 数据发送完之后,主芯片就会发送一个停止信号。

下图:白色背景表示"主→从",灰色背景表示"从→主"

2.3.3 I2C信号

I2C协议中数据传输的单位是字节,也就是8位。但是要用到9个时钟:前面8个时钟用来传输8数据,第9个时钟用来传输回应信号。传输时,先传输最高位(MSB)。

* 开始信号(S):SCL为高电平时,SDA山高电平向低电平跳变,开始传送数据。
* 结束信号(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
* 响应信号(ACK):接收器在接收到8位数据后,在第9个时钟周期,拉低SDA
* SDA上传输的数据必须在SCL为高电平期间保持稳定,SDA上的数据只能在SCL为低电平期间变化

I2C协议信号如下:

2.3.4 协议细节

  • 如何在SDA上实现双向传输? 主芯片通过一根SDA线既可以把数据发给从设备,也可以从SDA上读取数据,连接SDA线的引脚里面必然有两个引脚(发送引脚/接受引脚)。

  • 主、从设备都可以通过SDA发送数据,肯定不能同时发送数据,怎么错开时间? 在9个时钟里, 前8个时钟由主设备发送数据的话,第9个时钟就由从设备发送数据; 前8个时钟由从设备发送数据的话,第9个时钟就由主设备发送数据。

  • 双方设备中,某个设备发送数据时,另一方怎样才能不影响SDA上的数据? 设备的SDA中有一个三极管,使用开极/开漏电路(三极管是开极,CMOS管是开漏,作用一样),如下图:

从真值表和电路图我们可以知道:

  • 当某一个芯片不想影响SDA线时,那就不驱动这个三极管

  • 想让SDA输出高电平,双方都不驱动三极管(SDA通过上拉电阻变为高电平)

  • 想让SDA输出低电平,就驱动三极管

从下面的例子可以看看数据是怎么传的(实现双向传输)。
举例:主设备发送(8bit)给从设备

* 前8个clk
  * 从设备不要影响SDA,从设备不驱动三极管
  * 主设备决定数据,主设备要发送1时不驱动三极管,要发送0时驱动三极管

* 第9个clk,由从设备决定数据
  * 主设备不驱动三极管
  * 从设备决定数据,要发出回应信号的话,就驱动三极管让SDA变为0
  * 从这里也可以知道ACK信号是低电平

从上面的例子,就可以知道怎样在一条线上实现双向传输,这就是SDA上要使用上拉电阻的原因。

为何SCL也要使用上拉电阻?
在第9个时钟之后,如果有某一方需要更多的时间来处理数据,它可以一直驱动三极管把SCL拉低。
当SCL为低电平时候,大家都不应该使用IIC总线,只有当SCL从低电平变为高电平的时候,IIC总线才能被使用。
当它就绪后,就可以不再驱动三极管,这是上拉电阻把SCL变为高电平,其他设备就可以继续使用I2C总线了。

对于IIC协议它只能规定怎么传输数据,数据是什么含义由从设备决定。

3. SMBus协议

3.1 SMBus是I2C协议的一个子集

 SMBus: System Management Bus,系统管理总线。
SMBus最初的目的是为智能电池、充电电池、其他微控制器之间的通信链路而定义的。
SMBus也被用来连接各种设备,包括电源相关设备,系统传感器,EEPROM通讯设备等等。
SMBus 为系统和电源管理这样的任务提供了一条控制总线,使用 SMBus 的系统,设备之间发送和接收消息都是通过 SMBus,而不是使用单独的控制线,这样可以节省设备的管脚数。
SMBus是基于I2C协议的,SMBus要求更严格,SMBus是I2C协议的子集。

SMBus有哪些更严格的要求?跟一般的I2C协议有哪些差别?

* VDD的极限值不一样

  * I2C协议:范围很广,甚至讨论了高达12V的情况
  * SMBus:1.8V~5V

* 最小时钟频率、最大的`Clock Stretching `

  * Clock Stretching含义:某个设备需要更多时间进行内部的处理时,它可以把SCL拉低占住I2C总线

  * I2C协议:时钟频率最小值无限制,Clock Stretching时长也没有限制
  * SMBus:时钟频率最小值是10KHz,Clock Stretching的最大时间值也有限制

* 地址回应(Address Acknowledge)

  * 一个I2C设备接收到它的设备地址后,是否必须发出回应信号?
  * I2C协议:没有强制要求必须发出回应信号
  * SMBus:强制要求必须发出回应信号,这样对方才知道该设备的状态:busy,failed,或是被移除了

* SMBus协议明确了数据的传输格式
  * I2C协议:它只定义了怎么传输数据,但是并没有定义数据的格式,这完全由设备来定义
  * SMBus:定义了几种数据格式(后面分析)

* REPEATED START Condition(重复发出S信号)
  * 比如读EEPROM时,涉及2个操作:
    * 把存储地址发给设备
    * 读数据
  * 在写、读之间,可以不发出P信号,而是直接发出S信号:这个S信号就是`REPEATED START`
  * 如下图所示

  • SMBus Low Power Version

    • SMBus也有低功耗的版本

3.2 SMBus协议分析

对于I2C协议,它只定义了怎么传输数据,但是并没有定义数据的格式,这完全由设备来定义。
对于SMBus协议,它定义了几种数据格式。

**注意**:

* 下面文档中的`Functionality flag`是Linux的某个I2C控制器驱动所支持的功能。
* 比如`Functionality flag: I2C_FUNC_SMBUS_QUICK`,表示需要I2C控制器支持`SMBus Quick Command`

3.2.1 symbols(符号)

S     (1 bit) : Start bit(开始位)
Sr    (1 bit) : 重复的开始位
P     (1 bit) : Stop bit(停止位)
R/W#  (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.(读写位)
A, N  (1 bit) : Accept and reverse accept bit.(回应位)
Address(7 bits): I2C 7 bit address. Note that this can be expanded as usual to
                get a 10 bit I2C address.
                (地址位,7位地址)
Command Code  (8 bits): Command byte, a data byte which often selects a register on
                the device.
                (命令字节,一般用来选择芯片内部的寄存器)
Data Byte (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh
                for 16 bit data.
                (数据字节,8位;如果是16位数据的话,用2个字节来表示:DataLow、DataHigh)
Count (8 bits): A data byte containing the length of a block operation.
				(在block操作总,表示数据长度)
[..]:           Data sent by I2C device, as opposed to data sent by the host
                adapter.
                (中括号表示I2C设备发送的数据,没有中括号表示host adapter发送的数据)

3.2.2 SMBus Quick Command

只是用来发送一位数据:R/W#本意是用来表示读或写,但是在SMBus里可以用来表示其他含义。
比如某些开关设备,可以根据这一位来决定是打开还是关闭。

3.2.3 SMBus Receive Byte

I2C-tools中的函数:i2c_smbus_read_byte()。
读取一个字节,Host adapter接收到一个字节后不需要发出回应信号(上图中N表示不回应)。

3.2.4 SMBus Send Byte

I2C-tools中的函数:i2c_smbus_write_byte()。
发送一个字节。

3.2.5 SMBus Read Byte

I2C-tools中的函数:i2c_smbus_read_byte_data()。

先发出`Command Code`(它一般表示芯片内部的寄存器地址),再读取一个字节的数据。
上面介绍的`SMBus Receive Byte`是不发送Comand,直接读取数据。

3.2.6 SMBus Read Word

2C-tools中的函数:i2c_smbus_read_word_data()。

先发出Command Code(它一般表示芯片内部的寄存器地址),再读取2个字节的数据。

3.2.7 SMBus Write Byte

I2C-tools中的函数:i2c_smbus_write_byte_data()。

先发出Command Code(它一般表示芯片内部的寄存器地址),再发出1个字节的数据。

3.2.8 SMBus Write Word

I2C-tools中的函数:i2c_smbus_write_word_data()。

先发出Command Code(它一般表示芯片内部的寄存器地址),再发出1个字节的数据。

3.2.9 SMBus Block Read

I2C-tools中的函数:i2c_smbus_read_block_data()。

先发出Command Code(它一般表示芯片内部的寄存器地址),再发起度操作:

  • 先读到一个字节(Block Count),表示后续要读的字节数

  • 然后读取全部数据

3.2.10 SMBus Block Write

I2C-tools中的函数:i2c_smbus_write_block_data()。

先发出Command Code(它一般表示芯片内部的寄存器地址),再发出1个字节的Byte Conut(表示后续要发出的数据字节数),最后发出全部数据。

3.2.11 I2C Block Read

在一般的I2C协议中,也可以连续读出多个字节。 它跟SMBus Block Read的差别在于设备发出的第1个数据不是长度N,如下图所示:

I2C-tools中的函数:i2c_smbus_read_i2c_block_data()。

先发出Command Code(它一般表示芯片内部的寄存器地址),再发出1个字节的Byte Conut(表示后续要发出的数据字节数),最后发出全部数据。

3.2.12 I2C Block Write

在一般的I2C协议中,也可以连续发出多个字节。
它跟`SMBus Block Write`的差别在于发出的第1个数据不是长度N,如下图所示:

I2C-tools中的函数:i2c_smbus_write_i2c_block_data()。

先发出Command Code(它一般表示芯片内部的寄存器地址),再发出1个字节的Byte Conut(表示后续要发出的数据字节数),最后发出全部数据。

3.2.13 SMBus Block Write - Block Read Process Call

先写一块数据,再读一块数据。

3.2.14 Packet Error Checking (PEC)

PEC是一种错误校验码,如果使用PEC,那么在P信号之前,数据发送方要发送一个字节的PEC码(它是CRC-8码)。

SMBus Send Byte为例,下图中,一个未使用PEC,另一个使用PEC:

3.3. SMBus和I2C的建议

因为很多设备都实现了SMBus,而不是更宽泛的I2C协议,所以优先使用SMBus。 即使I2C控制器没有实现SMBus,软件方面也是可以使用I2C协议来模拟SMBus。 所以:Linux建议优先使用SMBus。

SMBus(System Management Bus)和I2C(Inter-Integrated Circuit)都是串行通信协议,用于设备间的通信,但它们在一些方面存在差异:

  1. 定义和目的

    • I2C:最初设计用于连接微控制器和低速外围设备,如传感器、EEPROM等。
    • SMBus:是I2C的一个变种,专为系统管理任务而设计,如监测系统状态、配置系统参数等。
  2. 数据块大小

    • I2C:通常支持较小的数据块传输,数据块大小取决于具体实现,但通常较小。
    • SMBus:支持更大的数据块传输,最大可达32字节。
  3. 协议扩展

    • I2C:基本的I2C协议不包括对PEC(Packet Error Code)的支持,这是一种错误检测机制。
    • SMBus:包括对PEC的支持,可以提供更可靠的数据传输。
  4. 时钟频率

    • I2C:标准速率为100kHz,快速速率可达400kHz,高速(Hs-mode)可达3.4MHz。
    • SMBus:通常使用I2C的标准速率,但在某些情况下,也可以使用快速或高速模式。
  5. 总线仲裁

    • I2C:在多主设备的情况下,I2C协议通过总线仲裁机制来决定哪个主设备控制总线。
    • SMBus:通常不涉及多主设备的情况,但在多主设备环境中,也会使用I2C的仲裁机制。
  6. 信号线

    • 两者都使用两条信号线:SDA(数据线)和SCL(时钟线)。
  7. 应用领域

    • I2C:广泛应用于各种电子设备,特别是嵌入式系统。
    • SMBus:更多用于计算机系统,用于系统管理任务,如电源管理、温度监控等。
  8. 兼容性

    • 大多数支持SMBus的设备也支持I2C,因为SMBus是I2C的一个子集,但增加了一些特性。

尽管SMBus是I2C的一个变种,但在实际应用中,两者的界限可能并不明显,许多设备和总线都支持这两种协议。开发者在选择使用哪种协议时,通常会根据具体的应用需求和设备特性来决定。

使用SMBus(System Management Bus)协议有以下几个优点:

  1. 错误检测:SMBus 支持PEC(Packet Error Code)错误检测机制,这可以提高数据传输的可靠性。

  2. 数据块传输:SMBus 允许更大的数据块传输,最大可达32字节,这使得它更适合需要传输大量数据的应用。

  3. 系统管理功能:SMBus 设计用于系统管理任务,如监测系统状态、配置系统参数等,这使得它在计算机系统中特别有用。

  4. 兼容性:由于SMBus 是I2C的一个子集,大多数支持SMBus的设备也支持I2C,这提供了很好的向后兼容性。

  5. 标准化:SMBus 遵循严格的标准化协议,这有助于不同设备和系统之间的互操作性。

  6. 多主设备支持:虽然SMBus主要用于单主设备环境,但它也支持多主设备,通过总线仲裁机制来管理多个主设备对总线的访问。

  7. 简单性:SMBus 协议相对简单,易于在微控制器和其他设备上实现。

  8. 低速通信:SMBus 通常运行在较低的时钟频率(标准速率100kHz),适合低速通信需求,有助于降低功耗。

  9. 成本效益:由于SMBus 使用的硬件资源较少(仅需两条线),因此在成本敏感的应用中非常有吸引力。

  10. 广泛的应用:SMBus 被广泛应用于各种电子设备,包括计算机、服务器、移动设备等,用于实现各种系统管理功能。

  11. 软件支持:许多操作系统和硬件平台提供了对SMBus 的支持,这使得开发者可以更容易地集成和使用SMBus 协议。

使用SMBus 时,开发者应该考虑到其特定的应用场景和需求,以充分利用其优点。

4. I2C系统的重要结构体

使用一句话概括I2C传输:APP通过I2C Controller与I2C Device传输数据。

在Linux中:

  • 怎么表示I2C Controller

    • 一个芯片里可能有多个I2C Controller,比如第0个、第1个、……

    • 对于使用者,只要确定是第几个I2C Controller即可

    • 使用i2c_adapter表示一个I2C BUS,或称为I2C Controller

    • 里面有2个重要的成员:

      • nr:第几个I2C BUS(I2C Controller)

      • i2c_algorithm,里面有该I2C BUS的传输函数,用来收发I2C数据

    • i2c_adapter

  • i2c_algorithm

 

怎么表示I2C Device

  • 一个I2C Device,一定有设备地址

  • 它连接在哪个I2C Controller上,即对应的i2c_adapter是什么

  • 使用i2c_client来表示一个I2C Device

怎么表示要传输的数据

  • 在上面的i2c_algorithm结构体中可以看到要传输的数据被称为:i2c_msg

  • i2c_msg

  • i2c_msg中的flags用来表示传输方向:bit 0等于I2C_M_RD表示读,bit 0等于0表示写

  • 一个i2c_msg要么是读,要么是写

  • 举例:设备地址为0x50的EEPROM,要读取它里面存储地址为0x10的一个字节,应该构造几个i2c_msg?

    • 要构造2个i2c_msg

    • 第一个i2c_msg表示写操作,把要访问的存储地址0x10发给设备

    • 第二个i2c_msg表示读操作

    • 代码如下

使用一句话概括I2C传输:

  • APP通过I2C Controller与I2C Device传输数据

  • APP通过i2c_adapter与i2c_client传输i2c_msg

  • 内核函数i2c_transfer

    • i2c_msg里含有addr,所以这个函数里不需要i2c_client

 5. 编写APP访问设备

5.1 无需编写驱动直接访问设备_I2C-Tools介绍

APP访问硬件肯定是需要驱动程序的, 对于I2C设备,内核提供了驱动程序drivers/i2c/i2c-dev.c,通过它可以直接使用下面的I2C控制器驱动程序来访问I2C设备。 框架如下:

i2c-tools是一套好用的工具,也是一套示例代码。

使用一句话概括I2C传输:APP通过I2C Controller与I2C Device传输数据。 所以使用I2C-Tools时也需要指定:

  • 哪个I2C控制器(或称为I2C BUS、I2C Adapter)

  • 哪个I2C设备(设备地址)

  • 数据:读还是写、数据本身

5.1.1 交叉编译

  • 在Ubuntu设置交叉编译工具链

    • STM32MP157

 

修改I2C-Tools的Makefile指定交叉编译工具链

在Makefile中,“?=”在第一次设置变量时才会起效果,如果之前设置过该变量,则不会起效果。

  • 执行make即可

    • 执行make时,是动态链接,需要把libi2c.so也放到单板上

    • 想静态链接的话,执行:make USE_STATIC_LIB=1

5.1.2 用法 

i2cdetect:I2C检测

// 列出当前的I2C Adapter(或称为I2C Bus、I2C Controller)
i2cdetect -l

// 打印某个I2C Adapter的Functionalities, I2CBUS为0、1、2等整数
i2cdetect -F I2CBUS

// 看看有哪些I2C设备, I2CBUS为0、1、2等整数
i2cdetect -y -a I2CBUS

// 效果如下
# i2cdetect -l
i2c-1   i2c             STM32F7 I2C(0x40013000)                 I2C adapter
i2c-2   i2c             STM32F7 I2C(0x5c002000)                 I2C adapter
i2c-0   i2c             STM32F7 I2C(0x40012000)                 I2C adapter

# i2cdetect -F 0
Functionalities implemented by /dev/i2c-0:
I2C                              yes
SMBus Quick Command              yes
SMBus Send Byte                  yes
SMBus Receive Byte               yes
SMBus Write Byte                 yes
SMBus Read Byte                  yes
SMBus Write Word                 yes
SMBus Read Word                  yes
SMBus Process Call               yes
SMBus Block Write                yes
SMBus Block Read                 yes
SMBus Block Process Call         yes
SMBus PEC                        yes
I2C Block Write                  yes
I2C Block Read                   yes

// --表示没有该地址对应的设备, UU表示有该设备并且它已经有驱动程序,
// 数值表示有该设备但是没有对应的设备驱动
# i2cdetect -y -a 0  
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- 1e --
20: -- -- UU -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

这段代码是Linux系统中用于检测和显示I2C总线(I2C Bus)及其设备信息的命令行操作示例。以下是对这些命令和输出结果的解释:

  1. i2cdetect -l

    • 这个命令用于列出系统中所有的I2C适配器(I2C Adapter)。
    • 输出显示了三个I2C适配器:i2c-0、i2c-1、i2c-2,每个适配器都关联了一个特定的硬件地址和设备名称。
  2. i2cdetect -F I2CBUS

    • 这个命令用于显示指定I2C总线(I2CBUS)的功能。
    • 例如,i2cdetect -F 0 显示了与 /dev/i2c-0 相关的功能。输出包括:
      • I2C:支持基本的I2C通信。
      • SMBus Quick Command:支持SMBus快速命令。
      • SMBus Send/Receive Byte:支持发送和接收单个字节。
      • SMBus Write/Read Word:支持写入和读取两个字节。
      • SMBus Process Call:支持SMBus过程调用。
      • SMBus Block Write/Read:支持块写入和块读取。
      • SMBus Block Process Call:支持块过程调用。
      • SMBus PEC:支持SMBus包错误校验(Packet Error Checking)。
      • I2C Block Write/Read:支持I2C块写入和块读取。
  3. i2cdetect -y -a I2CBUS

    • 这个命令用于扫描指定I2C总线(I2CBUS)上的设备,并显示它们的地址。
    • 例如,i2cdetect -y -a 0 显示了在I2C总线0上的设备地址。
    • 输出结果中的每个字符代表一个设备地址:
      • -- 表示该地址没有设备。
      • UU 表示该地址有设备,并且设备已经有驱动程序。
      • 数值(如 1e)表示该地址有设备,但没有对应的设备驱动。
  4. 输出示例

    • 从输出结果中可以看到,I2C总线0上有几个设备地址:
      • 地址 1e(30)有一个设备,但没有驱动。
      • 地址 20(32)有一个设备,且设备已经有驱动。
      • 其他地址没有设备或设备没有驱动。

这些命令和输出结果对于系统管理员和开发者来说非常有用,因为它们可以帮助识别和调试系统中的I2C设备和适配器。

i2cget:I2C读 使用说明如下:

# i2cget
Usage: i2cget [-f] [-y] [-a] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
  I2CBUS is an integer or an I2C bus name
  ADDRESS is an integer (0x03 - 0x77, or 0x00 - 0x7f if -a is given)
  MODE is one of:
    b (read byte data, default)
    w (read word data)
    c (write byte/read byte)
    Append p for SMBus PEC

i2cget 是一个在Linux系统中使用的命令行工具,它用于从I2C总线上的设备读取数据。以下是对命令使用方式的解释:

  • i2cget:这是命令的基本名称。
  • [-f]:强制执行操作,即使I2C总线似乎忙碌。
  • [-y]:在检测到设备时不打印提示信息。
  • [-a]:允许访问扩展的地址范围(0x00-0x78)。
  • I2CBUS:指定要使用的I2C总线,可以是总线编号(如0、1、2等)或总线名称。
  • CHIP-ADDRESS:指定I2C设备的地址,通常是7位或10位地址的8位形式(即加上0x18的偏移量)。
  • DATA-ADDRESS:指定在设备内部要读取数据的寄存器或数据地址。
  • MODE:指定读取数据的模式,可以是以下几种之一:
    • b:以字节为单位读取数据(默认模式)。
    • w:以字(16位)为单位读取数据。
    • c:写入一个字节到指定的DATA-ADDRESS,然后读取一个字节的数据。
  • Append p for SMBus PEC:如果使用SMBus协议,并且需要启用包错误校验(Packet Error Checking,简称PEC),则在模式后面添加p
// 读一个字节: I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
i2cget -f -y I2CBUS CHIP-ADDRESS

// 读某个地址上的一个字节: 
//    I2CBUS为0、1、2等整数, 表示I2C Bus
//    CHIP-ADDRESS表示设备地址
//    DATA-ADDRESS: 芯片上寄存器地址
//    MODE:有2个取值, b-使用`SMBus Read Byte`先发出DATA-ADDRESS, 再读一个字节, 中间无P信号
//                   c-先write byte, 在read byte,中间有P信号 
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE  

// 读某个地址上的2个字节: 
//    I2CBUS为0、1、2等整数, 表示I2C Bus
//    CHIP-ADDRESS表示设备地址
//    DATA-ADDRESS: 芯片上寄存器地址
//    MODE:w-表示先发出DATA-ADDRESS,再读2个字节
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE  

示例用法:

使用i2cget可以帮助开发者或系统管理员读取I2C设备的状态或配置信息,这对于硬件调试和系统维护非常有用。

i2cset:I2C写 使用说明如下:

i2cset 是Linux系统中用于向I2C总线上的设备写入数据的命令行工具。以下是对命令使用方式的详细解释:

  • i2cset:命令的基本名称。
  • [-f]:强制执行操作,即使I2C总线似乎忙碌。
  • [-y]:在检测到设备时不打印提示信息。
  • [-m MASK]:对写入的数据应用掩码。这通常用于只修改寄存器的某些位。
  • [-r]:随机模式。这允许你写入随机数据,而不是指定的值。
  • [-a]:允许访问扩展的地址范围(0x00-0x78)。
  • I2CBUS:指定要使用的I2C总线,可以是总线编号(如0、1、2等)或总线名称。
  • CHIP-ADDRESS:指定I2C设备的地址,通常是7位或10位地址的8位形式(即加上0x18的偏移量)。
  • DATA-ADDRESS:指定在设备内部要写入数据的寄存器或数据地址。
  • [VALUE] ...:要写入的数据值。根据模式不同,可以是一个或多个值。
  • MODE:指定写入数据的模式,可以是以下几种之一:
    • c:写入一个字节,不指定值(通常用于发送一个命令)。
    • b:以字节为单位写入数据(默认模式)。
    • w:以字(16位)为单位写入数据。
    • i:I2C块数据写入。
    • s:SMBus块数据写入。
  • Append p for SMBus PEC:如果使用SMBus协议,并且需要启用包错误校验(Packet Error Checking,简称PEC),则在模式后面添加p

示例用法:

使用示例:

  // 写一个字节: I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
  //           DATA-ADDRESS就是要写的数据
  i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS
  
  // 给address写1个字节(address, value):
  //           I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
  //           DATA-ADDRESS: 8位芯片寄存器地址; 
  //           VALUE: 8位数值
  //           MODE: 可以省略,也可以写为b
  i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE [b]
  
  // 给address写2个字节(address, value):
  //           I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
  //           DATA-ADDRESS: 8位芯片寄存器地址; 
  //           VALUE: 16位数值
  //           MODE: w
  i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE w
  
  // SMBus Block Write:给address写N个字节的数据
  //   发送的数据有:address, N, value1, value2, ..., valueN
  //   跟`I2C Block Write`相比, 需要发送长度N
  //           I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
  //           DATA-ADDRESS: 8位芯片寄存器地址; 
  //           VALUE1~N: N个8位数值
  //           MODE: s
  i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE1 ... VALUEN s
  
  // I2C Block Write:给address写N个字节的数据
  //   发送的数据有:address, value1, value2, ..., valueN
  //   跟`SMBus Block Write`相比, 不需要发送长度N
  //           I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
  //           DATA-ADDRESS: 8位芯片寄存器地址; 
  //           VALUE1~N: N个8位数值
  //           MODE: i
  i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE1 ... VALUEN i

i2ctransfer:I2C传输(不是基于SMBus) 使用说明如下:

# i2ctransfer
Usage: i2ctransfer [-f] [-y] [-v] [-V] [-a] I2CBUS DESC [DATA] [DESC [DATA]]...
  I2CBUS is an integer or an I2C bus name
  DESC describes the transfer in the form: {r|w}LENGTH[@address]
    1) read/write-flag 2) LENGTH (range 0-65535) 3) I2C address (use last one if omitted)
  DATA are LENGTH bytes for a write message. They can be shortened by a suffix:
    = (keep value constant until LENGTH)
    + (increase value by 1 until LENGTH)
    - (decrease value by 1 until LENGTH)
    p (use pseudo random generator until LENGTH with value as seed)

Example (bus 0, read 8 byte at offset 0x64 from EEPROM at 0x50):
  # i2ctransfer 0 w1@0x50 0x64 r8
Example (same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0):
  # i2ctransfer 0 w17@0x50 0x42 0xff-

i2ctransfer 是Linux系统中用于执行I2C总线上的复合数据传输操作的命令行工具。它允许用户发送一系列的读写操作,这些操作可以是连续的,也可以是带有中间的暂停(例如,写入一个命令后立即读取响应)。以下是对命令使用方式的详细解释:

  • i2ctransfer:命令的基本名称。
  • [-f]:强制执行操作,即使I2C总线似乎忙碌。
  • [-y]:在检测到设备时不打印提示信息。
  • [-v]:详细模式,打印更多的调试信息。
  • [-V]:更详细的模式,打印更多的调试信息。
  • [-a]:允许访问扩展的地址范围(0x00-0x78)。
  • I2CBUS:指定要使用的I2C总线,可以是总线编号(如0、1、2等)或总线名称。
  • DESC:描述传输操作,格式为 {r|w}LENGTH[@address]
    • r:表示读操作。
    • w:表示写操作。
    • LENGTH:要读写的数据长度,范围是0到65535。
    • @address:可选的,指定I2C设备内部的寄存器地址。如果省略,则使用上一次的地址。
  • DATA:对于写操作,是LENGTH个字节的数据。数据可以通过后缀进行简化:
    • =:保持值不变,直到LENGTH。
    • +:值递增1,直到LENGTH。
    • -:值递减1,直到LENGTH。
    • p:使用伪随机生成器,以当前值为种子,直到LENGTH。

5.1.3 使用I2C-Tools操作传感器AP3216C

AP3216C是红外、光强、距离三合一的传感器,以读出光强、距离值为例,步骤如下:

  • 复位:往寄存器0写入0x4

  • 使能:往寄存器0写入0x3

  • 读光强:读寄存器0xC、0xD得到2字节的光强

  • 读距离:读寄存器0xE、0xF得到2字节的距离值

AP3216C的设备地址是0x1E,假设节在I2C BUS0上,操作命令如下:

  • 使用SMBus协议

  • 使用I2C协议

5.1.4 I2C-Tools的访问I2C设备的2种方式

I2C-Tools可以通过SMBus来访问I2C设备,也可以使用一般的I2C协议来访问I2C设备。 使用一句话概括I2C传输:APP通过I2C Controller与I2C Device传输数据。 在APP里,有这几个问题:

  • 怎么指定I2C控制器?

    • i2c-dev.c提供为每个I2C控制器(I2C Bus、I2C Adapter)都生成一个设备节点:/dev/i2c-0、/dev/i2c-1等待

    • open某个/dev/i2c-X节点,就是去访问该I2C控制器下的设备

  • 怎么指定I2C设备?

    • 通过ioctl指定I2C设备的地址

    • ioctl(file, I2C_SLAVE, address)

      • 如果该设备已经有了对应的设备驱动程序,则返回失败

    • ioctl(file, I2C_SLAVE_FORCE, address)

      • 如果该设备已经有了对应的设备驱动程序

      • 但是还是想通过i2c-dev驱动来访问它

      • 则使用这个ioctl来指定I2C设备地址

  • 怎么传输数据?

    • 两种方式

    • 一般的I2C方式:ioctl(file, I2C_RDWR, &rdwr)

    • SMBus方式:ioctl(file, I2C_SMBUS, &args)

5.1.5 源码框架分析

使用I2C方式 示例代码:i2ctransfer.c

使用SMBus方式示例代码:i2cget.c、i2cset.c

6. 编写APP直接访问EEPROM

EEPROM(Electrically Erasable Programmable Read-Only Memory,电可擦除可编程只读存储器)是一种非易失性存储技术,用于存储数据。与普通的ROM(只读存储器)不同,EEPROM可以在不移除芯片的情况下,通过电信号擦除和重新编程。以下是EEPROM的一些关键特性:

  1. 非易失性:EEPROM在断电后仍能保持其存储的数据,这使得它非常适合用于存储需要长期保存的信息。

  2. 电可擦除:EEPROM可以通过电子方式擦除,而不需要物理方法,这简化了数据更新的过程。

  3. 字节可寻址:EEPROM允许对单个字节进行读写操作,这与一些只能按块擦除和写入的存储器不同。

  4. 耐用性:EEPROM可以承受数十万次的擦写周期,这比闪存等其他类型的非易失性存储器要高。

  5. 速度:EEPROM的读写速度通常比闪存慢,但对于某些应用来说,其灵活性和耐用性更为重要。

  6. 应用:EEPROM广泛应用于各种电子设备中,用于存储系统设置、校准数据、设备标识信息等。

  7. 接口:EEPROM通常通过I2C、SPI等串行接口与微控制器或计算机系统通信。

  8. 容量:EEPROM的容量范围从几千字节到几百千字节不等,适合存储小量数据。

由于这些特性,EEPROM是嵌入式系统和便携式设备中存储关键数据的理想选择。

6.1 硬件连接

STM32MP157的I2C模块连接方法

6.2 AT24C02访问方法

AT24C02 是一种电可擦除可编程只读存储器(EEPROM)

6.2.1 设备地址

从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:

I2C模块的原理图

从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。

6.2.2 写数据

6.2.3 读数据

可以读1个字节,也可以连续读出多个字节。
连续读多个字节时,芯片内部的地址会自动累加。
当地址到达存储空间最后一个地址时,会从0开始。

6.3 实验测试步骤

使用I2C-Tools的函数编程

上机测试

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值