RK3568笔记四十八:ADC驱动开发测试

news2024/11/28 16:42:53

若该文为原创文章,转载请注明原文出处。

一、ADC介绍

RK3568集成了一个逐次逼近模数转换器(Successive Approximation ADC),通常简称为SAR ADC。

这种转换器能够将连续的模拟信号转换为离散的数字信号,其特点在于具有较高的分辨率和转换速度。

在RK3568中,这个SAR ADC支持六通道单端10位的SAR-ADC,时钟频率必须小于13MHZ。

从硬件角度来看,RK3568的ADC接口包括温度传感器(Temperature Sensor)和逐次逼近ADC(Successive Approximation Register)两种类型。

TSADC 具有高达 50KS/s 的采样率,支持两通道,温度范围在-20℃~120℃和 5℃ 温度分辨率。

SARADC 具有高达 1MS/s 的采样率,支持八通道单端 10 位,时针频率要小于13MHZ。

其中,SAR-ADC支持六通道单端10位的转换,这六个通道分别对应处理器上的六个引脚。

每个通道都可以独立配置并执行AD转换,这使得RK3568能够灵活地满足不同应用场景的需求。

在软件层面,RK3568的ADC模块通过设备树(Device Tree)进行描述和配置。

二、硬件原理

ATK-DLRK        3568 开发板 ADC 硬件原理图

根据手册
SARADC_VIN3是ADC3 , 通过ADC3接口来采集 VR1 可调电位器的电压.
注意: ADC 的采集电压绝对值最大是 1.8V,请不要超过 1.8V,否则可能对芯片造成损坏

三、设备树修改

修改/home/alientek/rk3568_linux_sdk/kernel/arch/arm64/boot/dts/rockchip/rk3568-atk-evb1-ddr4- v10.dtsi 文件,添加adc节点。

 adc_vol: adc_vol {
	 status = "okay";
	 compatible = "yifeng,rk356x-adc";
	 io-channels = <&saradc 3>;
	 vref-supply = <&vcca_1v8>;
 }; 

修改后重新编译内核,并重新烧写boot.img文件。

三、驱动程序

1、adc_drv.c

/*
 adc_vol: adc_vol {
	 status = "okay";
	 compatible = "yifeng,rk356x-adc";
	 io-channels = <&saradc 3>;
	 vref-supply = <&vcca_1v8>;
 }; 
*/

#include <linux/moduleparam.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#include <linux/iio/consumer.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
 
#define VREF 1800
#define ADCDEV_CNT           1              /* 设备号长度 */
#define ADCDEV_NAME          "adc_drv"   /* 设备名字 */


struct adcdev_dev{
  dev_t devid;                  /* 设备号 */
  struct cdev cdev;             /* cdev */
  struct class *class;          /* 类 */
  struct device *device;        /* 设备 */ 
  struct device_node *node;     /* adcdev 设备节点 */
  struct iio_channel *chan;     /* 通道 */
};
 
struct adcdev_dev adcdev; /* adcd ev 设备 */

/*
* @description : 打开设备
* @param – inode : 传递给驱动的 inode
* @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int adc_open(struct inode *inode, struct file *filp)
{
  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  
  return 0;
}
 
 
/*
    linux driver 驱动接口: 
    实现对应的open/read/write等函数,填入file_operations结构体
*/
static ssize_t adc_drv_read ( struct file *file, char __user *buf, 
                                size_t size, loff_t *offset)
{
  int ret = 0, raw = 0;
  int result = -1;
  
  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 
  ret = iio_read_channel_raw(adcdev.chan, &raw);
  if (ret < 0) {
    printk("read hook adc channel() error: %d\n", ret);
    return -1;
  }
  
  result = (VREF*raw)/1023;
  printk(" out! raw= %d Voltage=%dmV\n",raw, result);

  ret = copy_to_user(buf, &result, sizeof(result));
	
  return ret;
 
}
 
 
 
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t adc_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  
  return 0;
}
 
