FL2440—基于platform 模型的DS18B20驱动实例

基于platform 模型的DS18B20驱动实例

通过前面的学习了解到DS18B20是基于单总线协议靠一个单线端口与CPU通信实现数据传输,关于单总线设备之前接触的比较少,这次通过学习编写DS18B20的驱动,总算对这种通信协议有了多一点的了解。

单总线通信协议时序理解

初始化序列——复位和存在脉冲

这里写图片描述

如图为DS18B20的单总线通信协议的初始化序列时序,DS18B20的所有通信都由由复位脉冲组成的初始化序列开始。该初始化序列由主机发出,后跟由DS18B20发出的存在脉冲(presence pulse)。当发出应答复位脉冲的存在脉冲后,DS18B20通知主机它在总线上并且准备好操作了。
在初始化步骤中,总线上的主机通过拉低单总线至少480μs来产生复位脉冲。然后总线主机释放总线并进入接收模式。 当总线释放后,5kΩ的上拉电阻把单总线上的电平拉回高电平。当DS18B20检测到上升沿后等待15到60us,然后以拉低总线60-240us的方式发出存在脉冲。

读写时序

写时序

这里写图片描述
由以上的时序图可知,总线主机使用写“1”时间隙向DS18B20写入逻辑1,使用写“0”时间隙向DS18B20写入逻辑0.所有的写时隙必须有最少60us的持续时间,相邻两个写时隙必须要有最少1us的恢复时间。两种写时隙都通过主机拉低总线产生。为产生写1时隙,在拉低总线后主机必须在15μs内释放总线。在总线被释放后,由于5kΩ上拉电阻的作用,总线恢复为高电平。为产生写0时隙,在拉低总线后主机必须继续拉低总线以满足时隙持续时间的要求(至少60μs)。 在主机产生写时序后,DS18B20会在其后的15到60us的一个时间窗口内采样单总线。在采样的时间窗口内,如果总线为高电平,主机会向DS18B20写入1;如果总线为低电平,主机会向DS18B20写入0。

读时序

这里写图片描述
DS18B20只有在主机发出读暂存器命令 [BEh]或温度转换命令T [44h]后,主机才会产生读时隙以便DS18B20提供所需数据。在主机产生读时序后,DS18B20开始发送0或1到总线上。DS18B20让总线保持高电平的方式发送1,以拉低总线的方式表示发送0.当发送0的时候,DS18B20在读时序的末期将会释放总线,总线将会被上拉电阻拉回高电平(也是总线空闲的状态)。DS18B20输出的数据在下降沿(下降沿产生读时隙)产生后15us后有效。因此,主机释放总线和采样总线等动作要在15μs内完成。

DS18B20温度值获取原理

这里写图片描述
DS18B20的测温原理如图2所示,图中低温度系数晶振的振荡频率受温度的影响很小[1],用于产生固定频率的脉冲信号送给减法计数器1,高温度系数晶振随温度变化其震荡频率明显改变,所产生的信号作为减法计数器2的脉冲输入,图中还隐含着计数门,当计数门打开时,DS18B20就对低温度系数振荡器产生的时钟脉冲后进行计数,进而完成温度测量。计数门的开启时间由高温度系数振荡器来决定,每次测量前,首先将-55 ℃所对应的基数分别置入减法计数器1和温度寄存器中,减法计数器1和温度寄存器被预置在-55 ℃所对应的一个基数值。减法计数器1对低温度系数晶振产生的脉冲信号进行减法计数,当减法计数器1的预置值减到0时温度寄存器的值将加1,减法计数器1的预置将重新被装入,减法计数器1重新开始对低温度系数晶振产生的脉冲信号进行计数,如此循环直到减法计数器2计数到0时,停止温度寄存器值的累加,此时温度寄存器中的数值即为所测温度。

驱动程序&&测试程序

下面是自己参考ds18b20的datasheet以及前辈们的博客编写的驱动和测试程序:

  • 驱动程序:s3c_plat_ds18b20.c

/*********************************************************************************
 *      Copyright:  (C) 2017 Li Wanneng<liwjng@gmail.com>
 *                  All rights reserved.
 *
 *       Filename:  s3c_ds18b20.c
 *    Description:  This file is driver for s3c_ds18b20
 *                 
 *        Version:  1.0.0(04/24/2017)
 *         Author:  Li Wanneng <liwjng@gmail.com>
 *      ChangeLog:  1, Release initial version on "04/24/2017 07:34:17 PM"
 *                 
 ********************************************************************************/

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/delay.h>
#include<linux/gpio.h>
#include<linux/types.h>
#include<asm/irq.h>
#include<mach/regs-gpio.h>
#include<mach/hardware.h>
#include<linux/device.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<linux/errno.h>
#include<asm/uaccess.h>
#include<linux/platform_device.h>

