> 文章列表 > Linux下ds18b20驱动开发获取温度

Linux下ds18b20驱动开发获取温度

Linux下ds18b20驱动开发获取温度

文章目录

    • 一、修改并且编译设备
      • (1)修改设备树
      • (2)修改开发板设备树进行reboot
    • 二、硬件连接
    • 三、驱动开发与测试
      • (1)编写设备驱动
      • (2)编写测试代码
      • (3)Makefile
      • (4)运行结果
    • 四、代码重难点分析
      • (1)ds18b20时序解析
        • 【1】宏定义
        • 【2】复位脉冲和应答脉冲
        • 【3】主机写时序
        • 【4】主机读时序
      • (2)移位获取每个byte进行发送
      • (3)获取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 dtbsDTC     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:~#

二、硬件连接

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


三、驱动开发与测试

(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
#endifstruct 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//测试udelayDS18B20_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 0dev_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);
#endiftemp = 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); //初始化cdevpriv->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);//释放gpiodgpiod_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 0DS18B20_IO_WRITE(1);DS18B20_UDELAY(2);
#endifDS18B20_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_appPWD := $(shell pwd)EXTRA_CFLAGS+=-DMODULEobj-m += ds18b20_platdrv.o
ds18b20_platdrv-objs := ds18b20.o platdrv_ds18b20.omodules:@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}/*.koinstall: uninstallcp -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.dclean: 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;

在这里插入图片描述
在这里插入图片描述

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

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

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

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

在这里插入图片描述
代码实现:

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。

在这里插入图片描述
代码实现:

/*写一个位(字)*/
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。

在这里插入图片描述
代码实现:

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

结果:

wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ gcc shift.c             
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao$ ./a.out     
原始的数据:
[  7] 00000111
[208] 11010000转换后数据:
[224] 11100000
[ 11] 00001011