Linux之I2C驱动


本文将介绍Linux环境下的I2C驱动开发。文中内容涉及shell脚本,C语言和Linux内核驱动的基础知识。限于篇幅,本文只专注I2C的相关内容,且假设读者对以上基础知识已熟悉。

I2C总线仅使用两条信号线就可实现与设备之间的数据通信。其应用广泛且使用简单。但在Linux内核中却是相当复杂。其核心部分的代码量就有几千行。目的是要构成一个通用的,统一的I2C框架,以适用于各种I2C设备和应用。这个框架自底向上包括四个部分:I2C适配器,I2C核心,I2C通用设备驱动以及I2C用户设备驱动。

I2C适配器

I2C适配器实际上就是一个标准的,用platform_driver结构定义的内核驱动。它包含驱动名称,驱动匹配,以及probe和remove函数等(这些是Linux内核驱动的基础内容,这里就不解释了)。在probe函数中,芯片的I2C接口会被初始化并使能,然后最重要的是它会生成一个I2C适配器结构体(struct i2c_adapter),并调用I2C核心中的i2c_add_numbered_adapter或i2c_add_adapter函数,将这个适配器加载进I2C核心中。
由于各个芯片厂家的I2C总线控制接口不同,因而适配器种类也非常多。Linux内核drivers/i2c/busses目录下的上百个文件就是各种芯片的I2C适配器。I2C适配器通常是由芯片厂家提供的,无需开发者自行开发。
尽管I2C适配器很多,但它们都会生成一个共通的I2C适配器结构体(struct i2c_adapter)作为对上层的 接口。这个结构体定义在include/linux/i2c.h文件中,其中有一项非常重要。它是一个指针,指向一个定义在相同头文件中的I2C算法结构体(struct i2c_algorithm)。该结构体中的master_xfer函数具体实现了对芯片的I2C硬件控制,即I2C的读写操作。这个函数还为上层提供了一个统一的指针接口,指向I2C传参结构体(struct i2c_msg),它定义在include/uapi/linux/i2c.h文件中,是一个应用层也会使用到的结构体。
好了,关于I2C适配器的重要内容都讲到了。归纳起来就是一个内核驱动和三个数据结构体:

  • I2C适配器内核驱动:负责初始化和使能I2C,然后生成I2C适配器结构体,并加载进I2C核心。
  • I2C适配器结构体(struct i2c_adapter):提供统一的I2C数据接口。
  • I2C算法结构体(struct i2c_algorithm):提供I2C的读写操作接口。
  • I2C传参结构体(struct i2c_msg):提供I2C收发通信的传参接口。

芯片的I2C接口

上面讲了I2C适配器是内核驱动,用于驱动各类芯片的I2C硬件总线。与之对应,还要有被驱动的设备,即各芯片还要定义I2C设备的各种硬件信息,比如I2C的控制和状态寄存器的地址,中断,时钟,IO口等。这些内容通常都在设备树(.dtsi或.dts)文件中定义。其形式如:

 i2c2: i2c@21a4000 {
   
     #address-cells = <1>;
     #size-cells = <0>;
     compatible = "fsl,imx6sx-i2c", "fsl,imx21-i2c";
     reg = <0x021a4000 0x4000>;
     interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
     clocks = <&clks IMX6SX_CLK_I2C2>;
     pinctrl-names = "default";
     pinctrl-0 = <&pinctrl_i2c2>;
     clock-frequency = <100000>;
     status = "okay";
};

该I2C设备通过compatible字段与I2C适配器匹配,然后调用probe函数加载进I2C核心。

I2C核心

I2C核心提供了I2C设备的统一管理,并提供了大量的公共函数供内核驱动调用。其代码主要在drivers/i2c/i2c-core-base.c文件中。通过I2C核心提供的函数可以查找到所有加载的I2C适配器。同时也对I2C用户设备和驱动进行统一管理。这在后面的I2C用户设备驱动中会讲到。尽管I2C核心非常重要,代码量也很大,但它涉及I2C内核驱动的内部结构,限于篇幅这里就不对其深入解析了。