#define DRV_AUTHOR                "Li Wanneng<liwjng@gmail.com>"
#define DRV_DESC                  "S3C_DS18B20 driver"
#define DISABLE                   0
/* Driver version */
#define DRV_MAJOR_VER             1
#define DRV_MINOR_VER             0
#define DRV_REVER_VER             0

/* Set major static */
#define DEV_NAME                 "s3c_ds18b20"

/* dynamic major by default  */
#ifndef DEV_MAJOR
#define DEV_MAJOR                 0
#endif

#define DQ S3C2410_GPG(0)  
#define INPUT S3C2410_GPIO_INPUT  
#define OUTPUT S3C2410_GPIO_OUTPUT 

static int debug = DISABLE;
static int dev_major = DEV_MAJOR;
static int dev_minor = 0;

struct ds18b20_device
{
    struct s3c_ds18b20_platform_data *data;
    struct class *dev_class;
    struct cdev cdev;
} ds18b20_device;

static struct s3c_ds18b20_platform_data
{
    unsigned int gpio;
    unsigned int is_open_drain:1;
};

static struct s3c_ds18b20_platform_data s3c_ds18b20_data = {
    .gpio = S3C2410_GPG(0),
    .is_open_drain = 0,
};

/* Reset W1-ds18b20 and read presence pluse */
static unsigned int w1_ds18b20_reset(void)
{
    int err;
    s3c2410_gpio_cfgpin(DQ, OUTPUT);
    s3c2410_gpio_pullup(DQ, 0);

    s3c2410_gpio_setpin(DQ, 1);
    udelay(10);
    s3c2410_gpio_setpin(DQ, 0);
    udelay(600);

    s3c2410_gpio_setpin(DQ, 1);
    udelay(60);

    s3c2410_gpio_cfgpin(DQ, INPUT);
    udelay(400);
    err = s3c2410_gpio_getpin(DQ);
    return err;
}

/* w1 write bit */
static unsigned int w1_ds18b20_write(unsigned char data)
{
    unsigned int i;

    s3c2410_gpio_cfgpin(DQ, OUTPUT);

    for (i = 0; i < 8; i++)
    {
        s3c2410_gpio_setpin(DQ, 0);
        udelay(5);
        if (data & 0x01)
        {
            s3c2410_gpio_setpin(DQ, 1);
            udelay(60);
        }
        else
            udelay(60);
        data >>= 1;
        s3c2410_gpio_setpin(DQ, 1);
        udelay(1);
    }
    return 2;
}

/* w1 read byte */
static unsigned int w1_ds18b20_read(void)
{
    unsigned int i;
    unsigned char data = 0x00;

    for (i = 0; i < 8; i++)
    {
        s3c2410_gpio_cfgpin(DQ, OUTPUT);
        s3c2410_gpio_setpin(DQ, 1);
        udelay(1);
        s3c2410_gpio_setpin(DQ, 0);
        udelay(2);
        s3c2410_gpio_setpin(DQ, 1);
        s3c2410_gpio_cfgpin(DQ, INPUT);
        data >>= 1;
        if (0 != s3c2410_gpio_getpin(DQ))
            data |= 0x80;
        udelay(60);
    }
    return data;
}

static ssize_t ds18b20_read(struct file *filp, char __user * buf, size_t count, loff_t * f_pos)
{
    unsigned char Data[2] = { 0x00, 0x00 };
    unsigned long err;

    /* Enable temperature conversion*/
    if ((w1_ds18b20_reset()) <= 0)
    {
        printk(KERN_ERR "ds18b20 reset fail!\n");
        return -1;
    }
    w1_ds18b20_write(0xcc);/* Skip ROM command. */
    w1_ds18b20_write(0x44);/* Initiates temperature conversion*/

    /* Read temperature value.*/
    if ((w1_ds18b20_reset()) <= 0)
    {
        printk(KERN_ERR "ds18b20 reset fail!\n");
        return -1;
    }
    w1_ds18b20_write(0xcc);/* Skop ROM commond. */
    w1_ds18b20_write(0xbe);/* Read Scratchpad. */

    Data[0] = w1_ds18b20_read();/* Read low byte */
    Data[1] = w1_ds18b20_read();/* Read high byte */

    w1_ds18b20_reset();
    err = copy_to_user(buf, Data, sizeof(Data));
    return err ? -EFAULT : count;
}

