I2C使用参考

Version 1.0


1. 概述

I2C Group SCL SDA DEV
HW I2C group0 PAD_I2C0_SCL PAD_I2C0_SDA /dev/i2c-0
HW I2C group1 PAD_I2C1_SCL PAD_I2C1_SDA /dev/i2c-1,
HW I2C group2 PAD_I2C2_SCL PAD_I2C2_SDA /dev/i2c-2,
HW I2C group3 PAD_I2C3_SCL PAD_I2C3_SDA /dev/i2c-3,
HW I2C group4 PAD_I2C4_SCL PAD_I2C4_SDA /dev/i2c-4,
HW I2C group5 PAD_I2C5_SCL PAD_I2C5_SDA /dev/i2c-5,

提供6组HW I2C:

  • 第一组HW I2C 对应pad 是PAD_I2C0_SCL/PAD_I2C0_SDA,对应节点是/dev/i2c-0;

  • 第二组HW I2C 对应pad 是PAD_I2C1_SCL/PAD_I2C1_SDA,对应节点是/dev/i2c-1;

  • 第三组HW I2C 对应pad 是PAD_I2C2_SCL/PAD_I2C2_SDA,对应节点是/dev/i2c-2;

  • 第四组、第五组、第六组以此类推。

这是所有i2c全开的情况,如果实际情况中没有打开这么多i2c,要根据mhal_iic.h#define HAL_HWI2C_PORTS 6的定义和mercury6.dtsi中i2c device node的定义情况,而对应的节点一般看i2c-group = <0>;定义的值是多少。

图1-1 下面所有的描述都是以第一组HW I2C 为例,其他 I2C 请参照第一组的使用方法。


2. 读写I2C

通过标准的ms_i2c_xfer 函数来读写I2C,示例如下。

#include <stdio.h>  
#include <linux/types.h>  
#include <stdlib.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/ioctl.h>  
#include <errno.h>  
#include <assert.h>  
#include <string.h>  
#include <linux/i2c.h>  
#include <linux/i2c-dev.h>

#define FILE_NAME "/dev/i2c-0"

static int i2c_write(int fd,unsigned char slave_addr, unsigned char reg_addr, unsigned char value) 
{
    unsigned char outbuf[2];
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[1];

    messages[0].addr  = slave_addr;
    messages[0].flags = 0;
    messages[0].len   = sizeof(outbuf);
    messages[0].buf   = outbuf;

    /* The first byte indicates which register we‘ll write */
    outbuf[0] = reg_addr;

    /* 
    * The second byte indicates the value to write.  Note that for many
    * devices, we can write multiple, sequential registers at once by
    * simply making outbuf bigger.
    */
    outbuf[1] = value;

    /* Transfer the i2c packets to the kernel and verify it worked */
    packets.msgs  = messages;
    packets.nmsgs = 1;
    if(ioctl(fd, I2C_RDWR, &packets) < 0) 
    {
        perror("Unable to send data");
        return 1;
    }

    return 0;
}

static int i2c_read(int fd, unsigned char slave_addr, unsigned char reg_addr, unsigned char *value) 
{
    unsigned char inbuf, outbuf;
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[2];

    /*
    * In order to read a register, we first do a "dummy write" by writing
    * 0 bytes to the register we want to read from.  This is similar to
    * the packet in set_i2c_register, except it‘s 1 byte rather than 2.
    */
    outbuf = reg_addr;
    messages[0].addr  = slave_addr;
    messages[0].flags = 0;
    messages[0].len   = sizeof(outbuf);
    messages[0].buf   = &outbuf;

    /* The data will get returned in this structure */
    messages[1].addr  = slave_addr;
    messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
    messages[1].len   = sizeof(inbuf);
    messages[1].buf   = &inbuf;

    /* Send the request to the kernel and get the result back */
    packets.msgs      = messages;
    packets.nmsgs     = 2;
    if(ioctl(fd, I2C_RDWR, &packets) < 0) 
    {
        perror("Unable to send data");
        return 1;
    }
    *value = inbuf;

    return 0;
}

int main(int argc, char **argv)  
{  
    int fd;  
    unsigned int slave_addr=0, reg_addr=0, value = 0;

    if (argc < 4){  
        printf("Usage:\n%s r[w] start_addr reg_addr [value]\n",argv[0]);  
        return 0;  
    }

    fd = open(FILE_NAME, O_RDWR);      
    if (!fd)
    {  
        printf("can not open file %s\n", FILE_NAME);  
        return 0;  
    }

    sscanf(argv[2], "%x", &slave_addr);  
    sscanf(argv[3], "%x", &reg_addr);

    if(!strcmp(argv[1],"r")) 
    {
        i2c_read(fd, slave_addr, reg_addr, (unsigned char*)&value);
    }
    else if(argc>4&&!strcmp(argv[1],"w")) 
    {
        sscanf(argv[4], "%x", &value);  
        i2c_write(fd, slave_addr, reg_addr, value);
    }

    close(fd);
    return 0;
}