【C语言】树莓派(Raspberry Pi)+DS18B20 获取当前温度


前言

这次使用C语言来实现树莓派读取DS18B20测量的温度,加深一下对文件I/O的理解
DS18B20是一个比较常用的温度传感器,采用单总线控制,以前用单片机编程控制时严格按照单总线的时序控制,今天来看看在linux系统下如何通过DS18B20来获取温度信息。


一、实验准备

1、硬件准备

硬件数量
Raspberry 4B1
DS18B20 温度传感器1
杜邦线若干

2、软件准备

在开始之前,要使能树莓派内核的单总线协议驱动模块(1-Wire)
具体操作如下

sudo raspi-config

在这里插入图片描述
在这里插入图片描述
单总线的接口默认是GPIO 4(BCM),使用默认接口的话可以忽略下面更换引脚的操作
若想更换自己选择的端口,则需要在 /boot/config.txt 文件的最后那行中加上 “,gpiopin=你想要的端口”。

vim /boot/config.txt

在最后一行添加以下内容,这里的 gpiopin = 4 是采用BCM GPIO的标准。
如果是模块化的DS18B20,则添加以下内容【因为模块中已包含上拉电阻】

dtoverlay=w1-gpio, gpiopin=4

如果是单独的一个DS18B20芯片【也就是普通直插三极管的样子】

dtoverlay=w1-gpio-pullup, gpiopin=4

在这里插入图片描述
最后重启系统

sudo reboot

确认系统有自动加载单总线协议的驱动模块:

lsmod | grep w1

以下为正常加载
在这里插入图片描述

二、实现过程

配置好单总线的驱动协议模块后,将DS18B20接上树莓派

Raspberry PiDS18B20
3.3VVCC
GNDGND
4 (BCM)DQ

接上树莓派后,可以在 /sys/bus/w1/devices/ 目录下找到一个28-xxxxxx的文件夹(xxxxxxx为该DS18B20芯片的序列号)
该文件夹存放 DS18B20 的数据信息
DS18B20测量的温度数据存放在该文件夹的 w1_slave 文件中。
在这里插入图片描述
从图中看出,这时DS18B20测量的温度值为 31.562 ℃

1、找该DS18B20芯片的设备文件夹

  • ①、打开 “/sys/bus/w1/devices/” 这个文件夹,并让dirp指向该文件夹
  • ②、从该文件夹中,逐个找 “ 文件名中包含有 28- 的文件 ”
  • ③、找到则将文件夹名复制给字符数组 dir_name
  • ③、找不到则报错,退出函数
char 			 path[50] = "/sys/bus/w1/devices/";

//打开文件夹"/sys/bus/w1/devices/"
if ((dirp = opendir(path)) == NULL)
{
	printf("ERROR: Open Directory '%s' Failure: %s\n", path, strerror(errno));
	return -1;
}
printf("Open Directory '%s' Successfully\n", path);

//在文件夹中找DS18B20的文件夹28-xx
while ((direntp = readdir(dirp)) != NULL)
{
	if (strstr(direntp->d_name, "28-") != NULL)
	{
		strncpy(dir_name, direntp->d_name, strlen(direntp->d_name));
		printf("Find file: %s\n", dir_name);
		found = 1;
		break;
	}
}
closedir(dirp);

//找不到该文件夹
if (found == -1)
{
	printf("ERROR: Can not find DS18B20 in %s\n", path);
	return -2;
}

2、拼接文件夹和文件名到path中

  • ①、拼接文件夹和文件名到path中,得到一个完整的文件路径【绝对路径】
  • /sys/bus/w1/devices/28-3c01e0761972/w1_slave
//将文件夹名和文件名分别拼接到path上	
strncat(path, dir_name, strlen(dir_name));
strncat(path, "/w1_slave", sizeof(path)-strlen(path));

3、打开w1_slave文件

  • ①、上一步骤得到 w1_slave 文件的绝对路径后,便可使用系统调用函数 open() 直接打开文件w1_slave
  • ②、因为只需要读该文件中的内容,所以我们以只读的形式(O_RDONLY)打开即可
  • ③、打开成功后,便会返回一个指向该文件的文件描述符fd
  • 在运行最终代码的时候,会看到 fd=3 ,是因为
  • 操作系统默认会打开 0、1、2 这三个文件描述符
  • 0:标准输入【默认是键盘】
  • 1:标准输出【默认是屏幕】
  • 2:标准出错【默认是屏幕】
  • 而新建的文件描述符 fd,是当前可用的文件描述符中最小的非负整数,所以这里的 fd 为 3
//打开w1_slave文件
if ((fd = open(path, O_RDONLY)) < 0)
{
	printf("ERROR: Open file '%s' Failure: %s\n", path, strerror(errno));
	return -3;
}
printf("Open file '%s' fd[%d] Successfully\n", path, fd);

