Linux下ds18b20驱动开发获取温度

这篇具有很好参考价值的文章主要介绍了Linux下ds18b20驱动开发获取温度。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


对ds18b20不了解的可以查看这篇文章,讲解的比较详细的:STM32一线协议-DS18B20温度传感器采样实现

源码是根据上一届学长的,想要参考的可以去拜访一下gitee:代码链接


一、修改并且编译设备树

(1)修改设备树

在路径linux-imx/arch/arm/boot/dts/下修改设备树igkboard.dts
主节点:

w1: w1 {
        compatible = "ds18b20-gpio";
        status = "disabled";
    };

从节点:

&w1 {
    compatible = "ds18b20-gpio";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_w1>;
    w1-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
};

在源码路径下执行make dtbs进行编译:

wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx$ make dtbs
  DTC     arch/arm/boot/dts/igkboard.dtb

成功。

(2)修改开发板设备树进行reboot

涉及到的tftp相关的知识不懂的可以参考这篇文章:wpa_supplicant无线网络配置imx6ull以及搭建tftp服务器

将我们源码下的igkboard.dtszImage文件加载到开发板:

wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot$ ls -l zImage 
-rwxrwxr-x 1 wangdengtao wangdengtao 9466168  49 16:18 zImage
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot$ cd dts/
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot/dts$ ls -l igkboard.dts
-rw-rw-r-- 1 wangdengtao wangdengtao 16742  49 17:06 igkboard.dt

然后tftp命令将我们的两个文件上传到我们的开发板即可。

修改开发板上的这两个文件:

root@igkboard:~# find / -name zImage
/run/media/mmcblk1p1/zImage
root@igkboard:~# find / -name igkboard.dtb
/run/media/mmcblk1p1/igkboard.dtb

修改好只有只需要进行sudo reboot命令即可。

成功启动后你会在开发板的proc/device-tree/路径下看见设备树节点:

root@igkboard:~# ls /proc/device-tree/w1/
compatible  name  phandle  pinctrl-0  pinctrl-names  status  w1-gpios
root@igkboard:~# cat /proc/device-tree/w1/compatible 
ds18b20-gpioroot@igkboard:~#

二、硬件连接

Linux下ds18b20驱动开发获取温度
Linux下ds18b20驱动开发获取温度
Linux下ds18b20驱动开发获取温度


三、驱动开发与测试

(1)编写设备驱动

驱动源码:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>

#include "ds18b20.h"

#define DEV_NAME        "ds18b20"

#ifndef DEV_MAJOR
#define DEV_MAJOR       0
#endif

struct ds18b20_priv {
    struct cdev     cdev;
    struct class    *dev_class;
    struct device   *dev;
    spinlock_t lock;
};

struct gpio_desc *g_ds18b20_gpiod;
// int ds18b20_gpio;
static int dev_major = DEV_MAJOR;

/* @description: 温度读取函数
 *
 * @parm  : priv - 私有数据结构体指针
 * @parm  : 
 * @return: 温度,或者直接返回错误码
 */
static short temp_recv_from_sensor(struct ds18b20_priv *priv)
{
    //struct ds18b20_priv *priv = container_of(&devp, struct ds18b20_priv, dev);
    unsigned long flags;
    uint8_t temp_l = 0;
    uint8_t temp_h = 0;
    short temp = 0;
    short ret = 0;

    spin_lock_irqsave(&priv->lock, flags);
    if(ds18b20_reset() != 0)
    {
        printk("%d ds18b20 reset failure.\n", __LINE__);
        ret = -EFAULT;
        goto undo_spin_unlock;
    }
    
    ds18b20_write_byte(CMD_DS18B20_SKIP_ROM_ID);
    ds18b20_write_byte(CMD_DS18B20_CONVERT_TEMP);
    
    spin_unlock_irqrestore(&priv->lock, flags);

    msleep(750);

    spin_lock_irqsave(&priv->lock, flags);
    if(ds18b20_reset() != 0)
    {
        printk("%d ds18b20 reset failure.\n", __LINE__);
        ret = -EFAULT;
        goto undo_spin_unlock;
    }
    
    ds18b20_write_byte(CMD_DS18B20_SKIP_ROM_ID);
    ds18b20_write_byte(CMD_DS18B20_READ_DATA);

    temp_l = ds18b20_read_byte();
    temp_h = ds18b20_read_byte();

    spin_unlock_irqrestore(&priv->lock, flags);
    temp = (temp_h << 8) | temp_l;

    //printk("temp :%d temp_h : %d temp_l : %d \n", temp, temp_h, temp_l);
    return temp;

undo_spin_unlock:
    spin_unlock_irqrestore(&priv->lock, flags);
    return ret;
}

