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

linux adc是什么设备

Apr 17, 2023 am 09:33 AM
linuxadc

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
Debian如何提升Hadoop数据处理速度Debian如何提升Hadoop数据处理速度Apr 13, 2025 am 11:54 AM

本文探讨如何在Debian系统上提升Hadoop数据处理效率。优化策略涵盖硬件升级、操作系统参数调整、Hadoop配置修改以及高效算法和工具的运用。一、硬件资源强化确保所有节点硬件配置一致,尤其关注CPU、内存和网络设备性能。选择高性能硬件组件对于提升整体处理速度至关重要。二、操作系统调优文件描述符和网络连接数:修改/etc/security/limits.conf文件,增加系统允许同时打开的文件描述符和网络连接数上限。JVM参数调整:在hadoop-env.sh文件中调整

Debian syslog如何学习Debian syslog如何学习Apr 13, 2025 am 11:51 AM

本指南将指导您学习如何在Debian系统中使用Syslog。Syslog是Linux系统中用于记录系统和应用程序日志消息的关键服务,它帮助管理员监控和分析系统活动,从而快速识别并解决问题。一、Syslog基础知识Syslog的核心功能包括:集中收集和管理日志消息;支持多种日志输出格式和目标位置(例如文件或网络);提供实时日志查看和过滤功能。二、安装和配置Syslog(使用Rsyslog)Debian系统默认使用Rsyslog。您可以通过以下命令安装:sudoaptupdatesud

Debian中Hadoop版本怎么选Debian中Hadoop版本怎么选Apr 13, 2025 am 11:48 AM

选择适合Debian系统的Hadoop版本,需要综合考虑以下几个关键因素:一、稳定性与长期支持:对于追求稳定性和安全性的用户,建议选择Debian稳定版,例如Debian11(Bullseye)。该版本经过充分测试,拥有长达五年的支持周期,能够确保系统稳定运行。二、软件包更新速度:如果您需要使用最新的Hadoop功能和特性,则可以考虑Debian的不稳定版(Sid)。但需注意,不稳定版可能存在兼容性问题和稳定性风险。三、社区支持与资源:Debian拥有庞大的社区支持,可以提供丰富的文档和

Debian上TigerVNC共享文件方法Debian上TigerVNC共享文件方法Apr 13, 2025 am 11:45 AM

本文介绍如何在Debian系统上使用TigerVNC共享文件。你需要先安装TigerVNC服务器,然后进行配置。一、安装TigerVNC服务器打开终端。更新软件包列表:sudoaptupdate安装TigerVNC服务器:sudoaptinstalltigervnc-standalone-servertigervnc-common二、配置TigerVNC服务器设置VNC服务器密码:vncpasswd启动VNC服务器:vncserver:1-localhostno

Debian邮件服务器防火墙配置技巧Debian邮件服务器防火墙配置技巧Apr 13, 2025 am 11:42 AM

配置Debian邮件服务器的防火墙是确保服务器安全性的重要步骤。以下是几种常用的防火墙配置方法,包括iptables和firewalld的使用。使用iptables配置防火墙安装iptables(如果尚未安装):sudoapt-getupdatesudoapt-getinstalliptables查看当前iptables规则:sudoiptables-L配置

Debian邮件服务器SSL证书安装方法Debian邮件服务器SSL证书安装方法Apr 13, 2025 am 11:39 AM

在Debian邮件服务器上安装SSL证书的步骤如下:1.安装OpenSSL工具包首先,确保你的系统上已经安装了OpenSSL工具包。如果没有安装,可以使用以下命令进行安装:sudoapt-getupdatesudoapt-getinstallopenssl2.生成私钥和证书请求接下来,使用OpenSSL生成一个2048位的RSA私钥和一个证书请求(CSR):openss

Debian邮件服务器虚拟主机配置方法Debian邮件服务器虚拟主机配置方法Apr 13, 2025 am 11:36 AM

在Debian系统上配置邮件服务器的虚拟主机通常涉及安装和配置邮件服务器软件(如Postfix、Exim等),而不是ApacheHTTPServer,因为Apache主要用于Web服务器功能。以下是配置邮件服务器虚拟主机的基本步骤:安装Postfix邮件服务器更新系统软件包:sudoaptupdatesudoaptupgrade安装Postfix:sudoapt

Debian邮件服务器DNS设置指南Debian邮件服务器DNS设置指南Apr 13, 2025 am 11:33 AM

要配置Debian邮件服务器的DNS设置,您可以按照以下步骤进行操作:打开网络配置文件:使用文本编辑器(例如vi或nano)打开网络配置文件/etc/network/interfaces。sudonano/etc/network/interfaces查找网络接口配置:在配置文件中找到要修改的网络接口。通常情况下,以太网接口的配置位于ifaceeth0区块中。

See all articles

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

热工具

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用