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驱动模块_第1张图片
在FL2440的开发板上,ADC的通道0外接一个电位器CN3,当调节电位器CN3的阻值,通道0采集到的电压值会随之在0~3.3V 的范围变化。
FL2440—独立ADC驱动模块_第2张图片

驱动程序&&测试程序

参考datasheet和网上的博客文章,下面是本人编写的驱动和测试程序。

  • 驱动程序
/********************************************************************************
 *      Copyright:  (C) 2017 Li Wanneng
 *                  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 
 *      ChangeLog:  1, Release initial version on "04/21/2017 02:43:21 PM"
 *                 
 ********************************************************************************/


#include 
#include 
#include 
#include 
#include 
#include     /* copy_to_user copy_from_user */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define DRV_AUTHOR          "Liwanneng "
#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  */
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
 *                  All rights reserved.
 *
 *       Filename:  adc_test.c
 *    Description:  This file test s3c_adc driver
 *                 
 *        Version:  1.0.0(04/21/2017)
 *         Author:  Li Wanneng 
 *      ChangeLog:  1, Release initial version on "04/21/2017 07:36:33 PM"
 *                 
 ********************************************************************************/

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


/********************************************************************************
 *  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:
FL2440—独立ADC驱动模块_第3张图片

2.接下来将编译生成的驱动文件和测试文件下载到开发板测试ADC(因为我开发板的AD模块坏了,所以在同学的板子上运行,就是界面丑了点蛤)
FL2440—独立ADC驱动模块_第4张图片

你可能感兴趣的:(STM32学习笔记,FL2440驱动)