/* @description: 打开设备 
 *
 * @parm  : inode - 传递给驱动的inode
 * @parm  : filp - 设备文件,利用其私有数据成员
 * @return: 0 successfully , !0 failure
 */
static int ds18b20_open(struct inode *inode, struct file *filp)
{
    struct ds18b20_priv *priv = container_of(inode->i_cdev, struct ds18b20_priv, cdev);
    filp->private_data = priv;
#if 0
    //测试udelay
    DS18B20_IO_OUT();
    for (i = 0; i < 20; i++) {
        DS18B20_IO_WRITE(0);
        udelay(1);
        DS18B20_IO_WRITE(1);
        udelay(1);
    }
#endif
    //printk("open ds18b20 successfully.\n");

        return 0;
}

/* @description: 从设备读取文件
 *
 * @parm  : filp - 设备文件,文件描述符
 * @parm  : buf - 返回给用户空间的数据缓冲区
 * @parm  : cnt - 要读取的数据长度
 * @parm  : offt - 相对于文件首地址的偏移
 * @return: 读取的字节数,负数 - 读取失败
 */
static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    struct ds18b20_priv *priv = filp->private_data;
    int ret = 0;
    short temp = 0;
    temp = temp_recv_from_sensor(priv);

    if(copy_to_user(buf, &temp, sizeof(temp)))
    {
        ret = -EFAULT;
    }
    else
    {
        ret = sizeof(temp);
    }
    return ret;
}

/* @description: poll函数
 *
 * @parm  : filp - 要打开的设备文件 fd
 * @parm  : poll_table - 等待列表
 * @return: 设备或者资源状态,mask掩码相关
 */
static unsigned int ds18b20_poll(struct file *filp, struct poll_table_struct *poll_table)
{
    //TODO: 待完善
    return 0;
}

/* @description: 关闭设备
 *
 * @parm  : inode - 传递给驱动的inode
 * @parm  : filp - 设备文件,file结构体有个私有数据区可以使用
 * @return: 0 successfully , !0 failure
 */
static int ds18b20_release(struct inode *inode, struct file *filp)
{
    return 0;
}

//设备操作函数
static struct file_operations ds18b20_fops = {
    .owner = THIS_MODULE,
    .open  = ds18b20_open,
    .release = ds18b20_release,
    .read = ds18b20_read,
    .poll = ds18b20_poll,
};

/* @description: sysfs - 温度属性显示函数
 *
 * @parm  : devp - 设备指针,创建file时候会指定dev
 * @parm  : attr - 设备属性,创建时候传入
 * @parm  : buf - 传出给sysfs中显示的buf
 * @return: 显示的字节数
 * @TODO: 函数不够正规,了解PAGE_SIZE
 */
static ssize_t temp_show(struct device *devp, struct device_attribute *attr, char *buf)
{
    short temp = 0;
    //struct ds18b20_priv *priv0 = container_of(&devp, struct ds18b20_priv, dev);
    struct ds18b20_priv *priv = dev_get_drvdata(devp);

#if 0
    dev_info(devp, "%s driver probe okay.\n", DEV_NAME);
    dev_info(priv0->dev, "%s driver probe okay.\n", DEV_NAME);
    dev_info(priv1->dev, "%s driver probe okay.\n", DEV_NAME);
    printk("priv0 %p priv1 %p\n", priv0, priv1);
    printk("priv0->dev %p priv1->dev %p\n", priv0->dev, priv1->dev);
    printk("devp %p\n", devp);
    printk("priv0->lock %p priv1->lock %p\n", &priv0->lock, &priv1->lock);
#endif

    temp = temp_recv_from_sensor(priv);
    return sprintf(buf, "temp=%d\n", temp*62);//1000倍
}

/* @description: sysfs - echo写入属性函数
 *
 * @parm  : dev - 设备指针,创建file时候会指定dev
 * @parm  : attr - 设备属性,创建时候传入
 * @parm  : buf - 用户空间的buf
 * @parm  : count - 传入buf的size
 * @return: 写入的buf大小
 */
static ssize_t temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    char k_buf[10] = {0,};
    snprintf(k_buf, sizeof(k_buf), "%s", buf);

    dev_info(dev, "Don't echo to me  -> [%s] size [%d]\n", k_buf, count);

    return count;
}

static DEVICE_ATTR(temp, 0644, temp_show, temp_store);

