Rumah >Operasi dan penyelenggaraan >operasi dan penyelenggaraan linux >Apakah peranti linux adc
linux adc ialah pemacu peranti hibrid; dalam linux2.6.30.4, sistem sudah disertakan dengan fail pemacu universal ADC "arch/arm/plat-s3c24xx/adc.c", yang berdasarkan pada pemacu platform Ia ditulis mengikut seni bina model peranti, dan mengandungi beberapa kod yang agak umum dan stabil.
Persekitaran pengendalian tutorial ini: sistem linux2.6.30.4, komputer Dell G3.
Apakah jenis peranti linux adc?
Pemacu peranti hibrid Linux pemacu adc
Dalam linux2.6.30.4, sistem mempunyai secara automatik Ia disertakan dengan fail pemacu universal ADC ---arch/arm/plat-s3c24xx/adc.c, yang ditulis berdasarkan seni bina model peranti pemacu platform Ia mengandungi beberapa kod yang agak biasa dan stabil, tetapi linux2.6.30 .4 Versi fail pemacu universal ADC tidak lengkap, dan tiada fungsi membaca. Kemudian, saya melihat fail umum ADC versi Linux 3.8 - arch/arm/plat-samsung/adc.c yang agak lengkap.
Tetapi bahagian ini bukan untuk menganalisis fail ini, tetapi untuk menulis pemacu ADC dalam seni bina lain Kerana pemacu ADC agak mudah, ia tidak digunakan . Model peranti pemacu platform ditulis untuk seni bina, kali ini kami menggunakan pemacu peranti lain.
S: Apakah itu pemacu peranti misc?
Jawapan: miscdevice berkongsi nombor peranti utama MISC_MAJOR (10), tetapi nombor peranti kecil adalah berbeza. Semua peranti miscdevice membentuk senarai terpaut Apabila mengakses peranti, kernel mencari peranti miscdevice yang sepadan berdasarkan nombor peranti, dan kemudian memanggil antara muka operasi fail yang didaftarkan dalam struktur operasi_failnya untuk beroperasi.
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; };analisis fungsi kemasukan 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; }Pertama ialah memetakan alamat daftar ADC dan tukar ia adalah alamat maya, kemudian dapatkan jam ADC dan dayakan jam ADC, dan kemudian gunakan sampukan ADC Fungsi pemprosesan sampukan ialah
adcdone_int_handler, dan bendera ialah IRQF_SHARED, yang dikongsi. sampuk, kerana skrin sentuh juga perlu memohon sampukan ADC Akhirnya daftarkan peranti rambang.
Apabila aplikasi dibuka ("/dev/adc",...), fungsi terbuka dalam pemacu akan dipanggil, jadi mari kita lihat Lihat apa yang dilakukan oleh fungsi terbuka?
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; }Sangat mudah, mula-mula mulakan kepala giliran menunggu, kerana kerana terdapat permohonan untuk gangguan ADC dalam entri fungsi, ia mestilah Gunakan baris gilir menunggu, dan kemudian tetapkan saluran ADC Kerana saluran input ADC TQ2440 ialah 2 secara lalai, tetapkan nilai praskala kepada 0xff.
Apabila aplikasi dibaca, fungsi baca dalam pemandu akan dipanggil. Jadi mari kita lihat fungsi baca?
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; } }Fungsi tq2440_adc_read mula-mula cuba mendapatkan semafor ADC_LOCK, kerana pemacu skrin sentuh juga menggunakan sumber ADC, dan dua bersaing antara satu sama lain , selepas memperoleh sumber ADC, dayakan praskala, pilih saluran ADC, dan akhirnya mulakan penukaran ADC, kemudian panggil fungsi wait_event_interruptible untuk menunggu sehingga proses ev_adc>0 akan terus berjalan, berjalan ke bawah. akan data adc_data dibacakan, fungsi copy_to_user dipanggil untuk memindahkan data ADC ke ruang aplikasi, dan akhirnya ADC_LOCK semaphore dikeluarkan.
S: Bilakah ev_adc>0? Ev_adc lalai = 0
Jawapan: Dalam fungsi pemprosesan gangguan adcdone_int_handler, selepas data dibacakan, ev_adc ditetapkan kepada 1.
Fungsi pengendali gangguan 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; }Apabila penukaran AD selesai, gangguan ADC akan dicetuskan dan adcdone_int_handler akan dimasukkan Fungsi ini akan membaca data penukaran AD ke adc_data, kemudian menetapkan bendera bangun ev_adc kepada 1, dan akhirnya memanggil. fungsi wake_up_interruptible untuk bangun adcdev tunggu menunggu giliran.
1 Dalam fungsi terbuka, tetapkan saluran input analog dan tetapkan nilai praskala
. 2. Dalam fungsi baca, penukaran AD dimulakan dan proses tidur
三、adc_irq函数里,AD转换结束后触发ADC中断,在ADC中断处理函数将数据读出,唤醒进程
四、read函数里,进程被唤醒后,将adc转换数据传给应用程序
ADC驱动参考源码:
/************************************* NAME:EmbedSky_adc.c COPYRIGHT:www.embedsky.net *************************************/ #includeADC应用测试参考源码:#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视频教程》
Atas ialah kandungan terperinci Apakah peranti linux adc. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!