作者:曹忠明,华清远见嵌入式培训中心讲师。
ADC是嵌入式产品常用的设备,它的驱动并不是很难,但是如果有多个这类设备的话,就得考虑ADC中断共享的问题了,这样反而是驱动变得较复杂,且不易控制。在2.6.29内核后在arch/arm/plat-samsung目录下加入了adc.c这个代码,这是一个通用adc驱动代码。这个代码用来初始化adc设备并构建了一个客户请求列表,用来接受客户请求转换数据。
下面这个结构体用来描述一个客户:
struct s3c_adc_client {
structplatform_device *pdev;
structlist_head pend; //用来构建客户请求列表
wait_queue_head_t *wait; //等待队列头,用来睡眠
unsignedint nr_samples;
int result;
unsigned char is_ts; //是否是触摸屏
unsigned char 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通用驱动中注册这个结构体。注册函数为:
struct s3c_adc_client *s3c_adc_register(structplatform_device *pdev,
void (*select)(struct s3c_adc_client *client,unsignedint selected),
void (*conv)(struct s3c_adc_client *client,unsigned d0, unsigned d1,unsigned *samples_left),
unsignedintis_ts)
然后我们就可以读取相应通道的数据了:
int s3c_adc_read(struct s3c_adc_client *client, unsigned intch);
现在我们开始写我们的驱动:
1、 构建我们的设备结构体
在arch/arm/mach-s3c2410/mach-smdk2410.c中添加如下内容
structplatform_device s3c_device_adc_conversion = {
.name = "adc_conversion",
.id = -1,
.dev.parent = &s3c_device_adc.dev,
};
2、 注册我们的设备
修改arch/arm/mach-s3c2410/mach-smdk2410.c,在结构体数组smdk2410_devices中添加我们的设备:
staticstructplatform_device *smdk2410_devices[] __initdata = {
……
&s3c_device_adc, //添加内容
&s3c_device_adc_conversion, //添加内容
……
};
修改后重新编译内核
3、 构建我们的驱动
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <plat/adc.h>
#include <plat/regs-adc.h>
MODULE_LICENSE ("GPL");
intadc_major = 250;
intadc_minor = 0;
intnumber_of_devices = 1;
struct s3c_adc_client *client;
structcdevcdev;
dev_tdevno = 0;
staticssize_tadc_conversion_read(struct file *file, char __user *buff, size_t count, loff_t *offset)
{
unsigned data;
unsignedch;
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;
}
staticintadc_conversion_open(structinode *inode, struct file *file)
{
return 0;
}
staticintadc_conversion_release(structinode *inode, struct file *file)
{
return 0;
}
staticstructfile_operationsadc_conversion_fops = {
.owner = THIS_MODULE,
.read = adc_conversion_read,
.open = adc_conversion_open,
.release = adc_conversion_release,
};
staticint __devinitadc_conversion_probe( structplatform_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_conversion");
if( ret )
{
dev_err(dev, "failed to register device number\n");
gotoerr_register_chrdev_region;
}
cdev_init(&cdev, &adc_conversion_fops);
cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev, devno, number_of_devices);
if( ret )
{
dev_err(dev, "failed to add device\n");
gotoerr_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;
}
staticint __devexitadc_conversion_remove(structplatform_device *pdev)
{
s3c_adc_release(client);
cdev_del(&cdev );
unregister_chrdev_region(devno, number_of_devices);
return 0;
}
staticstructplatform_driveradc_conversion_driver = {
.driver = {
.name = "adc_conversion",
.owner = THIS_MODULE,
},
.probe = adc_conversion_probe,
.remove = __devexit_p(adc_conversion_remove)
};
staticint __initadc_conversion_init (void)
{
returnplatform_driver_register( &adc_conversion_driver );
}
static void __exit adc_conversion_exit (void)
{
platform_driver_unregister(&adc_conversion_driver );
}
module_init (adc_conversion_init);
module_exit (adc_conversion_exit);
4、 编译驱动,并加载到内核里,使用下面代码测试,即可读到相应通道的数据
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main (void)
{
intfd;
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;
}