/* @description: ds18b20 驱动安装 probe函数
 *
 * @parm  : pdev - platform 设备指针
 * @parm  : 
 * @return: 0 successfully , !0 failure
 */
static int ds18b20_probe(struct platform_device *pdev)
{
        struct ds18b20_priv     *priv = NULL;
        dev_t devno;
        int rv = 0;

    //0.给priv分配空间
    priv = devm_kzalloc(&pdev->dev, sizeof(struct ds18b20_priv), GFP_KERNEL);
    if(!priv)
    {   
        return -ENOMEM;
    }  
    
    //1. 获取gpio 
    g_ds18b20_gpiod = gpiod_get(&pdev->dev, "w1", GPIOD_ASIS);
    if(IS_ERR(g_ds18b20_gpiod))
    {
        dev_err(&pdev->dev, "Get %s gpiod failure.\n", DEV_NAME);
        return PTR_ERR(g_ds18b20_gpiod);
    }
    //ds18b20_gpio = desc_to_gpio(g_ds18b20_gpiod);
    //printk("request ds18b20_gpio [%d] \n", ds18b20_gpio);

    //2.创建设备号
    if(0 != dev_major)
    {   
        devno = MKDEV(dev_major, 0); 
        rv = register_chrdev_region(devno, 1, DEV_NAME); //静态创建
    }   
    else
    {   
        rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);//动态创建
        dev_major = MAJOR(devno);//获主设备号
    }   
    
    if(rv < 0)
    {   
        dev_err(&pdev->dev, "%s driver can't get major %d\n", DEV_NAME, dev_major);
        return rv; 
    }   

    //2.注册字符设备
    cdev_init(&priv->cdev, &ds18b20_fops); //初始化cdev
    priv->cdev.owner = THIS_MODULE;

    rv = cdev_add(&priv->cdev, devno, 1); 
    if(0 != rv) 
    {   
        dev_err(&pdev->dev, "error %d add %s device failure.\n", rv, DEV_NAME);
        goto undo_major;
    }   

    //3.创建类,驱动进行节点创建
    priv->dev_class = class_create(THIS_MODULE, DEV_NAME);
    if(IS_ERR(priv->dev_class))
    {   
        dev_err(&pdev->dev, "%s driver create class failure.\n", DEV_NAME);
        rv = -ENOMEM;
        goto undo_cdev;
    }   
 
    //4.创建设备 
    priv->dev = device_create(priv->dev_class, NULL, devno, NULL, DEV_NAME);
    if(IS_ERR(priv->dev))
    {
        rv = -ENOMEM;
        goto undo_class;
    }

    //5. 初始化自旋锁
    spin_lock_init(&priv->lock);

    //6. 创建sys 属性 在platform下
    if(device_create_file(priv->dev, &dev_attr_temp))
    {
        rv = -ENOMEM;
        goto undo_device;
    }

    //7. 保存私有数据
    platform_set_drvdata(pdev, priv);
    dev_set_drvdata(priv->dev, priv);
    dev_info(&pdev->dev, "%s driver probe okay.\n", DEV_NAME);
    // dev_info(priv->dev, "%s driver probe okay.\n", DEV_NAME);

    return 0;

undo_device:
    device_destroy(priv->dev_class, devno);

undo_class:
    class_destroy(priv->dev_class);

undo_cdev:
    cdev_del(&priv->cdev);

undo_major:
    unregister_chrdev_region(devno, 1);
    devm_kfree(&pdev->dev, priv);

    return rv;
}

/* @description: 驱动卸载 执行函数
 *
 * @parm  : pdev - 设备指针
 * @parm  : 
 * @return: 0 successfully , !0 failure
 */
static int ds18b20_remove(struct platform_device *pdev)
{
    struct ds18b20_priv *priv = platform_get_drvdata(pdev);
    dev_t devno = MKDEV(dev_major, 0);

    //删除sys中的属性
    device_remove_file(priv->dev, &dev_attr_temp);

    //设备销毁
    device_destroy(priv->dev_class, devno);

    //注销类
    class_destroy(priv->dev_class);

    //删除字符设备
    cdev_del(&priv->cdev);
    unregister_chrdev_region(devno, 1);

    //释放gpiod
    gpiod_put(g_ds18b20_gpiod);

    //释放堆
    devm_kfree(&pdev->dev, priv);
    dev_info(&pdev->dev, "%s driver remove.\n", DEV_NAME);

    return 0;
}

static const struct of_device_id of_ds18b20_match[] = { 
    {.compatible = "ds18b20-gpio"},
    {}, 
};

MODULE_DEVICE_TABLE(of, of_ds18b20_match);

