网上流行很多基于2410的ADC驱动及测试程序。本文所使用的是开发板光盘中带有了经过修改后的adc驱动。笔者在这个基础上再作一点修改。由于那个文件已经删除了版权信息,这里也不知道如何添加了,可以肯定的是,它使用了GPL,这里公开源代码,也算是GPL了。

原来的代码默认使用ADC第0个通道,本文将添加ioctl接口,可以通过应用层的ioctl来选择多个通道。

与原来的代码相比,添加了如下几个方面:

1、添加头文件<linux/ioctl.h>,不过经测试,没有也可以通过编译。

2、修改原来的调试信息为:

#define DEBUG
#ifdef DEBUG /* define it in Makefile of somewhere */
/* KERN_INFO */
#define DPRINTK(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
#else
#define DPRINTK(fmt, ...)
#endif

这个便于查看调试信息。

3、添加ioctl相关宏定义:

/* ioctl */
#ifndef u16
#define u16 unsigned short
#endif

#define ADC_IOC_MAGIC 0xd2

#define ADC_SET_CHANNEL _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV _IOW(ADC_IOC_MAGIC, 1, u16)

#define ADC_MAX_IOC 2 /* we only have 2 ioctl commands */
#define MAX_ADC  4 /* we have 4 adc chnnels */
/* end of ioctl */

4、添加ioctl接口:

/* ioctl发生错误,返回-1,并设置errno,这个便是驱动中ioctl中的那个。
     网上有资料说要返回-ENOTTY,不过个人认为这里返回-EINVAL更恰当一些。  
  */
static int s3c2410_adc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
        if ((_IOC_TYPE(cmd) != ADC_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_MAX_IOC))
                return -EINVAL;

switch (cmd) {
        /* set channel */
        case ADC_SET_CHANNEL:
                arg &=3;  //多此一举??
                if (arg > 3)
                        arg = 3;
                adcdev.channel=arg;
                break;
        case ADC_SET_CLKDIV:
                arg &= 0xff; // ??
                if (arg > 0xff)
                        arg = 0xff;
                adcdev.prescale=arg;
                break;
        default:
                return -EINVAL;
                break;
        }
        return 0;
}

当然,也要在这个文件的file_operations结构体添加这个接口:

.ioctl   = s3c2410_adc_ioctl,

5、copy_to_user

原来的代码使用了sprintf将ADC转换的结果转换为字符串类型,不过却在后面添加一个“/n”,不知是何意。

//len = sprintf(str, "%d/n", value); // why '/n'?
 len = sprintf(str, "%d", value);
         ……
 copy_to_user(buffer, str, len);

也正是这个原因,在测试程序中要使用sscanf将字符串转换成整数类型的才能得到正常的结果。

其它的修改不影响使用。

测试代码也简单,如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <linux/ioctl.h>

#define ADC "/dev/adc"

#ifdef DEBUG
#define debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif

#ifndef u16
#define u16        unsigned short
#endif

// 控制字,与驱动一致
#define ADC_IOC_MAGIC        0xd2

#define ADC_SET_CHANNEL        _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV        _IOW(ADC_IOC_MAGIC, 1, u16)

// 特意的错误控制字
// test
#define AAA 333
// test end
int fd;

// 由于是死循环,需要捕获SIGINT信号
void sig_handle(int sig)
{
        //debug("you press ^C: %d/n", sig);
        close(fd);        /* we colse it here */
        exit(0);
}
int main(void)
{
        char buff[10];
        int val;
        int len;
        int i;
        signal(SIGINT, sig_handle);

debug("Test of ADC. ^C to exit/n");
        fd = open(ADC, O_RDWR);
        if (fd < 0){
                perror("Open /dev/adc failed");
                exit(1);
        }
        // test
        /* it will return -EINVAL(which specified in ADC driver) */
        if (ioctl(fd, AAA,0) < 0)
                perror("ioctl");

while (1) {
                /* we have 4 channels ADC*/
                for (i=0; i<4; i++) {
                        ioctl(fd, ADC_SET_CHANNEL, i);
                        len = read(fd, buff,sizeof(buff));
                        if (len < 0) {
                                perror("read adc device failed");
                                exit(0);
                        } else {
                                buff[len] = '/0';
                                sscanf(buff, "%d", &val);
                                printf("read AIN[%d]: %d value:%d/n", i, len, val);
                        }
                        sleep(1); // 每隔1秒采1个通道
                }
                //sleep(1); // 每隔1秒采集4个通道
        }

close(fd); /* just kidding */
        
        return 0;
}

