linux字符设备DS18B20驱动源码

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) = 104us
void 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 20
int 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;
}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值