static struct platform_driver ds18b20_driver = { 
    .probe      = ds18b20_probe,    //驱动安装时候会执行的钩子函数
    .remove     = ds18b20_remove,   //驱动卸载时候
    .driver     = { 
        .name   = "ds18b20-gpios",    //不建议用的name域
        .of_match_table = of_ds18b20_match,
    },  
};

static int __init platdrv_ds18b20_init(void)
{
    int     rv = 0;

    rv = platform_driver_register(&ds18b20_driver);  //注册platform驱动
    if(rv)
    {   
        printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__, __LINE__, rv);
        return rv; 
    }   
    printk("Regist imx ds18b20 Platform Driver successfully!\n");
    return 0;
}

static void __exit platdrv_ds18b20_exit(void)
{
    printk("%s():%d remove ds18b20 platform driver\n", __FUNCTION__, __LINE__);
    platform_driver_unregister(&ds18b20_driver);    //卸载驱动
}

//module_platform_driver(ds18b20_driver);

module_init(platdrv_ds18b20_init);
module_exit(platdrv_ds18b20_exit);

MODULE_AUTHOR("Wei Huihong <weihuihui586@gmail.com>");
MODULE_DESCRIPTION("i.MX6ULL ds18b20 driver platform driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:platdrv_ds18b20");

ds18b20.c

#include "ds18b20.h"


/* @description: ds18b20 复位 ,检测设备是否存在 
 *
 * @parm  : 
 * @parm  : 
 * @return: 0 successfully , !0 failure
 */
int ds18b20_reset(void)
{
    int ret = -1;
    
    DS18B20_IO_OUT();
    DS18B20_IO_WRITE(0);
    
    DS18B20_UDELAY(480);
    DS18B20_IO_WRITE(1);

    DS18B20_UDELAY(70);
    DS18B20_IO_IN();
    
    ret = DS18B20_IO_READ();
    DS18B20_UDELAY(10);
    DS18B20_IO_OUT();
    DS18B20_IO_WRITE(1); /* 释放总线 */

    return ret;
}

/* @description: ds18b20 写一个字
 *
 * @parm  : bit - 一个字
 * @parm  : 
 * @return: 
 */
static void _ds18b20_write_bit(unsigned char bit)
{
    DS18B20_IO_OUT();

    /* 判断 bit */
    bit = bit > 1 ? 1 : bit;
    //printk("write bit [%d]\n", bit);
    //mdelay(1);
    DS18B20_UDELAY(50); //TODO: 此处未知

        //写周期开始
    DS18B20_IO_WRITE(0);
    DS18B20_UDELAY(2);
        //写0还是写1,拉高或者拉低后持续60微妙
    DS18B20_IO_WRITE(bit);
    DS18B20_UDELAY(60);
        //然后大搞电平2us(写0),写1时候自己本身就是高的
    DS18B20_IO_WRITE(1);

    DS18B20_UDELAY(12);
}

/* @description: ds18b20 读一个字
 *
 * @parm  : 
 * @parm  : 
 * @return: 一个字 bit
 */
static unsigned char _ds18b20_read_bit(void)
{
    unsigned char bit;

    DS18B20_IO_OUT();
    DS18B20_IO_WRITE(0);
    DS18B20_UDELAY(2);
#if 0
    DS18B20_IO_WRITE(1);
    DS18B20_UDELAY(2);
#endif
    DS18B20_IO_IN();

    DS18B20_UDELAY(10); //NOTE: 根据实际进行修改
    bit = DS18B20_IO_READ();
    //printk("bit : %d\n", bit);
    DS18B20_UDELAY(50);

    return bit;
}

/* @description: ds18b20 写一个字节
 *
 * @parm  : byte - 写入的字节
 * @parm  : gpiod - gpio 描述符
 * @return: void
 */
void ds18b20_write_byte(unsigned char byte)
{
    int i;
    for(i=0; i<8; i++)
    {
        _ds18b20_write_bit((byte >> i) & 0x01);
    }
}


/* @description: ds18b20 读一个字节
 *
 * @parm  : 
 * @parm  : 
 * @return: 读到的字节
 */
unsigned char ds18b20_read_byte(void)
{
    uint8_t byte = 0;
    uint8_t bit;
    int i;

    for(i=0; i<8; i++)
    {
        bit = _ds18b20_read_bit();
        if(bit)
            byte |= (0x01 << i);
        //byte = _ds18b20_read_bit() ? (byte | (0x01 << i)) : byte;
    }

    return byte;
}

ds18b20.h

#ifndef __DS18B20_H__
#define __DS18B20_H__

