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,因此内容反而更简单,这里就不做介绍了。

相关推荐
FoldWinCard1 小时前
D5 Linux 网络及端口命令
linux·运维·服务器
零涂毕业设计2 小时前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud1232 小时前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
霸道流氓气质2 小时前
KMS 密钥管理服务(Key Management Service)原理与实践
linux·服务器·数据库
SakitamaX3 小时前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.3 小时前
Linux系统管理
linux·运维
阿龙的工作记录3 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手4 小时前
Linux的基本指令
linux·运维·服务器
hkj88084 小时前
Ubuntu 切换java版本
java·linux·ubuntu
caimouse6 小时前
protoc-gen-c 支持 proto3 `optional` 关键字修改记录
c语言·学习