[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(7) Linux字符驅(qū)動(dòng)之ADC模數(shù)轉(zhuǎn)換驅(qū)動(dòng)
[OK210開(kāi)發(fā)板體驗(yàn)]入門(mén)篇(2):板載資源
[OK210開(kāi)發(fā)板體驗(yàn)]入門(mén)篇(3):開(kāi)發(fā)環(huán)境(軟件安裝,開(kāi)發(fā)環(huán)境,燒寫(xiě)系統(tǒng))
[OK210開(kāi)發(fā)板體驗(yàn)]入門(mén)篇(4):編程入門(mén)(NFS登錄,驅(qū)動(dòng)入門(mén))
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(1):Linux字符驅(qū)動(dòng)之Led
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(2):Linux字符驅(qū)動(dòng)之Key按鍵
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(3):Linux Input子系統(tǒng)之Key按鍵
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(4):Linux字符驅(qū)動(dòng)之DS18B20
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(5):Linux字符驅(qū)動(dòng)之PWM蜂鳴器
[OK210開(kāi)發(fā)板體驗(yàn)]功能篇(6):Linux字符驅(qū)動(dòng)之紅外遙控
今天是功能篇的第七篇:Linux字符驅(qū)動(dòng)之ADC模數(shù)轉(zhuǎn)換,本節(jié)主要分3部分:硬件分析,軟件基礎(chǔ),驅(qū)動(dòng)編程。
一、硬件分析
在【OK210試用體驗(yàn)】的第二篇:板載資源中,簡(jiǎn)單分析了ADC的功能和作用。其實(shí)對(duì)ADC的操作,可以看作是對(duì)一些模擬量的采集操作,如溫濕度,光度。
首先從OK210的底板原理圖中可知,通過(guò)一個(gè)可調(diào)電位器RP1,連接到S5PV210的模擬輸入0通道,通過(guò)對(duì)該可調(diào)電位器的調(diào)節(jié)可以改變模擬通道的輸入電壓值,電壓的調(diào)節(jié)范圍為0V-3V,可以利用這個(gè)可調(diào)電位器來(lái)熟悉S5PV210的ADC控制器的使用,如下圖所示
 
 
	模擬輸入0通首家引腳為XADCAIN0,由S5PV210用戶(hù)手冊(cè),可知,該引腳是一個(gè)模塊引腳,如下圖所示。
所以,我們要對(duì)ADC進(jìn)行操作,就是通過(guò)對(duì)配置ADC的各寄存器進(jìn)行實(shí)現(xiàn)。
二、軟件基礎(chǔ)
1、S5PV210的ADC的主要特征:
(1)分辨率(輸出離散值的個(gè)數(shù))可以是10位或者12位(可以通過(guò)TSADCCON0/TSADCCON1的第16位RES進(jìn)行設(shè)定,將RES設(shè)為0表示10位,設(shè)為1表示12位)。
(2)10通道的模擬輸入(AIN[9]---AIN[0])。
(3)輸入電壓為0--3.3V。
(4)最大轉(zhuǎn)換速率:1MSPS。
對(duì)于轉(zhuǎn)換速率的計(jì)算,S5PV210_UM手冊(cè)上是這樣介紹的:當(dāng)PCLK=66MHz時(shí),預(yù)分頻比P=65時(shí),12位分辨率的轉(zhuǎn)換時(shí)間如下:
A/D轉(zhuǎn)換頻率=66MHz/(65+1)=1MHz
A/D轉(zhuǎn)換時(shí)間=1/(1MHz/5cycles)=1/200kHz=5us
PS
1)A/D的轉(zhuǎn)換頻率最大可達(dá)5MHz,所以A/D轉(zhuǎn)換時(shí)間最大可達(dá)1MSPS
2)對(duì)于官方手冊(cè)上,所說(shuō)的A/D轉(zhuǎn)換時(shí)間的計(jì)算,可能我們第一次看的時(shí)候會(huì)不太理解,會(huì)什么要1MHz/5cycles?原因是這樣的,因?yàn)锳/D轉(zhuǎn)換時(shí)間包括A/D建立時(shí)間,1位1位轉(zhuǎn)換時(shí)間,保存數(shù)據(jù)時(shí)間等等,加起來(lái)一共是5個(gè)時(shí)鐘周期,所以會(huì)是1MHz/5cycles。其實(shí)對(duì)于A/D轉(zhuǎn)換時(shí)間=1/(1MHz/5cycles) 可以換種寫(xiě)法:A/D轉(zhuǎn)換時(shí)間=1/1MHz *5=5us 也是一樣的,所以當(dāng)最大轉(zhuǎn)換頻率達(dá)到5MHz時(shí),A/D轉(zhuǎn)換時(shí)間可達(dá)1MSPS(SPS為每秒的采樣率)。
(5)具有采樣保持功能。
(6)ADC有10通道的模擬輸入,但是只有AIN[0],AIN[1]沒(méi)有復(fù)用,而AIN[2]--AIN[9]是復(fù)用為觸摸屏的兩路控制信號(hào)(XM,XP)。
2、S5PV210的ADC的寄存器
操作AIN[0],在編寫(xiě)ADC的驅(qū)動(dòng)代碼的時(shí)候,一般情況下只需考慮三個(gè)寄存器,分別是
| 寄存器 | 映射地址 | 讀/寫(xiě) | 描述 | 復(fù)位值 | 
| TSADCCON0 | 0xE170_0000 | R/W | TS0/ADC控制寄存器 | 0x0000_3FC4 | 
| TSDLY0 | 0xE170_0008 | R/W | TS0/ADC延時(shí)寄存器 | 0x0000_00FF | 
| TSDATX0 | 0xE170_000C | R | TS0/ADC轉(zhuǎn)換數(shù)據(jù)X寄存器 | --- | 
3、S5PV210的ADC操作流程
主要分三步:
(1)使用clk_get獲取adc時(shí)鐘,接著使用clk_enable使能adc時(shí)鐘;
(2)設(shè)置ADCCON的工作方式,預(yù)分頻比之類(lèi)的;
(3)當(dāng)開(kāi)始讀取數(shù)據(jù)值時(shí),開(kāi)啟ADC轉(zhuǎn)換,判斷是否開(kāi)始轉(zhuǎn)換,判斷是否轉(zhuǎn)換完成,最終讀取數(shù)據(jù),返回給用戶(hù)空間。值得注意的是,S5PV210的控制寄存器,是沒(méi)有通道選擇的,因?yàn)樵谑褂弥埃苯佑胕oremap進(jìn)行地址的映射時(shí),就相當(dāng)于選擇了不同的通道,所以無(wú)需通道選擇了。
三、驅(qū)動(dòng)編程
有圖有真相,
 
 
	1 驅(qū)動(dòng)代碼
- 
					#include <linux/kernel.h>
 
- 
					#include <linux/module.h>
 
- 
					#include <linux/slab.h>
 
- 
					#include <linux/input.h>
 
- 
					#include <linux/init.h>
 
- 
					#include <linux/errno.h>
 
- 
					#include <linux/serio.h>
 
- 
					#include <linux/delay.h>
 
- 
					#include <linux/clk.h>
 
- 
					#include <linux/wait.h>
 
- 
					#include <linux/sched.h>
 
- 
					#include <linux/cdev.h>
 
- 
					#include <linux/miscdevice.h>
 
- 
					
 
- 
					#include <asm/io.h>
 
- 
					#include <asm/irq.h>
 
- 
					#include <asm/uaccess.h>
 
- 
					
 
- 
					#include <mach/map.h>
 
- 
					#include <mach/regs-clock.h>
 
- 
					#include <mach/regs-adc.h>
 
- 
					#include <mach/regs-gpio.h>
 
- 
					#include <plat/regs-timer.h>
 
- 
					
 
- 
					#undef DEBUG
 
- 
					//#define DEBUG
 
- 
					#ifdef DEBUG
 
- 
					#define DPRINTK(x...) {printk(__FUNCTION__"(%d): ",__LINE__);printk(##x);}
 
- 
					#else
 
- 
					#define DPRINTK(x...) (void)(0)
 
- 
					#endif
 
- 
					
 
- 
					#define DEVICE_NAME        "adc"
 
- 
					static void __iomem *base_addr;
 
- 
					
 
- 
					typedef struct {
 
- 
					        wait_queue_head_t wait;
 
- 
					        int channel;
 
- 
					        int prescale;
 
- 
					} ADC_DEV;
 
- 
					
 
- 
					static int __ADC_locked = 0;
 
- 
					static ADC_DEV adcdev;
 
- 
					static volatile int ev_adc = 0;
 
- 
					static int adc_data;
 
- 
					static struct clk        *adc_clock;
 
- 
					
 
- 
					#define __ADCREG(name)        (*(volatile unsigned long *)(base_addr + name))
 
- 
					#define ADCCON                        __ADCREG(S3C_ADCCON)        // ADC control
 
- 
					#define ADCTSC                        __ADCREG(S3C_ADCTSC)        // ADC touch screen control
 
- 
					#define ADCDLY                        __ADCREG(S3C_ADCDLY)        // ADC start or Interval Delay
 
- 
					#define ADCDAT0                        __ADCREG(S3C_ADCDAT0)        // ADC conversion data 0
 
- 
					#define ADCDAT1                        __ADCREG(S3C_ADCDAT1)        // ADC conversion data 1
 
- 
					#define ADCUPDN                        __ADCREG(S3C_ADCUPDN)        // 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)
 
- 
					
 
- 
					#define START_ADC_AIN(ch, prescale) \
 
- 
					        do { \
 
- 
					                ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \
 
- 
					                ADCCON |= ADC_START; \
 
- 
					        } while (0)
 
- 
					
 
- 
					
 
- 
					static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
 
- 
					{
 
- 
					#if        1
 
- 
					        if (__ADC_locked) {
 
- 
					                adc_data = ADCDAT0 & 0x3ff;
 
- 
					
 
- 
					                ev_adc = 1;
 
- 
					                wake_up_interruptible(&adcdev.wait);
 
- 
					
 
- 
					                /* clear interrupt */
 
- 
					                __raw_writel(0x0, base_addr + S3C_ADCCLRINT);
 
- 
					        }
 
- 
					#endif
 
- 
					
 
- 
					        return IRQ_HANDLED;
 
- 
					}
 
- 
					
 
- 
					static ssize_t ok210_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
 
- 
					{
 
- 
					        char str[20];
 
- 
					        int value;
 
- 
					        size_t len;
 
- 
					
 
- 
					    __ADC_locked = 1;
 
- 
					    START_ADC_AIN(adcdev.channel, adcdev.prescale);
 
- 
					    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);
 
- 
					    value = adc_data;
 
- 
					    __ADC_locked = 0;
 
- 
					        len = sprintf(str, "%d\n", value);
 
- 
					        if (count >= len) {
 
- 
					                int r = copy_to_user(buffer, str, len);
 
- 
					                return r ? r : len;
 
- 
					        } else {
 
- 
					                return -EINVAL;
 
- 
					        }
 
- 
					}
 
- 
					
 
- 
					static int ok210_adc_open(struct inode *inode, struct file *filp)
 
- 
					{
 
- 
					        init_waitqueue_head(&(adcdev.wait));
 
- 
					
 
- 
					        adcdev.channel=0;
 
- 
					        adcdev.prescale=0xff;
 
- 
					
 
- 
					        DPRINTK("adc opened\n");
 
- 
					        return 0;
 
- 
					}
 
- 
					
 
- 
					static int ok210_adc_release(struct inode *inode, struct file *filp)
 
- 
					{
 
- 
					        DPRINTK("adc closed\n");
 
- 
					        return 0;
 
- 
					}
 
- 
					
 
- 
					
 
- 
					static struct file_operations dev_fops = {
 
- 
					        owner:        THIS_MODULE,
 
- 
					        open:        ok210_adc_open,
 
- 
					        read:        ok210_adc_read,
 
- 
					        release:        ok210_adc_release,
 
- 
					};
 
- 
					
 
- 
					static struct miscdevice misc = {
 
- 
					        .minor        = MISC_DYNAMIC_MINOR,
 
- 
					        .name        = "adc",        //DEVICE_NAME,
 
- 
					        .fops        = &dev_fops,
 
- 
					};
 