在开发板上运行结果如下:

./myadc
read AIN[0]: 3 value:629
read AIN[1]: 3 value:316
read AIN[2]: 3 value:257
read AIN[3]: 3 value:303
read AIN[0]: 1 value:0
read AIN[1]: 1 value:0
read AIN[2]: 3 value:259
read AIN[3]: 3 value:297
……
read AIN[1]: 3 value:468
read AIN[2]: 3 value:299
read AIN[3]: 3 value:327
read AIN[0]: 3 value:629
read AIN[1]: 1 value:0
……
read AIN[0]: 3 value:629
read AIN[1]: 3 value:287
read AIN[2]: 3 value:273
read AIN[3]: 1 value:0
read AIN[0]: 3 value:629
read AIN[1]: 3 value:296
read AIN[2]: 3 value:271
read AIN[3]: 3 value:306
read AIN[0]: 3 value:629
read AIN[1]: 3 value:281
read AIN[2]: 1 value:0
read AIN[3]: 3 value:269

上面显示0的就是用导线将开发板上几个ADC通道分别接触板子上GND出现的。如果接VCC,值应该是1023。就是说,ADC的值为0~1023,ADC驱动将转换结果与0x3ff相与,这可以看datasheet。

至于测试代码中使用sscanf,是因为驱动中使用了sprintf。

驱动的debug信息如下(举例,与前面显示无直接关系):

# dmesg | tail
AIN[3] = 0x012c, 1
AIN[0] = 0x0275, 1
AIN[1] = 0x0146, 1
AIN[2] = 0x0126, 1
AIN[3] = 0x0150, 1
AIN[0] = 0x0275, 1
AIN[1] = 0x012d, 1
AIN[2] = 0x0109, 1
AIN[3] = 0x0126, 1
adc closed

本人虽然学过一段时间的单片机,但是对于电机控制、PWM、ADC等等兴趣不大,了解也不多。其实我也不知这个多通道的ADC能做什么。

文中ioctl控制字参考《ARM徽处理器与应用开发》一书,电子工业出版社出版。

完整的驱动程序如下:

#include <linux/errno.h>
#include <linux/kernel.h>
#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/ioctl.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>

#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#define DEBUG
#ifdef DEBUG /* define it in Makefile of somewhere */
/* KERN_INFO */
#define DPRINTK(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
#else
#define DPRINTK(fmt, ...)
#endif

#define DEVICE_NAME        "adc"

static void __iomem *base_addr;

typedef struct {
        wait_queue_head_t wait;
        int channel;
        int prescale;
}ADC_DEV;

DECLARE_MUTEX(ADC_LOCK);
/* you may need to change is to the following */
//DEFINE_SEMAPHORE(ADC_LOCK);

static int OwnADC = 0;

static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;

static struct clk        *adc_clock;

#define ADCCON      (*(volatile unsigned long *)(base_addr + S3C2410_ADCCON))        //ADC control
#define ADCTSC      (*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC))        //ADC touch screen control
#define ADCDLY      (*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY))        //ADC start or Interval Delay
#define ADCDAT0     (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0))        //ADC conversion data 0
#define ADCDAT1     (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1))        //ADC conversion data 1
#define ADCUPDN     (*(volatile unsigned long *)(base_addr + 0x14))        //Stylus Up/Down interrupt status

#define PRESCALE_DIS        (0 << 14)
#define PRESCALE_EN         (1 << 14)
#define PRSCVL(x)           ((x) << 6)
#define ADC_INPUT(x)        ((x) << 3)
#define ADC_START           (1 << 0)
#define ADC_ENDCVT          (1 << 15)

#define START_ADC_AIN(ch, prescale) /
        do{ /
                ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; /
                ADCCON |= ADC_START; /
        }while(0)
/* ioctl */
#ifndef u16
#define u16        unsigned short
#endif

#define ADC_IOC_MAGIC        0xd2

#define ADC_SET_CHANNEL        _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV        _IOW(ADC_IOC_MAGIC, 1, u16)

#define ADC_MAX_IOC        2 /* we only have 2 ioctl commands */
#define MAX_ADC                4 /* we have 4 adc chnnels */
/* end of ioctl */

