I2C(Inter-Integrated Circuit)是一种常用的串行总线,它可以连接多个设备,如传感器、EEPROM、LCD等,实现数据的传输和控制。I2C总线的优点是简单、灵活、节省引脚,但是它的速度相对较慢,适合于低速的设备通信。在嵌入式Linux系统中,I2C总线是一种重要的驱动子系统,它为上层应用提供了统一的接口和服务。本文将介绍I2C总线的基本原理和特点,以及Linux内核中I2C驱动子系统的架构和组成,帮助读者了解和掌握Linux驱动开发中I2C总线的相关知识。
硬件特性
1.2 I2C总线传输时序
1.3 I2C总线的信号状态
1、 空闲状态:SDA和SCL都是高电平;
2、 开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据;
3、 结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据;
4、 数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间;
5、 ACK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
读寄存器的标准流程为:
\1. Master发送I2C addr(7bit)和w操作1(1bit),等待ACK
\2. Slave发送ACK
\3. Master发送reg addr(8bit),等待ACK
\4. Slave发送ACK
\5. Master发起START
\6. Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
\7. Slave发送ACK
\8. Slave发送data(8bit),即寄存器里的值
\9. Master发送ACK
\10. 第8步和第9步可以重复多次,即顺序读多个寄存器
10bit地址
10bit的寻址扩展可能寻址的数目.有7bit地址和10bit地址的设备可以连接到相同的I2C总线上,而且7bit寻址和10bit寻址都可以用在所有的总线速度模式下.不过,10bit寻址用的不多.
10bit的从机地址由开始条件(S)或重复开始条件(Sr)后的两个字节组成.第一个字节的前7位是1111 0XX,XX是10bit地址的最高有效位的前两位.第一个字节的第8bit是读写位,决定传输方向.
尽管1111 XXX有8种可能的组合,然后只有1111 0XX这四种可以用于10bit寻址.剩下的1111 1XX这四种是为将来I2C扩展用的.
1.4 从设备地址
从datasheet发现,有三个IO口确定I2C从设备地址后三位,I2C总线从设备使用7位地址,最后一个为读写控制位。下图是TVP5158的原理图,我们可以计算出它的地址,在读取SII9135A的时候,手册上写得是0X60、0X68,这是8位,前7位有效,所以真实的I2C地址为0x30、0x34,第八位代表读写。
1.5 I2C读写方式
下面I2C写操作的步骤:
多字节写的时序
下面是I2C读操作的步骤:
多字节读的时序
具体可参考datasheet
2 I2C子系统
2.1 LinuxI2C子系统架构
在内核中已经提供I2C子系统,所以在做I2C驱动之前,就必须要熟悉该子系统。
2.2 三大组成部分
1、I2C核心(i2c-core)
I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(algorithm)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
2、I2C总线驱动(I2Cadapter/Algo driver)
I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力。
I2C总线驱动由i2c_adapter和i2c_algorithm来描述
3、I2C客户驱动程序(I2Cclient driver)
I2C客户驱动是对I2C从设备的软件实现,一个具体的I2C客户驱动包括两个部分:一部分是i2c_driver,用于将设备挂接于i2c总线;另一部分是设备本身的驱动。
I2C客户驱动程序由i2c_driver和i2c_client来描述
2.3 所有的I2C驱动代码位于drivers/i2c目录下
I2c-core.c 实现I2C核心的功能
I2c-dev.c 通用的从设备驱动
Chips 特定的I2C设备驱动
Busses I2C适配器的驱动
Algos 实现了一些I2C总线适配器的algorithm
2.4 I2C驱动编写的两种方法
从上面的图我们可以看到两种编写驱动方法,一种是利用系统提供的i2c-dev.c来实现一个i2c适配器的设备文件,然后通过在应用层操作I2C适配器来控制I2C设备;另一种是为I2C从设备独立编写一个设备驱动,不需要i2c-dev.c文件。
2.5 重要的数据结构
每次分析子系统免不了分析它的数据结构,OK我们先来分析一下。
I2c_adapter结构体代表I2C总线控制器
struct i2c_adapter {
struct module *owner;
unsigned int class; /*classes to allow probing for */
const struct i2c_algorithm*algo; /* 总线上数据传输的算法*/
void *algo_data; /* algorithm 数据 */
int timeout; /* injiffies */
int retries; /* 重试次数 */
struct device dev; /* the adapter device */
int nr;
char name[48]; /* 适配器名字 */
struct completion dev_released; /* 用于同步 */
};
I2c_algorithm对应一套通信方法
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, charread_write,
u8 command, int size, unioni2c_smbus_data *data);
u32 (*functionality) (structi2c_adapter *);
};
Functionality 函数用于返回algorithm所支持的通信协议,比如I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR等。
smbus_xfer 函数SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。
Master_xfer 函数实现总线上数据传输,与具体的适配器有关
Master_xfer 函数实现模板:
static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
{
......
for (i = 0; i if (msgs[i]->flags & I2C_M_RD) { /*读取*/
i2c_adapter_xxx_setaddr((msg->addr buf,msgs[i]->len); /*读取len长度的数据到buf中*/
} else {
i2c_adapter_xxx_setaddr(msg->addr buf, msgs[i]->len);
}
}
i2c_adapter_xxx_stop(); /*产生停止位*/
}
上面调用的函数用于完成适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的数据手册来实现。在内核源码中,针对不同的I2C适配器都有master_xfer的实现,风格与模板不尽相同,但是可以用该模板作为参考来看源代码,受益匪浅。
I2c_driver代表I2C从设备驱动
struct i2c_driver {
unsignedint class;
int(*attach_adapter)(struct i2c_adapter *) __deprecated; /*依附i2c适配器函数指针*/
int(*detach_adapter)(struct i2c_adapter *) __deprecated;/*脱离i2c适配器函数指针*/
int (*probe)(struct i2c_client*, const struct i2c_device_id *);
int (*remove)(struct i2c_client*);
int(*suspend)(struct i2c_client *, pm_message_t mesg);
int(*resume)(struct i2c_client *);
void(*alert)(struct i2c_client *, unsigned int data);
int(*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id*id_table; /* 该驱动所支持的设备ID表 */
/*Device detection callback for automatic device creation */
int(*detect)(struct i2c_client *, struct i2c_board_info *);
constunsigned short *address_list;
structlist_head clients;
};
在新内核中,attach_adapter和detach_adapter已经被probe和remove取代
Id_table用于i2c_driver和i2c_client的匹配
I2c_client代表I2C从设备
struct i2c_client {
unsigned short flags; /*I2C_CLIENT_TEN:使用10位从地址,I2C_CLIENT_PEC:使用SMBus包错误检测*/
unsignedshort addr; /* chipaddress - NOTE: 7bit */
charname[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* 依附的i2c_adapter */
struct i2c_driver *driver; /* 依附的i2c_driver*/
structdevice dev; /* the devicestructure */
intirq; /* irq issuedby device */
structlist_head detected;
};
2.6 核心层提供的接口函数
1、 增加/删除I2C适配器
int i2c_add_adapter(struct i2c_adapter *adapter)->static int i2c_register_adapter(struct i2c_adapter *adap)
int i2c_del_adapter(struct i2c_adapter *adap)
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] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\n", 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;
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\n", 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\n");
#endif
/* create pre-declared device nodes */
if (adap->nr nr);
mutex_unlock(&core_lock);
return res;
}
Device_register(&adap->dev) 向I2C总线注册一个adapter设备
i2c_scan_static_board_info(adap) 注册所有已知的i2c_client
2、 增加/删除I2C从设备驱动
int i2c_add_driver(struct i2c_driver *driver)->int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
/*
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter.
*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
mutex_lock(&core_lock);
bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
mutex_unlock(&core_lock);
return 0;
}
EXPORT_SYMBOL(i2c_register_driver);
向I2C总线注册一个i2c_driver
3、 i2c传输,发送和接收
int i2c_transfer(struct i2c_adapter adap, struct i2c_msgmsgs, int num)
int i2c_master_send(const struct i2c_client *client, constchar *buf, int count)
int i2c_master_recv(const struct i2c_client client, charbuf, int count)
/**
* i2c_transfer - execute a single or combined I2C message
* @adap: Handle to I2C bus
* @msgs: One or more messages to execute before STOP is issued to
* terminate the operation; each message begins with a START.
* @num: Number of messages to be executed.
*
* Returns negative errno, else the number of messages executed.
*
* Note that there is no requirement that each message be sent to
* the same slave address, although that is the most common model.
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
i2c_unlock_adapter(adap);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL(i2c_transfer);
最终会调用到适配器实现的master_xfer函数来完成数据传输工作
2.6 I2C子系统初始化
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
//////////////////////////////
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}
static void __exit i2c_exit(void)
{
i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
#endif
bus_unregister(&i2c_bus_type);
}
/* We must initialize early, because some subsystems register i2c drivers
* in subsys_initcall() code, but are linked (and initialized) before i2c.
*/
postcore_initcall(i2c_init);
module_exit(i2c_exit);
3 i2c-dev
3.1 概述
之前在介绍I2C子系统时,提到过使用i2c-dev.c文件在应用程序中实现我们的I2C从设备驱动。不过,它实现的是一个虚拟,临时的i2c_client,随着设备文件的打开而产生,并随着设备文件的关闭而撤销。I2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主题是”i2c_driver成员函数+字符设备驱动”。
3.2 i2c-dev.c源码分析
初始化模块
static int __init i2c_dev_init(void)
{
res= register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
i2c_dev_class= class_create(THIS_MODULE, "i2c-dev");
/*Keep track of adapters which will be added or removed later */
res= bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
/*绑定已经存在的适配器 */
i2c_for_each_dev(NULL,i2cdev_attach_adapter);
}
I2c-dev初始化函数主要做了注册名为”i2c”的字符设备文件和”i2c-dev”的类
i2cdev_read和i2cdev_write
I2c-dev.c中实现的i2cdev_read和i2cdev_write函数不具有太强的通用性,只适合下面这种单开始信号情况:
而不适合多开始信号的情况:
所以我们经常会使用i2cdev_ioctl函数的I2C_RDWR,在分析i2cdev_ioctl函数之前,我们需要了解一个结构体:
/* This is the structure as used in theI2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
structi2c_msg __user *msgs; /* pointersto i2c_msgs */
__u32nmsgs; /* number ofi2c_msgs */
};
Msgs 表示单个开始信号传递的数据;
Nmsgs 表示有多少个msgs,比如上图,单开始信号时,nmsgs等于1;多开始信号时,nmsgs等于2
struct i2c_msg {
__u16addr; /* slave address */
__u16flags; /* 默认为写入 */
#define I2C_M_TEN 0x0010 /*this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data,from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /*if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /*if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16len; /* msg length */
__u8*buf; /* pointer to msgdata */
};
使用i2cdev_ioctl函数的I2C_RDWR指令会调用到i2cdev_ioctl_rdrw函数:
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
unsignedlong arg)
{
structi2c_rdwr_ioctl_data rdwr_arg;
structi2c_msg *rdwr_pa;
u8__user **data_ptrs;
inti, res;
if(copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return-EFAULT;
if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return-EINVAL;
rdwr_pa= kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);
if(copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return-EFAULT;
}
res= i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
while(i-- > 0) {
if(res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if(copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
rdwr_pa[i].len))
res= -EFAULT;
}
kfree(rdwr_pa[i].buf);
}
}
咋一看,还挺复杂,其实主要做了一件事情:把用户空间传递过来的i2c_rdwr_ioctl_data数据进行错误检查,然后调用i2c_transfer函数与适配器进行通信,如果是接收数据,代码会将访问到的数据传回i2c_rdwr_ioctl_data的buf中。I2c_transfer最终会调用到I2C适配器具体实现的master_xfer函数来与硬件进行通信。
3.3 用户空间驱动模板
#include
#include
#include
#include
#include
#include
#include
#define OSA_SOK 0 ///
#define OSA_EFAIL -1 ///
#define I2C_DEFAULT_INST_ID (2)
#define I2C_TRANSFER_SIZE_MAX (254)
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef unsigned short Bool;
typedef unsigned long long Uint64; ///integer
typedef unsigned int Uint32; ///integer
typedef unsigned short Uint16; ///integer
typedef unsigned char Uint8; ///integer
#define OSA_ERROR(...) \
do \
{ \
fprintf(stderr, " ERROR (%s|%s|%d): ", __FILE__, __func__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
} \
while(0);
typedef struct {
int fd;
} OSA_I2cHndl;
#define OSA_I2C_DEBUG
static char xtod(char c) {
if (c>='0' && c'9') return c-'0';
if (c>='A' && c'F') return c-'A'+10;
if (c>='a' && c'f') return c-'a'+10;
return c=0; // not Hex digit
}
static int HextoDec(char *hex, int l)
{
if (*hex==0)
return(l);
return HextoDec(hex+1, l*16+xtod(*hex)); // hex+1?
}
int xstrtoi(char *hex) // hex string to integer
{
return HextoDec(hex,0);
}
int OSA_i2cOpen(OSA_I2cHndl *hndl, Uint8 instId)
{
char deviceName[20];
int status = 0;
sprintf(deviceName, "/dev/i2c-%d", instId);
printf("dev name = %s \n", deviceName);
hndl->fd = open(deviceName, O_RDWR);
if(hndl->fdreturn OSA_EFAIL;
return status;
}
int OSA_i2cRead8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *reg, Uint8 *value, Uint32 count)
{
int i, j;
int status = 0;
struct i2c_msg * msgs = NULL;
struct i2c_rdwr_ioctl_data data;
msgs = (struct i2c_msg *) malloc(sizeof(struct i2c_msg) * count * 2);
if(msgs==NULL)
{
printf(" I2C (0x%02x): Malloc ERROR during Read !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
return OSA_EFAIL;
}
for (i = 0, j = 0; i fd, I2C_RDWR, &data);
if(status #ifdef OSA_I2C_DEBUG
printf(" I2C (0x%02x): Read ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
#endif
}
else
status = OSA_SOK;
free(msgs);
return status;
}
int OSA_i2cWrite8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *reg, Uint8 *value, Uint32 count)
{
int i,j;
unsigned char * bufAddr;
int status = 0;
struct i2c_msg * msgs = NULL;
struct i2c_rdwr_ioctl_data data;
msgs = (struct i2c_msg *) malloc(sizeof(struct i2c_msg) * count);
if(msgs==NULL)
{
printf(" I2C (0x%02x): Malloc ERROR during Write !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
return OSA_EFAIL;
}
bufAddr = (unsigned char *) malloc(sizeof(unsigned char) * count * 2);
if(bufAddr == NULL)
{
free(msgs);
printf(" I2C (0x%02x): Malloc ERROR during Write !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
return OSA_EFAIL;
}
for (i = 0, j = 0; i fd, I2C_RDWR, &data);
if(status #ifdef OSA_I2C_DEBUG
printf(" I2C (0x%02x): Write ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
#endif
}
else
status = OSA_SOK;
free(msgs);
free(bufAddr);
return status;
}
int OSA_i2cRawWrite8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *value, Uint32 count)
{
int status = 0;
struct i2c_msg msgs[1];
struct i2c_rdwr_ioctl_data data;
msgs[0].addr = devAddr;
msgs[0].flags = 0;
msgs[0].len = count;
msgs[0].buf = value;
data.msgs = msgs;
data.nmsgs = 1;
status = ioctl(hndl->fd, I2C_RDWR, &data);
if(status #ifdef OSA_I2C_DEBUG
printf(" I2C (0x%02x): Raw Write ERROR !!! (count = %d)\n", devAddr, count);
#endif
}
else
status = OSA_SOK;
return status;
}
int OSA_i2cRawRead8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *value, Uint32 count)
{
int status = 0;
struct i2c_msg msgs[1];
struct i2c_rdwr_ioctl_data data;
msgs[0].addr = devAddr;
msgs[0].flags = I2C_M_RD;
msgs[0].len = count;
msgs[0].buf = value;
data.msgs = msgs;
data.nmsgs = 1;
status = ioctl(hndl->fd, I2C_RDWR, &data);
if(status #ifdef OSA_I2C_DEBUG
printf(" I2C (0x%02x): Raw Read ERROR !!! count = %d)\n", devAddr, count);
#endif
}
else
status = OSA_SOK;
return status;
}
int OSA_i2cClose(OSA_I2cHndl *hndl)
{
return close(hndl->fd);
}
int OSA_i2cTestShowUsage(char *str)
{
printf(" \n");
printf(" I2C Test Utility, \r\n");
printf(" Usage: %s -r|-w \r\n" , str);
printf(" \n");
return 0;
}
int main(int argc, char **argv)
{
OSA_I2cHndl i2cHndl;
Uint8 devAddr, numRegs;
Bool doRead;
int status, i;
static Uint8 regAddr[I2C_TRANSFER_SIZE_MAX], regValue8[I2C_TRANSFER_SIZE_MAX];
if(argcreturn -1;
}
if(strcmp(argv[1], "-r")==0)
doRead=TRUE;
else
if(strcmp(argv[1], "-w")==0)
doRead=FALSE;
else {
OSA_i2cTestShowUsage(argv[0]);
return -1;
}
devAddr = 0;
numRegs = 4;
regValue8[0] = 0;
regAddr[0] = 0;
if(argc>2)
devAddr = xstrtoi(argv[2]);
if(argc>3)
regAddr[0] = xstrtoi(argv[3]);
if(argc>4) {
if(doRead)
{
numRegs = atoi(argv[4]);
if(numRegs>I2C_TRANSFER_SIZE_MAX)
numRegs = I2C_TRANSFER_SIZE_MAX;
}
else {
regValue8[0] = xstrtoi(argv[4]);
}
}
if(devAddr==0) {
printf(" I2C: Invalid device address\n");
OSA_i2cTestShowUsage(argv[0]);
return -1;
}
status = OSA_i2cOpen(&i2cHndl, I2C_DEFAULT_INST_ID);
if(status != OSA_SOK) {
OSA_ERROR("OSA_i2cOpen( instId = %d )\n", I2C_DEFAULT_INST_ID);
return status;
}
if(status==OSA_SOK)
{
if(doRead) {
for(i=0; ifor(i=1; iif(status==OSA_SOK) {
for(i=0; iprintf(" I2C (0x%02x): 0x%02x = 0x%02x \n", devAddr, regAddr[i], regValue8[i] );
}
} else {
printf(" I2C (0x%02x): Read ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, regAddr[0], numRegs);
}
} else {
status = OSA_i2cWrite8(&i2cHndl, devAddr, regAddr, regValue8, 1);
if(status==OSA_SOK) {
status = OSA_i2cRead8(&i2cHndl, devAddr, regAddr, regValue8, 1);
}
if(status==OSA_SOK) {
printf(" I2C (0x%02x): 0x%02x = 0x%02x \n", devAddr, regAddr[0], regValue8[0] );
} else {
printf(" I2C (0x%02x): Write ERROR !!! (reg[0x%02x], value = 0x%02x\n", devAddr, regAddr[0], regValue8[0]);
}
}
OSA_i2cClose(&i2cHndl);
}
return 0;
}
本文通过一个简单的例子,展示了如何在Linux内核中编写I2C驱动程序和设备树节点,以及如何在用户空间使用I2C设备。我们首先介绍了I2C总线的基本原理和特点,包括物理层、链路层、协议层等。然后,我们介绍了Linux内核中I2C驱动子系统的架构和组成,包括I2C核心层、I2C适配器层、I2C算法层、I2C客户端层等。最后,我们介绍了如何编写I2C驱动程序和设备树节点,以及如何在用户空间使用I2C API来访问I2C设备,并展示了一个简单的测试程序。通过本文,我们希望能够帮助读者理解和掌握Linux驱动开发中I2C总线的相关知识,从而能够更加轻松地开发自己的I2C设备驱动程序。
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !