天天看點

ADC 驅動分析

一,ADC原理

ADC,模數轉換器,将模拟量轉換成數字量,采樣,保持電路與AD轉換器內建在一起。AD轉換器模拟輸入信号是直流電壓信号,0~3.3V之間。S3C2440有8路模拟輸入信号,AD轉換器内部有一個模拟多路選擇器,某一時刻隻能将一路模拟輸入信号,通過模拟多路選擇器接通進行AD轉換,而其他路模拟輸入信号被斷開。

片内ADC電路

ADC 驅動分析

ADC轉換電路

ADC 驅動分析

子產品圖

ADC 驅動分析

XP,XM,YP,YM占用了四個AD通道,用于觸摸屏操作

功能描述

1.AD轉換時間(轉換速率)

ADC 驅動分析

2.正常轉換模式

ADC 驅動分析

二,寄存器操作

ADC控制寄存器

ADC 驅動分析

ADC資料轉換寄存器0([9~0]保留AD轉換之後的資料)

ADC 驅動分析

三,ADC驅動源代碼

#include <linux/errno.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/input.h>

#include <linux/serio.h>

#include <linux/clk.h>

#include <linux/miscdevice.h>

#include <asm/io.h>

#include <asm/irq.h>

#include <asm/uaccess.h>

#include <linux/errno.h>

#include <linux/kernel.h>//printk()

#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 <linux/wait.h>

#include <linux/sched.h>//包含很多驅動使用的核心API的定義。睡眠函數,變量聲明

#include <asm/io.h>//定義IO映射

#include <asm/irq.h>

#include <asm/uaccess.h>

#include <mach/regs-clock.h>

#include <plat/regs-timer.h>

#include <plat/regs-adc.h>//ADC寄存器的定義

#include <mach/regs-gpio.h>

#include <linux/cdev.h>

#include <linux/miscdevice.h>

#define DEVICE_NAME    "my2440_adc"

static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);

static volatile int ev_adc = 0;

static int adc_data;

static void __iomem *adc_base;

static struct clk *adc_clk;

//DECLARE_MUTEX(ADC_LOCK);

extern struct semaphore ADC_LOCK;

static irqreturn_t adc_irq(int irq, void *dev_id)

{

    if(!ev_adc)

    {

        adc_data = readl(adc_base + S3C2410_ADCDAT0) & 0x3ff;

        ev_adc = 1;

        wake_up_interruptible(&adc_waitq);

    }

    return IRQ_HANDLED;

}

static void start_adc(void)

{

    unsigned int tmp;

    tmp = (1 << 14) | (255 << 6) | (0 << 3);

    writel(tmp, adc_base + S3C2410_ADCCON);

    tmp = readl(adc_base + S3C2410_ADCCON);

    tmp = tmp | (1 << 0);                

    writel(tmp, adc_base + S3C2410_ADCCON);

}

static ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)

{

    if (down_trylock(&ADC_LOCK))

    {

        return -EBUSY;

    }

    if(!ev_adc)

    {

        if(filp->f_flags & O_NONBLOCK)

        {

            return -EAGAIN;

        }

        else

        {

            start_adc();

            wait_event_interruptible(adc_waitq, ev_adc);

        }

    }

    ev_adc = 0;

    copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

    up(&ADC_LOCK);

    return sizeof(adc_data);

}

static int adc_open(struct inode *inode, struct file *file)

{

    int ret;

    ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, 1);

    if (ret)

    {

        printk(KERN_ERR "IRQ%d error %d\n", IRQ_ADC, ret);

        return -EINVAL;

    }

    return 0;

}

static int adc_release(struct inode *inode, struct file *filp)

{

    return 0;

}

static struct file_operations adc_fops =

{

    .owner    = THIS_MODULE,

    .open     = adc_open,

    .read     = adc_read,    

    .release  = adc_release,

};

static struct miscdevice adc_miscdev =

{

    .minor   = MISC_DYNAMIC_MINOR,

    .name    = DEVICE_NAME,       

    .fops    = &adc_fops,         

};

static int __init adc_init(void)

{

    int ret;

    adc_clk = clk_get(NULL, "adc");

    if (!adc_clk)

    {

        printk(KERN_ERR "failed to find adc clock source\n");

        return -ENOENT;

    }

    clk_enable(adc_clk);

    adc_base = ioremap(S3C2410_PA_ADC, 0x20);

    if (adc_base == NULL)

    {

        printk(KERN_ERR "Failed to remap register block\n");

        ret = -EINVAL;

        goto err_noclk;

    }

    ret = misc_register(&adc_miscdev);

    if (ret)

    {

        printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n", MISC_DYNAMIC_MINOR, ret);

        goto err_nomap;

    }

    printk(DEVICE_NAME " initialized!\n");

    return 0;

//以下是上面錯誤處理的跳轉點

err_noclk:

    clk_disable(adc_clk);

    clk_put(adc_clk);

err_nomap:

    iounmap(adc_base);

    return ret;

}

static void __exit adc_exit(void)

{

    free_irq(IRQ_ADC, 1);   

    iounmap(adc_base);      

    if (adc_clk)            

    {

        clk_disable(adc_clk);    

        clk_put(adc_clk);

        adc_clk = NULL;

    }

    misc_deregister(&adc_miscdev);

}

//EXPORT_SYMBOL(ADC_LOCK);

module_init(adc_init);

module_exit(adc_exit);

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("apple");

MODULE_DESCRIPTION("My2440 ADC Driver");

測試程式:

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

int main(int argc, char **argv)

{

    int fd;

    //以阻塞方式打開裝置檔案,非阻塞時flags=O_NONBLOCK

    fd = open("/dev/my2440_adc", 0);

    if(fd < 0)

    {

        printf("Open ADC Device Faild!\n");

        exit(1);

    }

    while(1)

    {

        int ret;

        int data;   

        ret = read(fd, &data, sizeof(data));     //讀裝置

        if(ret != sizeof(data))

        {

            if(errno != EAGAIN)

            {

                printf("Read ADC Device Faild!\n");

            }

            continue;

        }

        else

        {

            printf("Read ADC value is: %d\n", data);

        }

    }

    close(fd);

    return 0;

}