i2c子系统—i2c驱动框架
  FWgI4FhDiAvJ 2023年11月02日 21 0



I2C子系统

  • 9.1 i2c物理总线
  • 9.2 i2c基本通信协议
  • 9.2.1 i2c总线特点
  • 9.2.2 起始信号与停止信号
  • 9.2.3 数据格式与应答信号
  • 9.2.4 主机与从机通信
  • 9.3 i2c驱动框架
  • 9.3 驱动框架的组成部分
  • 9.4 I2C设备核心驱动函数



9.1 i2c物理总线

i2c子系统—i2c驱动框架_#define


如上图所示,i2c支持一主多从,各设备地址独立,标准模式传输速率为100kbit/s,快速模式为400kbit/s。总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都空 闲,都输出高阻态时,由上拉电阻把总线拉成高电平。

i2c物理总线是由两根双向信号线组成的,一根是数据线SDA(传输具体数据),另一根是时钟线SCL(数据收发同步)。i2c总线由主设备控制,主设备作为主机,可以控制各个从设备,这些从设备包括但不限于IO扩展器、各种传感器、EEPROM、ADC/DAC等。i2c总线采用漏极开路的设计,只会拉低总线,或“释放”它并让上拉电阻将其拉高。在空闲状态下,两条信号线经由上拉电阻后同时处于高电平,当想要传输低电平的逻辑时,会激活下拉场效应管,实现总线的拉低。

9.2 i2c基本通信协议

9.2.1 i2c总线特点

  • 只需要SDA.SCL两条总线,这样芯片硬件设计、布线非常简单;
  • 它是—种同步、串行、半双工通信方式,没有严格的波特率要求;
  • 它是一种主从模式的通信协议,通信开始总是由主设备发起,从设备被动响应请求;
  • 每个从设各都有一个独一无二的从设备地址用来表示该器件,同一总线上要仅持该地址唯—;
  • l2C是真正的多主设各总线,可提供仲裁和冲突检测;

9.2.2 起始信号与停止信号

当SCL线为高电平时,SDA线由高到低的下降沿,为传输开始标志(S)。直到主设备发出结束信号§, 否则总线状态一直为忙。结束标志§为,当SCL线为高电平时,SDA线由低到高的上升沿。

i2c子系统—i2c驱动框架_驱动开发_02

  • 开始信号(S),SCL为高,SDA由高变低(SDA拉低)
  • 结束信号§,SCL为高,SDA由低变高(SDA拉高)
  • 响应信号(ACK):接收器在收到8位数据后,在第9

9.2.3 数据格式与应答信号

i2c的数据字节定义为8-bits长度,对每次传送的总字节数量没有限制,但对每一次传输必须伴有一个应答(ACK)信号, 其时钟由主设备提供,而真正的应答信号由从设备发出,在时钟为高时,通过拉低并保持SDA的值来实现。如果从设备忙, 它可以使 SCL保持在低电平,这会强制使主设备进入等待状态。当从设备空闲后,并且释放时钟线,原来的数据传输才会继续。

9.2.4 主机与从机通信

