s3c2410的A/D驱动
作者:陈刚,华清远见嵌入式学院讲师。
我们要写s3c2410的A/D驱动需要了解用户2410的A/D。我们来看看2410用户手册
The 10-bit CMOS analog to digital converter (ADC) of the S3C2410A is a recycling typed device with 8-channel analog inputs.
2410提供的是10位精度8通道的A/D。
用户手册给出公式:
A/D converter freq. = 50 MHz/(49+1) = 1 MHz
Conversion time = 1/(1 MHz / 5cycles) = 1/200 kHz = 5 us
并且提示maximum 2.5 MHz clock
This A/D converter is designed to operate at maximum 2.5 MHz clock, so the conversion rate can go up to 500 KSPS.
所以我们为A/D提供的分频值最小为17;
我们主要关心两个寄存器
ADCCON (ADC控制寄存器) 和ADCDAT1(A/D转换后,数据存放在此寄存器)
我们只关心ADCDAT1(获得A/D转换后的数据(数据为10位精度)) 的低10位 。
下面我们在FS2410上实现一个linux下得A/D驱动示例:
首先我们看外围芯片电路图:
选择A/D的AIN0通道.
示例代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/clk.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/sched.h>
#define DEVICE_NAME "adc_driver"
#define pADCCON 0x58000000
#define pADCDAT0 0x5800000C
MODULE_LICENSE ("GPL");
unsigned long *vADCCON; /*ADC控制寄存器地址*/
unsigned long *vADCDAT0;/*ADCDAT0寄存器地址*/
int major = 250;
int minor = 0;
struct class *my_class;
wait_queue_head_t my_queue;
int sleep_flag = 0;
int number_of_devices = 1;
struct cdev cdev;
dev_t devno = 0;
/* open 打开设备 */
static int adc_driver_open(struct inode *inode, struct file *file)
{
struct clk *adc_clock = clk_get(NULL, "adc"); /*获得adc时钟*/
if (adc_clock == NULL) {
printk(KERN_WARNING "clk_get failed");
return -ENOENT;
}
clk_enable(adc_clock); /*时能ADC时钟,打开ADC*/
writel((1<<14)|(49<<6)|(0x00),vADCCON);
printk (KERN_INFO "adc_driver is open");
return 0;
}
static int adc_driver_release(struct inode *inode, struct file *file)
{
struct clk *adc_clock = clk_get(NULL, "adc");
clk_disable(adc_clock);
return 0;
}
/*中断处理函数(ADC 可以使用中断方式和轮询方式获得转换好的数据)*/
irqreturn_t interrupt_adc(int irq, void *dev_id)
{
sleep_flag = 1;
wake_up_interruptible(&my_queue);
return IRQ_HANDLED;
}
/*阻塞读*/
static ssize_t adc_driver_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
unsigned int num, ret;
writel((readl(vADCCON) & ~(0x3)) | (0x1),vADCCON);
wait_event_interruptible(my_queue, (sleep_flag == 1));
sleep_flag = 0;
num = readl(vADCDAT0) & (0x3FF);
ret = copy_to_user(buf, &num, sizeof(unsigned int));
if (ret < 0)
printk (KERN_WARNING "copy_to_user failed");
return ret;
}
struct file_operations adc_fops = {
.owner = THIS_MODULE,
.open = adc_driver_open,
.release = adc_driver_release,
.read = adc_driver_read,
};
static void char_reg_setup_cdev (void)
{
int error;
cdev_init (&cdev, &adc_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &adc_fops;
error = cdev_add (&cdev, devno , 1);
if (error)
printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev\n", error);
}
static int __init adc_driver_init (void)
{
int result;
if (major) {
devno = MKDEV(major,0);
result = register_chrdev_region(devno,number_of_devices,DEVICE_NAME);
}
else
result = alloc_chrdev_region(&devno, 0, number_of_devices, DEVICE_NAME);
if (result < 0) {
printk(KERN_WARNING "adc_driver: can't get major number %d\n", major);
return result;
}
vADCCON = ioremap(pADCCON, 4);
if (vADCCON == NULL) {
printk("ioremap vADCCON failed");
return -1;
}
vADCDAT0 = ioremap(pADCDAT0, 4);
if (vADCDAT0 == NULL) {
printk("ioremap vADCDAT0 failed");
goto err0;
}
init_waitqueue_head(&my_queue);
result = request_irq(IRQ_ADC, interrupt_adc, IRQF_DISABLED, "adc", NULL);
if (result < 0) {
printk (KERN_WARNING "request irq interrupt_adc failed\n");
}
my_class = class_create( THIS_MODULE, "adc_class" );
if(IS_ERR(my_class)) {
printk("Err: failed to create class.\n");
return -1;
}
device_create( my_class, NULL, devno, NULL, "adc_device");
char_reg_setup_cdev ();
printk (KERN_INFO "char device registered\n");
return 0;
err0:
iounmap(vADCDAT0);
return -1;
}
static void __exit adc_driver_exit (void)
{
iounmap(vADCCON);
iounmap(vADCDAT0);
free_irq(IRQ_ADC, NULL);
device_destroy(my_class, devno);
class_destroy(my_class);
cdev_del (&cdev);
unregister_chrdev_region (devno, number_of_devices);
printk (KERN_INFO "adc_driver is clean up\n");
}
module_init (adc_driver_init);
module_exit (adc_driver_exit);