如果不了解DS18B20温度传感器的小伙伴可以看这篇文章,里面介绍了DS18B20的相关知识(功能结构,操作时序,代码实现等)
STM32一线协议-DS18B20温度传感器采样实现
这篇文章就介绍一下如何利用IGKBoard(imx6ull)开发板实现温度采样。
1- 硬件模块介绍以及连接说明
DS18B20传感器的工作电压范围为3~5.5v,所以其电源连接3.3v和5v都可以。这样DS18B20在与开发板相连时,主要连接如下三个引脚:
- GND:该引脚要连到开发板的GND扩展引脚上;
- VDD:该引脚要连到开发板的3.3v 或 5v 供电引脚上;
- DQ:是DS18B20的数据通信引脚,该引脚应该连开发板上具有1-Wire 协议功能的GPIO引脚上;
在IGKBoard开发板上,提供了与树莓派兼容的 40Pin扩展引脚,其定义如下。
其中物理引脚#7 (GPIO1_IO18) 在Linux系统启动时如果启用了 w1 overlay 后,它将会默认作为 DS18B20 传感器的一线协议接口使用。这样,DS18B20的 DQ 引脚应该连接它上。
连接之后就是如下图所示:
2- 驱动配置使用说明
在前面,我们将DS18B20的DQ引脚连到了IGKBoard开发板扩展接口的 #7 引脚上(GPIO01_IO18),该引脚在系统启动时有可能默认作为GPIO功能使用。如果想作为DS18B20的通信引脚使用的话,我们需要修改开发板上的DTOverlay配置文件,添加该引脚的1-Wire 协议支持。
具体方法为修改 eMMC 启动介质的 boot 分区下的 config.txt文件,在 dtoverlay_extra 选项中添加 w1 支持即可。具体操作如下:
vi /run/media/mmcblk1p1/config.txt
在 dtoverlay_extra 选项中添加 w1 支持即可。没有直接新建,直接复制进去就可。
# Enable extra overlays
dtoverlay_extra=w1
修改完成后重启系统,系统启动时将会自动加载1-wire 协议驱动。待系统启动完成后,我们可以使用 dmesg 命令确认驱动是否成功加载。
root@igkboard:~# dmesg | grep 1-wire
[ 1.819225] Driver for 1-wire Dallas network protocol.
3- 查看 DS18B20 温度传感器的相关文件
重启系统之后,我们应该可以在/sys/bus/w1/devices/路径下,看到 DS18B20 温度传感器的相关文件28-xxxxxx,事实上它是一个链接到其它文件夹的符号连接,我们暂时不关心。如下命令执行的结果所示,其中 0121a3a5758a 为现在所使用的DS18B20芯片的产品序列号。需要注意的是,板子上连接不同的DS18B20芯片这个值就不同。
root@igkboard:~# ls /sys/bus/w1/devices/
28-0121a3a5758a w1_bus_master1
我们使用 ls 命令看看,这个文件夹下有哪些文件。
在这里,最重要的一个文件是 w1_slave 文件。当我们在Linux下开始读该文件时,它将触发Linux内核中的 DS18B20 驱动开始采样。在采样完成之后,Linux内核驱动将会把采样温度值写入到该文件中,这样我们读取该文件里的内容即可。
root@igkboard:/sys/bus/w1/devices/28-0121a3a5758a# ls
alarms driver ext_power hwmon name resolution temperature w1_slave
conv_time eeprom_cmd features id power subsystem uevent
我们可以使用 cat 命令查看 w1_slave 文件里的内容,注意这里只能使用 cat 命令而不能使用 vim 命令查看,因为该文件是只读的。
另外在执行这个命令的时候,我们会发现执行 cat 命令之后,结果需要延时一会才会显示出来。这是因为,Linux内核在触发传感器采样,并等待采样完成、写入采样温度值到该文件中还需要一段时间。
root@igkboard:/sys/bus/w1/devices/28-0121a3a5758a# cat w1_slave
09 01 55 00 7f ff 0c 10 a1 : crc=a1 YES
09 01 55 00 7f ff 0c 10 a1 t=16562
在上面的输出结果中:
- crc=a1:它是DS18B20采样时传输数据的CRC校验和,主要用来校验传感器将采样数据发送给CPU时是否出错;
- t=16562:后面的16562就是采样温度值,其中16是整数部分,562为小数部分,即本次采样温度为16.562℃。
所以我们要获取温度的话,一般都是获取“t=”后面的数值,然后转换为小数显示出来。
4- 编写代码获取温度
(1)ds18b20获取温度代码
在前面我们知道,DS18B20采样后的温度值存放在 /sys/bus/w1/devices/28-0121a3a5758a/w1_slave 文件中,这里的28-0121a3a5758a 是DS18B20的产品序列号,不同的DS18B20芯片其序列号不同。所以在编写代码时,我们不能直接读这个路径的文件来获取温度,因为传感器或设备换了之后,我们的代码就不能工作了。
这样,我们在编程时就应该能够根据不同的芯片来动态获取这个文件路径。因为DS18B20的温度传感器文件总是在 /sys/bus/w1/devices/ 路径下,并且传感器文件夹名是以 “28-” 开头,所以我们可以通过打开文件夹 /sys/bus/w1/devices/ ,并在里面找到以 “28-” 开头的文件。然后再使用相应的字符串处理函数把它们组合成一个完整的路径: /sys/bus/w1/devices/28-xxxxxxxxxxxx/w1_slave 。
接下来,我们编写 DS18B20传感器采样获取温度的代码如下。
/*************************************************************************
> File Name: ds18b20.c
> Author: WangDengtao
> Mail: 1799055460@qq.com
> Created Time: 2023年01月12日 星期一四15时25分51秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
int ds18b20_get_temperature(float *temp);/*获取温度的函数,返回负数失败,否则成功*/
int main(int argc, char *argv[])
{
float temp; /*温度*/
if( ds18b20_get_temperature(&temp) < 0 )
{
printf("ERROR: ds18b20 get temprature failure\n");
return 1;
}
printf("DS18B20 get temperature: %f 'C\n", temp);
return 0;
}
/*获取温度的函数,传入&temp*/
int ds18b20_get_temperature(float *temp)
{
const char *w1_path = "/sys/bus/w1/devices/";
char ds_path[50]; /* DS18B20 采样文件路径 */
char chip[20]; /* DS18B20 芯片序列号文件名 */
char buf[128]; /* read() 读数据存储 buffer */
DIR *dirp; /* opendir()打开的文件夹句柄 */
struct dirent *direntp; /* readdir()读文件夹内容时的目录项*/
int fd =-1; /* open()打开文件的文件描述符 */
char *ptr; /* 一个字符指针,用来字符串处理 */
int found = 0; /* 是否找到DS18B20的标志,默认设置为没找到(0) */
int rv = 0; /* 函数返回值,默认设置为成功返回(0) */
/* 进入函数的第一件事应该进行函数参数的合法性检测,检查参数非法输入。
* 否则调用者"不小心"通过 $temp 传入一个空指针,下面的代码就有可能出现段错误。
*/
if( !temp )
{
return -1;
}
/* 打开 "/sys/bus/w1/devices/" 文件夹,如果打开失败则打印错误信息并退出。 */
if((dirp = opendir(w1_path)) == NULL)
{
printf("opendir error: %s\n", strerror(errno));
return -2;
}
/*
使用while()循环读取/sys/bus/w1/devices/,使用 strstr() 函数判断文件名中是否包含 "28-",如果找到则将完整的文件名通过strcpy()函数保存到 chip 中;并设置 found 标志为1,跳出循环。
*/
while((direntp = readdir(dirp)) != NULL)
{
if(strstr(direntp->d_name,"28-"))
{
strcpy(chip,direntp->d_name);
found = 1;
break;
}
}
/* 文件夹打开用完后,要记得第一时间关闭 */
closedir(dirp);
/*found标记,本身为0,找到了就置为1,否则返回-3没找到*/
if( !found )
{
printf("Can not find ds18b20 in %s\n", w1_path);
return -3;
}
/* 使用snprintf()函数生成完整路径/sys/bus/w1/devices/28-xxxxx/w1_slave
* 并保存到 ds_path 中。
*/
snprintf(ds_path, sizeof(ds_path), "%s/%s/w1_slave", w1_path, chip);
/* 接下来打开 DS18B20 的采样文件,如果失败则返回相应的错误码-4。 */
if( (fd=open(ds_path, O_RDONLY)) < 0 )
{
printf("open %s error: %s\n", ds_path, strerror(errno));
return -4;
}
/* 读取文件中的内容将会触发 DS18B20温度传感器采样,这里读取文件内容保存到buf中 */
if(read(fd, buf, sizeof(buf)) < 0)
{
printf("read %s error: %s\n", ds_path, strerror(errno));
rv = -5;
goto cleanup;
}
/* 采样温度值是在字符串"t="后面,这里我们从buf中找到"t="字符串的位置并保存到ptr指针中 */
ptr = strstr(buf, "t=");
if( !ptr )
{
printf("ERROR: Can not get temperature\n");
rv = -6;
goto cleanup;
}
/* 因为此时ptr是指向 "t="字符串的地址(即't'的地址),那跳过2个字节(t=)后面的就是采样温度值 */
ptr+=2;
/* 接下来我们使用 atof() 函数将采样温度值字符串形式,转化成 float 类型。*/
*temp = atof(ptr)/1000;
cleanup:
close(fd);
return rv;
}
(2)交叉编译运行代码
大家不了解交叉编译的可以查看这篇文章:交叉编译介绍以及测试讲解
我是在自己的虚拟机上编写的,编写好之后交叉编译。
编译好之后并且用file命令检查一下是否是ARM开发板Linux系统上运行的程序。
然后在我们的开发板上使用tftp命令将文件传过来运行:
root@igkboard:~# tftp -gr ds18b20 192.168.1.7
root@igkboard:~# chmod a+x ds18b20
root@igkboard:~# ./ds18b20
DS18B20 get temperature: 17.000000 'C
可以看到获取到了温度。