#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>

/* ds18b20 CMD */
/* function */
#define CMD_DS18B20_CONVERT_TEMP    0x44//温度变换
#define CMD_DS18B20_READ_DATA       0xBE//读暂存存储器
#define CMD_DS18B20_WRITE_DATA      0x4E//写暂存存储器
#define CMD_DS18B20_COPY_DATA       0x48//复制暂存存储器
#define CMD_DS18B20_RESET_EPEROM    0xB8//重新调出
#define CMD_DS18B20_POWER_READ      0xB4//读电源

/* ROM */
#define CMD_DS18B20_SKIP_ROM_ID     0xCC//跳过ROM
#define CMD_DS18B20_MATCH_ROM_ID    0x55//匹配ROM
#define CMD_DS18B20_READ_ROM_ID     0x33  /* 读ROM总线仅一个设备使用 */
#define CMD_DS18B20_SEARCH_ROM_ID   0xF0//搜索ROM
#define CMD_DS18B20_SEARCH_ALARM    0xEC//告警搜索


/* for gpio */
#define DS18B20_IO_OUT() gpiod_direction_output(g_ds18b20_gpiod, 1)
#define DS18B20_IO_IN() gpiod_direction_input(g_ds18b20_gpiod)
#define DS18B20_IO_WRITE(bit) gpiod_set_value(g_ds18b20_gpiod, bit)
#define DS18B20_IO_READ()  gpiod_get_value(g_ds18b20_gpiod)

#define DS18B20_UDELAY(us) udelay(us)

//extern int ds18b20_gpio;
extern struct gpio_desc *g_ds18b20_gpiod;


/* @description: ds18b20 复位 ,检测设备是否存在 
 *
 * @parm  : 
 * @parm  : 
 * @return: 0 successfully , !0 failure
 */
extern int ds18b20_reset(void);

/* @description: ds18b20 写一个字节
 *
 * @parm  : byte - 写入的字节
 * @parm  : 
 * @return: void
 */
extern void ds18b20_write_byte(unsigned char byte);

/* @description: ds18b20 读一个字节
 *
 * @parm  : gpiod - gpio 描述符
 * @parm  : 
 * @return: 读到的字节
 */
extern unsigned char ds18b20_read_byte(void);

#endif

(2)编写测试代码

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

#define DEV_DS18B20     "/dev/ds18b20"

int main(int argc, char *argv[])
{
    int fd_ds18b20 = -1;
    int rv = -1;
    short temp = 0;
    float temperature = 0.0;

    fd_ds18b20 = open(DEV_DS18B20, O_RDONLY);
    if(fd_ds18b20 < 0)
    {
        printf("open %s failure.\n", DEV_DS18B20);
        return -1;
    }
    printf("open %s successfully.\n", DEV_DS18B20);
    while (1) {
        if((rv = read(fd_ds18b20, &temp, sizeof(temp))) <= 0)
        {
            printf("read %s failure %s\n", DEV_DS18B20, strerror(errno));
            return -2;
        }
        else 
        {
            temperature = temp * 0.0625; //右移4bit,保证小数点数据正确
            printf("[ds18b20] temp : %.3f℃ \n", temperature);
        }

        sleep(2);
    }
    close(fd_ds18b20);
    
    return 0;
}

(3)Makefile

LINUX_SRC = /home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx
CROSS_COMPILE=arm-linux-gnueabihf-
INST_PATH=/tftp
APP=ds18b20_app

PWD := $(shell pwd)

EXTRA_CFLAGS+=-DMODULE

obj-m += ds18b20_platdrv.o
ds18b20_platdrv-objs := ds18b20.o platdrv_ds18b20.o


modules:
        @make clean
        @echo ${LINUX_SRC}
        @make -C $(LINUX_SRC) M=$(PWD) modules
        @make clear
        @$(CROSS_COMPILE)gcc $(APP).c -o $(APP)

