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:~#

二、硬件连接

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


三、驱动开发与测试

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

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

【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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值