static int ds18b20_release(struct inode *inode, struct file *file)
{
    return 0;
}

static int ds18b20_open(struct inode *inode, struct file *file)
{
    int flag = 0;
    struct ds18b20_device *pdev;
    struct s3c_ds18b20_platform_data *pdata;

    /* debug */
    printk(KERN_INFO"ds18b20_open.\n");
    pdev = container_of(inode->i_cdev,struct ds18b20_device, cdev);
    pdata = pdev->data;
    file->private_data = pdata;

    flag = w1_ds18b20_reset();
    if (flag)
    {
        printk(KERN_INFO "open ds18b20 successful!\n");
    }
    else
        printk("open ds18b20 failed!\n");
    return 0;
}


static struct platform_device s3c_ds18b20_device = {
    .name = "s3c_ds18b20",
    .id = -1,
    .dev = {
            .platform_data = &s3c_ds18b20_data,
            },
};

static struct file_operations ds18b20_fops = {
    .owner = THIS_MODULE,
    .open = ds18b20_open,
    .read = ds18b20_read,
    .release = ds18b20_release,
};

static int s3c_ds18b20_probe(struct platform_device *dev)
{
    struct s3c_ds18b20_platform_data *pdata = dev->dev.platform_data;
    int result = 0;
    dev_t devno;


    /* Alloc the device for driver */
    if (0 != dev_major)
    {
        devno = MKDEV(dev_major, dev_minor);
        result = register_chrdev_region(devno, 1, DEV_NAME);
    }
    else
    {
        result = alloc_chrdev_region(&devno, dev_minor, 1, DEV_NAME);
        dev_major = MAJOR(devno);
    }
    /* debug */
    printk(KERN_INFO "major is %d.\n",dev_major);

    /* Alloc for device major failure */
    if (result < 0)
    {
        printk("%s driver can't get major %d\n", DEV_NAME, dev_major);
        return result;
    }

    /*  Initialize ds18b20 structure and register cdev */
    memset(&ds18b20_device, 0, sizeof(ds18b20_device));
    ds18b20_device.data = dev->dev.platform_data;
    cdev_init(&(ds18b20_device.cdev), &ds18b20_fops);
    ds18b20_device.cdev.owner = THIS_MODULE;

    result = cdev_add(&(ds18b20_device.cdev), devno, 1);
    /* debug */
    printk(KERN_INFO "result is %d.\n",result);
    if (result)
    {
        printk(KERN_NOTICE "error %d add %s device", result, DEV_NAME);
        goto ERROR;
    }

    ds18b20_device.dev_class = class_create(THIS_MODULE, DEV_NAME);
    if (IS_ERR(ds18b20_device.dev_class))
    {
        printk("%s driver create class failture\n", DEV_NAME);
        result = -ENOMEM;
        goto ERROR;
    }

    device_create(ds18b20_device.dev_class, NULL, devno, NULL, DEV_NAME);

    printk("S3C %s driver version %d.%d.%d initiliazed.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,
           DRV_REVER_VER);

    return 0;

  ERROR:
    printk("S3C %s driver version %d.%d.%d install failure.\n", DEV_NAME, DRV_MAJOR_VER,
           DRV_MINOR_VER, DRV_REVER_VER);
    cdev_del(&(ds18b20_device.cdev));

    unregister_chrdev_region(devno, 1);
    return result;
}

static int s3c_ds18b20_remove(struct platform_device *dev)
{
    dev_t devno = MKDEV(dev_major, dev_minor);


    cdev_del(&(ds18b20_device.cdev));
    device_destroy(ds18b20_device.dev_class, devno);
    class_destroy(ds18b20_device.dev_class);

    unregister_chrdev_region(devno, 1);
    printk("%s driver removed\n", DEV_NAME);

    return 0;
}

static struct platform_driver s3c_ds18b20_driver = {
    .probe = s3c_ds18b20_probe,
    .remove = s3c_ds18b20_remove,
    .driver = {
               .name = "s3c_ds18b20",
               .owner = THIS_MODULE,
               },
};

static int __init s3c_ds18b20_init(void)
{
    int ret = 0;

    ret = platform_device_register(&s3c_ds18b20_device);
    if (ret)
    {
        printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__, __LINE__, ret);
        goto fail_reg_plat_dev;
    }

    ret = platform_driver_register(&s3c_ds18b20_driver);
    if (ret)
    {
        printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__, __LINE__, ret);
        goto fail_reg_plat_drv;
    }

    return 0;

  fail_reg_plat_dev:
    return ret;

  fail_reg_plat_drv:
    platform_driver_unregister(&s3c_ds18b20_driver);
    return ret;
}

