首页 运维 linux运维 linux adc是什么设备

linux adc是什么设备

Apr 17, 2023 am 09:33 AM
linux adc

linux adc是混杂设备驱动;在linux2.6.30.4中,系统已经自带有了ADC通用驱动文件“arch/arm/plat-s3c24xx/adc.c”,它是以平台驱动设备模型的架构来编写的,里面是一些比较通用稳定的代码。

linux adc是什么设备

本教程操作环境:linux2.6.30.4系统、Dell G3电脑。

linux adc是什么设备?

linux 混杂设备驱动之adc驱动

linux2.6.30.4中,系统已经自带有了ADC通用驱动文件---arch/arm/plat-s3c24xx/adc.c,它是以平台驱动设备模型的架构来编写的,里面是一些比较通用稳定的代码,但是linux2.6.30.4版本的ADC通用驱动文件并不完善,居然没有读函数。后来去看了linux3.8版本的ADC通用文件----arch/arm/plat-samsung/adc.c才是比较完善的。

但是本节并不是分析这个文件,而是以另外一种架构来编写ADC驱动,因为ADC驱动实在是比较简单,就没有使用平台驱动设备模型为架构来编写了,这次我们使用的是混杂(misc)设备驱动。

问:什么是misc设备驱动?

答:miscdevice共享一个主设备号MISC_MAJOR(10),但次设备号不同。所有的miscdevice设备形成一条链表,对设备访问时内核根据设备号来查找对应的miscdevice设备,然后调用其file_operations结构体中注册的文件操作接口进行操作。

struct miscdevice  {
	int minor;				//次设备号,如果设置为MISC_DYNAMIC_MINOR则系统自动分配
	const char *name;		//设备名
	const struct file_operations *fops;		//操作函数
	struct list_head list;
	struct device *parent;
	struct device *this_device;
};
登录后复制
dev_init入口函数分析:

static int __init dev_init(void)
{
	int ret;

	base_addr=ioremap(S3C2410_PA_ADC,0x20);
	if (base_addr == NULL)
	{
		printk(KERN_ERR "failed to remap register block\n");
		return -ENOMEM;
	}

	adc_clock = clk_get(NULL, "adc");
	if (!adc_clock)
	{
		printk(KERN_ERR "failed to get adc clock source\n");
		return -ENOENT;
	}
	clk_enable(adc_clock);
	
	ADCTSC = 0;

	ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);
	if (ret)
	{
		iounmap(base_addr);
		return ret;
	}

	ret = misc_register(&misc);

	printk (DEVICE_NAME" initialized\n");
	return ret;
}
登录后复制
首先是映射ADC寄存器地址将其转换为虚拟地址,然后获得ADC时钟并使能ADC时钟,接着申请ADC中断,其中断处理函数为

adcdone_int_handler,而flags为IRQF_SHARED,即共享中断,因为触摸屏里也要申请ADC中断,最后注册一个混杂设备。

当应用程序open ("/dev/adc",...)时,就会调用到驱动里面的open函数,那么我们来看看open函数做了什么?

static int tq2440_adc_open(struct inode *inode, struct file *filp)
{
	/* 初始化等待队列头 */
	init_waitqueue_head(&(adcdev.wait));

	/* 开发板上ADC的通道2连接着一个电位器 */
	adcdev.channel=2;	//设置ADC的通道
	adcdev.prescale=0xff;

	DPRINTK( "ADC opened\n");
	return 0;
}
登录后复制
很简单,先初始化一个等待队列头,因为入口函数里既然有申请ADC中断,那么肯定要使用等待队列,接着设置ADC通道,因为TQ2440的ADC输入通道默认是2,设置预分频值为0xff。

当应用程序read时,就会调用到驱动里面的read函数,那么我们来看看read函数做了些什么?

