S3C2410多通道adc驱动及测试程序

ipioneer 2011-10-15

网上流行很多基于S3C2410的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;
}

 

注:文中代码注释出现了中文,是为了方便文章的阅读(根据经验,有清楚注释的代码才算代码),在实际代码中是没有中文注释的。

相关推荐

zhengkai00 / 0评论 2010-12-03
linux爱好者 / 0评论 2009-08-17