- 
					
 
- 
					static int __init dev_init(void)
 
- 
					{
 
- 
					        int ret;
 
- 
					
 
- 
					        base_addr = ioremap(SAMSUNG_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);
 
- 
					
 
- 
					        /* normal ADC */
 
- 
					        ADCTSC = 0;
 
- 
					
 
- 
					#if        1
 
- 
					        ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);
 
- 
					        if (ret) {
 
- 
					                printk("request IRQ %d failed for adc, %d\n", IRQ_ADC, ret);
 
- 
					                iounmap(base_addr);
 
- 
					                return ret;
 
- 
					        }
 
- 
					#endif
 
- 
					
 
- 
					        ret = misc_register(&misc);
 
- 
					
 
- 
					        printk (DEVICE_NAME"\tinitialized\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);
 
- 
					}
 
- 
					
 
- 
					module_init(dev_init);
 
- 
					module_exit(dev_exit);
 
- 
					
 
- 
					MODULE_LICENSE("GPL");
 
- 
					MODULE_AUTHOR("gjianw217@163.com");
 
- MODULE_DESCRIPTION("ADC driver");
- 
					#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)
 
- 
					{
 
- 
					        fprintf(stderr, "press Ctrl-C to stop\n");
 
- 
					        int fd = open("/dev/adc", "r");
 
- 
					        if (fd < 0) {
 
- 
					                perror("open ADC device:");
 
- 
					                return 1;
 
- 
					        }
 
- 
					        ioctl(fd,'s',0);
 
- 
					        int ADCValue=0;
 
- 
					        double voltage=0;
 
- 
					        for(;;) {
 
- 
					                ADCValue=0;
 
- 
					                voltage=0;
 
- 
					                read(fd, &ADCValue, sizeof(int));
 
- 
					                if(ADCValue==-1) continue;
 
- 
					                //channel 0 12bit ADC max voltage = 3.3v /10K+1k *10K =3v
 
- 
					                voltage=(float)3.3*ADCValue/4096;
 
- 
					                printf("adc = %d voltage=%f V \n",ADCValue,voltage);
 
- 
					                usleep(500* 1000);
 
- 
					        }
 
- 
					
 
- 
					        close(fd);
 
- }
- 
					#adc Makefile
 
- 
					ARCH=arm
 
- 
					CROSS_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
 
- 
					APP_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
 
- 
					#obj-m := app-drv.o
 
- 
					obj-m := pwm-drv.o
 
- 
					#KDIR := /path/to/kernel/linux/
 
- 
					KDIR := /home/ok210/android-kernel-samsung-dev/
 
- 
					PWD := $(shell pwd)
 
- 
					default:
 
- 
					        make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
 
- 
					app:pwm-app.c
 
- 
					        $(APP_COMPILE)gcc -o app pwm-app.c
 
- 
					clean:
 
- $(MAKE) -C $(KDIR) M=$(PWD) clean
相關(guān)產(chǎn)品 >
- 
                OKMX6UL-C開(kāi)發(fā)板 飛凌嵌入式專(zhuān)注imx6系列imx6ul開(kāi)發(fā)板、飛思卡爾imx6ul核心板等ARM嵌入式核心控制系統(tǒng)研發(fā)、設(shè)計(jì)和生產(chǎn),i.mx6UL系列產(chǎn)品現(xiàn)已暢銷(xiāo)全國(guó),作為恩智浦imx6ul,imx6ul開(kāi)發(fā)板,i.mx6提供者,飛凌嵌入式提供基于iMX6 iMX6UL解決方案定制。 了解詳情 
- 
                OKMX6ULL-C開(kāi)發(fā)板 40*29mm,雙網(wǎng)雙CAN,8路串口| i.MX6ULL開(kāi)發(fā)板是基于NXP i.MX6ULL設(shè)計(jì)開(kāi)發(fā)的的一款Linux開(kāi)發(fā)板 ,主頻800MHz,體積小,其核心板僅40*29mm,采用板對(duì)板連接器,適應(yīng)場(chǎng)景豐富。 了解詳情 

 
                                             換一批
換一批