static ssize_t tq2440_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
	char str[20];
	int value;
	size_t len;

	/* 尝试获得ADC_LOCK信号量,如果能够立刻获得,它就获得信号量并返回0 
	 * 否则,返回非零,它不会导致调用者睡眠,可以在中断上下文使用
	 */
	if (down_trylock(&ADC_LOCK) == 0)
	{
		/* 表示A/D转换器资源可用 */
		ADC_enable = 1;

		/* 使能预分频,选择ADC通道,最后启动ADC转换*/
		START_ADC_AIN(adcdev.channel, adcdev.prescale);

		/* 等待事件,当ev_adc = 0时,进程被阻塞,直到ev_adc>0 */
		wait_event_interruptible(adcdev.wait, ev_adc);

		ev_adc = 0;

		DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ((ADCCON & 0x80) ? 1:0));

		/* 将在ADC中断处理函数读取的ADC转换结果赋值给value */
		value = adc_data;
		sprintf(str,"%5d", adc_data);
		copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

		ADC_enable = 0;
		up(&ADC_LOCK);
	}
	else
	{
		/* 如果A/D转换器资源不可用,将value赋值为-1 */
		value = -1;
	}

	/* 将ADC转换结果输出到str数组里,以便传给应用空间 */
	len = sprintf(str, "%d\n", value);
	if (count >= len)
	{
		/* 从str数组里拷贝len字节的数据到buffer,即将ADC转换数据传给应用空间 */
		int r = copy_to_user(buffer, str, len);
		return r ? r : len;
	}
	else
	{
		return -EINVAL;
	}
}
登录后复制
tq2440_adc_read函数首先尝试获得ADC_LOCK信号量,因为触摸屏驱动也有使用ADC资源,两者互有竞争关系,获得ADC资源后,使能预分频,选择ADC通道,最后启动ADC转换,接着就调用wait_event_interruptible 函数进行等待,直到ev_adc>0进程才会继续往下跑,往下跑就会将adc_data数据读出来,调用copy_to_user函数将ADC数据传给应用空间,最后释放ADC_LOCK信号量。

问:什么时候ev_adc>0?默认ev_adc = 0

答:在adcdone_int_handler中断处理函数里,等数据读出后,ev_adc被设置为1。

ADC中断处理函数adcdone_int_handler

/* ADC中断处理函数 */
static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{
	/* A/D转换器资源可用 */
	if (ADC_enable)
	{
		/* 读ADC转换结果数据 */
		adc_data = ADCDAT0 & 0x3ff;

		/* 唤醒标志位,作为wait_event_interruptible的唤醒条件 */
		ev_adc = 1;
		wake_up_interruptible(&adcdev.wait);
	}
	return IRQ_HANDLED;
}
登录后复制
当AD转换完成后就会触发ADC中断,就会进入adcdone_int_handler,这个函数就会讲AD转换数据读到adc_data,接着将唤醒标志位ev_adc置1,最后调用wake_up_interruptible函数唤醒adcdev.wait等待队列。
总结一下ADC的工作流程:

一、open函数里,设置模拟输入通道,设置预分频值

二、read函数里,启动AD转换,进程休眠

三、adc_irq函数里,AD转换结束后触发ADC中断,在ADC中断处理函数将数据读出,唤醒进程

四、read函数里,进程被唤醒后,将adc转换数据传给应用程序

ADC驱动参考源码:

/*************************************

NAME:EmbedSky_adc.c
COPYRIGHT:www.embedsky.net

*************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
	 
#include 
#include 
#include 
#include 

#include "tq2440_adc.h"

#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk(KERN_DEBUG "EmbedSky_adc: " x);}
#else
#define DPRINTK(x...) (void)(0)
#endif

#define DEVICE_NAME	"adc"		/* 设备节点: /dev/adc */

static void __iomem *base_addr;

typedef struct
{
	wait_queue_head_t wait;		/* 定义等待队列头 */
	int channel;
	int prescale;
}ADC_DEV;

DECLARE_MUTEX(ADC_LOCK);	/* 定义并初始化信号量,并初始化为1 */
static int ADC_enable = 0;			/* A/D转换器资是否可用标志位 */

static ADC_DEV adcdev;				/* 用于表示ADC设备 */
static volatile int ev_adc = 0;		/* 作为wait_event_interruptible的唤醒条件 */
static int adc_data;

static struct clk	*adc_clock;

#define ADCCON		(*(volatile unsigned long *)(base_addr + S3C2410_ADCCON))	//ADC control
#define ADCTSC		(*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC))	//ADC touch screen control
#define ADCDLY		(*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY))	//ADC start or Interval Delay
#define ADCDAT0		(*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0))	//ADC conversion data 0
#define ADCDAT1		(*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1))	//ADC conversion data 1
#define ADCUPDN		(*(volatile unsigned long *)(base_addr + 0x14))			//Stylus Up/Down interrupt status

#define PRESCALE_DIS	(0 << 14)
#define PRESCALE_EN		(1 << 14)
#define PRSCVL(x)		((x) << 6)
#define ADC_INPUT(x)	((x) << 3)
#define ADC_START		(1 << 0)
#define ADC_ENDCVT		(1 << 15)


/* 使能预分频,选择ADC通道,最后启动ADC转换*/
#define START_ADC_AIN(ch, prescale) \
	do{ 	ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \
		ADCCON |= ADC_START; \
	}while(0)


