字符设备驱动实例(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 的资源&…...
VS2022远程Linux使用cmake开发c++工程配置方法
文章目录 远程连接CMakePresets.json的配置Task.vs.json配置launch.vs.json配置最近使用别人在VS2015上使用visualgdb搭建的linux开发环境,各种不顺手,一会代码不能调转了,一会行号没了,调试的时候断不到正确的位置,取消的断点仍然会进。因此重新摸索了一套使用vs的远程开…...
《强化学习:原理与Python实战》——可曾听闻RLHF
前言: RLHF(Reinforcement Learning with Human Feedback,人类反馈强化学习)是一种基于强化学习的算法,通过结合人类专家的知识和经验来优化智能体的学习效果。它不仅考虑智能体的行为奖励,还融合了人类专家…...
STM32——RTC实时时钟
文章目录 Unix时间戳UTC/GMT 时间戳转换BKP简介BKP基本结构读写BKP备份寄存器电路设计关键代码 RTC简介RTC框图RTC基本结构硬件电路RTC操作注意事项读写实时时钟电路设计关键代码 Unix时间戳 Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日…...
webSocket 开发
1 认识webSocket WebSocket_ohana!的博客-CSDN博客 一,什么是websocket WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽…...
c#设计模式-结构型模式 之 代理模式
前言 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接 引用目标对象,代理对象作为访问对象和目标对象之间的中介。在学习代理模式的时候,可以去了解一下Aop切面编程AOP切面编程_aop编程…...
openpnp - 自动换刀的设置
文章目录 openpnp - 自动换刀的设置概述笔记采用的openpnp版本自动换刀库的类型选择自动换刀设置前的注意事项先卸掉吸嘴座上所有的吸嘴删掉所有的吸嘴设置自动换刀的视觉识别设置吸嘴座为自动换刀 - 以N1为例备注补充 - 吸嘴轴差个0.3mm, 就有可能怼坏吸嘴END openpnp - 自动换…...
《HeadFirst设计模式(第二版)》第十章代码——状态模式
如下图所示,这是一个糖果机的状态机图,要求使用代码实现: 初始版本: package Chapter10_StatePattern.Origin;/*** Author 竹心* Date 2023/8/19**/public class GumballMachine {final static int SOLD_OUT 0;final static int…...
day-25 代码随想录算法训练营(19)回溯part02
216.组合总和||| 思路:和上题一样,差别在于多了总和,但是数字局限在1-9 17.电话号码的字母组合 思路:先纵向遍历第i位电话号码对于的字符串,再横向递归遍历下一位电话号码 93.复原IP地址 画图分析: 思…...
PG逻辑备份与恢复
文章目录 创建测试数据pg_dump 备份pg_restore 恢复pg_restore 恢复并行备份的文件PG 只导出指定函数 创建测试数据 drop database if exists test; create database test ; \c test create table t1(id int primary key); create table t2(id serial primary key, name varch…...
图数据库_Neo4j和SpringBoot整合使用_实战创建明星关系图谱---Neo4j图数据库工作笔记0010
然后我们再来看一下这个明星关系图谱 可以看到这里 这个是原来的startRelation 我们可以写CQL去查询对应的关系 可以看到,首先查询出来以后,然后就可以去创建 我们可以把写的创建明星关系的CQL,拿到 springboot中去执行 可以看到,这里我们先写一个StarRelationRepository,然…...
wordpress关于我们插件/武汉官网优化公司
今天,接到一个朋友的电话。他是做集成项目的,当时正在客户那边验收Cisco交换机,打电话问我如何鉴别Cisco设备的真伪。 老实说,这方面我也没什么经验,不过确实听说过目前市面上Cisco的假冒和水货很多,一不小…...
中山移动网站建设报价/世界杯大数据
最近在看李沐的实用机器学习课程,讲到regression问题的loss的时候有弹幕问:“为什么要平方?”如果是几年前学生问我这个问题,我会回答:“因为做回归的时候的我们的残差有正有负,取个平方求和以后可以很简单…...
返利网站建设/上海整站seo
我加载一个xml内容,并将其保存到磁盘.然后我读了它,并尝试解析.当我成功解析xml时,我应该忽略7行中的IOException吗?catch (IOException ignore) {}或者可能会出现一些问题?private HashMap loadContent(String url){try {BufferedInputStream bStream …...
做网站基础教程/最好的网络营销软件
东莞数控机床上加工模具 编程时,应该遵守编程的工艺流程,否则极容易出现错误。首先需要分析图纸、编写 工艺卡等,接着需要编写模具的加工程序,然后将程序输入到数控机床,最后进行程序检 验和切试。 (1&am…...
钢琴网站建设原则/许昌网络推广外包
本文档以实战的形式介绍系统如何禁用 IPV6, 使用的操纵系统是 RHEL7.3更改/etc/sysctl.conf配置文件[rootJaking vsftpd]# vim /etc/sysctl.conf# sysctl settings are defined through files in# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.## Vendors settings…...
wordpress的分类目录做成树/seo培训教程
文章目录一、题目1、题目描述2、基础框架3、原题链接二、解题报告1、思路分析2、时间复杂度3、代码详解三、本题小知识四、加群须知一、题目 1、题目描述 给你一个字符串数组 words,找出并返回数组中的第一个回文字符串 。如果不存在满足要求的字符串,返…...