I2C通用设备驱动

I2C通用设备驱动不针对任何一种具体的I2C外围硬件设备。它只为应用层提供访问芯片I2C硬件的能力,进而利用其功能与外围设备通信。
I2C通用设备驱动的代码在drivers/i2c/i2c-dev.c文件中。它通过I2C适配器和I2C核心为所有在设备树中定义的I2C设备建立一个设备入口文件。设备文件名是/dev/i2c-N,其中N是一个正整数,当芯片有多个I2C时,N代表各个I2C的序号。通过这个设备文件,用户可以直接访问芯片的I2C。

访问I2C通用设备文件的方法之一

Linux系统中的i2c-tools软件包为访问I2C设备文件提供了极大的方便。用户可以使用命令行与I2C外围设备通信。下面简单介绍这些命令的使用方法。

  • 列出I2C设备接口在这里插入图片描述
    本例使用的是意大利SECO公司的UDOO-NEO开发板(见https://www.udoo.org/udoo-neo/),该开发板使用的是NXP i.MX 6SoloX芯片。此芯片最多支持4个I2C。上面的结果显示只有3个I2C在设备树中被使能。下面再用i2cdetect扫描一下各个接口,看看有哪些设备地址挂接在这些I2C上。
  • 扫描I2C设备地址
    在这里插入图片描述
    由于已知i2c-1没有挂接设备,这里只扫描了i2c-0和i2c-3。I2c-0在地址0x08有一个设备,它连接到了电源管理芯片上,UU表示这个设备已被某个驱动所占用了。I2c-2上挂接有两个设备,地址分别是0x1e和0x20,且还没有被占用。
  • 读写I2C设备
    这个开发板上0x20地址挂接的是FXAS21002陀螺仪。这个芯片的寄存器地址是从0x00至0x15。其中0x0C寄存器是陀螺仪设备ID,其值固定为0xD7。下面用i2cdump命令查看一下。
    在这里插入图片描述
    也可以使用i2cget命令读某个指定地址的值。
    在这里插入图片描述
    当然也可以使用i2cset命令设置某个指定地址的值。这里就不示范了。
    另外还有一个命令i2ctransfer可以支持I2C的多字节连续读写,读写时寄存器地址自动加1或循环至某个指定位置。比如FXAS21002陀螺仪就支持从0x00至0x06地址的连续循环读操作。下面的命令利用这个功能反复读0x00至0x06地址的内容四遍。
    在这里插入图片描述

有了i2c-tools软件包的支持,就可以编写一个简单的陀螺仪采样监控脚本了。

#!/bin/sh
 
GYRO_PORT=3
GYRO_ADDR=0x20
 
gyro_start() {
   
    # 启动陀螺仪采样
    i2cset -y $GYRO_PORT $GYRO_ADDR 0x13 0x1A
    # 循环读取陀螺仪的状态和采样值,直到用户按CTRL-C键
while true; do
        # 读取陀螺仪采样状态和数据
        OUT=`i2ctransfer -y $GYRO_PORT r7@$GYRO_ADDR`
        STS=$(echo $OUT | cut -d' ' -f 1)
        # 判断采样状态OK后打印出采样值
        [ "$STS" != "0x00" ] && echo $OUT
    done
}
 
gyro_stop() {
   
    # 停止陀螺仪采样
    i2cset -y $GYRO_PORT $GYRO_ADDR 0x13 0
    exit 0
}
 
trap "gyro_stop" 2
trap "gyro_stop" 3
gyro_start

i2c-tools软件包的使用虽然方便,但也有局限。比如上面这个脚本运行速度很慢,并且对采样值的加工也不方便。那么如果使用编程语言代替脚本就更为有利了。

访问I2C通用设备文件的方法之二

下面是用C语言编写的陀螺仪采样监控程序,其功能与上面脚本基本相同,只是提高了采样速率,并且采样输出改进为实际陀螺仪的采样浮点值。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
 
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值