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

相关推荐
小丑西瓜66640 分钟前
CMake基础用法,cmake_minimum_required,project,add_executable
linux·服务器·c++·camke
●VON40 分钟前
0基础也能行!「Flutter 跨平台开发训练营」1月19日正式启动!
学习·flutter·von·openjiuwen
晚风吹长发1 小时前
初步了解Linux中的命名管道及简单应用和简单日志
linux·运维·服务器·开发语言·数据结构·c++·算法
敲敲了个代码1 小时前
如何优化批量图片上传?队列机制+分片处理+断点续传三连击!(附源码)
前端·javascript·学习·职场和发展·node.js
大熊背1 小时前
多核架构中,RTOS小核跑sensor 的normal模式,大核linux核跑WDR模式,将小核的曝光时间映射到WDR模式中的曝光时间的方法
linux·自动曝光·wdr
全栈测试笔记1 小时前
异步函数与异步生成器
linux·服务器·前端·数据库·python
weixin_462446232 小时前
Linux 下使用 xfreerdp3 远程连接 Windows(从安装到实战使用)
linux·运维·windows
知识分享小能手2 小时前
Oracle 19c入门学习教程,从入门到精通,Oracle 其他数据对象 —— 语法详解与综合实践(11)
数据库·学习·oracle
EndingCoder2 小时前
配置 tsconfig.json:高级选项
linux·前端·ubuntu·typescript·json
爱丶狸2 小时前
Linux三剑客之sed
linux·运维·服务器