uninstall:
        rm -f ${INST_PATH}/*.ko

install: uninstall
        cp -af *.ko ${INST_PATH}

clear:
        @rm -f *.o *.cmd *.mod.c .*.cmd *.mod
        @rm -rf  *~ core .depend  .tmp_versions Module.symvers modules.order -f
        @rm -f .*ko.cmd .*.o.cmd .*.o.d

clean: clear
        @rm -f *.ko
        @rm -f $(APP)

(4)运行结果

root@igkboard:~# insmod ds18b20_platdrv.ko 
root@igkboard:~# lsmod
Module                  Size  Used by
ds18b20_platdrv        16384  0
rtl8188fu             999424  0
imx_rngc               16384  0
rng_core               20480  1 imx_rngc
secvio                 16384  0
error                  20480  1 secvio
root@igkboard:~# ls -l /dev/ds18b20 
crw------- 1 root root 243, 0 Apr 22 08:39 /dev/ds18b20
root@igkboard:~# ./ds18b20_app 
open /dev/ds18b20 successfully.
[ds18b20] temp : 23.375[ds18b20] temp : 23.375[ds18b20] temp : 23.375[ds18b20] temp : 26.562[ds18b20] temp : 28.000[ds18b20] temp : 28.812

四、代码重难点分析

(1)ds18b20时序解析

【1】宏定义

函数声明与宏定义:

/* ds18b20 CMD */
/* function */
#define CMD_DS18B20_CONVERT_TEMP    0x44//温度变换
#define CMD_DS18B20_READ_DATA       0xBE//读暂存存储器
#define CMD_DS18B20_WRITE_DATA      0x4E//写暂存存储器
#define CMD_DS18B20_COPY_DATA       0x48//复制暂存存储器
#define CMD_DS18B20_RESET_EPEROM    0xB8//重新调出
#define CMD_DS18B20_POWER_READ      0xB4//读电源

/* ROM */
#define CMD_DS18B20_SKIP_ROM_ID     0xCC//跳过ROM
#define CMD_DS18B20_MATCH_ROM_ID    0x55//匹配ROM
#define CMD_DS18B20_READ_ROM_ID     0x33  /* 读ROM总线仅一个设备使用 */
#define CMD_DS18B20_SEARCH_ROM_ID   0xF0//搜索ROM
#define CMD_DS18B20_SEARCH_ALARM    0xEC//告警搜索

/* for gpio */
#define DS18B20_IO_OUT() gpiod_direction_output(g_ds18b20_gpiod, 1)//输出模式,默认高电平
#define DS18B20_IO_IN() gpiod_direction_input(g_ds18b20_gpiod)//输入模式
#define DS18B20_IO_WRITE(bit) gpiod_set_value(g_ds18b20_gpiod, bit)//
#define DS18B20_IO_READ()  gpiod_get_value(g_ds18b20_gpiod)//

#define DS18B20_UDELAY(us) udelay(us)

extern struct gpio_desc *g_ds18b20_gpiod;

Linux下ds18b20驱动开发获取温度
Linux下ds18b20驱动开发获取温度

【2】复位脉冲和应答脉冲

主机首先发出一个480us - 960us的低电平脉冲(复位),然后释放总线变为高电平,并在随后的480微秒时间内,对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。

作为从机的DS18B20上电后就一直检测总线上是否有480us - 960us的低电平出现,如果检测到该复位脉冲则在总线变为高电平后,等待15us - 60us,之后将总线电平拉低60us - 240us(响应存在脉冲),告诉主机本器件已做好准备。若检测不到复位脉冲则一直处于检测等待。

我们简单来看就是:主机将DQ拉成低电平保持最少480us后释放总线,延时15 ~ 60us后的60~240us时间内检测DQ是否为低电平,再延时240us保持起始时序的完整。(从主机角度看进行初始化的过程)

Linux下ds18b20驱动开发获取温度
代码实现:

int ds18b20_reset(void)
{
    int ret = -1; 
        
    DS18B20_IO_OUT();
    DS18B20_IO_WRITE(0);
        
    DS18B20_UDELAY(480);//输出480us的低电平
    DS18B20_IO_WRITE(1);//随后高电平

    DS18B20_UDELAY(70);//等待70us去查看总线,0表示有响应
    DS18B20_IO_IN();//输入模式,读
        
    ret = DS18B20_IO_READ();
    DS18B20_UDELAY(10);
    DS18B20_IO_OUT();
    DS18B20_IO_WRITE(1); /* 释放总线 */

    return ret;
}
【3】主机写时序

写周期最少为60微秒,最长不超过120微秒。写周期的开始,主机先把总线拉低大于1微秒表示写周期开始。

若主机想写0:则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平延时2s。
若主机想写1:在拉低总线电平1微秒后就释放总线为高电平,延时60us。

作为从机的DS18B20在检测到总线被拉低后等待15微秒后从15us ~ 45us对总线采样(典型时间15us),在采样时间内,若检测到总线为高电平则认为主机发送了1,若检测到总线为低电平则认为主机发送了0。

Linux下ds18b20驱动开发获取温度
代码实现:

