FL2440—獨立ADC驅動模塊

fl2440 ADC驅動編寫

前面的學習中多次瞭解到linux內核對於各種常見驅動的支持相當的好,ADC也不例外。但是爲了驅動的學習,參考datasheet的內容以及網友的博客文章,依然自己編寫單獨的ADC驅動模塊。下面對ADC驅動的學習做一個簡單的總結。

在s3c2440中集成了一個8通道的10位ADC,其從有4個通道即XP, XM, YP, YM連接到觸摸屏的接口,用來檢測轉換觸摸屏的模擬信號。剩下的4個通道A0,A1,A2,A3可以外接設備採集外設的模擬信號。其最高轉換速率可達到5KHz.
這裏寫圖片描述
在FL2440的開發板上,ADC的通道0外接一個電位器CN3,當調節電位器CN3的阻值,通道0採集到的電壓值會隨之在0~3.3V 的範圍變化。
這裏寫圖片描述

驅動程序&&測試程序

參考datasheet和網上的博客文章,下面是本人編寫的驅動和測試程序。

  • 驅動程序
/********************************************************************************
 *      Copyright:  (C) 2017 Li Wanneng<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  s3c_adc.h
 *    Description:  This head file is a driver for adc
 *
 *        Version:  1.0.0(04/21/2017)
 *         Author:  Li Wanneng <[email protected]>
 *      ChangeLog:  1, Release initial version on "04/21/2017 02:43:21 PM"
 *                 
 ********************************************************************************/


#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <plat/regs-adc.h>
#include <linux/errno.h>
#include <asm/uaccess.h>    /* copy_to_user copy_from_user */
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/clk.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <asm/io.h>
#include <linux/ioport.h>


#define DRV_AUTHOR          "Liwanneng <[email protected]>"
#define DRV_DESC            "S3C2440 ADC DRIVER"

#define DEV_NAME             "S3C_ADC"

/* Set ADC dev major number*/
#ifndef ADC_MAJOR
#define ADC_MAJOR       0
#endif

#define DRV_MAJOR_VER             1
#define DRV_MINOR_VER             0
#define DRV_REVER_VER             0

#define DISABLE                   0
#define ENABLE                    1
#define S3C_ADC_CON              0x58000000
#define S3C_CON_LEN              4
#define S3C_ADC_DATA             0x5800000C
#define S3C_DATA_LEN             4

#define PRSC_EN                  1
#define PRSC_VAL                 49
#define SEL_MUX                  0

static void __iomem *adc_con; 
static void __iomem *adc_data;

#define s3c_adc_write(val, reg) __raw_writel((val),(reg))
#define s3c_adc_read(reg)       __raw_readl((reg))

static struct clk *adc_clk;
DEFINE_MUTEX(ADC_CLK); 

int debug = DISABLE;
int dev_cnt = 1;
int dev_major = ADC_MAJOR;
int dev_minor = 0;


static struct cdev *adc_cdev;


static ssize_t adc_read(struct file *file, char *buff, size_t count,loff_t *ppos)
{
    unsigned temp;
    volatile unsigned long adc_value;
    adc_con = ioremap(S3C_ADC_CON, S3C_CON_LEN);
    adc_data = ioremap(S3C_ADC_DATA,S3C_DATA_LEN);

    temp = s3c_adc_read(adc_con);
    temp = (PRSC_EN << 14) | (PRSC_VAL << 6) | (SEL_MUX << 3);/* 0 1 00000011 000 0 0 0 */
    s3c_adc_write(temp, adc_con);/* Enable prescaler */

    temp = s3c_adc_read(adc_con);
    temp = temp | (1 << 0); 
    s3c_adc_write(temp, adc_con); /* Enable ADC Conversion start */

    printk(KERN_INFO "write in kernel.\n");
    while(s3c_adc_read(adc_con) &0x1); 
    printk(KERN_INFO "write in kernel.\n");
    while(!(s3c_adc_read(adc_con) &0x8000)); 

    printk(KERN_INFO "will lock in kernel.\n");
    mutex_lock(&ADC_CLK); /* Lock */
    printk(KERN_INFO "locked in kernel.\n");
    adc_value = s3c_adc_read(adc_data) &0x3ff;/* Acquire adc_value */

    printk(KERN_INFO "adc_value getted.\n");

    copy_to_user(buff, (char *)&adc_value, sizeof(adc_value));
    mutex_unlock(&ADC_CLK);/* Unlock */

    return sizeof(adc_value);
}



static int adc_open(struct inode *inode, struct file *file)
{
    int minor = iminor(inode);
    file->private_data = (void *)minor;
    printk(KERN_INFO "/dev/S3C_ADC%d opened.\n", minor);

    return 0;
}