/* ADC中断处理函数 */
static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{
	/* A/D转换器资源可用 */
	if (ADC_enable)
	{
		/* 读ADC转换结果数据 */
		adc_data = ADCDAT0 & 0x3ff;

		/* 唤醒标志位,作为wait_event_interruptible的唤醒条件 */
		ev_adc = 1;
		wake_up_interruptible(&adcdev.wait);
	}
	return IRQ_HANDLED;
}

static ssize_t tq2440_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
	char str[20];
	int value;
	size_t len;

	/* 尝试获得ADC_LOCK信号量,如果能够立刻获得,它就获得信号量并返回0 
	 * 否则,返回非零,它不会导致调用者睡眠,可以在中断上下文使用
	 */
	if (down_trylock(&ADC_LOCK) == 0)
	{
		/* 表示A/D转换器资源可用 */
		ADC_enable = 1;

		/* 使能预分频,选择ADC通道,最后启动ADC转换*/
		START_ADC_AIN(adcdev.channel, adcdev.prescale);

		/* 等待事件,当ev_adc = 0时,进程被阻塞,直到ev_adc>0 */
		wait_event_interruptible(adcdev.wait, ev_adc);

		ev_adc = 0;

		DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ((ADCCON & 0x80) ? 1:0));

		/* 将在ADC中断处理函数读取的ADC转换结果赋值给value */
		value = adc_data;
		sprintf(str,"%5d", adc_data);
		copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

		ADC_enable = 0;
		up(&ADC_LOCK);
	}
	else
	{
		/* 如果A/D转换器资源不可用,将value赋值为-1 */
		value = -1;
	}

	/* 将ADC转换结果输出到str数组里,以便传给应用空间 */
	len = sprintf(str, "%d\n", value);
	if (count >= len)
	{
		/* 从str数组里拷贝len字节的数据到buffer,即将ADC转换数据传给应用空间 */
		int r = copy_to_user(buffer, str, len);
		return r ? r : len;
	}
	else
	{
		return -EINVAL;
	}
}

static int tq2440_adc_open(struct inode *inode, struct file *filp)
{
	/* 初始化等待队列头 */
	init_waitqueue_head(&(adcdev.wait));

	/* 开发板上ADC的通道2连接着一个电位器 */
	adcdev.channel=2;	//设置ADC的通道
	adcdev.prescale=0xff;

	DPRINTK( "ADC opened\n");
	return 0;
}

static int tq2440_adc_release(struct inode *inode, struct file *filp)
{
	DPRINTK( "ADC closed\n");
	return 0;
}


static struct file_operations dev_fops = {
	owner:	THIS_MODULE,
	open:	tq2440_adc_open,
	read:	tq2440_adc_read,	
	release:	tq2440_adc_release,
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret;

	base_addr=ioremap(S3C2410_PA_ADC,0x20);
	if (base_addr == NULL)
	{
		printk(KERN_ERR "failed to remap register block\n");
		return -ENOMEM;
	}

	adc_clock = clk_get(NULL, "adc");
	if (!adc_clock)
	{
		printk(KERN_ERR "failed to get adc clock source\n");
		return -ENOENT;
	}
	clk_enable(adc_clock);
	
	ADCTSC = 0;

	ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);
	if (ret)
	{
		iounmap(base_addr);
		return ret;
	}

	ret = misc_register(&misc);

	printk (DEVICE_NAME" initialized\n");
	return ret;
}

static void __exit dev_exit(void)
{
	free_irq(IRQ_ADC, &adcdev);
	iounmap(base_addr);

	if (adc_clock)
	{
		clk_disable(adc_clock);
		clk_put(adc_clock);
		adc_clock = NULL;
	}

	misc_deregister(&misc);
}

EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.embedsky.net");
MODULE_DESCRIPTION("ADC Drivers for EmbedSky SKY2440/TQ2440 Board and support touch");
登录后复制
ADC应用测试参考源码:

