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;}