linux adc는 하이브리드 장치 드라이버입니다. linux2.6.30.4의 시스템에는 이미 플랫폼 기반 장치 모델 아키텍처인 ADC 범용 드라이버 파일 "arch/arm/plat-s3c24xx/adc.c"가 함께 제공됩니다. 여기에 작성되었으며 비교적 일반적이고 안정적인 코드가 포함되어 있습니다.
이 튜토리얼의 운영 환경: 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; };
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; }
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; }
애플리케이션이 읽으면 드라이버의 읽기 기능이 호출됩니다. 그럼 읽기 기능이 무엇인지 살펴보겠습니다.
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; } }
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; }
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");
/************************************* 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!