Linux嵌入式 驱动开发(IIO子系统)

首先来介绍两个概念:

ADC和DAC:ADC(Analog-to-digital converter):模拟数字转换器,将模拟数据量转换成数字数据量,DAC就刚好相反,数字转模拟,当使用的传感器本质是ADC或DAC器件的时候,可以优先考虑IIO驱动框架。

1.IIO子系统介绍

1.iio_dev

用来描述一个具体的IIO设备,常用API:

iio_dev申请与释放/iio_dev注册与注销

2.iio_info

这个是上面的那个结构体的成员之一,在编写IIO驱动的时候着重需要去实现的,因为用户空间对设备的具体操作最终都会反应到这个结构体上,其中有read_raw和write_raw函数,实现对寄存器的读写操作。

里面的变量:

indio_dev:需要读写的IIO设备

chan:需要读取的通道

val,val2:其实就是一个数据分成了整数和小数部分,val是整数部分,val2是小数部分,但是我们不能让内核向应用程序返回一个小数,所以这里有倍数可以选。

mask:掩码,这个掩码指定我们读取的是什么数据,比如 ICM20608 这样的传感器,他既有原

始的测量数据,比如 X,Y,Z 轴的陀螺仪、加速度计等,也有测量范围值,或者分辨率。

例:Linux 内核使用 IIO_CHAN_INFO_RAW 和 IIO_CHAN_INFO_SCALE 这两个宏来表示原始值以及分辨率,这两个宏就是掩码

3.iio_chan_spec(通道结构体)

上一小节里面的那个chan,就是这个结构体的变量。IIO的核心就是通道,比如一个 ADC 芯片支持 8 路采集,那

么这个 ADC 就有 8 个通道。我们所使用的ICM20608,是一个六轴传感器,可以输出三轴陀螺仪,三轴加速度计,一路温度,共7路数据。

里面的变量:

type通道类型,是一个枚举类型,里面东西很多,例子:其中就包括了上述三个:加速度类型,陀螺仪类型,温度类型

当成员变量 indexed 为 1时候, channel 为通道索引。(什么通道)

成员变量 modified 为 1 的时候, channel2通道修饰符,Linux内核给出了可用的通道修饰符。例子:比如我们使用了加速度类型,那么IIO_MOD_X, IIO_MOD_Y, IIO_MOD_Z就分别对应X Y Z三个轴。(第几个通道)

2.IIO驱动框架创建

IIO框架主要应用于ADC类传感器,这些传感器都是IIC或者SPI接口的。所以IIO驱动的基础是IIC或者SPI,再加上之前的regmap,淡化接口的概念,使得代码具有更高的模块性。

1.基础驱动框架

以SPI为例子

1.probe函数

2.remove函数

3.传统匹配方式ID列表/设备树匹配列表/SPI驱动结构体

4.驱动出口/入口函数

2.IIO设备申请和初始化

IIO设备的申请和初始化在probe函数里面完成,注销驱动的时候同样要在remove里注销掉IIO设备,释放内存

1.自定义设备结构体(包含:spi_device,regmap,regmap_config,mutex)

2.ICM20608扫描元素,3 轴加速度计、 3 轴陀螺仪、 1 路温度传感器, 1 路时间戳

3.通道数组

(其中包括1路温度通道,3路陀螺仪,3路加速度计),详细说一下每个通路需要包括的东西

{

通道类型/需要包括的掩码/通道需要包含的属性(例,温度需要:原始值/offset/分辨率)/扫描数据在缓冲区中的存储格式

}

4.读函数(xxx_read_raw(IIO设备,chan,val,val2,mask))具体功能实现下面再说

5.写函数(xxx_write_raw (同上))同上

6.用户空间写数据格式

比如我们在用户空间操作 sysfs 来设置传感器的分辨率,如果分辨率带小数,那么这个小数传递到内核空间应该扩大多少倍,此函数就是用来设置这个的

7.iio_info结构体的xxx_info,就相当于是之前那些驱动里面的ops