static int adc_drv_close(struct inode *node, struct file *file)
{
  printk(" %s line %d \r\n",  __FUNCTION__, __LINE__);

  return 0;
}
 

/* 设备操作函数 */
static struct file_operations adc_fops = {
  .owner   = THIS_MODULE,
  .open    = adc_open,
  .read    = adc_drv_read,
  .write   = adc_write,
  .release = adc_drv_close,
};
 

 
static int atk_adc_probe(struct platform_device *pdev)
{
	int ret = -1;
	
    printk("atk_adc_probe!\n");
 
    adcdev.chan = iio_channel_get(&(pdev->dev), NULL);
    if (IS_ERR(adcdev.chan))
    {
        adcdev.chan = NULL;
        printk("%s() have not set adc chan\n", __FUNCTION__);
        return -1;
    }
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 1、设置设备号 */
    ret = alloc_chrdev_region(&adcdev.devid, 0, ADCDEV_CNT, ADCDEV_NAME);
    if(ret < 0) {
		pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", ADCDEV_NAME, ret);
    }
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 2、初始化 cdev */
	adcdev.cdev.owner = THIS_MODULE;
	cdev_init(&adcdev.cdev, &adc_fops);
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 3、添加一个 cdev */
	ret = cdev_add(&adcdev.cdev, adcdev.devid, ADCDEV_CNT);
	if(ret < 0)
		goto del_unregister;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);  
	/* 4、创建类 */
	adcdev.class = class_create(THIS_MODULE, ADCDEV_NAME);
	if (IS_ERR(adcdev.class)) {
		goto del_cdev;
	}
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);  
	/* 5、创建设备 */
	adcdev.device = device_create(adcdev.class, NULL, adcdev.devid, NULL, ADCDEV_NAME);
	if (IS_ERR(adcdev.device)) {
		goto destroy_class;
	}
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	return 0;

destroy_class:
    class_destroy(adcdev.class);
del_cdev:
    cdev_del(&adcdev.cdev);
del_unregister:
    unregister_chrdev_region(adcdev.devid, ADCDEV_CNT);

	return -EIO;
}
 
static int atk_adc_remove(struct platform_device *pdev)
{
    printk("atk_adc_remove!\n");
    iio_channel_release(adcdev.chan);

    cdev_del(&adcdev.cdev); /* 删除 cdev */
    unregister_chrdev_region(adcdev.devid, ADCDEV_CNT);
    device_destroy(adcdev.class, adcdev.devid); /* 注销设备 */
    class_destroy(adcdev.class); /* 注销类 */
    return 0;
}
 
static const struct of_device_id atk_adc_match[] = {
    { .compatible = "yifeng,rk356x-adc" },
    {},
};
 
static struct platform_driver atk_adc_driver = {
    .probe      = atk_adc_probe,
    .remove     = atk_adc_remove,
    .driver     = {
        .name   = "yifeng_adc",
        .owner  = THIS_MODULE,
        .of_match_table = atk_adc_match,
    },
};
 
static int atk_adc_init(void)
{
    return platform_driver_register(&atk_adc_driver);
}

 
static void atk_adc_exit(void)
{
    platform_driver_unregister(&atk_adc_driver);
}

module_init(atk_adc_init);
module_exit(atk_adc_exit);
 
MODULE_AUTHOR("yifeng");
MODULE_DESCRIPTION(" adc demo driver");
MODULE_ALIAS("platform:yifeng-adc");
MODULE_LICENSE("GPL");
 

驱动程序是直接在内核打印电压值,如果想要使用APP来读取,可以增加字符设备read等函数,在使用APP来读取。

2、makefile

KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
ARCH=arm64
CROSS_COMPILE=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-
 
export  ARCH  CROSS_COMPILE
 
CURRENT_PATH := $(shell pwd)
obj-m := adc_drv.o
 
build: kernel_modules
 
kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

四、编写测试 APP

注意:编写APP测试使用的不是上面的驱动,原子的出厂系统默认已经适配好了,进入/sys/bus/iio/devices 目录下,此目录下就有 ADC 对应的 IIO 设备:iio:deviceX。

