fuzidage
专注嵌入式、linux驱动 、arm裸机研究

导航

 

I2C控制器框架

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

 

1.重要结构体

1.1 i2c_adapter

i2c_adapter 表示一个 I2C BUS,或称为 I2C Controller,里面有 2 个重要的成员:

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

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

怎么表示 I2C Controller ,  一个芯片里可能有多个 I2C Controller,比如第 0 个、第 1 个、……

1.2 i2c_algorithm

 

1.3 I2C Device/I2C Client

一个 I2C Device,一定有设备地址, 那它连接在哪个 I2C Controller 上,即对应的 i2c_adapter 是什么。

使用 i2c_client 来表示一个 I2C Device。

 

1.4 i2c_msg

在上面的i2c_algorithm结构体中可以看到要传输的数据被称为: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 表示读操作,并且返回读出的数据

  如:

u8 data_addr = 0x10;
i8 data;
struct i2c_msg msgs[2];

msgs[0].addr = 0x50;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = &data_addr;
msgs[1].addr = 0x50;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 1;
msgs[1].buf = &data;

2. I2C-Tools移植

源代码地址:https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/

2.1 工具链配置

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueab ihf_sdk-buildroot/bin

2.2.编译

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

CC ?= gcc
AR ?= ar
STRIP ?= strip

改为(指定交叉编译工具链前缀, 去掉问号):

CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip

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

执行 make 时,是动态链接,需要把 libi2c.so 也放到单板上。 想静态链接的话,执行:make USE_STATIC_LIB=1

 

 

 

2.3.用法

2.3.1  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 

效果如下

 

 

2.3.2 i2cget:I2C 读(SMBus协议)

 

 

使用示例:

 

2.3.3 i2cset:I2C 写 (SMBus协议)

 

使用示例:

 

 

2.3.4 i2ctransfer:I2C 传输(不是基于 SMBus)

 

使用示例:

 

 

从i2c总线0,往0x1e设备地址,写2个字节,第一个字节写入寄存器地址0,第二个字节表示往寄存器0地址写入0x4

从i2c总线0,往0x1e设备地址,写2个字节,第一个字节写入寄存器地址0,第二个字节表示往寄存器0地址写入0x3

从i2c总线0,往0x1e设备地址,写1个字节,写入寄存器地址0xc表示要读0xc里面的值,再从0xc读2个字节

2.4 I2C-Tools 访问 I2C 设备的方式

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

       Open("dev/i2c-0");

       ioctl(file, I2C_SLAVE, address) ;

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

      ioctl(file, I2C_SLAVE_FORCE, address)

 如果该设备已经有了对应的设备驱动程序但是还是想通过 i2c-dev 驱 动来访问它,则使用这个 ioctl 来指定 I2C 设备地址。

数据传输:

①ioctl(file, I2C_RDWR, &rdwr);(使用I2C方式)

 

该结构体表示一个或者多个i2c_msg。

示例代码:i2ctransfer.c

以i2ctransfer -f -y 0 w1@0x1e 0xe r2为例:

流程如下:

 

构造i2c_rdwr_ioctl_data结构,要用2个i2c_msg结构,第一个表示写入0xe寄存器地址给设备,第二个表示要从0xe寄存器读出2个字节

②ioctl(file, I2C_SMBUS, &args) ;(使用SMBus方式)

示例代码:i2cget.c i2cset.c

 

③直接使用read()/write();

int gc2053_read_register(VI_PIPE ViPipe, int addr)
{
        int ret, data;
        CVI_U8 buf[8];
        CVI_U8 idx = 0;
 
        if (g_fd[ViPipe] < 0)
                return CVI_FAILURE;

        if (gc2053_addr_byte == 2)
                buf[idx++] = (addr >> 8) & 0xff;

        // add address byte 0
        buf[idx++] = addr & 0xff;

        ret = write(g_fd[ViPipe], buf, gc2053_addr_byte);
        if (ret < 0) {
                CVI_TRACE_SNS(CVI_DBG_ERR, "I2C_WRITE error!\n");
                return ret;
        }

        buf[0] = 0;
        buf[1] = 0;
        ret = read(g_fd[ViPipe], buf, gc2053_data_byte);
        if (ret < 0) {
                CVI_TRACE_SNS(CVI_DBG_ERR, "I2C_READ error!\n");
                return ret;
        }
 
        // pack read back data
        data = 0;
        if (gc2053_data_byte == 2) {
                data = buf[0] << 8;
                data += buf[1];
        } else {
                data = buf[0];
        }

        syslog(LOG_DEBUG, "i2c r 0x%x = 0x%x\n", addr, data);
        return data;
}

int gc2053_write_register(VI_PIPE ViPipe, int addr, int data)
{
        CVI_U8 idx = 0;
        int ret;
        CVI_U8 buf[8];

        if (g_fd[ViPipe] < 0)
                return CVI_SUCCESS;
 
        if (gc2053_addr_byte == 1) {
                buf[idx] = addr & 0xff;
                idx++;
        }

        if (gc2053_data_byte == 1) {
                buf[idx] = data & 0xff;
                idx++;
        }

        ret = write(g_fd[ViPipe], buf, gc2053_addr_byte + gc2053_data_byte);
        if (ret < 0) {
                CVI_TRACE_SNS(CVI_DBG_ERR, "I2C_WRITE error!\n");
                return CVI_FAILURE;
        }

        ret = read(g_fd[ViPipe], buf, gc2053_addr_byte + gc2053_data_byte);
        //syslog(LOG_DEBUG, "i2c w 0x%x 0x%x\n", addr, data);

        return CVI_SUCCESS;
}

 

logo
 
posted on 2022-12-23 18:35  fuzidage  阅读(509)  评论(0编辑  收藏  举报