8.probe(申请iio_dev内存/获取自定义设备结构体的地址/初始化iio_dev成员变量/regmap设置/SPI相关设置/ICM20608初始化)

9.remove(regmap删除/注销IIO)

3.xxx_read(write)_raw,以及用户空间写数据格式的实现

接下来欣赏俄罗斯套娃,

1.read

先讲最外面的套娃,先理清整体的大框架:

1.icm20608_read_raw()

根据mask选取我们要读取什么数据

可选项:

1.读取寄存器原始数据,此时用icm20608_read_channel_data,详见2

2.读取分辨率,此时先选择是读取哪个的分辨率(陀螺仪/加速度计/温度)。然后用icm20608_read_onereg,见4

3.读取温度的offset,可直接返回对应数据

4.读取加速度计和陀螺仪的校准值,此时用icm20608_read_channel_data,详见2

2.icm20608_read_channel_data(iio_dev,iio_chan_spec,val)

根据通道的type,选择读取哪个寄存器的原始数据(温度/陀螺仪/加速度)

使用icm20608_sensor_show,详见3

3.icm20608_sensor_show(dev,要读取的寄存器的首地址,要读取的通道,保存要读取的值)

根据通道判断出我们要读取数据的寄存器的偏移量,然后利用

regmap_bulk_read(regmap,寄存器首地址+偏移量,数据缓冲区,要读取的寄存器的数量)

可以读取到数据

4.icm20608_read_onereg(dev,reg)

里面就是嵌套了regmap_read(regmap,reg,data)

读取原始数据流程1->2->3

读取分辨率流程1->4

读取校准数据流程1->2->3

2.write

1.icm20608_write_raw()

根据mask看设置什么东西

1.设置陀螺仪(详见2)和加速度计(详见3)的分辨率

2.设置陀螺仪和加速度计的校准值,详见4 icm20608_sensor_set

2.icm20608_write_gyro_scale(dev,val)

根据分辨率数组,如果给的val和数组里面的元素对应,就利用regmap_write写入

3.icm20608_write_accel_scale(dev,val)

根据分辨率数组,如果给的val和数组里面的元素对应,就利用regmap_write写入

4.icm20608_sensor_set()

类似于read里面的icm20608_sensor_show。

3.icm20608_write_raw_get_fmt

用户空间写数据格式(指定分辨率数据乘多少,在iio_info里面的val,val2里面看详细)

4.iio_info结构体的xxx_info

就相当于是之前那些驱动里面的ops,会把这些东西写到一起

3.测试程序编写

IIO 驱动框架提供了 sysfs 接口,因此加载成功以后我们可以在用户空间访问对应的 sysfs目录项,进入目录“/sys/bus/iio/devices/”目录里面,此目录下都是 IIO 框架设备,进入IIO框架设备后:

比如 in_accel_scale、 in_accel_x_calibias、 in_accel_x_raw 等,这些就是设置的通道、用cat去显示通道内容的话,就会调用read_raw函数。但是如果要连续不断的读取传感器数据的话,就不能用cat了,至此我们需要编写测试程序。

这些通道都是流文件,也叫做标准文件I/O流,这个在应用编程里面提到过,所以需要使用文件流操作函数。

比如fopen/fread等等,这个就在应用编程里面标准I/O里可以看到。

#C/C++##Linux##C++工程师##嵌入式工程师##嵌入式#
全部评论

相关推荐

头像
09-05 10:14
已编辑
门头沟学院 Java
赫一鸣:我昨天投的,今天就oc了,也没和我说要面试笔试啊?不说了这单要超时了
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
09-11 19:15
今天mt请我吃了一顿鱼(巨好吃),告诉我9.22号开始转正答辩,他到时候给我过,再加个面试就行,让我放宽心
纸飞机15445:绝对不要因为口头承诺放弃秋招,别人不会对你负责的。 而且其他offer 会也能影响转正的薪资。
点赞 评论 收藏
分享
3 4 评论
分享
牛客网
牛客企业服务