Linux下ds18b20驱动开发获取温度

news2024/11/24 18:34:46

文章目录

    • 一、修改并且编译设备树
      • (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 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/461499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第四章——数学知识1

质数 质数&#xff1a;在大于1的整数中&#xff0c;如果只包含1和本身这俩个约束&#xff0c;就被叫质数或素数。 质数判定试除法 质数的判定——试除法&#xff1a;如果d能整除n&#xff0c;则n/d再除n&#xff0c;结果是一个整数。 d≤n/d。 bool is_prime(int x) {if (x <…

【大数据之Hadoop】二十、Yarn基础框架及工作机制

1、Yarn基础框架 Yarn是一个资源调度平台&#xff0c;负责为运算程序提供服务器运算资源&#xff0c;相当于一个分布式的操作系统平台&#xff0c;而MapReduce等运算程序则相当于运行于操作系统之上的应用程序。 YARN主要由ResourceManager、NodeManager、ApplicationMaster和…

202303-1 田地丈量

代码 #include<iostream> #include<vector> #include<string> #include<cmath> #include<algorithm> #include<stack> using namespace std; int n, a, b;int main() {cin >> n >> a >> b;int x1, y1, x2, y2;int x, y;…

科学计算NumPy之Ndarray数组对象的创建、切片、索引、修改等操作汇总

NumPy的操作汇总 NumPy概述Ndarray对象基本使用Ndarray的属性Ndarray的类型Ndarray的形状 创建数组创建数组创建全1数组创建全1数组从已有数组创建新数组从现有数组生成创建等差数列数组创建等比数列数组创建等间隔数列数组创建随机数数组创建正态分布创建创建均匀分布 数组切片…

【JUC高并发编程】—— 再见JUC

一、读写锁 读写锁概述 1️⃣ 什么是读写锁&#xff1f; 读写锁是一种多线程同步机制&#xff0c;用于在多线程环境中保护共享资源的访问 与互斥锁不同的是&#xff0c;读写锁允许多个线程同时读取共享资源&#xff0c;但在有线程请求写操作时&#xff0c;必须将其他读写锁…

windows10 ubuntu子系统安装perf工具

文章目录 1&#xff0c;ubuntu子系统中perf工具安装不了1.1&#xff0c;查看perf版本如下所示1.2&#xff0c;网上找不到对应的版本的内核源码&#xff0c;下载别的版本后&#xff0c;编译各种报错 2&#xff0c;百度查到说是WSL1不支持perf2.1&#xff0c;查看WSL版本 2.2&…

MySQ基础知识整合

目录 模糊查询 排序 单行函数 多行函数 分组函数 having 单表查询执行顺序总结 distinct 连接查询 子查询 union limit DQL语句执行顺序 DDL语句 日期化 date和date_format区别 update table 的快速创建以及删除&#xff08;及回滚&#xff09; 约束 事务 …

HTTP基础知识汇总

伴随着云原生(Cloud Native)的兴起&#xff0c;面向服务架构(Service-Oriented Architecture&#xff0c;SOA)、微服务(Microservice)、容器(Container)等相关概念与技术正在逐渐影响CAx(CAD/CAE/CAM)软件的架构设计与开发。 在云原生CAx软件中&#xff0c;首先需要把系统按照…

MySQL锁详解及案例分析

MySQL锁详解及案例分析 一、一条update语句二、MySQL锁介绍三、全局锁全局锁演示1.环境准备2.全局锁演示 四、MySQL表级锁&#xff08;都是Server层实现&#xff09;1、表级锁介绍2、表读S、写锁X1&#xff09;表锁相关命令2&#xff09;表锁演示1、表级的共享锁(读锁)2、表级的…

VLAN实验

SW1 [sw1]int g0/0/2 [sw1-GigabitEthernet0/0/2]dis this interface GigabitEthernet0/0/2 port link-type access port default vlan 2 pc1划分到vlan 2 [sw1-GigabitEthernet0/0/3]dis t…

【C++STL】set

前言 前面的CSTL的博客&#xff0c;我们介绍了string&#xff0c;vector&#xff0c;list&#xff0c;deque&#xff0c;priority_queue还有stack和queue。 这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 而从本节开…

【正点原子STM32精英V2开发板体验】体验LVGL的SD NAND文件系统

目的 验证基于SD NAND卡在正点原子STM32精英V2开发板上的兼容效果 实验材料 正点原子STM32精英V2开发板 TF 卡一片 SD NAND卡一片 实验步骤 1、打开例程【正点原子】精英STM32F103开发板 V2-资料盘(A盘)\4&#xff0c;程序源码\3&#xff0c;扩展例程\4&#xff0c;LVGL…

数据库系统概论(二)关系数据库,SQL概述和数据库安全性

作者的话 前言&#xff1a;总结下知识点&#xff0c;自己偶尔看一看。 目录 一、关系模型概述 1.1关系数据结构及形式化定义 1.1.1域&#xff08;Domain&#xff09; 1.1.2笛卡尔积&#xff08;Cartesian Product&#xff09; 1.1.3关系&#xff08;Relation&#xff09; …

Android之 WebView的使用

一 简介 1.1 WebView是用来展示网页的控件&#xff0c;底层是google的WebKit的引擎。 比起苹果的WebView&#xff0c;webkit一些不足地方&#xff1a; 不能支持word等文件的预览 纯标签加载&#xff0c;并不支持所有标签的加载 不支持文件的下载&#xff0c;图片的放大&#…

MATLAB连续时间信号的实现和时域基本运算(八)更新中...

1、实验目的&#xff1a; 1&#xff09;熟悉常用连续时间信号的实现方法&#xff1b; 2&#xff09;掌握连续时间信号的时域基本运算&#xff1b; 3&#xff09;掌握实现基本函数及其运算的函数的使用方法&#xff1b; 4&#xff09;加深对信号基本运算的理解。 2、实验内容&am…

Python Selenium 关键字驱动

目录 项目目录结构 action目录 config目录 exceptionpictures目录 log目录 testCases目录 testData目录 util目录 总结 之前写过一篇Java版的关键字驱动&#xff0c;现在来写一篇Python版本的&#xff0c;网上好多教程都是虎头蛇尾的不完整~ 说下思路&#xff0c;这边没…

十、ElasticSearch 实战 - 源码运行

一、概述 想深入理解 Elasticsearch&#xff0c;了解其报错机制&#xff0c;并有针对性的调整参数&#xff0c;阅读其源码是很有必要的。此外&#xff0c;了解优秀开源项目的代码架构&#xff0c;能够提高个人的代码架构能力 阅读 Elasticsearch 源码的第一步是搭建调试环境&…

C++的左值引用和右值引用

引用和指针的区别&#xff1f; 引用必须初始化&#xff0c;指针可以不初始化 定义一个指针和引用汇编指令上一样的&#xff0c;引用底层还是指针 引用只有一级引用&#xff0c;没有多级引用&#xff0c;而指针可以有多级指针 定义一个引用变量和指针变量&#xff0c;它们汇…

【c语言】函数指针详解

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…

Android Framework—Service

介绍 Service是一种可以在后台执行长时间运行操作而不提供界面的应用组件。服务可以由其他应用组件启动&#xff0c;而且即使用户切换到其他应用&#xff0c;服务仍将在后台继续运行。此外&#xff0c;组件可以通过绑定到服务与之进行交互&#xff0c;甚至是执行进程之间的通信…