Linux应用开发笔记(七)IIC应用层开发及其框架


前言

  之前笔者在STM32和FPGA中已经多次讲述了IIC的基础知识,这里不在展开“扫盲”,感兴趣的朋友可以看一下往期笔记,此次仅仅带大家简单回顾并展开Linux下的IIC体系的学习。
在这里插入图片描述
在这里插入图片描述

一、IIC 第三方工具- i2c-tools

  i2c-tools是一个专门用于调试I2C的开源工具集。它提供了一组命令行工具,使得用户可以在应用层实现对I2C设备的扫描、读取、写入等操作,常用于对I2C设备进行交互、调试和测试。这些工具在Linux操作系统上广泛使用,并由于Android系统的内核也是Linux,因此也可以方便地移植到Android中使用。

//安装iic-tools
sudo apt install i2c-tools wget gcc -y

  i2c-tools工具集中的一些主要工具包括:

  • i2cdetect:用于扫描I2C总线并列出已连接的设备及其地址。
参数名称功能
-F i2cbus查询i2c总线的功能,参数i2cbus表示i2c总线
-V打印软件
-l检测当前系统有几组i2c总线
  • i2cget:用于从指定I2C设备的寄存器中读取数据。
    格式:i2cget [-f] [-y] i2cbus chip-address [data-address [mode]]
参数名称功能
-f强制访问设备
-y i2cbus关闭交互模式,使用该参数时,不会提示警告信息
-i2cbus指定i2c总线的编号
-chip-addressi2c设备地址
-data-address设备的寄存器的地址
-mode设备的寄存器的地址
-y关闭交互模式,使用该参数时,不会提示警告信息
  • i2cset:用于向指定I2C设备的寄存器中写入数据。
    格式:i2cset [-f] [-y] [-m mask] [-r] i2cbus chip-address data-address [value] … [mode]
  • i2cdump:用于以十六进制格式显示I2C设备的寄存器内容,即读取某个I2C设备所有寄存器的值。
    格式:i2cdump [-f] [-r first-last] [-y] i2cbus address [mode [bank [bankreg]]]
  • i2ctransfer:用于执行复杂的I2C传输操作,如一次性读写多个字节。

  这些工具在调试新的设备驱动时特别有用,因为它们允许用户直接修改和读取设备寄存器的值,从而观察结果现象。与传统的修改驱动代码、编译、下载、运行和查看结果的方法相比,使用i2c-tools可以大大节省时间,尤其是当每次只需要修改一个位(bit)时。

二、IIC框架

  和TTY框架类似,IIC的框架下也分为应用层,设备驱动和控制器驱动,其中APP只负责“单纯的用”,而设备驱动则主要编写地址格式、数据格式、判别标准及一些指令信号,最后由控制驱动需要判断向什么地址(设备/存储)发什么数据。
在这里插入图片描述
如果再细化一下,可以分为以下四层:

  • 第一层:提供i2c adapter的硬件驱动,探测,初始化i2c adapter(如申请i2c的io地址和中断号)驱动soc控制的i2c adapter在硬件上产生信号(stop,start,ack)以及处理I2c中断。覆盖图中硬件实现层。

  • 第二层:提供i2c adapter 的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层。

  • 第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备attch_adapter();detach_adapter();方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中driver驱动层。

  • 第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体的设备device的read(),write(),ioctl();等方法,赋值给file_operations,然后注册字符设备(多数是字符设备),覆盖图中的driver驱动层。

  其中第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。

三、IIC的几个重要结构体

  上一小节中提到了几个重要的结构体,这部分将展开讲述一下。

1. i2c_adapter

  i2c_adapter结构体用于表示一个I2C适配器,即I2C总线控制器,具体对应的结构体如下:

  • struct i2c_algorithm *algo:指向i2c_algorithm结构体的指针,该结构体包含了I2C适配器的算法和函数实现,如读写操作等。
  • struct device dev:表示与I2C适配器关联的设备。这个字段通常包含了设备的名称、设备树节点、父设备等信息。
  • const char *name:适配器的名称,通常用于调试和日志记录。
  • unsigned int class:适配器的类,用于区分不同类型的适配器。
  • unsigned long features:适配器支持的特性标志位,如中断处理、DMA等。
  • u32 quirks:适配器的特殊行为或限制,用于处理某些特定硬件的异常情况。

