linux字符设备DS18B20驱动源码
来源:互联网 发布:照片字体识别软件 编辑:程序博客网 时间:2024/06/05 19:49
DS18B20是常用的数字温度传感器,经常用单片机来控制,本文基于arm11芯片来实现ds18b20驱动,系统是linux。
驱动是大学的时候实现的,里面主要涉及到寄存器的读写和ds18b20时序操作。需要参考arm11的芯片手册和ds18b20的芯片手册。想想都好多年没碰过驱动了。驱动是linux系统必不可少的一环,了解驱动流程开发也是对整个系统能更加深刻理解。保存的年代有点久,可能会有些纰漏,放在这里,主要是想给自己提个醒,回忆起整个驱动流程。
基于linux的ds18b20驱动:
#include <linux/init.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/errno.h>#include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/gpio.h> #include <linux/io.h> #include <mach/hardware.h> #include <mach/regs-gpio.h> #include <mach/map.h> #include <mach/gpio-bank-n.h> #include <mach/regs-clock.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <plat/gpio-core.h> #include <plat/gpio-cfg.h> #include <linux/slab.h>#include <linux/poll.h> //轮询文件#include <linux/wait.h> //等代队列相关头文件//内核等待队列,它包含了自旋锁的头文件#include<linux/semaphore.h> //使用信号量必须的头文件#define DQ 8 #define CFG_IN 0 #define CFG_OUT 1 #define DEV_NAME "ds18b20"#define DS18B20_MAJOR 243 /*预设的globalmem的主设备号*/#define DS18B20_MINOR 0 int i=0,j=0; //用于中断服务程序static int ds18b20_major = DS18B20_MAJOR;static int ds18b20_minor = DS18B20_MINOR;static void second_timer_handle(unsigned long arg);struct class *ds18b20_class;unsigned int current_len = 0; //fifo有效数据长度 unsigned char result[16];//globalmem设备结构体struct ds18b20_dev { struct cdev cdev; //cdev结构体 struct semaphore sem; //sem用于两个文件之间的互斥,sem1用于读写互斥 struct semaphore sem1; struct timer_list s_timer; //设备要使用的定时器 wait_queue_head_t r_wait; //阻塞读用的等待队列头 struct fasync_struct *async_queue; //异步结构体指针,用于读 };struct ds18b20_dev *ds18b20_devp;void s3c6410_gpio_cfgpin(unsigned int pin, unsigned int function) { //s3c_gpio_cfgpin(pin,function); unsigned int tmp; tmp = readl(S3C64XX_GPNCON); tmp = (tmp & ~(3<<pin*2))|(function<<pin*2); writel(tmp, S3C64XX_GPNCON); } void s3c6410_gpio_pullup(unsigned int pin, unsigned int to) { //s3c_gpio_setpull(pin,to); unsigned int tmp; tmp = readl(S3C64XX_GPNPUD); tmp = (tmp & ~(3<<pin*2))|(to<<pin*2); writel(tmp, S3C64XX_GPNPUD); } unsigned int s3c6410_gpio_getpin(unsigned int pin) { unsigned int tmp; tmp = readl(S3C64XX_GPNDAT); tmp =( tmp & (1 << (pin))) >> 8; return tmp; } void s3c6410_gpio_setpin(unsigned int pin, unsigned int dat) { unsigned int tmp; tmp = readl(S3C64XX_GPNDAT); tmp &= ~(1 << (pin)); tmp |= ( (dat) << (pin) ); writel(tmp, S3C64XX_GPNDAT);} unsigned char ow_reset(void){ // u32 presence=1; s3c6410_gpio_cfgpin(DQ, CFG_OUT); s3c6410_gpio_setpin(DQ, 1); udelay(1); //查看原理图,GPN8在空闲的时候被上拉电阻抬高电平,所以这里设不设置无所谓 s3c6410_gpio_setpin(DQ, 0); //pull SetDqValue(1) line low udelay(500); //至少480us,一般500到600都可以 s3c6410_gpio_setpin(DQ, 1); // allow line to return high udelay(60); //这里等待15us到60us就可以了 s3c6410_gpio_cfgpin(DQ, CFG_IN); // presence = GetDqValue(); // get presence signal udelay(450); // wait for end of timeslot 这里的值加上udelay(60)要不少于480us return 0;} void write_bit(char bitval){ s3c6410_gpio_cfgpin(DQ, CFG_OUT); s3c6410_gpio_setpin(DQ, 0); // pull SetDqValue(1) low to start timeslot udelay(2); //写时序开始后的15us释放总线,这里小于15us if(bitval==1) s3c6410_gpio_setpin(DQ, 1); // return SetDqValue(1) high if write 1 udelay(40);//在写时序开始的15us到60us,开始采样,稍微大点 //delay(5); // hold value for remainder of timeslot s3c6410_gpio_setpin(DQ, 1); //每次采样完成把总线抬高}// Delay provides 16us per loop, plus 24us. Therefore delay(5) = 104usvoid write_byte(char val){ unsigned char i; unsigned char temp; for (i=0; i<8; i++) // writes byte, one bit at a time { udelay(2); //相邻两次采样必须大于1us temp = val>>i; // shifts val right 'i' spaces temp &= 0x01; // copy that bit to temp write_bit(temp); // write bit in temp into } udelay(120);//???}unsigned char read_bit(void){ unsigned char va; s3c6410_gpio_cfgpin(DQ, CFG_OUT); s3c6410_gpio_setpin(DQ, 0); // pull SetDqValue(1) low to start timeslot udelay(2); //经过测试,该值必须小于等于11才不会出错 s3c6410_gpio_setpin(DQ, 1); // then return high udelay(15); // delay 15us from start of timeslot从DSds18b20输出的数据在读时序的下降沿出现后15us内有效。 s3c6410_gpio_cfgpin(DQ, CFG_IN); va=s3c6410_gpio_getpin(DQ); return(va); // return value of SetDqValue(1) line}unsigned char read_byte(void){ unsigned char i; unsigned char value = 0; for (i=0;i<8;i++) { udelay(1);//相邻两次读周期必须大于1us if(read_bit()) value|=0x01<<i; // reads byte in, one byte at a time and then shifts it left udelay(60); //所有的读时序至少60us //delay(6); // wait for rest of timeslot } return(value);}static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int ret; //struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针 if (down_interruptible(&ds18b20_devp->sem1)) /* 获取信号量 */ { return - ERESTARTSYS; } /* FIFO为空 */ if (current_len == 0) { if (filp->f_flags & O_NONBLOCK) //非阻塞 { ret = -EAGAIN; goto out; } up(&ds18b20_devp->sem1); wait_event_interruptible(ds18b20_devp->r_wait, (current_len > 0)); if (down_interruptible(&ds18b20_devp->sem1)) /* 获取信号量 */ { return - ERESTARTSYS; } } /* 拷贝到用户空间 */ //if (count > dev->current_len) // count = dev->current_len; if (copy_to_user(buf, result, 2)) //读成功将唤醒写等待队列 { ret = -EFAULT; goto out; } else { memcpy(result,result + 2, current_len - 2); //fifo数据前移2个字节 current_len -= 2; //有效数据长度减少 printk(KERN_INFO "read 2 bytes(s),current_len:%d\n",current_len); ret = 2; }out: up(&ds18b20_devp->sem1); //释放信号量 return ret; //return 0;}//mask注意什么意思,在哪里引用,然后信号量怎么使用static unsigned int ds18b20_poll(struct file *filp, poll_table *wait){ unsigned int mask = 0; if (down_interruptible(&ds18b20_devp->sem1)) /* 获取信号量 */ { return - ERESTARTSYS; } poll_wait(filp, &ds18b20_devp->r_wait, wait); /*fifo非空*/ if (current_len != 0) { mask |= POLLIN | POLLRDNORM; /*标示数据可读*/ //POLLIN表示设备可以无阻塞地读;POLLRDNORM表示数据已经就绪,可以读取 } printk(KERN_INFO "poll mask %x.\n", mask); up(&ds18b20_devp->sem1); return mask;}/*定时器处理函数*/static void second_timer_handle(unsigned long arg){ //unsigned char result[2] = {0x00, 0x00}; //char get[10]; if(i == 0) { ow_reset(); write_byte(0xCC); //Skip ROM write_byte(0x44); // Start Conversion j=0; } i++; //delay(5); // >750ms if(j == 8) { ow_reset(); write_byte(0xCC); // Skip ROM write_byte(0xBE); // Read Scratch Pad if(current_len<16) { if (current_len == 0) { wake_up_interruptible(&ds18b20_devp->r_wait); //唤醒读等待队列 /* 产生异步读信号 */ if (ds18b20_devp->async_queue) kill_fasync(&ds18b20_devp->async_queue, SIGIO, POLL_IN); } result[current_len]=read_byte(); result[current_len+1]=read_byte(); current_len = current_len + 2 ; printk(KERN_INFO "written 2 bytes(s),current_len:%d\n", current_len); current_len = current_len + 2; } else { //如果已经将result写满,则fifo前移,把旧的数据摈弃,写入新的数据 memcpy(result,result + 2, current_len - 2); //fifo数据前移 result[14]=read_byte(); result[15]=read_byte(); printk(KERN_INFO "written 2 bytes(s),current_len:%d\n", current_len); } i = 0; } j++; // err = copy_to_user(buf, &result, sizeof(result)); // return err ? -EFAULT : min(sizeof(result),count); mod_timer(&ds18b20_devp->s_timer,jiffies + HZ/10); //atomic_inc(&ds18b20_devp->counter); printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);}static int ds18b20_fasync(int fd, struct file *filp, int mode){ printk("driver: ds18b20_fasync\n"); return fasync_helper(fd, filp, mode, &ds18b20_devp->async_queue);}int ds18b20_open(struct inode *inode, struct file *filp){ if (down_interruptible(&ds18b20_devp->sem)) /* 获取信号量 */ { return - ERESTARTSYS; } /*初始化定时器*/ init_timer(&ds18b20_devp->s_timer); ds18b20_devp->s_timer.function = &second_timer_handle; ds18b20_devp->s_timer.expires = jiffies + HZ/10; add_timer(&ds18b20_devp->s_timer); /*添加(注册)定时器*/ // atomic_set(&second_devp->counter,0); /*计数清0*/ return 0;}/*文件释放函数*/int ds18b20_release(struct inode *inode, struct file *filp){ up(&ds18b20_devp->sem); /* 释放信号量 */ ds18b20_fasync( - 1, filp, 0); /* 将文件从异步通知列表中删除 */ del_timer(&ds18b20_devp->s_timer); return 0;}static struct file_operations ds18b20_fops = { .owner = THIS_MODULE, .read = ds18b20_read, .open = ds18b20_open, .release = ds18b20_release, .poll = ds18b20_poll, .fasync = ds18b20_fasync,}; /*设备驱动模块加载函数*/int ds18b20_init(void){ int result; dev_t devno= MKDEV(ds18b20_major,ds18b20_minor); ds18b20_devp = kmalloc(sizeof(struct ds18b20_dev), GFP_KERNEL); cdev_init(&ds18b20_devp->cdev, &ds18b20_fops); ds18b20_devp->cdev.owner = THIS_MODULE; sema_init(&ds18b20_devp->sem,1); sema_init(&ds18b20_devp->sem1,1); //init_MUTEX(&ds18b20_devp->sem); /* 初始化信号量 */ // init_MUTEX(&ds18b20_devp->sem1); /* 初始化信号量 */ init_waitqueue_head(&ds18b20_devp->r_wait); /*初始化读等待队列头*/ /* ds18b20_major 如果初始化为0,就默认选择动态分配,可以在ds18b20.h中定义 ds18b20_minor 也要初始化 */ /* 申请设备号,ds18b20_major初始化为0,选择动态分配*/ if(ds18b20_major){ result = register_chrdev_region(devno,1,DEV_NAME); }else{ result = alloc_chrdev_region(&devno, ds18b20_minor , 1 ,DEV_NAME); ds18b20_major = MAJOR(devno); } if (result < 0){ printk(KERN_WARNING"can't get major %d\n",ds18b20_major); return result; } ds18b20_class = class_create(THIS_MODULE, DEV_NAME); device_create(ds18b20_class, NULL, MKDEV(ds18b20_major, ds18b20_minor), NULL, DEV_NAME); result=cdev_add(&ds18b20_devp->cdev,devno,1); //注册设备 return result;}/*模块卸载函数*/void ds18b20_exit(void){ device_destroy(ds18b20_class, MKDEV(ds18b20_major,ds18b20_minor)); class_destroy(ds18b20_class); //销毁类 cdev_del(&ds18b20_devp->cdev); /*注销cdev*/ kfree(ds18b20_devp); /*释放设备结构体内存*/ unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor), 1); /*释放设备号*/}MODULE_AUTHOR("Chen Ting");MODULE_LICENSE("Dual BSD/GPL");module_init(ds18b20_init);module_exit(ds18b20_exit);
接下来看看两个例子如何调用这个驱动:
运用fcntl 和signal 的例子:
#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <fcntl.h>#include <signal.h>#include <unistd.h>void input_handler(int signum);int fd, oflags;unsigned char buf[2];float temperature;main(){ fd = open("/dev/ds18b20", O_RDWR, S_IRUSR | S_IWUSR); if (fd != - 1) { //启动信号驱动机制 signal(SIGIO, input_handler); //让input_handler()处理SIGIO信号 fcntl(fd, F_SETOWN, getpid()); oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); while(1) { sleep(100); } } else { printf("device open failure\n"); }}/*接收到异步读信号后的动作*/void input_handler(int signum){ printf("receive a signal from globalfifo,signalnum:%d\n",signum); read(fd, buf, sizeof(buf)); buf[1] <<= 4; buf[1] += ((buf[0])&0xf0)>>4; temperature = (float)(buf[1]) + (float)((buf[0])&0x0f)*0.0625; printf("the temperature is %4.2f degrees\n",temperature);}
运用select的例子:
#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <stdio.h>#include <fcntl.h>#include <unistd.h>#include <sys/time.h>#define FIFO_CLEAR 1#define BUFFER_LEN 20int main(){ int fd; fd_set rfds, wfds; unsigned char buf[2]; float temperature; /*以非阻塞方式打开/dev/globalmem设备文件*/ fd = open("/dev/ds18b20", O_RDONLY | O_NONBLOCK); if (fd < 0) printf("Device open failure.\n"); while(1) { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(fd, &rfds); FD_SET(fd, &wfds); select(fd + 1, &rfds, &wfds, NULL, NULL); printf("the read is OK!"); /*数据可获得*/ if (FD_ISSET(fd, &rfds)) { read(fd, buf, sizeof(buf)); buf[1] <<= 4; buf[1] += ((buf[0])&0xf0)>>4; temperature = (float)(buf[1]) + (float)((buf[0])&0x0f)*0.0625; printf("the temperature is %4.2f degrees\n",temperature); } } return 0;}
阅读全文
0 0
- linux字符设备DS18B20驱动源码
- LINUX--字符设备驱动
- Linux字符设备驱动
- Linux字符设备驱动
- Linux字符设备驱动
- linux字符设备驱动
- linux字符设备驱动
- linux 字符设备驱动
- linux字符设备驱动
- linux 字符设备驱动
- Linux字符设备驱动
- linux字符设备驱动
- Linux字符设备驱动
- linux 字符设备驱动
- Linux字符设备驱动
- linux字符设备驱动
- Linux字符设备驱动
- Linux字符设备驱动
- oracl个人学习笔记
- 成为优秀程序员的黄金10条法则
- android DNS检测
- SqlSessionTemplate探究
- GDB调试
- linux字符设备DS18B20驱动源码
- Unity3D shader(16)——“Queue”、“IgnoreProjector”、"RenderType"、Ztest、Zwrite、cull
- 编程填空:第i位替换
- 线段树(二)
- 哪 些APK在当前product中是不提供的。
- mybatis+oracle实现分页查询--非常简单实用
- 架构真经 | 那些年,我们踩过的缓存坑
- 构造函数
- 越权漏洞