這篇文章主要介紹了linux adc設(shè)備指的是什么的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇linux adc設(shè)備指的是什么文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到定日網(wǎng)站設(shè)計(jì)與定日網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名與空間、虛擬主機(jī)、企業(yè)郵箱。業(yè)務(wù)覆蓋定日地區(qū)。
linux adc是混雜設(shè)備驅(qū)動(dòng);在linux2.6.30.4中,系統(tǒng)已經(jīng)自帶有了ADC通用驅(qū)動(dòng)文件“arch/arm/plat-s3c24xx/adc.c”,它是以平臺(tái)驅(qū)動(dòng)設(shè)備模型的架構(gòu)來(lái)編寫(xiě)的,里面是一些比較通用穩(wěn)定的代碼。
linux2.6.30.4中,系統(tǒng)已經(jīng)自帶有了ADC通用驅(qū)動(dòng)文件---arch/arm/plat-s3c24xx/adc.c,它是以平臺(tái)驅(qū)動(dòng)設(shè)備模型的架構(gòu)來(lái)編寫(xiě)的,里面是一些比較通用穩(wěn)定的代碼,但是linux2.6.30.4版本的ADC通用驅(qū)動(dòng)文件并不完善,居然沒(méi)有讀函數(shù)。后來(lái)去看了linux3.8版本的ADC通用文件----arch/arm/plat-samsung/adc.c才是比較完善的。
但是本節(jié)并不是分析這個(gè)文件,而是以另外一種架構(gòu)來(lái)編寫(xiě)ADC驅(qū)動(dòng),因?yàn)锳DC驅(qū)動(dòng)實(shí)在是比較簡(jiǎn)單,就沒(méi)有使用平臺(tái)驅(qū)動(dòng)設(shè)備模型為架構(gòu)來(lái)編寫(xiě)了,這次我們使用的是混雜(misc)設(shè)備驅(qū)動(dòng)。
問(wèn):什么是misc設(shè)備驅(qū)動(dòng)?
答:miscdevice共享一個(gè)主設(shè)備號(hào)MISC_MAJOR(10),但次設(shè)備號(hào)不同。所有的miscdevice設(shè)備形成一條鏈表,對(duì)設(shè)備訪問(wèn)時(shí)內(nèi)核根據(jù)設(shè)備號(hào)來(lái)查找對(duì)應(yīng)的miscdevice設(shè)備,然后調(diào)用其file_operations結(jié)構(gòu)體中注冊(cè)的文件操作接口進(jìn)行操作。
struct miscdevice { int minor; //次設(shè)備號(hào),如果設(shè)置為MISC_DYNAMIC_MINOR則系統(tǒng)自動(dòng)分配 const char *name; //設(shè)備名 const struct file_operations *fops; //操作函數(shù) struct list_head list; struct device *parent; struct device *this_device; };
dev_init入口函數(shù)分析:
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寄存器地址將其轉(zhuǎn)換為虛擬地址,然后獲得ADC時(shí)鐘并使能ADC時(shí)鐘,接著申請(qǐng)ADC中斷,其中斷處理函數(shù)為
adcdone_int_handler,而flags為IRQF_SHARED,即共享中斷,因?yàn)橛|摸屏里也要申請(qǐng)ADC中斷,最后注冊(cè)一個(gè)混雜設(shè)備。
當(dāng)應(yīng)用程序open ("/dev/adc",...)時(shí),就會(huì)調(diào)用到驅(qū)動(dòng)里面的open函數(shù),那么我們來(lái)看看open函數(shù)做了什么?
static int tq2440_adc_open(struct inode *inode, struct file *filp) { /* 初始化等待隊(duì)列頭 */ init_waitqueue_head(&(adcdev.wait)); /* 開(kāi)發(fā)板上ADC的通道2連接著一個(gè)電位器 */ adcdev.channel=2; //設(shè)置ADC的通道 adcdev.prescale=0xff; DPRINTK( "ADC opened\n"); return 0; }
很簡(jiǎn)單,先初始化一個(gè)等待隊(duì)列頭,因?yàn)槿肟诤瘮?shù)里既然有申請(qǐng)ADC中斷,那么肯定要使用等待隊(duì)列,接著設(shè)置ADC通道,因?yàn)門Q2440的ADC輸入通道默認(rèn)是2,設(shè)置預(yù)分頻值為0xff。
當(dāng)應(yīng)用程序read時(shí),就會(huì)調(diào)用到驅(qū)動(dòng)里面的read函數(shù),那么我們來(lái)看看read函數(shù)做了些什么?
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信號(hào)量,如果能夠立刻獲得,它就獲得信號(hào)量并返回0 * 否則,返回非零,它不會(huì)導(dǎo)致調(diào)用者睡眠,可以在中斷上下文使用 */ if (down_trylock(&ADC_LOCK) == 0) { /* 表示A/D轉(zhuǎn)換器資源可用 */ ADC_enable = 1; /* 使能預(yù)分頻,選擇ADC通道,最后啟動(dòng)ADC轉(zhuǎn)換*/ START_ADC_AIN(adcdev.channel, adcdev.prescale); /* 等待事件,當(dāng)ev_adc = 0時(shí),進(jìn)程被阻塞,直到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中斷處理函數(shù)讀取的ADC轉(zhuǎn)換結(jié)果賦值給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轉(zhuǎn)換器資源不可用,將value賦值為-1 */ value = -1; } /* 將ADC轉(zhuǎn)換結(jié)果輸出到str數(shù)組里,以便傳給應(yīng)用空間 */ len = sprintf(str, "%d\n", value); if (count >= len) { /* 從str數(shù)組里拷貝len字節(jié)的數(shù)據(jù)到buffer,即將ADC轉(zhuǎn)換數(shù)據(jù)傳給應(yīng)用空間 */ int r = copy_to_user(buffer, str, len); return r ? r : len; } else { return -EINVAL; } }
tq2440_adc_read函數(shù)首先嘗試獲得ADC_LOCK信號(hào)量,因?yàn)橛|摸屏驅(qū)動(dòng)也有使用ADC資源,兩者互有競(jìng)爭(zhēng)關(guān)系,獲得ADC資源后,使能預(yù)分頻,選擇ADC通道,最后啟動(dòng)ADC轉(zhuǎn)換,接著就調(diào)用wait_event_interruptible 函數(shù)進(jìn)行等待,直到ev_adc>0進(jìn)程才會(huì)繼續(xù)往下跑,往下跑就會(huì)將adc_data數(shù)據(jù)讀出來(lái),調(diào)用copy_to_user函數(shù)將ADC數(shù)據(jù)傳給應(yīng)用空間,最后釋放ADC_LOCK信號(hào)量。
問(wèn):什么時(shí)候ev_adc>0?默認(rèn)ev_adc = 0
答:在adcdone_int_handler中斷處理函數(shù)里,等數(shù)據(jù)讀出后,ev_adc被設(shè)置為1。
ADC中斷處理函數(shù)adcdone_int_handler
/* ADC中斷處理函數(shù) */ static irqreturn_t adcdone_int_handler(int irq, void *dev_id) { /* A/D轉(zhuǎn)換器資源可用 */ if (ADC_enable) { /* 讀ADC轉(zhuǎn)換結(jié)果數(shù)據(jù) */ adc_data = ADCDAT0 & 0x3ff; /* 喚醒標(biāo)志位,作為wait_event_interruptible的喚醒條件 */ ev_adc = 1; wake_up_interruptible(&adcdev.wait); } return IRQ_HANDLED; }
當(dāng)AD轉(zhuǎn)換完成后就會(huì)觸發(fā)ADC中斷,就會(huì)進(jìn)入adcdone_int_handler,這個(gè)函數(shù)就會(huì)講AD轉(zhuǎn)換數(shù)據(jù)讀到adc_data,接著將喚醒標(biāo)志位ev_adc置1,最后調(diào)用wake_up_interruptible函數(shù)喚醒a(bǔ)dcdev.wait等待隊(duì)列。
總結(jié)一下ADC的工作流程:
一、open函數(shù)里,設(shè)置模擬輸入通道,設(shè)置預(yù)分頻值
二、read函數(shù)里,啟動(dòng)AD轉(zhuǎn)換,進(jìn)程休眠
三、adc_irq函數(shù)里,AD轉(zhuǎn)換結(jié)束后觸發(fā)ADC中斷,在ADC中斷處理函數(shù)將數(shù)據(jù)讀出,喚醒進(jìn)程
四、read函數(shù)里,進(jìn)程被喚醒后,將adc轉(zhuǎn)換數(shù)據(jù)傳給應(yīng)用程序
ADC驅(qū)動(dòng)參考源碼:
/************************************* NAME:EmbedSky_adc.c COPYRIGHT:www.embedsky.net *************************************/ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/init.h> #include <linux/serio.h> #include <linux/delay.h> #include <linux/clk.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <mach/regs-clock.h> #include <plat/regs-timer.h> #include <plat/regs-adc.h> #include <mach/regs-gpio.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #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" /* 設(shè)備節(jié)點(diǎn): /dev/adc */ static void __iomem *base_addr; typedef struct { wait_queue_head_t wait; /* 定義等待隊(duì)列頭 */ int channel; int prescale; }ADC_DEV; DECLARE_MUTEX(ADC_LOCK); /* 定義并初始化信號(hào)量,并初始化為1 */ static int ADC_enable = 0; /* A/D轉(zhuǎn)換器資是否可用標(biāo)志位 */ static ADC_DEV adcdev; /* 用于表示ADC設(shè)備 */ 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) /* 使能預(yù)分頻,選擇ADC通道,最后啟動(dòng)ADC轉(zhuǎn)換*/ #define START_ADC_AIN(ch, prescale) \ do{ ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \ ADCCON |= ADC_START; \ }while(0) /* ADC中斷處理函數(shù) */ static irqreturn_t adcdone_int_handler(int irq, void *dev_id) { /* A/D轉(zhuǎn)換器資源可用 */ if (ADC_enable) { /* 讀ADC轉(zhuǎn)換結(jié)果數(shù)據(jù) */ adc_data = ADCDAT0 & 0x3ff; /* 喚醒標(biāo)志位,作為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信號(hào)量,如果能夠立刻獲得,它就獲得信號(hào)量并返回0 * 否則,返回非零,它不會(huì)導(dǎo)致調(diào)用者睡眠,可以在中斷上下文使用 */ if (down_trylock(&ADC_LOCK) == 0) { /* 表示A/D轉(zhuǎn)換器資源可用 */ ADC_enable = 1; /* 使能預(yù)分頻,選擇ADC通道,最后啟動(dòng)ADC轉(zhuǎn)換*/ START_ADC_AIN(adcdev.channel, adcdev.prescale); /* 等待事件,當(dāng)ev_adc = 0時(shí),進(jìn)程被阻塞,直到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中斷處理函數(shù)讀取的ADC轉(zhuǎn)換結(jié)果賦值給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轉(zhuǎn)換器資源不可用,將value賦值為-1 */ value = -1; } /* 將ADC轉(zhuǎn)換結(jié)果輸出到str數(shù)組里,以便傳給應(yīng)用空間 */ len = sprintf(str, "%d\n", value); if (count >= len) { /* 從str數(shù)組里拷貝len字節(jié)的數(shù)據(jù)到buffer,即將ADC轉(zhuǎn)換數(shù)據(jù)傳給應(yīng)用空間 */ 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) { /* 初始化等待隊(duì)列頭 */ init_waitqueue_head(&(adcdev.wait)); /* 開(kāi)發(fā)板上ADC的通道2連接著一個(gè)電位器 */ adcdev.channel=2; //設(shè)置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應(yīng)用測(cè)試參考源碼:
/************************************* 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); }
測(cè)試結(jié)果:
[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
關(guān)于“l(fā)inux adc設(shè)備指的是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“l(fā)inux adc設(shè)備指的是什么”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
當(dāng)前名稱:linuxadc設(shè)備指的是什么
鏈接分享:http://jinyejixie.com/article0/ggipio.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)、手機(jī)網(wǎng)站建設(shè)、建站公司、、網(wǎng)站設(shè)計(jì)公司、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)