字符设备驱动实例(ADC驱动)
四、ADC驱动
ADC是将模拟信号转换为数字信号的转换器,在 Exynos4412 上有一个ADC,其主要的特性如下。
(1)量程为0~1.8V。
(2)精度有 10bit 和 12bit 可选。
(3)采样时钟最高为5MHz,转换速率最高为1MSPS
(4)具有四路模拟输入,同一时刻只有一路进行转换
(5) 转换完成后可以产生中断。
下面是ADC的控制寄存器
下面是延时和数据寄存器
下面是中断清除和通道多路复用寄存器
根据上面的寄存器描述,我们大致可以设计出这个ADC 驱动实现的主要步骤。
(1)初始化 ADC,包括选择精度、设置分频值、设置 ADC 为正常工作模式、设置转换启动方式。
(2)注册ADC中断处理函数。
(3)在上层需要ADC数据时,选择好ADC 通道,启动转换,然后等待一个完成量
(4)转换结束后产生中断,在中断处理函数中获取转换结果值,向 CLRINTADC 寄存器写任意值清除中断,然后唤醒等待完成量的进程。
(5)进程被唤醒,返回转换结果值给上层。
接下来就是要编写ADC的设备树节点,代码如下。
adc@126C0000 {compatible = "fs4412,fsadc";reg = <0x126C0000 32>;interrupt-parent = <&combiner>;interrupts = <10 3>;
};
reg 属性可以查阅芯片手册得到其寄存器地址。在这里比较麻烦的是中断属性的设置。在 Exynos4412中有的中断是直接接入 GIC 中断控制器的,有的中断则是先通过中断组合器(Combiner)将多个中断复合后再接入 GIC 中断控制器的,连接图如图所示。
而ADC中断属于INTG10这一组中断,手册上的描述如图所示
这一组共用一根中断线,中断号如图 所示。
查阅Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt内核文档可知:对于这种中断的描述,combiner 是其父中断控制器,所以,interrupt-parent 的值为<&combiner>。interrupts 属性的第一个 cell是中断在组合器中的组号,第二个cell是中断在该组中的序号。
在FS4412日标板上有一个电位器的抽头接在了AIN3通道上原理图如图所示
图ADC电路图