i2C总线有7位寻址和10位寻址两种模式,通常我们使用7bit模式。这就使得/2C总线上理论上寻址模式的最大节点数为128(2"7)个或1024(2410)个。但I2C有16个保留从机地址,所有i2c最多有128-16=112个从机地址

开始标志(S)发出后,主设备会传送一个7位的Slave地址,并且后面跟着一个第8位为读写方向位,其中0表示写,1表示读,也称为Read/Write位。 R/W位表示主设备是在接受从设备的数据还是在向其写数据。然后,主设备释放SDA线,等待从设备的应答信号(ACK)。 每个字节的传输都要跟随有一个应答位。应答产生时,从设备将SDA线拉低并且在SCL为高电平时保持低。 数据传输总是以停止标志(P)结束,然后释放通信线路。 然而,主设备也可以产生重复的开始信号去操作另一台从设备, 而不发出结束标志。综上可知,所有的SDA信号变化都要在SCL时钟为低电平时进行,除了开始和结束标志。

9.3 i2c驱动框架

i2c子系统(I2C总线设备的驱动框架),目的是让驱动开发者可以在内核中方便地添加自己的I2C设备的驱动程序,从而可以更容易地在linux下驱动I2C设备。

我们知道一个i2c上可以挂在多个i2c设备,例如sht20、i2c接口的OLED显示屏、摄像头(摄像头通过i2c接口发送控制信息)等, 这些设备共用一个i2c,这个i2c的驱动我们称为i2c总线驱动。而对应具体的设备,例如sht20的驱动就是i2c设备驱动。 这样我们要使用sht20就需要拥有“两个驱动”一个是i2c总线驱动和sht20设备驱动。

i2c总线驱动由芯片厂商提供(驱动复杂,官方提供了经过测试的驱动,我们直接用)。
sht20设备驱动可以从sht20芯片厂家那里获得,也可以我们手动编写。

i2c子系统—i2c驱动框架_c语言_03


i2c总线包括i2c设备(i2c_client)和i2c驱动(i2c_driver), 当我们向linux中注册设备或驱动的时候,按照i2c总线匹配规则进行配对,配对成功,则可以通过i2c_driver中.prob函数创建具体的设备驱动。 在现代linux中,i2c设备不再需要手动创建,而是使用设备树机制引入,设备树节点是与paltform总线相配合使用的。 所以需先对i2c总线包装一层paltform总线,当设备树节点转换为平台总线设备时,我们在进一步将其转换为i2c设备,注册到i2c总线中。

设备驱动创建成功,我们还需要实现设备的文件操作接口(file_operations),file_operations中会使用到内核中i2c核心函数(i2c系统已经实现的函数,专门开放给驱动工程师使用)。 使用这些函数会涉及到i2c适配器,也就是i2c控制器。由于ic2控制器有不同的配置,所有linux将每一个i2c控制器抽象成i2c适配器对象。 这个对象中存在一个很重要的成员变量——Algorithm,Algorithm中存在一系列函数指针,这些函数指针指向真正硬件操作代码。

9.3 驱动框架的组成部分

  • I2C设备驱动层(I2C_client,I2C_driver)
    和具体I2C接口的外设相关,每种外设都有自己的专属I2C驱动代码。
    包括两部分:I2C从设备的注册、I2C设备驱动的注册。
  • I2C核心层(I2C_core)
    I2C核心层提供了I2C总线驱动和I2C设备驱动的注册、匹配与注销方法,I2C通信方法,上层中与具体硬件无关的代码,以及探测设备检测设备地址的上层代码等。换言之,管理I2C驱动和I2C设备的注册、匹配,实现I2C的通信方法,是对I2C通信的抽象框架,与具体硬件无关。
  • I2C适配器层(I2C_adapter, I2C_algorithm)
    这里说的I2C适配器,也就是I2C主设备,对应着Soc的I2C控制器,把I2C控制器看做一个设备,实现I2C控制器的驱动代码,和具体的Soc相关。
    I2C总线驱动是I2C适配器的软件实现,或者说是SoC中内置的I2C控制器的软件抽象,可以将其理解为I2C主设备,它提供了I2C适配器与从设备间数据通信的功能。
    I2C总线驱动用 I2C_adapter 和 I2C_algorithm 来描述I2C适配器。

9.4 I2C设备核心驱动函数

(1)i2c_add_driver()宏,注册一个i2c驱动 (内核源码/include/linux/i2c.h)

#define i2c_add_driver(driver)

(2)i2c_register_driver()函数,注册一个i2c驱动 (内核源码/drivers/i2c/i2c-core-base.c)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver) 
/**
*		owner: 一般为 THIS_MODULE    
*		driver: 要注册的 i2c_driver
**/

(3)i2c_del_driver()函数,将前面注册的 i2c_driver 从 Linux 内核中注销掉

void i2c_del_driver(struct i2c_driver *driver)   
/**
*		driver:要注销的 i2c_driver
**/

(4)i2c_transfer()函数,最终就是调用我们前面的i2c_imx_xfer()函数来实现数据传输。
收发i2c消息

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 
/**
*		adap : struct i2c_adapter 结构体,收发消息所使用的i2c适配器,i2c_client 会保存其对应的 i2c_adapter
*		msgs: struct i2c_msg 结构体,i2c要发送的一个或多个消息
*		num : 消息数量,也就是msgs的数量
**/

I2C 进行数据收发 说白了就是消息的传递,Linux 内核使用 i2c_msg 结构体来描述一个消息
使用i2c_transfer 函数之前需要先构建好i2c_msg

struct i2c_msg { 
	
	        __u16 addr; /* 从机地址 */ 
	        __u16 flags; /* 标志 */ 
	        #define I2C_M_TEN 0x0010
	        #define I2C_M_RD 0x0001 
	        #define I2C_M_STOP 0x8000
	        #define I2C_M_NOSTART 0x4000
	
	        #define I2C_M_REV_DIR_ADDR 0x2000
	        #define I2C_M_IGNORE_NAK 0x1000
	        #define I2C_M_NO_RD_ACK 0x0800 
	        #define I2C_M_RECV_LEN 0x0400 
	        __u16 len; /* 消息(本 msg)长度 */ 
	        __u8 *buf; /* 消息数据 */ 
};

(5)用于数据传输的其他API
I2C 数据发送函数为 i2c_master_send,函数原型如下:

int i2c_master_send(const struct i2c_client *client, const char *buf, int  count)   
/**
*		client:I2C 设备对应的 i2c_client。
*		buf:要发送的数据。
*		count:要发送的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无 符号 16 位)类型的数据。
**/

I2C 数据接收函数为 i2c_master_recv,函数原型如下:

int i2c_master_recv(const struct i2c_client *client, char *buf, int   count)   
/**
*		client:I2C 设备对应的 i2c_client。
*		buf:要接收的数据。
*		count:要接收的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无 符号 16 位)类型的数据。
**/


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
FWgI4FhDiAvJ