ls /sys/bus/iio/devices/iio:deviceX

标准的 IIO 设备文件目录,我们只关心两个文件:

in_voltage3_raw:ADC3 原始值文件。

in_voltage_scale:ADC 比例文件(分辨率),单位为 mV。

实际电压值(mV)= in_voltage3_raw  * in_voltage_scale。

1、编写测试 APP

adcApp.c

编译

/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc adcApp.c -o adcApp

测试正常,可以旋转板子上的滑动变阻器。

如有侵权,或需要完整代码,请及时联系博主。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nginx转发netty长链接(nginx负载tcp长链接配置)

首先要清楚一点&#xff0c;netty是长链接是tcp连接不同于http中负载在http中配置server监听。长连接需要开启nginx的stream模块(和http是并列关系) 安装nginx时注意开启stream&#xff0c;编译时加上参数 --with-stream &#xff08;其他参数根据自己所需来加&#xff09; …

rem实现屏幕适配(jQuery)

一、rem换算 1.根据视口宽度动态计算字体大小&#xff0c;如果宽度大于750px&#xff0c;则将字体大小设置为100px&#xff0c;否则按比例缩小。 tips:使用时记得引入jQuery.js // 在文档加载完成后执行函数&#xff0c;确保DOM已经准备就绪$(function () {// 定义一个自执行…

增量学习中Task incremental、Domain incremental、Class incremental 三种学习模式的概念及代表性数据集?

1 概念 在持续学习领域&#xff0c;Task incremental、Domain incremental、Class incremental 是三种主要的学习模式&#xff0c;它们分别关注不同类型的任务序列和数据分布变化。 1.1 Task Incremental Learning (Task-incremental) 任务增量学习&#xff0c;也称为任务增…

盐分反演关键:批量计算常用的盐分指数反演变量

盐分反演关键&#xff1a;批量计算常用的盐分指数反演变量 一、引言 盐分指数反演是遥感应用中的一个重要方面&#xff0c;尤其在农业和环境监测中有着广泛的应用。通过遥感影像&#xff0c;研究人员可以高效地获取和分析地表盐分信息&#xff0c;为土地管理和作物生产提供重…

YOLOX+PyQt5交通路口智能监测平台设计与实现

1.概述 交通要道的路口上人车穿行&#xff0c;特别是上下班早高峰&#xff0c;且时常发生交通事故。因此对交通路口的车流量和人流量的监测必不可少。 2.检测模型 使用的检测模型为YOLOX模型&#xff0c;模型权重为训练VOC数据集得来&#xff0c;其中包括了二十个类别&#…

ONLYOFFICE 协作空间 2.6 已发布:表单填写房间、LDAP、优化房间和文件管理等

更新后的 ONLYOFFICE 协作空间带来了超过 20 项新功能和优化&#xff0c;让工作更加高效和舒适。阅读本文了解详情。 表单填写房间 这次更新增加了一种新的房间类型&#xff0c;可在 ONLYOFFICE 协作空间中组织简单的表单填写流程。 通过表单填写房间&#xff0c;目前可以完成…

仓库物品与装备物品位置更换

一、装备物品与选中的仓库物品位置交换 1、准备工作 2、Inventory Items 3、给Warehouse添加Grid Layout Group组件 4、复制Inventory Items&#xff0c;设置Grid Layout Group组件 5、创建文本ItemName和ItemDescription 6、设置物品数据 (1) 创建 ItemData.cs using Syst…

Spring boot tomcat 读写超时时间设置

