AMR-linux S3c2440之ADC驱动实现

====本文系本站原创,欢迎转载! 转载请注明出处:http://blog.csdn.net/yyplc====

硬件描述:

S3c2440有一个10-bit的CMOS ADC 模数转换器,支持8个模拟通道输入,10位的分辨率,最高速度可达500KSPS(500 千次/每秒)。

 AMR-linux S3c2440之ADC驱动实现_第1张图片

 

从图中可知:模拟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保存了转换结果。

 

驱动程序ADC_DEV.ko:

#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <asm/io.h>    
#include <asm/irq.h>   
#include <linux/interrupt.h>
#include <mach/regs-clock.h>
#include <plat/regs-adc.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "ADC_DEV"

struct ADC_DEV {
	wait_queue_head_t wait;
	int ch;
	int pres;
	int data;
	int flag;
};

static struct ADC_DEV adc_dev;

static void __iomem *ADC_REG_BASE;
static struct clk *adc_clk;

#define ADCCON                  (*(volatile unsigned long *)(ADC_REG_BASE + S3C2410_ADCCON))
#define ADCDAT0	                (*(volatile unsigned long *)(ADC_REG_BASE + S3C2410_ADCDAT0)) 
#define START_ADC(ch,pres)    ADCCON = (0x01 | 0x01<<14 | ch<<3 | pres<<6)

	
static ssize_t adc_read(struct file *fp,  char __user *buf,  size_t count,  loff_t *ppos)
{
	START_ADC(adc_dev.ch, adc_dev.pres);              //启动ADC转换
	wait_event_interruptible(adc_dev.wait,adc_dev.flag);   //等待ADC转换完成
	adc_dev.flag = 0;
	
	copy_to_user(buf,(char*)&adc_dev.data,sizeof(adc_dev.data));

	return (sizeof(adc_dev.data));
}
static int adc_open(struct inode *in, struct file *fp)
{
	adc_dev.ch = 0x02;        //输入通道2
	adc_dev.pres = 0xff;       //prescale : 0 ~255 可选
	adc_dev.data = 0;
	adc_dev.flag = 0;
	init_waitqueue_head(&(adc_dev.wait));    
	
	return 0;
}
static irqreturn_t adc_done_handler(int irq, void *dev_id)
{
	adc_dev.data = ADCDAT0 & 0x3ff;
	adc_dev.flag = 1;
	wake_up_interruptible(&adc_dev.wait);      //唤醒等待事件

	return IRQ_HANDLED;
}

static struct file_operations dev_fops = {
	.owner = THIS_MODULE,
	.open  = adc_open,
	.read  = adc_read,
};

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

static int __init dev_init(void)
{
	int ret;

	ADC_REG_BASE = ioremap(S3C2410_PA_ADC,0x14);    //物理地址映射
	adc_clk = clk_get(NULL,"adc");
	if (!adc_clk) {
	    return -ENOENT;
	}
	clk_enable(adc_clk);            //开启adc 时钟,系统开机时默认是关闭的
	
	ret = request_irq(IRQ_ADC,adc_done_handler,IRQF_SHARED,DEVICE_NAME,&adc_dev);   //分配中断线
	if (ret) {
	   iounmap(ADC_REG_BASE);
	   return ret;
	}
	
	ret = misc_register(&misc);      //注册设备

	return ret;
}


static void __exit dev_exit(void)
{
	misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("itspy");
MODULE_DESCRIPTION("adc driver test");

应用程序adc_test:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>        

int main(void)
{
	int fd;
   	char buf[4];
	int i,res,adc_data;

	if ((fd = open("/dev/ADC_DEV",O_RDONLY)) < 0) {
            printf("open adc_dev failed\n");
	    return 1;
	}
   printf("adc_test result:\n");
	for (i= 0; i< 5; i++) {
	    if ((res =read(fd, buf, 4)) != 4) {
	        printf("read adc_dev failed \n");
	        return 1;
  	    } 
	    else {
	        adc_data = buf[0] + (int)(buf[1]<<8);
	        printf("adc value = %d (%.2f V) \n",adc_data, adc_data/1024.0*3.3);
  	    }
	}
	close(fd);

	return 0;
}

测试结果:

[root@WSN /]# ./adc_test 
adc_test result:
adc value = 740 (2.38 V) 
adc value = 744 (2.40 V) 
adc value = 744 (2.40 V) 
adc value = 744 (2.40 V)



你可能感兴趣的:(c,struct,user,File,Module,FP)