字符设备驱动实例(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 乘以采样值和满量程数字值之比就能得到当前的电压编译和测试的命令如下,旋转电位器旋钮可以看到电压变化。

 

新网线就是丝滑,之前还会突然断开。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/102440.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LVS集群】

目录 一、集群概述 1.负载均衡技术类型 2.负载均衡实现方式 二、LVS结构 1.三层结构 2.架构对象 三、LVS工作模式 四、LVS负载均衡算法 1.静态负载均衡 2.动态负载均衡 五、ipvsadm命令详解 1. -A 2. -D 3. -L 4. -a 5. -d 6. -l 7. -t 8. -s 9. -r 10. -…

css 实现四角边框样式

效果如图 此图只实现 左下与右下边角样式 右上与左上同理 /* 容器 */ .card-mini {position: relative; } /* 左下*/ .card-mini::before {content: ;position: absolute;left: 0;bottom: 0;width: 20px;height: 20px;border-bottom: 2px solid #253d64;border-left: 2px so…

redis 6个节点(3主3从),始终一个节点不能启动

redis节点&#xff0c;始终有一个节点不能启动起来 1.修改了配置文件 protected-mode no&#xff0c;重启 修改了配置文件 protected-mode no&#xff0c;重启redis问题依然存在 2、查看/var/log/message的redis日志 Aug 21 07:40:33 redisMaster kernel: Out of memory: K…

什么是神经网络

什么是神经网络 什么是神经网络&#xff1f;CNN、RNN、GNN&#xff0c;这么多的神经网络&#xff0c;有什么区别和联系&#xff1f; 既然我们的目标是打造人工智能&#xff0c;拥有智慧的大脑无疑是最好的模仿对象&#xff0c;人脑中有约860亿个神经元&#xff0c;这被认为是…

9 - 蓝图

蓝图: 将项目分成一个个单独的app模块&#xff0c;然后将所有app分配不同的处理功能&#xff0c;通过路由分配将它们连接成一个大项目 目录结构: 搭建框架: (1). 新键apps 包,编辑__init__.py文件 from flask import Flask import settings from apps.user.view import user_b…

03_缓存双写一致性

03——缓存双写一致性 一、缓存双写一致性 如果redis中有数据&#xff0c;需要和数据库中的值相同如果redis中无数据&#xff0c;数据库中的值要是最新值&#xff0c;且准备回写redis 缓存按照操作来分&#xff0c;可以分为两种&#xff1a; 只读缓存 读写缓存 同步直写操作…

构造不包含字母和数字的webshell

文章目录 利用不含字母与数字进行绕过知识介绍题目方法一&#xff1a;异或操作绕过方法二&#xff1a;取反进行绕过 过滤不是很严格的情况进阶绕过利用php7特性直接绕过 利用不含字母与数字进行绕过 知识介绍 <?phpecho "A"^"";?>从运行结果为! …

git拉取失败/git fatal终极解决方法

前言 被折磨不下20次总结出来的终极方案 步骤 0 首先关闭代理试试&#xff0c;不行就下一步 1 重置代理或者取消代理的方式 git config --global --unset http.proxy git config --global --unset https.proxy添加全局代理 git config --global http.proxy git config …

ubuntu 安装 postgresql以及 wal回滚

安装 sudo apt install postgresql postgresql-contrib设置远程连接 修改/etc/postgresql/12/main/postgresql.conf **将listen_addresses 改成 ***修改/etc/postgresql/12/main/pg_hba.conf 找到如下信息 #IPv4 local connections: 修改为 host all all 0.0.0.0/0 md5 重启…

Apache ShenYu 学习笔记一

1、简介 这是一个异步的&#xff0c;高性能的&#xff0c;跨语言的&#xff0c;响应式的 API 网关。 官网文档&#xff1a;Apache ShenYu 介绍 | Apache ShenYu仓库地址&#xff1a;GitHub - apache/shenyu: Apache ShenYu is a Java native API Gateway for service proxy, pr…

(6)(6.2) 任务命令

文章目录 前言 6.2.1 概述 6.2.2 导航命令 6.2.3 条件命令 6.2.4 DO命令 前言 本文介绍了 Copter、Plane 和 Rover 切换到自动模式时支持的任务指令。 &#xff01;Warning 这是一项正在进行中的工作&#xff0c;尚未经过全面审核。有关 Copter 的更佳列表&#xff0c;请…

docker搭建私有镜像harbor

docker安装搭建私有仓库 Harbor harbor用于存储和分布docker镜像企业级registry服务器的harbor使用的是官方的docker registry(v2命名是distribution)服务去完成。 安装harhor 启动harbor 6.

【C++】红黑树

目录 一、红黑树的概念二、红黑树的性质三、红黑树的插入操作四、红黑树的验证五、红黑树和AVL树的比较六、代码 一、红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从…

k8s容器加入host解析字段

一、通过edit或path来修改 kubectl edit deploy /xxxxx. x-n cattle-system xxxxx为你的资源对象名称 二、添加字段 三、code hostAliases:- hostnames:- www.rancher.localip: 10.10.2.180

Python爬虫实战案例——第二例

某某美剧剧集下载(从搜索片名开始) 本篇文章主要是为大家提供某些电影网站的较常规的下载电影的分析思路与代码思路(通过爬虫下载电影)&#xff0c;我们会从搜索某部影片的关键字开始直到成功下载某一部电影。 地址&#xff1a;aHR0cHM6Ly93d3cuOTltZWlqdXR0LmNvbS9pbmRleC5od…

简单理解Socket

TCP/IP 要想理解socket首先得熟悉一下TCP/IP协议族&#xff0c; TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;即传输控制协议/网间协议&#xff0c;定义了主机如何连入因特网及数据如何再它们之间传输的标准&#xff0c; 从字面意思来看T…

《剑指Offer》模块三 思维题【面试官可能考的13道思维算法题】

思维题 1. 扑克牌的顺子【思维题】 原题链接 1. 判断所有牌中 是否出现 重复 2. 有序sort后 判断是否最大差距 <4 class Solution { public:bool isContinuous( vector<int> nums) {sort(nums.begin(), nums.end());for (int i 1; i < nums.size(); i )if …

js使用for of遍历map

//使用for of遍历map console.log("---") console.log(odata.studentDetails) let obj odata.studentDetails[0].answerSituation for(let [key,value] of Object.entries(obj)){console.log(value) }

【业务功能篇73】web系统架构演变-单体-集群-垂直化-服务化-微服务化

1.服务架构的演 1.1 单体架构 单体架构应该是我们最先接触到的架构实现了&#xff0c;在单体架构中使用经典的三层模型&#xff0c;即表现层&#xff0c;业务逻辑层和数据访问层。 单体架构只适合在应用初期&#xff0c;且访问量比较下的情况下使用&#xff0c;优点是性价比很…

淘宝商品优惠券详情item_get_app-获得淘宝app商品详情原数据

item_get_app-获得淘宝app商品详情原数据 taobao.item_get_app 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;调用API接口入口secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09…