完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
ADC驱动(仅一路输入)
/********s3c2410-adc.h********/ #ifndef _S3C2410_ADC_H_ #define _S3C2410_ADC_H_ #define ADC_WRITE(ch, prescale) ((ch)<<16|(prescale)) //将频道和预分频合并为一个数据,这样可以作为一个参数传入 #define ADC_WRITE_GETCH(data) (((data)>>16)&0x7) //得到转换器是哪一路的,使用时要<<3 #define ADC_WRITE_GETPRE(data) ((data)&0xff) //得到预分频的值 #endif /* _S3C2410_ADC_H_ */ /*************************s3c2410-adc.c****************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "s3c2410-adc.h" #include #define DEVICE_NAME "adc" #define ADCRAW_MINOR 0 #define ADC_INPUT(x) ((x)<<3) #define PRSCVL(x) ((x)<<6) static void *adctsc; static void *adccon; static void *adcdata0; static void *clkcon; static int adc_major = 252; typedef struct { struct semaphore lock; //声明一个信号量 wait_queue_head_t wait; //声明一个等待队列头 int channel; //选择哪一路AD转换器 int prescale; //预分频值 }ADC_DEV; static ADC_DEV adcdev; /* *中断处理函数 */ static irqreturn_t adcdone_int_handler(int irq,void *dev_id,struct pt_regs *regs) { wake_up(&adcdev.wait);//唤醒等待队列 return IRQ_HANDLED ; } /* *对设备进行写操作,buffer一定是用户空间的 */ static ssize_t s3c2410_adc_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) { int data; if(count!=sizeof(data)){ printk(KERN_INFO"the size of input data must be %dn", sizeof(data)); return 0; } copy_from_user(&data, buffer, count); //从用户空间拷贝数据到内核空间 adcdev.channel=ADC_WRITE_GETCH(data); //得到哪一路AD转换器 adcdev.prescale=ADC_WRITE_GETPRE(data); //得到预分频值 //printk(KERN_INFO"set adc channel=%d, prescale=0x%xn", adcdev.channel, adcdev.prescale); return count; } /* *对设备进行读操作,buffer一定是用户空间的 */ static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) { int ret = 0; if (down_interruptible(&adcdev.lock))//获得信号量 return -ERESTARTSYS; // writel(readl(adccon) &(~1),adccon);//对AD控制寄存器进行操作,具体参看s3c2410 datasheet writel( (1<<14) | (255<<6) |(1<<0)| ADC_INPUT(adcdev.channel), adccon); //设置adccon,AD转换器频道从用户空间传入 sleep_on( &adcdev.wait ); //将当前进程加入等待队列 ret = readl(adcdata0); //读取adcdata0 ret &= 0x3ff; //获取前10位(其实adcdata0十位以前不可能有1的bit,不过为了放置万一而已,也可以理解为增强可读性) //printk(KERN_INFO"AIN[%d] = 0x%04x, %dn", adcdev.channel, ret, readl(adccon) & 0x80 ? 1:0); copy_to_user(buffer, (char *)&ret, sizeof(ret));//拷贝内核数据(即获取到的转换完成的数据)到用户空间 up(&adcdev.lock); //释放信号量 return sizeof(ret); } /* *打开设备 */ static int s3c2410_adc_open(struct inode *inode, struct file *filp) { int ret; printk("in adc open"); ret = request_irq(IRQ_ADC, adcdone_int_handler, SA_INTERRUPT, DEVICE_NAME, NULL); //注册中断 if (ret) { return ret; } init_MUTEX(&adcdev.lock); //初始化一个互斥的信号量,并设置为1 init_waitqueue_head(&(adcdev.wait));//初始化等待队列 adcdev.channel=0; adcdev.prescale=0xff; printk(KERN_INFO"adc openedn"); return 0; } /* *关闭设备 */ static int s3c2410_adc_release(struct inode *inode, struct file *filp) { free_irq(IRQ_ADC, NULL);//释放中断资源 printk(KERN_INFO"adc closedn"); return 0; } /* *初始化并添加结构提struct cdev到系统之中 */ static void adc_setup_cdev( struct cdev *dev, int minor, struct file_operations *fops) { int err, devno = MKDEV(adc_major, minor); cdev_init(dev, fops);//初始化结构体struct cdev dev->owner = THIS_MODULE; err = cdev_add (dev, devno, 1);//将结构提struct cdev添加到系统之中 /* 出错处理 */ if (err) printk (KERN_NOTICE "Error %d adding adc %d", err, minor); } static struct cdev AdcDevs; /* *定义一个file_operations结构体,来实现对设备的具体操作的功能 */ static struct file_operations adc_remap_ops = { owner: THIS_MODULE, open: s3c2410_adc_open, read: s3c2410_adc_read, write: s3c2410_adc_write, release: s3c2410_adc_release, }; /* *初始化设备驱动模块,主要完成对字符设备结构体的初始化和添加到系统中,并得到一个设备的设备号 */ static int adc_init(void) { adctsc = ioremap(0x58000000 + S3C2410_ADCTSC, 0x04); //映射虚拟地址 adccon = ioremap(0x58000000 + S3C2410_ADCCON, 0x04); adcdata0 = ioremap(0x58000000 + S3C2410_ADCDAT0, 0x04); clkcon = ioremap(0x4C00000C, 0x04); writel(0, adctsc); //将寄存器值设置为零,关闭touch screen的功能 writel(0, adcdata0); //初始化adcdata0寄存器为零 writel(readl(clkcon) | (1<<15), clkcon); //使能ADC时钟输入(注意内核启动说不定会关掉这个时钟呢) int result; dev_t dev = MKDEV(adc_major, 0);//将主设备号和次设备号定义到一个dev_t数据类型的结构体之中 /* Figure out our device number. */ if (adc_major) result = register_chrdev_region(dev, 1, "adc");//静态注册一个设备,设备号先前指定好,并得到一个设备名,cat /proc/device来查看信息 else { result = alloc_chrdev_region(&dev, 0, 1, "adc");//如果主设备号被占用,则由系统提供一个主设备号给设备驱动程序 adc_major = MAJOR(dev);//得到主设备号 } if (result < 0) { return result; } if (adc_major == 0) adc_major = result;//如果静态分配失败。把动态非配的设备号给设备驱动程序 adc_setup_cdev(&AdcDevs, 0, &adc_remap_ops);//初始化和添加结构体struct cdev到系统之中 printk(KERN_INFO"adc device installed, with major %dn", adc_major); return 0; } /* *卸载驱动模块 */ static void adc_cleanup(void) { iounmap(adctsc); iounmap(adccon); iounmap(adcdata0); iounmap(clkcon); cdev_del(&AdcDevs);//删除结构体struct cdev unregister_chrdev_region(MKDEV(adc_major, 0), 1);//卸载设备驱动所占有的资源 printk(KERN_INFO"adc device uninstalledn"); } module_init(adc_init);//初始化设备驱动程序的入口 module_exit(adc_cleanup);//卸载设备驱动程序的入口 MODULE_LICENSE("Dual BSD/GPL"); //模块应该指定代码所使用的许可证 /******************main-adc.c*****************/ #include #include #include #include #include #include #include #include "s3c2410-adc.h" #define ADC_DEV "/dev/adc" static int adc_fd = -1; static int init_ADdevice(void) { //打开设备 printf(ADC_DEV); if((adc_fd=open(ADC_DEV,O_RDWR))<0){ printf("Error opening %d adc devicen", adc_fd); return -1; } } static int GetADresult(int channel) { int PRESCALE=0XFF; int data=ADC_WRITE(channel, PRESCALE); write(adc_fd, &data, sizeof(data)); //写操作,将data值传入驱动中 read(adc_fd, &data, sizeof(data)); //读操作,此时data将得到转换好的数值 return data; //返回转换好的数据 } int main(void) { int i; float d; if(init_ADdevice()<0) return -1; while( 1 ){ d=((float)GetADresult(0)*3.3)/1024.0; //由于s3c2410的ADC分辨率1/1024位 printf("%8.4ft",d); printf("n"); sleep(1); printf("r"); } close(adc_fd);//关闭设备 return 0; } //备注:此驱动程序使用了等待队列,信号量,中断注册。另外要特别留意write函数被实现为从用户空间传入数模转换通道号(频道)和分频的值(预分频),read函数则传出转换好的数据。对于预分频传入和读取,本驱动程序并未使用到,当多路转换时,这个值则要充分利用,并且需要一种多路转换的实现方法了。
|
|
只有小组成员才能发言,加入小组>>
1157 浏览 0 评论
1916 浏览 0 评论
7613 浏览 0 评论
写了一个用DMA读取ADC数据的程序,记录下整个过程和一点心得
3026 浏览 0 评论
3306 浏览 0 评论
1081浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-4-24 12:08 , Processed in 0.733539 second(s), Total 71, Slave 52 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号