网上流行很多基于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<!--this tool is written by Late Lee(http://www.latelee.org) using lex.-->这个便于查看调试信息。 3、添加ioctl相关宏定义:/* ioctl */ #ifndef u16 #define u16 unsigned short #endif
#define ADC_MAX_IOC 2 /* we only have 2 ioctl commands */ #define MAX_ADC 4 /* we have 4 adc chnnels */ /* end of ioctl */<!--this tool is written by Late Lee(http://www.latelee.org) using lex.--> 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; }<!--this tool is written by Late Lee(http://www.latelee.org) using lex.--> 当然,也要在这个文件的file_operations结构体添加这个接口:<!--this tool is written by Late Lee(http://www.latelee.org) using lex.--> .ioctl = s3c2410_adc_ioctl, 5、copy_to_user原来的代码使用了sprintf将ADC转换的结果转换为字符串类型,不过却在后面添加一个“
”,不知是何意。 //len = sprintf(str, "%d
", value); // why "
"? len = sprintf(str, "%d", value); …… copy_to_user(buffer, str, len);也正是这个原因,在测试程序中要使用sscanf将字符串转换成整数类型才能得到正常的结果。 其它的修改不影响使用。 测试代码也简单,如下:<!--this tool is written by Late Lee(http://www.latelee.org) using lex.-->#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>
// 特意的错误控制字 // test #define AAA 333 // test end int fd;
// 由于是死循环,需要捕获SIGINT信号 void sig_handle(int sig) { //debug("you press ^C: %d
", 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
"); 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] = " "; sscanf(buff, "%d", &val); printf("read AIN[%d]: %d value:%d
", i, len, val); } sleep(1); // 每隔1秒采1个通道 } //sleep(1); // 每隔1秒采集4个通道 }