ds18b20-温度传感器-linux驱动-杂项设备

ds18b20读取温度数据步骤

  1. 初始化:将总线拉低至少480μs,然后释放总线并等待15μs。
  2. 发送“跳过ROM”命令(0xCC):该命令用于跳过在总线上连接的所有设备的唯一地址,直接定位到DS18B20。
  3. 发送“温度转换”命令(0x44):该命令告诉DS18B20开始进行温度转换,并将其存储在内部寄存器中。转换时间取决于DS18B20的分辨率设置,通常在750ms左右。
  4. 等待温度转换结束:可以使用忙等待方式,不断地向DS18B20发送“读取状态”命令(0xBE),当DS18B20返回0xFF时,表明温度转换已经完成。
  5. 发送“读取数据”命令(0xBE):该命令用于从DS18B20的内部寄存器中读取转换后的温度值。
  6. 读取温度值:从总线上读取9个字节的数据,其中第一个字节是温度的低8位,第二个字节是温度的高8位,第三个字节是温度的符号位(0代表正数,1代表负数),后面6个字节是校验位。
  7. 计算温度值:将低8位和高8位按照二进制拼接成16位的原始温度值,然后按照符号位进行判断,将原始温度值转换为实际温度值。

ds18b20时序图:

初始化时序

在这里插入图片描述

DS18B20初始化时序的步骤:

  1. 必须要拉低至少480us,这是复位信号;
  2. 然后拉高释放总线,等待15~60us之后,
  3. 若存在DS18B20,其会拉低总线60~240us表示DS18B20初始化复位成功。

读/写时序

在这里插入图片描述

DS18B20写步骤:

  • 写0,拉低至少60us,写周期为60-120us
  • 写1,拉低1us,写周期至少60us

DS18B20读步骤:

  • 整个读周期需要在15us内完成
  • 主机拉低总线至少1us,接着读取总线电平,为0表示读到的bit数据为0,若为1则表示读到数据是1

DS18B20驱动实现

使用混杂设备驱动框架,时序要求比较严格


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/ktime.h>


// DS18B20设备节点名称
#define DS18B20_DEV_NAME          "ds18b20"
// DS18B20设备资源定义
#define DS18B20_GPIO_PIN 	      (PAD_GPIO_C + 17)
#define GET_DS18B20_VAL_CMD       _IOR('s', 1, int)
typedef unsigned char uint8_t;

static void ds18b20_delay_us(int us)
{
	ktime_t kt;
	u64 pre,last;
	kt = ktime_get();
	pre = ktime_to_ns(kt);
	while(1)
	{
		kt = ktime_get();
		last = ktime_to_ns(kt);
		if(last-pre >= us*1000)
		{
			break;
		}
	}
}

static int ds18b20_pulse_init(void) {
    /* 设置引脚为输出模式 */
    gpio_direction_output(DS18B20_GPIO_PIN, 0);
    ds18b20_delay_us(500);  // 拉低至少480微秒
    gpio_direction_input(DS18B20_GPIO_PIN);
    ds18b20_delay_us(120);   // 等待传感器拉低
    if (gpio_get_value(DS18B20_GPIO_PIN) == 0) {  // 如果传感器存在
        ds18b20_delay_us(120);  // 等待传感器完成初始化
        return 1;
    }
    return 0;
}

static void ds18b20_write_bit(int bit) {
    gpio_direction_output(DS18B20_GPIO_PIN, 0);
    if (bit)
        ds18b20_delay_us(10);
    else
        ds18b20_delay_us(60);
    gpio_direction_input(DS18B20_GPIO_PIN);
    ds18b20_delay_us(15);
}

static void ds18b20_write_byte(uint8_t data) {
    int i;
    for (i = 0; i < 8; i++) {
        ds18b20_write_bit(data & 0x01);
        data >>= 1;
    }
}

static uint8_t ds18b20_read_bit(void) {
    uint8_t bit;
    gpio_direction_output(DS18B20_GPIO_PIN, 0);
    ds18b20_delay_us(2);
    gpio_direction_input(DS18B20_GPIO_PIN);
    ds18b20_delay_us(10);
    bit = gpio_get_value(DS18B20_GPIO_PIN);
    ds18b20_delay_us(48);
    return bit;
}

static uint8_t ds18b20_read_byte(void) {
    int i;
    uint8_t data = 0;
    for (i = 0; i < 8; i++) {
        data |= ds18b20_read_bit() << i;
    }
    return data;
}
/* 执行DS18B20转换命令 */
static void ds18b20_config_temperature(uint8_t data) {
    ds18b20_pulse_init();              // 复位
    ds18b20_write_byte(0xCC);     // 跳过ROM匹配
    ds18b20_write_byte(0x4E);     // 写入配置寄存器
    ds18b20_write_byte(0x00);     // 写入配置字节0,保留位为0
    ds18b20_write_byte(data);    
    ds18b20_write_byte(0x48);     // 复制到EEPROM
    mdelay(5);
}
static float ds18b20_read_temperature(int delay_ms) {
    uint8_t temp_l, temp_h;
    float temp;
    if (!ds18b20_pulse_init()) {
        printk(KERN_ERR "DS18B20 sensor not found\n");
        return -1.0;
    }
    ds18b20_write_byte(0xCC);  // 跳过ROM指令
    ds18b20_write_byte(0x44);  // 温度转换命令
    mdelay(delay_ms);  				// 等待转换完成
    if (!ds18b20_pulse_init()) {
        printk(KERN_ERR "DS18B20 sensor not found\n");
        return -1.0;
    }
    ds18b20_write_byte(0xCC);  // 跳过ROM指令
    ds18b20_write_byte(0xBE);  // 读取scratchpad命令
    temp_l = ds18b20_read_byte();
    temp_h = ds18b20_read_byte();

    temp = ((temp_h << 8) | temp_l) / 16.0;
    return temp;
}

