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++工程师##嵌入式工程师##嵌入式#