static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{
        if (OwnADC) {
                adc_data = ADCDAT0 & 0x3ff;

ev_adc = 1;
                wake_up_interruptible(&adcdev.wait);
        }

return IRQ_HANDLED;
}

static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
        char str[20];
        int value;
        size_t len;
        if (down_trylock(&ADC_LOCK) == 0) {
                OwnADC = 1;
                START_ADC_AIN(adcdev.channel, adcdev.prescale);
                wait_event_interruptible(adcdev.wait, ev_adc);

ev_adc = 0;

DPRINTK("AIN[%d] = 0x%04x, %d/n", adcdev.channel, adc_data, ADCCON & 0x80 ? 1:0);

value = adc_data;
                #if 0
                sprintf(str,"%5d", adc_data);
                copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));
                #endif
                OwnADC = 0;
                up(&ADC_LOCK);
        } else {
                value = -1;

//len = sprintf(str, "%d/n", value); // why '/n'?
        len = sprintf(str, "%d", value);
        if (count >= len) {
                int r = copy_to_user(buffer, str, len);
                return r ? r : len;
        } else {
                return -EINVAL;
        }
}

static int s3c2410_adc_open(struct inode *inode, struct file *filp)
{
        init_waitqueue_head(&(adcdev.wait));

adcdev.channel=0;
        adcdev.prescale=0xff;

DPRINTK( "adc opened/n");
        return 0;
}

static int s3c2410_adc_release(struct inode *inode, struct file *filp)
{
        DPRINTK( "adc closed/n");
        return 0;
}

static int s3c2410_adc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
        if ((_IOC_TYPE(cmd) != ADC_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_MAX_IOC))
                return -EINVAL;

switch (cmd) {
        /* set channel */
        case ADC_SET_CHANNEL:
                arg &=3; //??
                if (arg > 3)
                        arg = 3;
                adcdev.channel=arg;
                break;
        case ADC_SET_CLKDIV:
                arg &= 0xff; // ??
                if (arg > 0xff)
                        arg = 0xff;
                adcdev.prescale=arg;
                break;
        default:
                return -EINVAL;
                break;
        }
        return 0;
}
static struct file_operations dev_fops = {
        .owner   = THIS_MODULE,
        .open    = s3c2410_adc_open,
        .read    = s3c2410_adc_read,
        .release = s3c2410_adc_release,
        .ioctl   = s3c2410_adc_ioctl,
};

static struct miscdevice misc = {
        .minor = MISC_DYNAMIC_MINOR,
        .name  = DEVICE_NAME,
        .fops  = &dev_fops,
};

static int __init dev_init(void)
{
        int ret;

base_addr=ioremap(S3C2410_PA_ADC,0x20);
        if (base_addr == NULL) {
                printk(KERN_ERR "Failed to remap register block/n");
                return -ENOMEM;
        }
        /* test: ioremap: c4876000(kernel space) ADCCON: 3fc4 */
        DPRINTK("ioremap: %x ADCCON: %x/n", base_addr, ADCCON);
        adc_clock = clk_get(NULL, "adc");
        if (!adc_clock) {
                printk(KERN_ERR "failed to get adc clock source/n");
                return -ENOENT;
        }
        clk_enable(adc_clock);

/* normal ADC */
        ADCTSC = 0;

ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);
        if (ret) {
                iounmap(base_addr);
                return ret;
        }

ret = misc_register(&misc);

printk (DEVICE_NAME"/tinitialized/n");
        return ret;
}

static void __exit dev_exit(void)
{
        free_irq(IRQ_ADC, &adcdev);
        iounmap(base_addr);

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

misc_deregister(&misc);
}

EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

