samsung ADC 通用驱动

         ADC是嵌入式产品常用的设备,它的驱动并不是很难,但是如果有多个这类设备的话,就得考虑ADC中断共享的问题了,这样反而是驱动变得较复杂,且不易控制。在2.6.29内核后在arch/arm/plat-samsung目录下加入了adc.c这个代码,这是一个通用adc驱动代码。这个代码用来初始化adc设备并构建了一个客户请求列表,用来接受客户请求转换数据。

下面这个结构体用来描述adc设备

struct adc_device {
       struct platform_device    *pdev;
       struct platform_device    *owner;
        struct clk           *clk;
       struct s3c_adc_client      *cur;
       struct s3c_adc_client      *ts_pend;
       void __iomem           *regs;
       spinlock_t                   lock;
       unsigned int               prescale;
       int              irq;
};

  下面这个结构体用来描述一个客户端:   

structs3c_adc_client {
         structplatform_device    *pdev;              
         structlist_head        pend;                        //用来构建客户请求列表
         wait_queue_head_t         *wait;                //等待队列头,用来睡眠
         unsignedint              nr_samples;
         int                       result;
         unsignedchar           is_ts;                         //是否是触摸屏
         unsignedchar           channel;                            //通道
 
         void  (*select_cb)(struct s3c_adc_client *c,unsigned selected);
         void  (*convert_cb)(struct s3c_adc_client *c,
                                  unsigned val1, unsigned val2,
                                  unsigned *samples_left); //转换回调函数
};
下面就是结构图

 我们需要在我们的驱动中构建这个结构体,并且向adc通用驱动中注册这个结构体。注册函数为:

structs3c_adc_client *s3c_adc_register(struct platform_device *pdev,
               void(*select)(struct s3c_adc_client *client,
                             unsigned int selected),
               void(*conv)(struct s3c_adc_client *client,
                             unsigned d0, unsigned d1,
                             unsigned *samples_left),
                             unsignedint is_ts)
然后我们就可以读取相应通道的数据了:

ints3c_adc_read(struct s3c_adc_client *client, unsigned int ch);

现在我们开始写我们的驱动:

1、  构建我们的设备结构体

在arch/arm/mach-s3c2410/mach-smdk2410.c中添加如下内容
structplatform_device s3c_device_adc_convert = {
.name                         ="adc_convert",
.id                                 =-1,
.dev.parent                =&s3c_device_adc.dev,
};

2、  注册我们的设备

修改arch/arm/mach-s3c2410/mach-smdk2410.c,在结构体数组smdk2410_devices中添加我们的设备:

staticstruct platform_device *smdk2410_devices[] __initdata = {
……
&s3c_device_adc,                               //添加内容
&s3c_device_adc_convert,     //添加内容
……
};

 修改后重新编译内核

3、  构建我们的驱动

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

MODULE_LICENSE ("GPL");

int adc_major = 250;
int adc_minor = 0;
int number_of_devices = 1;
struct s3c_adc_client *client;

struct cdev cdev;
dev_t devno = 0;

static ssize_t adc_convert_read(struct file *file, char __user *buff, size_t count, loff_t *offset) 
{
	unsigned data;
	unsigned ch;
	data = 10;
	ch = 0;
	data = s3c_adc_read(client, ch);
	printk("data0 = %d\n", data);

	if(copy_to_user(buff, (char *)&data, sizeof(data)))
		return -EFAULT;

	return 0;
}

static int adc_convert_open(struct inode *inode, struct file *file)
{
	return 0;
}

static int adc_convert_release(struct inode *inode, struct file *file)
{
	return 0;
}

static struct file_operations adc_convert_fops = {
	.owner 	= THIS_MODULE,
	.read	= adc_convert_read,
	.open 	= adc_convert_open,
	.release	= adc_convert_release,
};


static int __devinit adc_convert_probe( struct platform_device *pdev )
{
	struct device *dev = &pdev->dev;
	int ret = -EINVAL;

	printk("function = %s\n", __func__);
	devno = MKDEV(adc_major, adc_minor);

	ret = register_chrdev_region(devno, number_of_devices, "adc_convert");
	if( ret )
	{
		dev_err(dev, "failed to register device number\n");
		goto err_register_chrdev_region;
	}

	cdev_init(&cdev, &adc_convert_fops);
	cdev.owner = THIS_MODULE;
	ret = cdev_add(&cdev, devno, number_of_devices);
	if( ret )
	{
		dev_err(dev, "failed to add device\n");
		goto err_cdev_add;
	}

	client = s3c_adc_register (pdev, NULL, NULL, 0);

	if(IS_ERR( client ))
	{
		dev_err(dev, "failed to register adc client\n");
		goto err_s3c_adc_register;
	}
	
	return 0;

err_s3c_adc_register:
	cdev_del( &cdev );
err_cdev_add:
	unregister_chrdev_region(devno, number_of_devices);
err_register_chrdev_region:
	return ret; 
}


static int __devexit adc_convert_remove(struct platform_device *pdev)
{
	s3c_adc_release(client);
	cdev_del( &cdev );
	unregister_chrdev_region(devno, number_of_devices);
	return 0;
}

static struct platform_driver adc_convert_driver = {
	.driver = {
		.name 	= "adc_convert",
		.owner 	= THIS_MODULE,
	},
	.probe	= adc_convert_probe,
	.remove = __devexit_p(adc_convert_remove)
};

static int __init adc_convert_init (void)
{
	return platform_driver_register( &adc_convert_driver );
}

static void __exit adc_convert_exit (void) 
{
	platform_driver_unregister( &adc_convert_driver );
}

module_init (adc_convert_init);
module_exit (adc_convert_exit);


4、  编译驱动,并加载到内核里,使用下面代码测试,即可读到相应通道的数据

#include 
#include 
#include 
#include 
#include 

int main (void) 
{
	int fd;
	int data;
	fd = open ("/dev/adc",O_RDWR);
	if (fd < 0) {
		perror("open");
	  	exit(0);
	}
	while(1)
	{
		read (fd, (char *)&data, sizeof(data));
		printf("Voltage = %.2f\n", 3.3/1024*data);
		sleep(1);
	}
	close (fd);
	printf ("/dev/adc closed :)\n");
	return 0;
}


你可能感兴趣的:(linux)