>운영 및 유지보수 >리눅스 운영 및 유지 관리 >Linux ADC는 어떤 장치인가요?

Linux ADC는 어떤 장치인가요?

藏色散人
藏色散人원래의
2023-04-17 09:33:001778검색

linux adc는 하이브리드 장치 드라이버입니다. linux2.6.30.4의 시스템에는 이미 플랫폼 기반 장치 모델 아키텍처인 ADC 범용 드라이버 파일 "arch/arm/plat-s3c24xx/adc.c"가 함께 제공됩니다. 여기에 작성되었으며 비교적 일반적이고 안정적인 코드가 포함되어 있습니다.

Linux ADC는 어떤 장치인가요?

이 튜토리얼의 운영 환경: linux2.6.30.4 시스템, Dell G3 컴퓨터.

리눅스 ADC는 어떤 기기인가요?

linux 혼합 장치 드라이버 adc 드라이버

linux2.6.30.4, 시스템에는 이미 ADC 범용 드라이버 파일 ---arch/arm/plat-s3c24xx/adc.c가 함께 제공됩니다. 플랫폼 드라이버 장치 모델의 아키텍처를 기반으로 작성되었으며 비교적 일반적이고 안정적인 코드가 포함되어 있습니다. 그러나 Linux 2.6.30.4 버전의 ADC 범용 드라이버 파일은 완전하지 않으며 읽기 기능이 없습니다. 나중에 Linux 3.8 버전의 ADC 일반 파일(arch/arm/plat-samsung/adc.c)을 살펴보았는데 비교적 완성도가 높습니다.

이 섹션에서는 이 파일을 분석하는 것이 아니라 다른 아키텍처로 ADC 드라이버를 작성하는 것입니다. ADC 드라이버는 비교적 간단하기 때문에 이번에는 플랫폼 드라이버 장치 모델을 아키텍처로 사용하여 작성하지 않습니다. 우리는 기타 장치 드라이버를 사용하고 있습니다.

Q: 기타 장치 드라이버란 무엇입니까?

답변: miscdevice는 주요 장치 번호 MISC_MAJOR(10)을 공유하지만 보조 장치 번호는 다릅니다. 모든 miscdevice 디바이스는 Linked List를 형성하여 해당 디바이스에 접근하면 커널은 해당 디바이스 번호를 기준으로 해당 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이고, 플래그는 공유 인터럽트인 IRQF_SHARED인데, 터치스크린도 ADC 인터럽트를 적용해야 하고, 최종적으로 하이브리드 장치를 등록해야 하기 때문이다.

애플리케이션이 열리면("/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로 설정합니다.

애플리케이션이 읽으면 드라이버의 읽기 기능이 호출됩니다. 그럼 읽기 기능이 무엇인지 살펴보겠습니다.

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 세마포어를 해제합니다.

Q: 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가 입력됩니다. 그런 다음 wake-up 플래그 ev_adc를 1로 설정하고 마지막으로 wake_up_interruptible 함수를 호출하여 adcdev.wait 대기 큐를 깨웁니다.
ADC 작업 흐름 요약:

1. 개방형 기능에서 아날로그 입력 채널을 설정하고 프리스케일러 값을 설정합니다.

2. 읽기 기능에서 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으로 문의하세요.