具体ADC硬件知识及裸机驱动请看: Exynos4412裸机开发 —— A/D转换器
1、原理图如下:
2、相关寄存器信息
ADC_BASE 0x126C0000
ADCCON 0x0000 1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6
ADCDLY 0x0008
ADCDAT 0x000C &0XFFF
CLRINTADC 0x0018
ADCMUX 0x001C
ADCDAT 0x000C &0XFFF
CLRINTADC 0x0018
ADCMUX 0x001C
3、大体驱动编写流程如下
read()
{
1、向adc设备发送要读取的命令
ADCCON 1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6
2、读取不到数据就休眠
wait_event_interruptible();
3、等待被唤醒读数据
havedata = 0;
}
adc_handler()
{
1、清中断 ADC使用中断来通知转换数据完毕的
2、状态位置位;
havedata=1;
3、唤醒阻塞进程
wake_up()
}
probe()
{
1、 读取中断号,注册中断处理函数
2、读取寄存器的地址,ioremap
3、字符设备的操作
}
|
4、设备树中的节点编写
fs4412-adc{ compatible = "fs4412,adc"; reg = <0x126C0000 0x20>; interrupt-parent = <&combiner>; interrupts = <10 3>; };
5、驱动编写
driver.c
#include <linux/module.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/wait.h> #include <linux/sched.h> #include <asm/uaccess.h> #include <asm/io.h> static int major = 250; static wait_queue_head_t wq; static int have_data = 0; static int adc; static struct resource *res1; static struct resource *res2; static void *adc_base; #define ADCCON 0x0000 #define ADCDLY 0x0008 #define ADCDAT 0x000C #define CLRINTADC 0x0018 #define ADCMUX 0x001C static irqreturn_t adc_handler(int irqno, void *dev) { have_data = 1; printk("11111\n"); /*清中断*/ writel(0x12,adc_base + CLRINTADC); wake_up_interruptible(&wq); return IRQ_HANDLED; } static int adc_open (struct inode *inod, struct file *filep) { return 0; } static ssize_t adc_read(struct file *filep, char __user *buf, size_t len, loff_t *pos) { writel(0x3,adc_base + ADCMUX); writel(1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6 ,adc_base +ADCCON ); wait_event_interruptible(wq, have_data==1); /*read data*/ adc = readl(adc_base+ADCDAT)&0xfff; if(copy_to_user(buf,&adc,sizeof(int))) { return -EFAULT; } have_data = 0; return len; } static int adc_release(struct inode *inode, struct file *filep) { return 0; } static struct file_operations adc_ops = { .open = adc_open, .release = adc_release, .read = adc_read, }; static int hello_probe(struct platform_device *pdev) { int ret; printk("match 0k \n"); res1 = platform_get_resource(pdev,IORESOURCE_IRQ, 0); res2 = platform_get_resource(pdev,IORESOURCE_MEM, 0); ret = request_irq(res1->start,adc_handler,IRQF_DISABLED,"adc1",NULL); adc_base = ioremap(res2->start,res2->end-res2->start); register_chrdev( major, "adc", &adc_ops); init_waitqueue_head(&wq); return 0; } static int hello_remove(struct platform_device *pdev) { free_irq(res1->start,NULL); free_irq(res2->start,NULL); unregister_chrdev( major, "adc"); return 0; } static struct of_device_id adc_id[]= { {.compatible = "fs4412,adc" }, }; static struct platform_driver hello_driver= { .probe = hello_probe, .remove = hello_remove, .driver ={ .name = "bigbang", .of_match_table = adc_id, }, }; static int hello_init(void) { printk("hello_init"); return platform_driver_register(&hello_driver); } static void hello_exit(void) { platform_driver_unregister(&hello_driver); printk("hello_exit \n"); return; } MODULE_LICENSE("GPL"); module_init(hello_init); module_exit(hello_exit);
test.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> main() { int fd,len; int adc; fd = open("/dev/hello",O_RDWR); if(fd<0) { perror("open fail \n"); return ; } while(1) { read(fd,&adc,4); printf("adc%0.2f V \n",(1.8*adc)/4096); } close(fd); }