/*写一个位(字)*/
static void _ds18b20_write_bit(unsigned char bit)
{
    DS18B20_IO_OUT();

    /* 判断 bit */
    bit = bit > 1 ? 1 : bit;
    //printk("write bit [%d]\n", bit);
    //mdelay(1);
    DS18B20_UDELAY(50); //TODO: 此处未知

    //写周期开始
    DS18B20_IO_WRITE(0);
    DS18B20_UDELAY(2);
    //写0还是写1,拉高或者拉低后持续60微妙
    DS18B20_IO_WRITE(bit);
    DS18B20_UDELAY(60);
    //然后大搞电平2us(写0),写1时候自己本身就是高的
    DS18B20_IO_WRITE(1);

    DS18B20_UDELAY(12);
}
【4】主机读时序

读周期最少为60微秒,最长不超过120微秒。读周期的开始,主机先把总线拉低大于1微秒,然后释放总线。主机释放总线后:

若DS18B20发送0:则把总线拉低并保持至少从读周期开始的15us,然后释放总线为高电平。
若DS18B20发送1:则在主机释放总线后不拉低总线(为高电平)。

主机须在读周期开始的15us内检测总线电平的高低,若检测到总线为低则表示DS18B20发送来0,若检测到总线为高则表示DS18B20发送来1。

Linux下ds18b20驱动开发获取温度
代码实现:

static unsigned char _ds18b20_read_bit(void)
{
    unsigned char bit;

    DS18B20_IO_OUT();
    DS18B20_IO_WRITE(0);
    DS18B20_UDELAY(2);

    DS18B20_IO_IN();

    DS18B20_UDELAY(10); //NOTE: 根据实际进行修改
    bit = DS18B20_IO_READ();
    //printk("bit : %d\n", bit);
    DS18B20_UDELAY(50);

    return bit;
}

(2)移位获取每个byte进行发送

这个一般再放发送的时候用的比较多,移位与运算获取到每一个位。

/*********************************************************************************
 *      Copyright:  (C) 2023 WangDengtao<1799055460@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  shift.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2023年04月21日)
 *         Author:  WangDengtao <1799055460@qq.com>
 *      ChangeLog:  1, Release initial version on "2023年04月21日 10时48分56秒"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#define CMD_DS18B20_CONVERT_TEMP    0x44//温度变换
/*0&x = 0, 1&x = x  
0|x = x,1|x = 1  */									
int main(int argc,  char* argv[])
{
	int i;

    for(i=0; i<8; i++)
    {
        printf("%d ", (CMD_DS18B20_CONVERT_TEMP >> i) & 0x01);
    }
	printf("\n");

	return 0;
}

结果:

wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ gcc shift.c 
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ ./a.out 
0 0 1 0 0 0 1 0 

(3)获取ds18b20发送的数据

ds18b20发送过来的数据我们需要获取,一个字地去获取,下面是举的一个例子,代码中的函数也是直接拿过来验证的。获取到的数据是相反的。因为ds18b20发送一个字节,连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前),所以验证时候的数据是相反的。

实际代码中这样做是正确的。

