Linux-ADC驱动实验

上一章我们讲解了如何给 ICM20608 编写 IIO 驱动,ICM20608 本质就是 ADC,因此纯粹的 ADC 驱动也是 IIO 驱动框架的。本章我们就来学习一下如何使用 I.MX6ULL 内部的 ADC,并且在学习巩固一下 IIO 驱动。


ADC****驱动源码简析

设备树下的ADC节点

I.MX6ULL 有 2 个 ADC,但是对应一个 ADC 控制器。本章实验我们使用 GPIO1_IO01 这个引脚来完成 ADC 实验,而 GPIO1_IO01 就是 ADC1 的通道 1 引脚,所以这里我们就以 ADC1为例进行讲解,imx6ull.dtsi 文件中的 adc1 节点信息如下:

第 2 行,compatible 属性值为"fsl,imx6ul-adc"和"fsl,vf610-adc",所以在整个 Linux 源码里面搜索这个两个字符串即可找到 I.MX6ULL 的 ADC 驱动核心文件,这个文件就是 drivers/iio/adc/vf610_adc.c。

关 于 I.MX6ULL 的 ADC 节点更为详细的信息请参考对应的绑定文档: Documentation/devicetree/bindings/iio/adc/vf610-adc.txt。接下来我们简单分析一下绑定文档,后 面我们需要根据绑定文档修改设备树,使能 ADC 对应的通道。
ADC 相关属性有:

  • compatible**:**兼容性属性,必须的,可以设置为"fsl,vf610-adc"。

  • reg:ADC 控制器寄存器信息。

- interrupts**:**中断属性,ADC1 和 ADC2 各对应一个中断信息。

- clocks**:**时钟属性。

- clock-names**:**时钟名字,可选"adc"。

- vref-supply**:**此属性对应 vref 参考电压句柄。

可以看出,ADC 节点的属性还是比较少的。
ADC****驱动源码分析

I.MX6ULL 的 ADC 驱动文件就一个 vf610_adc.c,vf610_adc.c 主体框架是 platform,配合 IIO 驱动框架实现 ADC 驱动。

1**、vf610_adc结构体**

NXP 自己将 ADC 外设抽象成了结构体 vf610_adc,vf610_adc 就相当于自定义的设备结构体。vf610_adc 结构体贯穿于整个驱动文件,结构体内容如下:

vf610_adc_probe****函数
接下来看一下 vf610_adc_probe 函数,内容如下(有省略):


第 10 行,调用 devm_iio_device_alloc 函数申请 iio_dev,这里也连 vf610_adc 内存一起申请 了。

第 16 行,调用 iio_priv 函数从 iio_dev 里面得到 vf610_adc 首地址。

第 24 行,调用 platform_get_irq 获取中断号。

第 30 行,调用 devm_request_irq 函数申请中断,中断服务函数为 vf610_adc_isr。

第 64~70 行,初始化 iio_dev,重点是第 67 行的 vf610_adc_iio_info,因为用户空间读取 ADC数据最终就是由 vf610_adc_iio_info 来完成的。

第 79 行,调用 vf610_adc_cfg_init 函数完成 ADC 的配置初始化。

第 80 行,调用 vf610_adc_hw_init 函数来初始化 ADC 硬件。

第 82 行,调用 iio_device_register 函数向内核注册 iio_dev。

可以看出 vf610_adc_probe 函数核心就是初始化 ADC 控制器,然后建立 ADC 的 IIO 驱动框架。

3**、vf610_adc_iio_info结构体**

vf610_adc_iio_info 结构体内容如下所示:


我们重点来看一下第 3 行的 vf610_read_raw 函数,因为此函数才是最终向用户空间发送ADC 原始数据的,函数内容如下:

第 12~49 行,读取 ADC 原始数据值,第 32 行 type 值为 IIO_VOLTAGE,也就是读取电压值。这里直接读取 vf610_adc 的 value 成员变量得到 ADC 转换结果,并没有看到读取 ADC 数据寄存器的过程。这是因为真正的 ADC 数据读取过程是在中断服务函数 vf610_adc_isr 中完成。
第 51~54 行,返回 ADC 对应的分辨率。

4**、vf610_adc_isr函数**

函数内容如下:


可以看出,vf610_adc_isr 函数很简单,重点就是在第 8 行通过调用 vf610_adc_read_data 函数来读取 ADC 原始值,然后将 ADC 的原始值保存在 vf610_adc 的 value 成员变量里面。


ADC****驱动编写

修改设备树

ADC 驱动 NXP 已经编写好了,我们只需要修改设备树即可。首先在 imx6ull-alientek-emmc.dts 文件中添加 ADC 使用的 GPIO1_IO01 引脚配置信息:


接下来在 imx6ull-alientek-emmc.dts 文件中的在 regulators 节点下添加参考电源子节点,内容如下:


最后在 imx6ull-alientek-emmc.dts 文件中向 adc1 节点追加一些内容,内容如下:


使能ADC驱动

使能内核里面自带的 I.MX6ULL ADC 驱动,打开 Linux 内核配置界面,配置路径如下:


如图 76.4.2.1 所示:


编写测试****APP

编译修改后的设备树,然后使用新的设备树启动系统。进入/sys/bus/iio/devices 目录下,此目录下就有 ADC 对应的 iio 设备:iio:deviceX,本章例程如图 76.4.3.1 所示:


图 76.4.3.1 中的"iio:device0"就是 ADC 设备,因为此时并没有加载其他的 IIO 设备驱动,只有一个 ADC。如果大家还加载了其他 IIO 设备驱动,那么就要依次进入 iio 设备目录,查看一下都对应的是什么设备。

进入"iio:device0"目录,内容如图 76.4.3.2 所示:


标准的 IIO 设备文件目录,我们只关心三个文件:

in_voltage1_raw**:**ADC1 通道 1 原始值文件。

in_voltage_scale:ADC1 比例文件(分辨率),单位为 mV。实际电压值(mV)=in_voltage1_raw* in_voltage_scale

我的开发板此时 in_voltage1_raw 和 in_voltage_scale 这两个文件内容如下:


经过计算,图 76.4.3.3 中实际电压:991*0.805664062≈798.4mV,也就是 0.7984V。

接下来就编写测试 APP,新建 adcApp.c 文件,然后在里面输入如下所示内容:


adcApp.c 就是在上一章的应用程序上修改而来的,由于只读取一路 ADC,因此内容反而更简单,这里就不做介绍了。

相关推荐
牢姐与蒯11 分钟前
Linux信号(一).信号产生
linux·运维·服务器·ubuntu
艾莉丝努力练剑32 分钟前
【AI大模型接入SDK】ChatSDK架构设计与实现
网络·c++·人工智能·学习·语言模型
雯宝6 小时前
mnt_init 函数
linux
UIU1147 小时前
递归算法与汉诺塔
c++·学习·算法·c#·递归
律宏阔12 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔12 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴12 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千12 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei3712 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei3712 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化