s3c2410多通道adc驱动及测试程序相关推荐

  1. 多通道ADC一致性的高精度测量方法

    在散射计数字处理系统中,需要多通道ADC电路来对数据进行采样.在该系统中,需要对ADC采集后的数据进行数字下变频.脉冲压缩和波束合成等处理.然而,由于采样电路的路数很多,各个ADC通道由于布线差异.时 ...

  2. STM32的ADC采样与多通道ADC采样

    一 单通道采样  参考资料: <STM32库开发实战指南> 刘火良,杨森著 原理性质的东西还是少讲,因为上面那本书里面讲解的很详细了,直接来看硬件电路图 这里使用的是3362电位器(10K ...

  3. 线性序列机与串行接口ADC驱动设计与验证

    线性序列机与串行接口ADC驱动设计与验证 ADC128S022 型 ADC 内部工作原理 在 AC620 开发板上使用的模数转换器为逐次逼近型的低功耗芯片 ADC128S022,其具有 8 通道以及 ...

  4. stm32f429之多通道ADC通过DMA数据采集

    stm32f429之多通道ADC通过DMA数据采集 原来的程序使用时stm32f103的芯片,现在给为stm32f429的芯片,查看一下几家开发板例程,发现没有使用adc+dma的,在网上也搜索了一下 ...

  5. 基于STM32F4:多通道ADC采集,采用DMA的形式,亲测有效

    基于STM32F4的多通道ADC采集 单片机源程序如下: #include "sys.h" #include "delay.h" #include " ...

  6. 外设驱动库开发笔记47:ADS111x系列ADC驱动

      关于ADC我们已经讨论过不少了,但在不同的应用需求下,我们会选择不同的原件.在这里我们将讨论ADS111x系列ADC驱动的设计与实现. 1.功能概述   ADS1113. ADS1114 和 AD ...

  7. cubemx 配置多通道ADC进行ADC采样

    cubemx 配置多通道ADC进行ADC采样 AD的基础知识 AD很复杂,其实也不复杂,因为我们用的不多. AD:模拟量转换数字量(模拟信号转换数字信号). ADC:模拟量转数字量的转换器. 为什么需 ...

  8. linux 混杂设备驱动之adc驱动

    linux2.6.30.4中,系统已经自带有了ADC通用驱动文件---arch/arm/plat-s3c24xx/adc.c,它是以平台驱动设备模型的架构来编写的,里面是一些比较通用稳定的代码,但是l ...

  9. S3C6410开发板adc驱动代码分析及测试代码分析

    在本文中,我们对S3C6410开发板adc驱动代码的实现过程进行分析,然后通过一个实例对adc功能进行测试.在本文的资源中包含了设备驱动的源码和测试的源码. 一.设备驱动源码分析 adc的设备驱动主要 ...

最新文章

  1. 判断一个字符串是另外字符串的旋转字符串
  2. 基于ArcGIS API for JavaScript加载百度各种类型切片地图
  3. JS编程建议——2:正确辨析JavaScript句法中的词、句和段
  4. asp.net mysql 中文乱码_mysql4导入mysql5中文乱码问题
  5. JAVA几何图注水,如何使用java绘制几何形状到图片?
  6. 前端-chromeF12 谷歌开发者工具详解 Network篇
  7. 函数可以作为Javascript对象(哈希表)的键吗
  8. 内存少导致编译出错,内存增加到24G
  9. 推荐CSDN排名前1000博主
  10. vsco怎么两个滤镜叠加_做图比设计师还快?!这帮饭圈女孩是怎么做到的?
  11. 作为技术面试官,我在面试时考虑什么?
  12. 万用表如何进行欧姆校零
  13. C语言推箱子完整代码
  14. ThinkPad电脑黑屏只显示鼠标
  15. 【计算机毕业设计】437物流管理系统设计与实现
  16. 一般树与二叉树的相互转换
  17. python生成曼德勃罗分形图形
  18. Office 2016 定制安装工具 v1.0 | Office 2016 自定义安装组件
  19. sql2000不显示服务器失败,安装sql server 2000数据库提示程序配置服务器失败.(解决方法)...
  20. CP2102国产替代DPU02— USB 转 UART 桥接芯片

热门文章

  1. 卢伟冰称天玑9000调校顺利 Redmi K50系列要来了
  2. 4999元!iQOO 9 Pro赛道版今日预售:创新性采用芳纶纤维材质
  3. realme GT2 Pro抢先开启盲售:史上屏幕最好高端旗舰
  4. 特斯拉推出通用钥匙带 官方售价145元
  5. 宁德时代:拟70亿投建储能电池项目
  6. 年会尽头是闲鱼!超11万人在闲鱼转卖年会奖品
  7. 啪的一下,马保国的流量被这帮人抢先变现了!
  8. 因涉嫌信披违规 神州优车被证监会立案调查
  9. 发牌一周年 国内5G发展如何?
  10. vivo X30系列发布会邀请函曝光:名副其实的“望远镜”