良许Linux教程网 干货合集 Linux驱动开发入门:I2C总线的原理和实现

Linux驱动开发入门:I2C总线的原理和实现

I2C(Inter-Integrated Circuit)是一种常用的串行总线,它可以连接多个设备,如传感器、EEPROM、LCD等,实现数据的传输和控制。I2C总线的优点是简单、灵活、节省引脚,但是它的速度相对较慢,适合于低速的设备通信。在嵌入式Linux系统中,I2C总线是一种重要的驱动子系统,它为上层应用提供了统一的接口和服务。本文将介绍I2C总线的基本原理和特点,以及Linux内核中I2C驱动子系统的架构和组成,帮助读者了解和掌握Linux驱动开发中I2C总线的相关知识。

硬件特性

1.2 I2C总线传输时序

img
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 从设备地址

img
从datasheet发现,有三个IO口确定I2C从设备地址后三位,I2C总线从设备使用7位地址,最后一个为读写控制位。下图是TVP5158的原理图,我们可以计算出它的地址,在读取SII9135A的时候,手册上写得是0X60、0X68,这是8位,前7位有效,所以真实的I2C地址为0x30、0x34,第八位代表读写。

img
img

img
1.5 I2C读写方式

下面I2C写操作的步骤:

img
多字节写的时序

img
img

下面是I2C读操作的步骤:

img
多字节读的时序

img
具体可参考datasheet

2 I2C子系统

2.1 LinuxI2C子系统架构
在内核中已经提供I2C子系统,所以在做I2C驱动之前,就必须要熟悉该子系统。

img
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函数不具有太强的通用性,只适合下面这种单开始信号情况:

img
而不适合多开始信号的情况:

img
所以我们经常会使用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”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部