/*************************************

NAME:EmbedSky_adc.c
COPYRIGHT:www.embedsky.net

*************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <errno.h>
#include <string.h>

int main(void)
{
	int fd ;
	char temp = 1;

	fd = open("/dev/adc", 0);
	if (fd < 0)
	{
		perror("open ADC device !");
		exit(1);
	}
	
	for( ; ; )
	{
		char buffer[30];
		int len ;

		len = read(fd, buffer, sizeof buffer -1);
		if (len > 0)
		{
			buffer[len] = '\0';
			int value;
			sscanf(buffer, "%d", &value);
			printf("ADC Value: %d\n", value);
		}
		else
		{
			perror("read ADC device !");
			exit(1);
		}
		sleep(1);
	}
adcstop:	
	close(fd);
}
登录后复制
测试结果:

[WJ2440]# ./adc_test 
ADC Value: 693
ADC Value: 695
ADC Value: 694
ADC Value: 695
ADC Value: 702
ADC Value: 740
ADC Value: 768
ADC Value: 775
ADC Value: 820
ADC Value: 844
ADC Value: 887
ADC Value: 937
ADC Value: 978
ADC Value: 1000
ADC Value: 1023
ADC Value: 1023
ADC Value: 1023
登录后复制

相关推荐:《Linux视频教程

以上是linux adc是什么设备的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Linux实际上有什么好处? Linux实际上有什么好处? Apr 12, 2025 am 12:20 AM

Linux适用于服务器、开发环境和嵌入式系统。1.作为服务器操作系统,Linux稳定高效,常用于部署高并发应用。2.作为开发环境,Linux提供高效的命令行工具和包管理系统,提升开发效率。3.在嵌入式系统中,Linux轻量且可定制,适合资源有限的环境。

将Docker与Linux一起使用:综合指南 将Docker与Linux一起使用:综合指南 Apr 12, 2025 am 12:07 AM

在Linux上使用Docker可以提高开发和部署效率。1.安装Docker:使用脚本在Ubuntu上安装Docker。2.验证安装:运行sudodockerrunhello-world。3.基本用法:创建Nginx容器dockerrun--namemy-nginx-p8080:80-dnginx。4.高级用法:创建自定义镜像,使用Dockerfile构建并运行。5.优化与最佳实践:使用多阶段构建和DockerCompose,遵循编写Dockerfile的最佳实践。

apache80端口被占用怎么办 apache80端口被占用怎么办 Apr 13, 2025 pm 01:24 PM

当 Apache 80 端口被占用时,解决方法如下:找出占用该端口的进程并关闭它。检查防火墙设置以确保 Apache 未被阻止。如果以上方法无效,请重新配置 Apache 使用不同的端口。重启 Apache 服务。

apache怎么启动 apache怎么启动 Apr 13, 2025 pm 01:06 PM

启动 Apache 的步骤如下:安装 Apache(命令:sudo apt-get install apache2 或从官网下载)启动 Apache(Linux:sudo systemctl start apache2;Windows:右键“Apache2.4”服务并选择“启动”)检查是否已启动(Linux:sudo systemctl status apache2;Windows:查看服务管理器中“Apache2.4”服务的状态)启用开机自动启动(可选,Linux:sudo systemctl

oracle怎么启动监听 oracle怎么启动监听 Apr 12, 2025 am 06:00 AM

启动 Oracle 监听器的步骤如下:检查监听器状态(使用 lsnrctl status 命令)对于 Windows,在 Oracle Services Manager 中启动 "TNS Listener" 服务对于 Linux 和 Unix,使用 lsnrctl start 命令启动监听器运行 lsnrctl status 命令验证监听器是否已启动

如何在Debian上监控Nginx SSL性能 如何在Debian上监控Nginx SSL性能 Apr 12, 2025 pm 10:18 PM

本文介绍如何在Debian系统上有效监控Nginx服务器的SSL性能。我们将使用NginxExporter将Nginx状态数据导出到Prometheus,再通过Grafana进行可视化展示。第一步:配置Nginx首先,我们需要在Nginx配置文件中启用stub_status模块来获取Nginx的状态信息。在你的Nginx配置文件(通常位于/etc/nginx/nginx.conf或其包含文件中)中添加以下代码段:location/nginx_status{stub_status

Debian系统中如何设置回收站 Debian系统中如何设置回收站 Apr 12, 2025 pm 10:51 PM

本文介绍两种在Debian系统中配置回收站的方法:图形界面和命令行。方法一:使用Nautilus图形界面打开文件管理器:在桌面或应用程序菜单中找到并启动Nautilus文件管理器(通常名为“文件”)。找到回收站:在左侧导航栏中寻找“回收站”文件夹。如果找不到,请尝试点击“其他位置”或“计算机”进行搜索。配置回收站属性:右键点击“回收站”,选择“属性”。在属性窗口中,您可以调整以下设置:最大大小:限制回收站可用的磁盘空间。保留时间:设置文件在回收站中自动删除前的保

怎么启动oracle监听器 怎么启动oracle监听器 Apr 11, 2025 pm 10:54 PM

如何启动 Oracle 监听器?检查监听器状态:使用命令 lsnrctl status。启动监听器:使用命令 lsnrctl start。验证监听器状态:再次使用命令 lsnrctl status。

See all articles