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

来源:互联网 发布:mac usb 以太网转接器 编辑:程序博客网 时间:2024/05/21 18:32

基于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.1CROSS_COMPILE=/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-obj-m := s3c_plat_ds18b20.omodules:    @make -C $(LINUX_SRC) M=`pwd` modules    @make cleanclean:    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-192Modules 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
原创粉丝点击