基于fl2440内核linux-3.0移植----添加adc驱动

2019-07-12 18:50发布

一、开发环境     内核版本:linux-3.0
    开发板:FL2440(nandflash:K9F1G08 128M)
    编译器:arm-linux-gcc 4.3.2
二、原理分析     1.  硬件原理图分析。由原理图得知:处理器S3C2440 提供了8 通道10 位模数转换接口(其中有四个通道用于触摸屏),其微分
线性误差(Differential Linearity Error)可达±1.0 LSB,积分线性误差(Integral LinearityError)可达±2.0 LSB,FL2440 开发板引出其中一路接1 个可调电阻,可做AD 模数转换测试,其硬件电路如下:
                                                               
三、配置内核 make menuconfig来配置内核,因为我用的内核是linux-3.0版本,其对ADC是默认选项的(不可选择),   System Type  --->  
-*- ADC common driver support  如果用的内核版本是不可选择的,那个可以直接创建设备节点,然后运行测试程序。如果不是默认选上的话,那么在内核就不要选上,然后编译内核下载到开发板上。 下面是自己写驱动加载的过程:
四、写驱动和测试程序:  编写适合fl2440开发板的adc驱动,文件名称:dev_adc.c(代码在后面),编译生成dev_adc.ko文件,并下载到开发板/目录下面。 编写适合自己驱动的adc测试程序,文件名称:s3c_adc.c(代码在后面),并有gcc工具编译生成可执行文件,并下载到开发板/usr/sbin目录下。
五、加载和测试adc驱动 [root@root /]# ls
apps          dev_adc       init          mnt           sys           var
bin           dev_adc.ko    jbs.mp3       proc          tmp           vim_conf.bin
data          etc           lib           root          tslib         yw.mp3
dev           info          linuxrc       sbin          usr
[root@root /]# insmod dev_adc.ko
[root@root /]# cat proc/devices 
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 
10 misc
 13 input
[root@root /]# mknod dev/adc c 10 59
[root@root /]# s3c_adc 
adc default channel value : 0
adc  channel 0 value : 0
adc  channel 1 value : 0
adc  channel 2 value : 0
adc  channel 3 value : 0
至此adc驱动添加就成功了,注意的是如果内核已经选上了System Type  --->  -*- ADC common driver support ,直接创建设备节点就好了,运行测试程序就好。添加驱动会不成功,显示insmod: can't insert 'dev_adc.ko': invalid parameter,我估计是中断号被内核支持的ADC占用。 以下是驱动和测试程序: /***************************dev_adc.c***********************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include


static void __iomem *adc_base;


static struct clk *adc_clk;


int dev_id = 1;


DEFINE_MUTEX(ADC_LOCK);


#define DEVICE_NAME    "adc"


static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);


static volatile int ev_adc = 0;


static int adc_data;




static void start_adc(void)
{
    unsigned int tmp;


    tmp = (1 << 14) | (255 << 6) | (0 << 3);    /*  0 1 00000011 000 0 0 0 */
    writel(tmp, adc_base + S3C2410_ADCCON); 


    tmp = readl(adc_base + S3C2410_ADCCON);
    tmp = tmp | (1 << 0);       /*  0 1 00000011 000 0 0 1 */
    writel(tmp, adc_base + S3C2410_ADCCON); 
}




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 int adc_open(struct inode *inode, struct file *file)
{
    return 0;
}


static ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t * ppos)
{
    mutex_lock(&ADC_LOCK);


    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));


    mutex_unlock(&ADC_LOCK);


    return sizeof(adc_data);
}


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;
    ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, &dev_id);


    {
       
        printk(KERN_ERR "IRQ%d error %d ", IRQ_ADC, ret);
        return -EINVAL;
    }


    adc_base = ioremap(S3C2410_PA_ADC, 0x20);
    if (adc_base == NULL)
    {
        printk(KERN_ERR "Failed to remap register block ");
        ret = -EINVAL;
        goto err_nomap;
    }
    
    adc_clk = clk_get(NULL, "adc");
    if (!adc_clk)
    {
        printk(KERN_ERR "failed to find adc clock source ");
        goto err_irq;
        return -ENOENT;
    }


    clk_enable(adc_clk);
    
    if (ret)
    {
        printk(KERN_ERR "cannot register miscdev on minor=%d (%d) ", MISC_DYNAMIC_MINOR, ret);
        goto err_noclk;
    }


    printk(DEVICE_NAME " initialized! ");


    return 0;


  err_nomap:
    iounmap(adc_base);


  err_noclk:
    clk_disable(adc_clk);
    clk_put(adc_clk);


  err_irq:
    free_irq(IRQ_ADC, &dev_id); 
    return ret;
}


static void __exit adc_exit(void)
{
    free_irq(IRQ_ADC, &dev_id); 
    iounmap(adc_base);         


    if (adc_clk)               
    {
        clk_disable(adc_clk);
        clk_put(adc_clk);
        adc_clk = NULL;
    }


    misc_deregister(&adc_miscdev); 
}


module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");

/**********************s3c_adc.c**********************************/


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int main(void)
{
    int adc_fd;
    int adc_value, adc_size;
    int i;


    adc_fd = open("/dev/adc", O_RDONLY);
    if (adc_fd < 0)
    {
        perror("open device adc");
        exit(1);
    }

    adc_size = read(adc_fd, &adc_value, sizeof(adc_value));
    printf("adc default channel value : %d ", adc_value);
    for (i = 0; i < 4; i++)
    {
        adc_size = read(adc_fd, &adc_value, sizeof(adc_value));
        printf("adc  channel %d value : %d ", i, adc_value);
    }

    close(adc_fd);
    return 0;
}