yaml配置 connection-timeout: 20000 server:port: 9898servlet:context-path: /testtomcat:connection-timeout: 20000max-connections: 250accept-count: 300 spring源码设置自定义tomcat参数 customizeConnector(connector); Overridepublic WebServer getWebServer(Serv…

【MySQL】表的约束{ 常见约束 空属性 默认值 列描述comment zerofill 主键 复合主键 自增长 唯一键 外键 }

文章目录 常见约束空属性默认值列描述commentzerofill主键复合主键自增长唯一键外键 2.总结 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。比…

MySQL基础练习题12-使用唯一标识码替换员工ID

题目&#xff1a;展示每位用户的 唯一标识码&#xff08;unique ID &#xff09;&#xff1b;如果某位员工没有唯一标识码&#xff0c;使用 null 填充即可。 准备数据 分析数据 题目&#xff1a;展示每位用户的 唯一标识码&#xff08;unique ID &#xff09;&#xff1b;如果…

一, 创建工程,引入依赖

一&#xff0c; 创建工程&#xff0c;引入依赖 文章目录 一&#xff0c; 创建工程&#xff0c;引入依赖创建工程工程间的关系的建立配置各个工程当中的 pow 配置信息&#xff0c;相关的依赖父工程(也就是总项目工程)的 pow 配置demo-module06-generate 模块中pow 配置&#xff…

基于IEC61499标准的在线工业编程平台open61499

基于IEC61499标准的在线工业编程平台open61499是一个专为工业自动化领域设计的编程环境&#xff0c;它遵循IEC 61499标准&#xff0c;为开发者提供了一种高效、灵活的方式来创建、配置和管理分布式控制系统&#xff08;DCS&#xff09;的应用程序。以下是对open61499的详细解析…

LeetCode热题 翻转二叉树、二叉树最大深度、二叉树中序遍历

目录 一、翻转二叉树 1.1 题目链接 1.2 题目描述 1.3 解题思路 二、二叉树最大深度 2.1 题目链接 2.2 题目描述 2.3 解题思路 三、二叉树中序遍历 3.1 题目链接 3.2 题目描述 3.3 解题思路 一、翻转二叉树 1.1 题目链接 翻转二叉树 1.2 题目描述 1.3 解题思路 根…

【多模态大模型】 BLIP in ICML 2022

一、引言 论文&#xff1a; BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation 作者&#xff1a; Salesforce Research 代码&#xff1a; BLIP 特点&#xff1a; 该方法分别使用ViT和BERT进行图像和文本特征提取&am…

【changchain-community安装失败】‘EntryPoints‘ object has no attribute ‘get‘报错解决

在安装changchain-community时报错信息如下&#xff1a; WARNING: Keyring is skipped due to an exception: EntryPoints object has no attribute get ERROR: Could not find a version that satisfies the requirement changchain-community ERROR: No matching distributio…

进程间通信与线程间通信的方法汇总

目录 一、进程间通信机制 管道(pipe)&#xff1a; 命名管道(FIFO)&#xff1a; 消息队列(MQ)&#xff1a; 信号量(semaphore)&#xff1a; 共享内存(shared memory)&#xff1a; 信号(signal)&#xff1a; 内存映射(mapped memory)&#xff1a; 内存映射和共享内存的区…

华杉研发九学习日记20 LinkedHashMap TreeMap Arrays 函数式接口 方法引用

华杉研发九学习日记20 一&#xff0c;LinkedHashMap 与HashMap相比&#xff0c;key是有序的 Map<Integer,String> map new LinkedHashMap<Integer,String>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); map.…

GitHub Desktop commit文件到repository

1. Clone a repository到本地 2. 在本地仓库修改/添加需要提交的文件或者文档 3. 添加comments并commit 4. 提交完成&#xff0c;点击Push origin提交代码到Github远程仓库 上传成功后&#xff0c;刷新Github网站页面就会出现上传的项目

鸿蒙应用框架开发【自绘编辑框】 输入法框架

自绘编辑框 介绍 本示例通过输入法框架实现自会编辑框&#xff0c;可以绑定输入法应用&#xff0c;从输入法应用输入内容&#xff0c;显示和隐藏输入法。 效果预览 使用说明 1.点击编辑框可以绑定并拉起输入法&#xff0c;可以从输入法键盘输入内容到编辑框。 2.可以点击a…

SSM老人服务管理系统小程序-计算机毕业设计源码91022

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…