4、读w1_slave文件

  • ①、使用系统调用函数 read() 将 fd 所指向的文件的内容读到 buf 中,一次读 sizeof(buf) 字节大小
//从w1_slave中读取内容
if ((rv = read(fd, buf, sizeof(buf))) < 0)
{
	printf("ERROR: Read data from file '%s' Failure: %s\n", path, strerror(errno));
	rv = -4;
	goto cleanup;
}
printf("Read %dB data from file '%s'\n", rv, path);

5、匹配数据并计算、赋值

  • ①、从 buf 中匹配 “t=”这个字符串,这时 strstr() 返回的是 “t” 所存放的地址
  • ②、要想最终获得的地址是数据的地址,则需要将该地址向右移 2 位,这时的地址就是数据的首地址
  • ③、这时候的数据还是字符串形式,我们使用 atof() 函数将数据转为double型,并/1000,即可得到正确的温度值了
//从buf里匹配"t=",并将ptr移到数据的首地址上
if ((ptr= strstr(buf, "t=") + 2) == NULL)
{
	printf("ERROR: Find data Failure: %s", strerror(errno));
	rv = -5;
	goto cleanup;
}

//将数据转为double型,除1000得到正常的十进制温度
*temp = atof(ptr)/1000;

三、具体代码

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

int Get_DS18B20_Temp(double *temp);

/*
 * The main function
 */
int main(int argc, char **argv)
{
	double	temp;
	
	if (Get_DS18B20_Temp(&temp) < 0)
	{
		printf("ERROR: DS18B20 Get Temperature Failure\n");
		return -1;
	}
	printf("Temperature is: %.3f C\n", temp);
	
	return 0;
} /*-----End of main function-----*/

/*
 * 获取DS18B20测量的温度信息
 */
int Get_DS18B20_Temp(double *temp)
{
	int 			 fd = -1;
	int 			 rv = -1;
	char 			 path[50] = "/sys/bus/w1/devices/";
	char			 dir_name[20];
	int				 found = -1;
	DIR 		    *dirp;
	struct dirent	*direntp;
	char 			 buf[128];
	char			*ptr;
	
	//清空dir_name和buf内存空间的值,避免随机值产生乱码
	memset(dir_name, 0, sizeof(dir_name));
	memset(buf, 0, sizeof(buf));

	//打开文件夹"/sys/bus/w1/devices/"
	if ((dirp = opendir(path)) == NULL)
	{
		printf("ERROR: Open Directory '%s' Failure: %s\n", path, strerror(errno));
		return -1;
	}
	printf("Open Directory '%s' Successfully\n", path);
	
	//在文件夹中找DS18B20的文件夹28-xx
	while ((direntp = readdir(dirp)) != NULL)
	{
		if (strstr(direntp->d_name, "28-") == NULL)
			continue;
		
		strncpy(dir_name, direntp->d_name, strlen(direntp->d_name));
		printf("Find file: %s\n", dir_name);
		found = 1;
		break;
	}
	closedir(dirp);
	
	//找不到该文件夹
	if (found == -1)
	{
		printf("ERROR: Can not find DS18B20 in %s\n", path);
		return -2;
	}
	
	//将文件夹名和文件名分别拼接到path上	
	strncat(path, dir_name, strlen(dir_name));
	strncat(path, "/w1_slave", sizeof(path)-strlen(path));
	
	//打开w1_slave文件
	if ((fd = open(path, O_RDONLY)) < 0)
	{
		printf("ERROR: Open file '%s' Failure: %s\n", path, strerror(errno));
		return -3;
	}
	printf("Open file '%s' fd[%d] Successfully\n", path, fd);
	
	//从w1_slave中读取内容
	if ((rv = read(fd, buf, sizeof(buf))) < 0)
	{
		printf("ERROR: Read data from file '%s' Failure: %s\n", path, strerror(errno));
		rv = -4;
		goto cleanup;
	}
	printf("Read %dB data from file '%s'\n", rv, path);
	
	//从buf里匹配"t=",并将ptr移到数据的首地址上
	if ((ptr = strstr(buf, "t=") + 2) == NULL)
	{
		printf("ERROR: Find data Failure: %s", strerror(errno));
		rv = -5;
		goto cleanup;
	}
	
	//将数据转为double型,除1000得到正常的十进制温度
	*temp = atof(ptr)/1000;

cleanup:
	if (fd > 0)
		close(fd);

	return rv;
}

看到这里可能很多小伙伴会有疑惑,为什么返回值又是-1又是-2的,因为在 Linux 中,一般函数正常运行的情况下,返回的值为 0,函数出错的话,一般是返回 -1 这种负整数,遵循这个规律写代码,也方便我们的调试。

四、测试结果

在这里插入图片描述


总结

以上是对树莓派读取DS18B20温度信息的一些理解,如有写的不好的地方,还请各位大佬不吝赐教

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Simply myself

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

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

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

打赏作者

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

抵扣说明:

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

余额充值