fl2440——驱动学习-ADC驱动

本文介绍了一种基于S3C2440芯片的ADC模数转换器驱动程序的设计与实现,包括硬件接口配置、寄存器操作、中断处理等关键步骤,并提供了完整的源代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

硬件描述:
S3c2440有一个10-bit的CMOS ADC 模数转换器,支持8个模拟通道输入,10位的分辨率,最高速度可达500KSPS(500 千次/每秒)。
这里写图片描述
fl2440上的原理图:
这里写图片描述
从图中可知:模拟ADC,包含了2部分功能,一部分是触屏功能,另一部分就是普通ADC功能,分别可以产生INT_TC和INT_ADC 两个中断。8个AIN模拟输入(A[3:0],YM,YP,XM,XP)通过一个8路模拟开关MUX进行通道片选。 ADC模块共有20个寄存器。对于普通ADC转换,使用ADCCON 和 ADCDAT0即可完成控制。ADCCON用于控制设置,ADCDAT0保存了转换结果。
在fl2440上,开发板通过一个10K的电位器(可变电阻)来产生电压模拟信号,然后通过第一个通道(即:AIN0)将模拟信号输入ADC。
下面是ADC驱动程序的实现:

/*********************************************************************************
 *      Copyright:  (C) 2017 TangBin<tangbinmvp@gmail.com>
 *                  All rights reserved.
 *
 *       Filename:  adc.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(04/07/2017)
 *         Author:  TangBin <tangbinmvp@gmail.com>
 *      ChangeLog:  1, Release initial version on "04/07/2017 12:48:02 PM"
 *                 
 ********************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.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 DEVICE_NAME "S3C_ADC"
#define S3C_ADC_MAJOR   0 
#define ENABLE      1
#define DISABLE     0

#define CON_BASE    0x58000000
#define DATA_BASE   0x5800000c
#define CON_LEN     4
#define DATA_LEN    4
#define s3c_adc_read(reg)       __raw_readl((reg))
#define s3c_adc_write(val,reg)  __raw_writel((val),(reg))

static void __iomem *adc_con;
static void __iomem *adc_dat0;/* 保存经过虚拟映射后的内存地址 */

static struct clk *adc_clk; /* 保存从平台时钟队列中获取ADC的时钟 */
DEFINE_MUTEX(ADC_CLK);/* 定义互斥锁 */

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

static struct cdev *adc_cdev;

static ssize_t adc_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
    unsigned int tmp;
    unsigned long adc_data;

    adc_con = ioremap(CON_BASE, CON_LEN);/* 将ADC的IO端口占用的这段IO空间映射到内存的虚拟地址 */
    adc_dat0 = ioremap(DATA_BASE, DATA_LEN);/* 将ADC的IO端口占用的这段IO空间映射到内存的虚拟地址 */

    tmp = s3c_adc_read(adc_con);
    tmp = (1 << 14) | (255 << 6) | (0 << 3);// 0 1 00000011 000 0 0 0
    s3c_adc_write(tmp, adc_con); //AD预分频器使能、模拟输入通道设为AIN0

    tmp = s3c_adc_read(adc_con);
    tmp = tmp | (1 << 0);// 0 1 00000011 000 0 0 1 
    s3c_adc_write(tmp, adc_con);//启动AD转换

    while(s3c_adc_read(adc_con) &0x1);//启动转换后,等待启动位清零
    while(!(s3c_adc_read(adc_con) &0x8000));//等待是否转换完毕

    mutex_lock(&ADC_CLK);//获取信号量,加锁
    adc_data = s3c_adc_read(adc_dat0) &0x3ff;//AD转换后的数据是保存在ADCDAT0的第0-9位,所以与上0x3ff(即:1111111111)后就得到第0-9位的数据,多余的位就都为0
                                                                                        //读取AD转换后的值保存到全局变量adc_data中
    copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));//将读取到的AD转换后的值发往到上层应用程序
    mutex_unlock(&ADC_CLK); //解锁

    return sizeof(adc_data);
}

static int adc_open(struct inode *inode, struct file *file)
{
    int minor = iminor(inode);

    file->private_data = (void *)minor;
    printk(KERN_DEBUG "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,
    .read = adc_read,
    .open = adc_open,
    .release = adc_release,
};

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

    /* 分配主次设备号 */
    if(0 != dev_major)  
    {
        devno = MKDEV(dev_major, 0);
        result = register_chrdev_region(devno, dev_cnt, DEVICE_NAME);
    }
    else
    {
        result = alloc_chrdev_region(&devno, dev_minor, dev_cnt, DEVICE_NAME);
        dev_major = MAJOR(devno);
    }
    if(result < 0)
    {
        printk(KERN_ERR "%s driver can't use major %d\n", DEVICE_NAME, dev_major);
        return -ENODEV;
    }

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

    /* 从平台时钟队列中获取ADC的时钟,因为ADC的转换频率跟时钟有关 */
    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);//时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中

    if(NULL == (adc_cdev=cdev_alloc()))
    {
        printk(KERN_ERR "%s driver can't alloc for the cdev.\n", DEVICE_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 "%s driver can't reigster cdev: result=%d\n", DEVICE_NAME, result);
        goto ERROR;
    }
    printk(KERN_ERR "%s installed successfully.\n",DEVICE_NAME);

    return 0;

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

void __exit 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_dat0);

    if(adc_clk)
    {
        clk_disable(adc_clk);
        clk_put(adc_clk);
        adc_clk = NULL;
    }
    printk(KERN_ERR " %s driver removed!\n", DEVICE_NAME);

    return ; 

}
module_init(adc_init);
module_exit(adc_exit);

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

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tang bin");
MODULE_DESCRIPTION("ADC Driver");

在本程序中,选择使用中断的方式来进行从ADC数据寄存器中读取AD转换后的值是比较好的。由于本人目前对中断的使用还较生疏,因此在本例中未使用。大致还是按照一个普通字符设备来编写,同样参考了之前学习的LED驱动里的一些方法。
另外,ADC可以注册为混杂设备,miscdevice.h中有相关的定义和函数。关于混杂设备的相关知识可以自行在网上搜索。

在开发板上通过调节电位器,可以读出并显示数值。测试程序:

/*********************************************************************************
 *      Copyright:  (C) 2017 TangBin<tangbinmvp@gmail.com>
 *                  All rights reserved.
 *
 *       Filename:  adc_test.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(04/07/2017)
 *         Author:  TangBin <tangbinmvp@gmail.com>
 *      ChangeLog:  1, Release initial version on "04/07/2017 03:01: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>

int main (int argc, char **argv)
{
    int fd;
    fd = open("/dev/S3C_ADC",0);
    if(fd < 0)
    {
        printf("open ADC device failed.\n");
        exit(1);
    }
    for(;;)
    {
        int ret;
        int data;

        ret = read(fd, &data, sizeof(data));
        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() ----- */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值