static int adc_release(struct inode *inode, struct file *file )
{
    return 0;
}


static struct file_operations adc_fops = {
    .owner = THIS_MODULE,
    .open = adc_open,
    .read = adc_read,
    .release = adc_release,
};

static int __init s3c_adc_init(void)
{
    int result;
    dev_t devno;

    if(0!=dev_major)
    {
        devno = MKDEV(dev_major, 0);
        result = register_chrdev_region(devno, dev_cnt, DEV_NAME);
    }
    else 
    {
        result = alloc_chrdev_region(&devno, dev_minor, dev_cnt, DEV_NAME);
        dev_major = MAJOR(devno);
    }

    if(result<0)
    {
        printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major);
         return -ENODEV;
    }

    printk(KERN_DEBUG "%s driver use major %d\n", DEV_NAME, dev_major);

    adc_clk = clk_get(NULL,"s3c_adc");

    if(!adc_clk)
    {
        printk(KERN_ERR "failed to find adc clock source\n");
        return -ENOENT;
    }

    clk_enable(adc_clk); 


     if(NULL == (adc_cdev=cdev_alloc()) )
     {
         printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
         unregister_chrdev_region(devno, dev_cnt);
         return -ENOMEM;
     }

     adc_cdev->owner = THIS_MODULE;
     cdev_init(adc_cdev, &adc_fops);

     result = cdev_add(adc_cdev, devno, dev_cnt);

     if(0!=result)
     {
         printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result); 
         goto ERROR;
     }
      printk(KERN_INFO "%s driver[major=%d] version %d.%d.%d installed successfully!\n",DEV_NAME, dev_major, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);
      return 0;


ERROR:
      printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
       cdev_del(adc_cdev);
       unregister_chrdev_region(devno, dev_cnt);
       return result;
}


static void __exit s3c_adc_exit(void)
{
    dev_t devno = MKDEV(dev_major, dev_minor);

    cdev_del(adc_cdev);
    unregister_chrdev_region(devno, dev_cnt);

    iounmap(adc_con); 
    iounmap(adc_data);

    if(adc_clk)
    {
        clk_disable(adc_clk);
        clk_put(adc_clk); /* ERROR */
        printk("exit4\n");
        adc_clk = NULL;
        printk("exit5\n");
    }

    printk(KERN_ERR "S3C %s driver version %d.%d.%d removed!\n",DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);

    return ;
}


/* This two function defined in <linux/init.h> */
module_init(s3c_adc_init);
module_exit(s3c_adc_exit);

module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);

MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
  • Makefile
[lwn@localhost adc]$ vim Makefile 
LINUX_SRC?=~/fl2440/kernel/linux-lwn-3.0.1
CROSS_COMPILE=/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-

obj-m := adc.o

modules:
    @make -C $(LINUX_SRC) M=`pwd` modules
    @make clean

clean:
    rm -f  *.ko.* *.o *mod.c *.order *.symvers
  • 測試程序
/*********************************************************************************
 *      Copyright:  (C) 2017 Li Wanneng<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  adc_test.c
 *    Description:  This file test s3c_adc driver
 *                 
 *        Version:  1.0.0(04/21/2017)
 *         Author:  Li Wanneng <[email protected]>
 *      ChangeLog:  1, Release initial version on "04/21/2017 07:36:33 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


/********************************************************************************
 *  Description:
 *   Input Args:
 *  Output Args:
 * Return Value:
 ********************************************************************************/
int main (int argc, char **argv)
{
    int fd;
    fd = open("/dev/S3C_ADC",O_RDONLY);
    if(fd<0)
    {
         printf("open S3C_ADC device failed.\n");
         exit(1);
    }
    printf("fd>0\n");


    while(1)
    {
         int ret;
         int data;
         printf("while read in user\n");
         ret = read(fd, &data, sizeof(data));
         printf("readed in user\n");
         if(sizeof(data) != ret)
         {
              if(errno != EAGAIN)
              {
                   printf("Read ADC Device Faild!\n");
              }
              continue;
         }
         else
         {
             printf("Read ADC value is: %d\n", data);
         }
         sleep(1);

    }

    close(fd);
    return 0;
} /* ----- End of main() ----- */

編譯測試

編寫好以上程序之後,接下來下載到開發板測試運行。
1.在虛擬機中用make命令和gcc交叉編譯器編譯驅動程序s3c_adc.c和測試程序test_adc.c:
這裏寫圖片描述

2.接下來將編譯生成的驅動文件和測試文件下載到開發板測試ADC(因爲我開發板的AD模塊壞了,所以在同學的板子上運行,就是界面醜了點蛤)
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章