static int ds18b20_read_temperature_raw(int delay_ms) {
    uint8_t temp_l, temp_h;
    int temp;
    if (!ds18b20_pulse_init()) {
        printk(KERN_ERR "DS18B20 sensor not found\n");
        return 1;
    }
    ds18b20_write_byte(0xCC);  // 跳过ROM指令
    ds18b20_write_byte(0x44);  // 温度转换命令
    mdelay(delay_ms);                // 等待转换完成
    if (!ds18b20_pulse_init()) {
        printk(KERN_ERR "DS18B20 sensor not found\n");
        return 1;
    }
    ds18b20_write_byte(0xCC);  // 跳过ROM指令
    ds18b20_write_byte(0xBE);  // 读取scratchpad命令
    temp_l = ds18b20_read_byte();
    temp_h = ds18b20_read_byte();
    
    temp = ((temp_h << 8) | temp_l);
    return temp;
}


static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{

	float temp = ds18b20_read_temperature(200); //开始转换,精度为10位需要延时200ms,获取温度数据
    if (copy_to_user(buf, &temp, sizeof(temp)))
    {
        printk(KERN_ERR "ds18b20: failed to copy data to user\n");
        return -EFAULT;
    }

    *f_pos += sizeof(temp);
    return sizeof(temp);
}

static long ds18b20_adc_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
    int temp = 0; 
    switch(cmd) {
        case GET_DS18B20_VAL_CMD:
            temp = ds18b20_read_temperature_raw(200);   //开始转换,精度为10位需要延时200ms,获取温度数据
            // printk(KERN_ERR "tmp kernel = %d\n",temp);
            if(copy_to_user((void *)args,&temp,sizeof(temp)) != 0) {
                return -EFAULT;
            }
            return 0;
        break;

        default:
            printk("IOCTLCMD failed\n");
            return -ENOIOCTLCMD;
    }
}

static struct file_operations ds18b20_fops = {
	.owner          =   THIS_MODULE,
	.read           =   ds18b20_read,
    .unlocked_ioctl =   ds18b20_adc_ioctl,
};

static struct miscdevice gec6818_adc_miscdev = {
	.minor		= MISC_DYNAMIC_MINOR,	//MISC_DYNAMIC_MINOR,动态分配次设备号
	.name		= "ds18b20",		//设备名称,/dev/ds18b20	
	.fops		= &ds18b20_fops,	//文件操作集
};

static int __init ds18b20_init(void)
{
	int ret = -1;
	//混杂设备的注册
	ret = misc_register(&gec6818_adc_miscdev);
	if (ret) {
		printk("7""misc_register fail\n");
		goto err_misc_register;
	}

	gpio_free(DS18B20_GPIO_PIN);
	ret = gpio_request(DS18B20_GPIO_PIN, "ds18b20");
	if(ret < 0) {
		printk(KERN_ERR"gpio_request fail\n");
		goto err_gpio_request;
	}
    ds18b20_config_temperature(0x3f);// 写入配置字节0,分辨率为10位
	printk(KERN_INFO "ds18b20: ds18b20 init.\n");
	return 0;

err_gpio_request:
	misc_deregister(&gec6818_adc_miscdev);
err_misc_register:
	return ret;
}

static void __exit ds18b20_exit(void)
{
	misc_deregister(&gec6818_adc_miscdev);
	gpio_free(DS18B20_GPIO_PIN);
	printk(KERN_INFO "ds18b20: ds18b20 exit.\n");
}

module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("GPL");

test.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define GET_DS18B20_VAL_CMD         _IOR('s', 1, int)
#define DS18B20_DEV_NAME            "/dev/ds18b20"

#define DHT11_DEV_NAME            "/dev/dht11"
int test_ds18b20()
{
    int fd;
    int ret;
    float temp;
    int tmp;
    // 打开设备节点
    fd = open(DS18B20_DEV_NAME, O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(1);
    }

    while(1) {

        // 读取温度值
        ret=ioctl(fd,GET_DS18B20_VAL_CMD,&tmp);
        if (ret < 0) {
            perror("read");
            close(fd);
            exit(1);
        }
        printf("tmp = %f\n",(float)tmp/16.0);

        usleep(4000);
        ret = read(fd, &temp, sizeof(temp));
        if (ret < 0) {
            perror("read");
            close(fd);
            exit(1);
        }
        printf("temp = %f\n",temp);
        usleep(4000);
    }

    // 关闭设备节点
    close(fd);

    return 0;
}

int main(void) {
    test_ds18b20();
    return 0;
}


结果如下:

在这里插入图片描述

参考:

https://cloud.tencent.com/developer/article/1974916

https://blog.csdn.net/qq_36413982/article/details/122674749

https://blog.csdn.net/qq_45661238/article/details/114002415

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值