static void s3c_ds18b20_exit(void)
{
    platform_driver_unregister(&s3c_ds18b20_driver);
    platform_device_unregister(&s3c_ds18b20_device);
}

module_init(s3c_ds18b20_init);
module_exit(s3c_ds18b20_exit);

module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);
module_param(dev_minor, int, S_IRUGO);

MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:S3C_DS18B20");
  • Makefile
[lwn@localhost ds18b20]$ vim Makefile 
 LINUX_SRC?=~/myfl2440/kernel/linux-lwn-3.0.1
CROSS_COMPILE=/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-

obj-m := s3c_plat_ds18b20.o

modules:
    @make -C $(LINUX_SRC) M=`pwd` modules
    @make clean

clean:
    rm -f  *.ko.* *.o *mod.c *.order *.symvers
  • 测试程序:test_ds18b20.c
 /*********************************************************************************
 *      Copyright:  (C) 2017 Li Wanneng<liwjng@gmail.com>
 *                  All rights reserved.
 *
 *       Filename:  test_ds18b20.c
 *    Description:  This file used for test ds18b20 driver
 *                 
 *        Version:  1.0.0(04/26/2017)
 *         Author:  Li Wanneng <liwjng@gmail.com>
 *      ChangeLog:  1, Release initial version on "04/26/2017 03:19:52 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <errno.h>  

/********************************************************************************
 *  Description:
 *   Input Args:
 *  Output Args:
 * Return Value:
 ********************************************************************************/
int main(int argc, char **argv)
{
    int fd;
    int data = 0;
    unsigned char result[2];
    float temperature = 0;

    fd = open("/dev/s3c_ds18b20", O_RDWR | O_NONBLOCK);

    if (fd < 0)
    {
        printf("user:open s3c_ds18b20 failed.\n");
        printf("user_Message : %s\n", strerror(errno)); 
        return -1;
    }


    while (1)
    {
        int ret = 0;

        usleep(100);
        ret = read(fd, result, sizeof(result));
        if (ret != 2)
        {
            printf("read wrong\n");
            exit(0);
        }

        data = (int)result[1];
        data <<= 8;
        data = data | result[0];
        temperature = data * 0.0625;

        printf("Temperature = %.2f\n", temperature);
        fflush(stdout);
        sleep(1);
    }

    close(fd);
    return 0;
}                               /* ----- End of main() ----- */

编译&&测试

完成上述的驱动和测试程序的编写,对于驱动程序基于上述makefile使用make命令编译生成ko文件,对于测试程序使用gcc交叉编译生成可执行二进制文件test,然后使用tftp将其下载到开发板测试。

  • 编译生成驱动文件和测试文件
    这里写图片描述
  • 开发板测试温度
    这里写图片描述

遇到的问题及解决:

1.Q:在测试驱动程序的时候,一直打不开设备节点,读取文件失败。提示错误信息:No such device or address。
A:通过调试发现,在ds18b20_open(struct inode *inode, struct file *file)函数中没有对参数file赋值。于是赶紧添加了下面三行代码,重新编译之后驱动程序可以运行了。

pdev = container_of(inode->i_cdev,struct ds18b20_device, cdev);
pdata = pdev->data;
file->private_data = pdata;

2.Q:执行测试程序的时候出现以下错误信息:

WARNING: at drivers/gpio/gpiolib.c:101 gpio_ensure_requested+0x54/0xd4()
autorequest GPIO-192
Modules linked in: s3c_plat_ds18b20
[<c002d508>] (unwind_backtrace+0x0/0xf0) from [<c003a9ac>] (warn_slowpath_common+0x48/0x60)
[<c003a9ac>] (warn_slowpath_common+0x48/0x60) from [<c003aa58>] (warn_slowpath_fmt+0x30/0x40)
[<c003aa58>] (warn_slowpath_fmt+0x30/0x40) from [<c01fad00>] (gpio_ensure_requested+0x54/0xd4)

A:经查阅资料以及警告信息(autorequest GPIO-192)得知是因为gpio冲突,联想到前两天添加了内核自带的ds18b20驱动,可能是两个驱动都同时使用了同一个GPIO管脚,于是我在make manuconfig中取消内核对于ds18b20驱动的选项,重新编译内核。最终问题得到了解决。

参考文章:
http://www.cnblogs.com/wangyuezhuiyi/archive/2012/10/12/2721839.html
http://blog.csdn.net/u010944778/article/details/48058433
在此感谢两位网友的文章给予的帮助和启发

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值