接下来就是驱动的实现,主要代码如下
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>#include <linux/fs.h>
#include <linux/cdev.h>#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>#include <linux/of.h>
#include <linux/interrupt.h>#include "fsadc.h"#define FSADC_MAJOR 256
#define FSADC_MINOR 6
#define FSADC_DEV_NAME "fsadc"struct fsadc_dev {unsigned int __iomem *adccon;unsigned int __iomem *adcdat;unsigned int __iomem *clrint;unsigned int __iomem *adcmux;unsigned int adcval;struct completion completion;atomic_t available;unsigned int irq;struct cdev cdev;
};static int fsadc_open(struct inode *inode, struct file *filp)
{struct fsadc_dev *fsadc = container_of(inode->i_cdev, struct fsadc_dev, cdev);filp->private_data = fsadc;if (atomic_dec_and_test(&fsadc->available))return 0;else {atomic_inc(&fsadc->available);return -EBUSY;}
}static int fsadc_release(struct inode *inode, struct file *filp)
{struct fsadc_dev *fsadc = filp->private_data;atomic_inc(&fsadc->available);return 0;
}static long fsadc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct fsadc_dev *fsadc = filp->private_data;union chan_val cv;if (_IOC_TYPE(cmd) != FSADC_MAGIC)return -ENOTTY;switch (cmd) {case FSADC_GET_VAL:if (copy_from_user(&cv, (union chan_val __user *)arg, sizeof(union chan_val)))return -EFAULT;if (cv.chan > AIN3)return -ENOTTY;writel(cv.chan, fsadc->adcmux);writel(readl(fsadc->adccon) | 1, fsadc->adccon);if (wait_for_completion_interruptible(&fsadc->completion))return -ERESTARTSYS;cv.val = fsadc->adcval & 0xFFF;if (copy_to_user( (union chan_val __user *)arg, &cv, sizeof(union chan_val)))return -EFAULT;break;default:return -ENOTTY;}return 0;
}static irqreturn_t fsadc_isr(int irq, void *dev_id)
{struct fsadc_dev *fsadc = dev_id;fsadc->adcval = readl(fsadc->adcdat);writel(1, fsadc->clrint);complete(&fsadc->completion);return IRQ_HANDLED;
}static struct file_operations fsadc_ops = {.owner = THIS_MODULE,.open = fsadc_open,.release = fsadc_release,.unlocked_ioctl = fsadc_ioctl,
};static int fsadc_probe(struct platform_device *pdev)
{int ret;dev_t dev;struct fsadc_dev *fsadc;struct resource *res;dev = MKDEV(FSADC_MAJOR, FSADC_MINOR);ret = register_chrdev_region(dev, 1, FSADC_DEV_NAME);if (ret)goto reg_err;fsadc = kzalloc(sizeof(struct fsadc_dev), GFP_KERNEL);if (!fsadc) {ret = -ENOMEM;goto mem_err;}platform_set_drvdata(pdev, fsadc);cdev_init(&fsadc->cdev, &fsadc_ops);fsadc->cdev.owner = THIS_MODULE;ret = cdev_add(&fsadc->cdev, dev, 1);if (ret)goto add_err;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {ret = -ENOENT;goto res_err;}fsadc->adccon = ioremap(res->start, resource_size(res));if (!fsadc->adccon) {ret = -EBUSY;goto map_err;}fsadc->adcdat = fsadc->adccon + 3;fsadc->clrint = fsadc->adccon + 6;fsadc->adcmux = fsadc->adccon + 7;fsadc->irq = platform_get_irq(pdev, 0);if (fsadc->irq < 0) {ret = fsadc->irq;goto irq_err;}ret = request_irq(fsadc->irq, fsadc_isr, 0, "adc", fsadc);if (ret)goto irq_err;writel((1 << 16) | (1 << 14) | (19 << 6), fsadc->adccon);init_completion(&fsadc->completion);atomic_set(&fsadc->available, 1);return 0;
irq_err:iounmap(fsadc->adccon);
map_err:
res_err:cdev_del(&fsadc->cdev);
add_err:kfree(fsadc);
mem_err:unregister_chrdev_region(dev, 1);
reg_err:return ret;
}static int fsadc_remove(struct platform_device *pdev)
{dev_t dev;struct fsadc_dev *fsadc = platform_get_drvdata(pdev);dev = MKDEV(FSADC_MAJOR, FSADC_MINOR);writel((readl(fsadc->adccon) & ~(1 << 16)) | (1 << 2), fsadc->adccon);free_irq(fsadc->irq, fsadc);iounmap(fsadc->adccon);cdev_del(&fsadc->cdev);kfree(fsadc);unregister_chrdev_region(dev, 1);return 0;
}static const struct of_device_id fsadc_of_matches[] = {{ .compatible = "fs4412,fsadc", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsadc_of_matches);struct platform_driver fsadc_drv = { .driver = { .name = "fsadc",.owner = THIS_MODULE,.of_match_table = of_match_ptr(fsadc_of_matches),}, .probe = fsadc_probe,.remove = fsadc_remove,
};module_platform_driver(fsadc_drv);MODULE_LICENSE("GPL");
MODULE_AUTHOR("name <e-mail>");
MODULE_DESCRIPTION("ADC driver");
#ifndef _FSADC_H
#define _FSADC_H#define FSADC_MAGIC 'f'union chan_val {unsigned int chan;unsigned int val;
};#define FSADC_GET_VAL _IOWR(FSADC_MAGIC, 0, union chan_val)#define AIN0 0
#define AIN1 1
#define AIN2 2
#define AIN3 3#endif
代码第 26 行至第 29 行是各寄存器的指针。代码第 32 行是用于保存采样(转换)结果的变量。代码第32 行是用于同步采结束的完成量。
在fsadc_probe 函数中,与字符设备相关的代码和前面基本一样,接下来就是资源的获取、IO 内存的映射、中断的获取和中断处理函数的注册。然后就是初始化 ADC,将ADCCON 寄存器的第 16 位和第 14 位置 1,表示采样精度为 12位,使能预分频。并且预分配值设为19,即为20分频,这是因为在 U-Boot 中,我们将 APB 的时钟设为了100MHz。最后没有设置读启动位,也就意味着,采样的启动是靠 ADCCON 寄存器的比特0来控制的。
fsadc_ioctl 函数是处理采样的关键函数,这里用到了自定义的一个联合体 unionchan_val,它的定义如下。
union chan_val {unsigned int chan;unsigned int val;
};
有两个成员分别是 chan 和 val。命令 FSADC_GET_VAL 是读写类型的,在应用层往驱动层的传递过程中传递的是要使用的ADC通道chan,在驱动层往应用层传递的过程中传递的是得到的采样值 val。代码第 69 行至第72行先从应用层得到通道号,然后代码第73行将通道号写入到ADCMUX 寄存器中,接下来将ADCCON 寄存器的比特0置1启动采样,最后就调用wait_for_completion_interruptible 来等待完成量。当采样完成时,中断处理函数自动被调用,代码第 92 行先读取了转换结果值寄存器 ADCDAT,然后将 1写入 CLRINT 寄存器中,清除中断,最后唤醒等待完成量的进程。进程被唤醒后,代码第77行和第78 行得到采样值,并返回给上层。
测试代码如下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>#include "fsadc.h"int main(int argc, char *argv[])
{int fd;int ret;union chan_val cv;fd = open("/dev/adc", O_RDWR);if (fd == -1)goto fail;while (1) {cv.chan = 3;ret = ioctl(fd, FSADC_GET_VAL, &cv);if (ret == -1)goto fail;printf("current volatage is: %.2fV\n", 1.8 * cv.val / 4095.0);sleep(1);}
fail:perror("adc test");exit(EXIT_FAILURE);
}
将数字值转换为对应的模拟值使用了下面的表达式
1.8 * cv.val / 4095.0
1.8 是满量程的电压,4095.0 是满量程时对应的数字值(因为是 12位),cv.val 是采样得到的数字,用1.8 乘以采样值和满量程数字值之比就能得到当前的电压编译和测试的命令如下,旋转电位器旋钮可以看到电压变化。
新网线就是丝滑,之前还会突然断开。
相关文章:
字符设备驱动实例(ADC驱动)
四、ADC驱动 ADC是将模拟信号转换为数字信号的转换器,在 Exynos4412 上有一个ADC,其主要的特性如下。 (1)量程为0~1.8V。 (2)精度有 10bit 和 12bit 可选。 (3)采样时钟最高为5MHz,转换速率最高为1MSPS (4)具有四路模拟输入,同一时…...
python基础5——正则、数据库操作
文章目录 一、数据库编程1.1 connect()函数1.2 命令参数1.3 常用语句 二、正则表达式2.1 匹配方式2.2 字符匹配2.3 数量匹配2.4 边界匹配2.5 分组匹配2.6 贪婪模式&非贪婪模式2.7 标志位 一、数据库编程 可以使用python脚本对数据库进行操作,比如获取数据库数据…...
SpringAOP原理:手写动态代理实现
0、基础知识 AOP我们知道,是在不修改源代码的情况下,为代码添加一些新功能的技术。通过动态代理,可以在不修改原始类代码的前提下,对方法进行拦截和增强。 动态代理常用于在不改变原有业务逻辑的情况下,对方法…...
【旅游度假】Axure酒店在线预订APP原型图 旅游度假子模块原型模板
作品概况 页面数量:共 10 页 兼容软件:Axure RP 9/10,不支持低版本 应用领域:旅游度假,生活服务 作品申明:页面内容仅用于功能演示,无实际功能 作品特色 本作品为「酒店在线预订」的移动端…...
Android JNI系列详解之CMake和ndk-build编译工具介绍
一、前提 CMake和ndk-build只是编译工具,本次主要介绍ndk-build和CMake的区别,下节课介绍他们的使用。 二、CMake工具介绍 CMake:cross platform make,是跨平台的编译工具 CMake是在AndroidStudio2.2之后引入(目前默认…...
【Linux取经路】解析环境变量,提升系统控制力
文章目录 一、进程优先级1.1 什么是优先级?1.2 为什么会有优先级?1.3 小结 二、Linux系统中的优先级2.1 查看进程优先级2.2 PRI and NI2.3 修改进程优先级2.4 进程优先级的实现原理2.5 一些名词解释 三、环境变量3.1 基本概念3.2 PATH:Linux系…...
TCP编程流程(补充)
目录 1、listen: 2、listen、tcp三次握手 3、 发送缓冲区和接收缓冲区: 4、tcp编程启用多线程 1、listen: 执行listen会创建一个监听队列 listen(sockfd,5) 2、listen、tcp三次握手 三次握手 3、 发送缓冲区和接收缓冲区:…...
每天一道leetcode:433. 最小基因变化(图论中等广度优先遍历)
今日份题目: 基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 A、C、G 和 T 之一。 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。 例如,&quo…...
【C++】做一个飞机空战小游戏(十)——子弹击落炮弹、炮弹与飞机相撞
[导读]本系列博文内容链接如下: 【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动【C】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动 【C】做一个飞…...
去除UI切图边缘上多余的线条
最近接到UI切图,放进项目,显示边缘有多余线条,影响UI美观。开始以为切图没切好,实则不是。如图: ->解决: 将该图片资源WrapMode改为Clamp...
Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor解析
文章目录 1. BeanFactoryPostProcessor 概览1.1 解读 BeanFactoryPostProcessor1.2. 如何使用 BeanFactoryPostProcessor 2. BeanDefinitionRegistryPostProcessor 深入探究2.1 解读 BeanDefinitionRegistryPostProcessor2.2 BeanDefinitionRegistryPostProcessor 的执行时机2.…...
【LeetCode动态规划】详解买卖票I~IV,经典dp题型买
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的最大利润。…...
【深入探究人工智能】:常见机器学习算法总结
文章目录 1、前言1.1 机器学习算法的两步骤1.2 机器学习算法分类 2、逻辑回归算法2.1 逻辑函数2.2 逻辑回归可以用于多类分类2.3 逻辑回归中的系数 3、线性回归算法3.1 线性回归的假设3.2 确定线性回归模型的拟合优度3.3线性回归中的异常值处理 4、支持向量机(SVM&a…...
设计模式之解释器模式详解及实例
1、解释器设计模式概述: 解释器模式(Interpreter Pattern)是一种设计模式,它主要用于描述如何构建一个解释器以解释特定的语言或表达式。该模式定义了一个文法表示和解释器的类结构,用于解释符合该文法规则的语句。解…...
Nodejs沙箱逃逸--总结
一、沙箱逃逸概念 JavaScript和Nodejs之间有什么区别:JavaScript用在浏览器前端,后来将Chrome中的v8引擎单独拿出来为JavaScript单独开发了一个运行环境,因此JavaScript也可以作为一门后端语言,写在后端(服务端&#…...
No115.精选前端面试题,享受每天的挑战和学习
文章目录 变量提升和函数提升的顺序Event Loop封装 FetchAPI,要求超时报错的同时,取消执行的 promise(即不继续执行)强缓存和协商缓存的区别token可以放在cookie里吗? 变量提升和函数提升的顺序 在JavaScript中&#…...
Elasticsearch:语义搜索 - Semantic Search in python
当 OpenAI 于 2022 年 11 月发布 ChatGPT 时,引发了人们对人工智能和机器学习的新一波兴趣。 尽管必要的技术创新已经出现了近十年,而且基本原理的历史甚至更早,但这种巨大的转变引发了各种发展的“寒武纪大爆炸”,特别是在大型语…...
Flink学习笔记(一)
流处理 批处理应用于有界数据流的处理,流处理则应用于无界数据流的处理。 有界数据流:输入数据有明确的开始和结束。 无界数据流:输入数据没有明确的开始和结束,或者说数据是无限的,数据通常会随着时间变化而更新。 在…...
[Raspberry Pi]如何用VNC遠端控制樹莓派(Ubuntu desktop 23.04)?
之前曾利用VMware探索CentOS,熟悉Linux操作系統的指令和配置運作方式,後來在樹莓派價格飛漲的時期,遇到貴人贈送Raspberry Pi 4 model B / 8GB,這下工具到位了,索性跳過樹莓派官方系統(Raspberry Pi OS),直…...
17.HPA和rancher
文章目录 HPA部署 metrics-server部署HPA Rancher部署Rancherrancher添加集群仪表盘创建 namespace仪表盘创建 Deployments仪表盘创建 service 总结 HPA HPA(Horizontal Pod Autoscaling)Pod 水平自动伸缩,Kubernetes 有一个 HPA 的资源&…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
针对药品仓库的效期管理问题,如何利用WMS系统“破局”
案例: 某医药分销企业,主要经营各类药品的批发与零售。由于药品的特殊性,效期管理至关重要,但该企业一直面临效期问题的困扰。在未使用WMS系统之前,其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...
Python学习(8) ----- Python的类与对象
Python 中的类(Class)与对象(Object)是面向对象编程(OOP)的核心。我们可以通过“类是模板,对象是实例”来理解它们的关系。 🧱 一句话理解: 类就像“图纸”,对…...