/*********************************************************************************
 *      Copyright:  (C) 2023 WangDengtao<1799055460@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  shift.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2023年04月21日)
 *         Author:  WangDengtao <1799055460@qq.com>
 *      ChangeLog:  1, Release initial version on "2023年04月21日 10时48分56秒"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

//十进制转换为二进制
uint8_t dectobin(int n);

int main(int argc,  char* argv[])
{
        int i;
        uint8_t byte_l = 0;
        uint8_t byte_h = 0;
        uint8_t temp_l[8] = {0,0,0,0,0,1,1,1}; 
        uint8_t temp_h[8] = {1,1,0,1,0,0,0,0}; 

        for(i=0; i<8; i++)
        {
                if(temp_l[i])
                {
                        byte_l |= (0x01 << i);
                }
                //printf("%d ", byte_l);
        }

        for(i=0; i<8; i++)
        {
                if(temp_h[i])
                {   
                        byte_h |= (0x01 << i); 
                }   
                //printf("%d ", byte_h);
        }

        printf("原始的数据:\n");
        dectobin(7);
        dectobin(208);
        //printf("[  7] temp_l 0000 0111\n");
        //printf("[208] temp_h 1101 0000\n");
        printf("\n");

        printf("转换后数据:\n");
        dectobin(byte_l);
        dectobin(byte_h);


        return 0;
}

uint8_t dectobin(int n)
{
        int a = n;
    int sum = 0;
    int y, x = 1; // y表示余数,x为叠加的系数
    while (n != 0)
    {
        y = n % 2;
        sum += x * y;
        x *= 10;
        n /= 2;
    }
    printf("[%3d] %08d\n", a, sum);
    return sum;
}

结果:文章来源地址https://www.toymoban.com/news/detail-421819.html

wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ gcc shift.c             
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ ./a.out     
原始的数据:
[  7] 00000111
[208] 11010000

转换后数据:
[224] 11100000
[ 11] 00001011

到了这里,关于Linux下ds18b20驱动开发获取温度的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【mcuclub】温度传感器DS18B20

        VCC:外接供电电源输入端。 DQ: 数字信号输入/输出端。 GND:电源地线 为什么接上拉电阻: 因为DS18B20的数据口是漏极开路,如果不接上拉电阻,则只能输出低电平和高阻态,不能输出高电平,因此需要外接上拉电阻,否则无法输出1。DS18B20的工作电流约为1mA,VCC一般为

    2023年04月22日
    浏览(44)
  • DS18B20温度传感器——测试环境温度及代码

    醒醒!,还在睡呢,开始干代码了! 单片机通过OneWire协议与DS18B20通信,最终测出环境温度 OneWire 总线的硬件接口很简单,只需要把 DS18B20 的数据引脚和单片机的一个 IO 口接上      说明:GND接地,DQ单引线用于数据的输入,VDD接电源正极(注意正负极不能接反) 通过编程,

    2024年02月09日
    浏览(70)
  • STM32——DS18B20温度传感器

    一、DS18B20介绍 (一)DS18B20技术性能特征 1、独特的单总线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯,大大提高了系统的抗干扰性。 2、测温范围  -55°C~+125°C 3、支持多点组网功能,多个DS18B20可以并联在唯一的三线上,最多只

    2024年01月19日
    浏览(45)
  • 【单片机】17-温度传感器DS18B20

    (1)测温度的方式:物理(汞柱,气压),电子(金属电性能随温度变化) (2)早期:热敏电阻(模拟接口---》 A/D转换 ) (3)现代:专用sensor(数字接口,如I2C,DS18B20单总线接口等) DS18B20 可编程分辨率单总线 温度传感器 (1)内置集成ADC,外部数字接口 (2) 单总线

    2024年02月04日
    浏览(53)
  • 基于51单片机的DS18B20温度显示

    本讲内容:       了解温度传感器DS18B20的使用,并通过一个例程展示温度传感器DS18B20测温过程。 DS18B20简介:       DS18B20 是单线数字温度传感器,即“一线器件”,其具有独特的优点:     (1)采用单总线的接口方式 与微处理器连接时 仅需要一条口线即可实现微处理器

    2024年02月12日
    浏览(53)
  • 51单片机温度传感器DS18B20

    实现功能 插上DS18B20温度传感器,数码管显示检测的温度值 单片机型号:STC89C52 DS18B20介绍 1、DS18B20简介 DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、 适用电压宽、与微处理

    2024年02月01日
    浏览(52)
  • DS18B20温度传感器原理及使用教程

           DS18B20数字温度传感器提供9-Bit到12-Bit的摄氏温度测量精度和一个用户可编程的非易失性且具有过温和低温触发报警的报警功能。DS18B20采用的1-Wire通信即仅采用一个数据线(以及地)与微控制器进行通信。该传感器的温度检测范围为-55℃至+125℃,并且在温度范围超过

    2024年02月11日
    浏览(38)
  • 温度传感器DS18B20【单总线时序结构】

    DS18B20是一种常见的 数字温度传感器 ,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点 测温范围:-55°C 到 +125°C 通信接口: 1-Wire(单总线) 其它特征:可形成 总线结构 、内置温度报警功

    2024年02月04日
    浏览(52)
  • 51单片机读取DS18B20温度传感器

    1 .首先我们知道DS18B20是 单总线协议 ,只有一根数据线。所以Data数据线即使发送端又是接收端,同时DS18B20内部接了弱上拉电阻(如图一所示),数据线默认为高电平。有了这些概念,我们就能进行下一步。                                                图一 (截取

    2024年02月08日
    浏览(41)
  • FPGA—DS18B20数字温度传感器(附代码)

    目录 1.理论学习 DS18B20概述 DS18B20特性 DS18B20内部结构 高速缓存器:  64位光刻ROM: 1-Wire 总线协议: 温度测量原理 DS18B20操作步骤 1-Wire总线时序控制 2.实操 2.1 整体说明 2.2 DS18B20控制模块 2.2.1 模块框图和流程图 2.2.2 状态图 2.2.3 波形图         2.2.4 RTL代码 2.3 顶层模块 2.3.1

    2024年02月04日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包