2. i2c_algorithm

  i2c_algorithm结构体包含了I2C适配器的算法和函数实现,以下是一些关键的字段:

  • int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num):I2C适配器的传输函数,用于执行与I2C设备之间的通信。
  • int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char *read_write, u8 command, int size, union i2c_smbus_data *data):SMBus(系统管理总线)的传输函数,用于执行SMBus协议下的通信。

3. i2c_client

  i2c_client结构体用于表示一个连接到I2C总线的设备,包含:

  • struct i2c_adapter *adapter:指向与设备关联的I2C适配器的指针。
  • unsigned short addr:设备的I2C地址。
  • char name[I2C_NAME_SIZE]:设备的名称。
  • struct device dev:表示与I2C设备关联的设备,包含了设备树节点、驱动等信息。
  • struct i2c_driver *driver:指向与设备关联的驱动程序的指针。

4. i2c_msg

  i2c_msg结构体用于描述在I2C总线上传输的单条消息,以下是一些关键的字段:

  • __u16 addr:目标设备的I2C地址。
  • __u16 flags:消息的标志位,如读/写操作、是否使用SMBus协议等。
  • __u16 len:消息的长度,即要发送或接收的数据字节数。
  • __u8 *buf:指向要发送或接收的数据的缓冲区的指针。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

四、应用层代码

  与FPGA和STM32类似,在应用层上我们主要编写读写函数,然后根据不同的外设进行组合,例如:OLED的初始化就是一堆write函数的组合,这里便不具体编写项目了。
  在编写应用程序时需要使用ioctl函数设置i2c相关配置,其函数原型如下

 //执行设备特定的操作
 #include <sys/ioctl.h>
 int ioctl(int fd, unsigned long request, ...);

  其中,request的参数可选项如下所示:

参数名称功能
I2C_RETRIES设置收不到ACK时的重试次数,默认为1
I2C_TIMEOUT设置超时时限的jiffies
I2C_SLAVE设置从机地址
I2C_SLAVE_FORCE强制设置从机地址
I2C_TENBIT选择地址长度0为7位地址,非0为10位
  • i2c_write( )函数
static int i2c_write(int fd, unsigned char addr,unsigned char reg,unsigned char val)
{
   int retries;
   unsigned char data[2];

   data[0] = reg;
   data[1] = val;

   //设置地址长度:0为7位地址
   ioctl(fd,I2C_TENBIT,0);

   //设置从机地址
   if (ioctl(fd,I2C_SLAVE,addr) < 0){
      printf("fail to set i2c device slave address!\n");
      close(fd);
      return -1;
   }

   //设置收不到ACK时的重试次数
   ioctl(fd,I2C_RETRIES,5);

   if (write(fd, data, 2) == 2){
      return 0;
   }
   else{
      return -1;
   }
}
  • i2c_read( )函数
static int i2c_read(int fd, uint8_t addr,uint8_t reg,uint8_t * val)
{
    int retries;

    //设置地址长度:0为7位地址
    ioctl(fd,I2C_TENBIT,0);

    //设置从机地址
    if (ioctl(fd,I2C_SLAVE,addr) < 0){
        printf("fail to set i2c device slave address!\n");
        close(fd);
        return -1;
    }

    //设置收不到ACK时的重试次数
    ioctl(fd,I2C_RETRIES,5);

    if (write(fd, &reg, 1) == 1){
        if (read(fd, val, 1) == 1){
                return 0;
        }
    }
    else{
        return -1;
    }
}

注:需要区分write和read中参数的不同,这里的最后一项分别为要写入的数据和拷贝数据的地址。val是一个uint8_t类型的变量,你通过取它的地址&val作为参数传递给i2c_read函数。如果读取成功,val将会包含从IIC设备读取的数据。

  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值