DS18B20是常用的数字温度传感器,其输出的是数字信号,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。这里不对DS18B20进行详细描述。
传感器的引脚有三个:
引脚 | 功能 |
---|---|
VCC | 提供3.3v电源 |
DQ | 一线通讯协议 |
GND | 地线 |
pi@raspberrypi:~ $ sudo raspi-config
选择5,进入接口选项配置
再使能1-wire协议
pi@raspberrypi:~ $ vim /boot/config.txt
再最后面添加
dtoverlay=w1-gpio-pullup,gpiopin=4
4就是pin7,采用BCM GPIO标准。
pi@raspberrypi:~ $ lsmod | grep w1
w1_therm 16384 0
w1_gpio 16384 0
wire 40960 2 w1_gpio,w1_therm
确定是否系统是否加载一线协议的驱动模块。
DS18B20连接树莓派后,其在linux操作系统中的存在形式为文件。其路径为/sys/bus/w1/devices/。此路径下如果出现28-开头的文件夹,说明DS18B20成功连接上树莓派。28-后的字符串代表该芯片的出厂ID,不同的DS18B20芯片其出厂ID不同。
如果DS18B20成功连接树莓派(即找到对应文件夹),进入目录,有如下:
pi@raspberrypi:/sys/bus/w1/devices/28-041731f7c0ff $ ls
driver hwmon id name power subsystem uevent w1_slave
w1_slave文件中存储了DS18B20采集到的数据。
pi@raspberrypi:/sys/bus/w1/devices/28-041731f7c0ff $ cat w1_slave
d3 01 4b 46 7f ff 0c 10 bf : crc=bf YES
d3 01 4b 46 7f ff 0c 10 bf t=29187
其中循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。这里不做分析(我也不懂)。
t=29187为我们需要的温度数据,此时温度为29.187℃。
代码分析
/**********************************************************************
> File Name: ds18b20.c
> Author: 0nism
> Created Time: Sun 16 Sep 2018 10:07:56 UTC
***********************************************************************/
#include
#include
#include
#include
#include
#include
#include
int ds18b20_get_temperature(float *);
int main(int argc, char *argv[])
{
float temp = 0;
if (ds18b20_get_temperature(&temp) < 0)
{
printf("ERROR: DS18B20 get temperature failure\n");
return 1;
}
printf("DS18B20 get temperature: %f C\n", temp);
return 0;
}
int ds18b20_get_temperature(float * temp)
{
// 1-wire 下设备目录字符串
char w1_path[50] = "/sys/bus/w1/devices/";
// 芯片ID字符串
char chip[20];
char buf[128];
DIR *dirp;
struct dirent *direntp;
int fd = -1;
char *ptr;
float value;
// 传感器存在标志位
int found = 0;
// 如果传入的参数错误
if (!temp)
return -1;
// 如果打开目录失败,返回空指针
// DIR opendir(const char *dirpath)
if ((dirp = opendir(w1_path)) == NULL)
{
// strerror()返回参数errnum描述的出错字符串
// errno 是记录系统的最后一次错误代码
printf("opendir error:%s\n", strerror(errno));
return -2;
}
// 读取DIR中的数据,读完返回NULL
// struct dirent *readdir(DIR *dirp)
// dirent directory entry
while ((direntp = readdir(dirp)) != NULL)
{
// char *strstr(const char *haystack, const char *needle);
// 从needle中寻找目标字符串haystack第一次出现的位置,'\0'不会被比较
if (strstr(direntp->d_name, "28-"))
{
// char *strcpy(char *dest, const char *src);
// 不安全strncpy,'\0'也会被复制
strcpy(chip, direntp->d_name);
// 找到了ds18b20
found = 1;
}
}
closedir(dirp);
if (!found)
{
printf("Error: Can not find ds18b20 in %s\n", w1_path);
return -3;
}
/*
* char *strncat(char *dest, const char *src, size_t n);
* 把src放到dest后,dest的'\0被覆盖',src的'\0被复制'
* n代表最多使用src中的n个字节
*/
// 将芯片名加在w1_path后
strncat(w1_path, chip, sizeof(w1_path));
// 将要读取的文件名放在w1_path下
strncat(w1_path, "/w1_slave", sizeof(w1_path));
// 打开w1_slave
if ((fd = open(w1_path, O_RDONLY)) < 0)
{
printf("open %s error: %s\n", w1_path, strerror(errno));
return -4;
}
// 读取数据
if (read(fd, buf, sizeof(buf)) < 0)
{
printf("read %s error: %s\n", w1_path, strerror(errno));
return -5;
}
// 找到t=,使ptr指向目标数据
ptr = strstr(buf, "t=") + 2;
if (!ptr)
{
printf("ERROR: Can not get temperature\n");
return -6;
}
// double atof(const char *nptr);
*temp = atof(ptr)/1000;
return 0;
}
代码修改
/**********************************************************************
> File Name: my_ds18b20.c
> Author: 0nism
> Created Time: Mon 17 Sep 2018 02:12:48 UTC
***********************************************************************/
#include
#include
#include
#include
#include
#include
#include
int ds18b20_get_temperature(float *);
int main(int argc, char *argv[])
{
float temp = 0;
if ( ds18b20_get_temperature(&temp) < 0)
{
printf("get temperature error.\n");
return 1;
}
printf("Temperature is %f C\n", temp);
return 0;
}
int ds18b20_get_temperature(float * temp)
{
char w1_path[50] = "/sys/bus/w1/devices/";
char buf[128];
int fd = -1;
char ID[20];
DIR *dirp;
struct dirent *direntp;
int found = 0;
char *ptr;
if (temp == NULL)
{
printf("argument error");
return -1;
}
if ( (dirp = opendir(w1_path)) == NULL)
{
printf("open %s failure.\n", w1_path);
return -2;
}
while ( (direntp = readdir(dirp)) != NULL)
{
if (strstr(direntp->d_name, "28-") != NULL)
{
strncpy(ID, direntp->d_name, 20 - strlen(ID));
found = 1;
}
}
closedir(dirp);
if (found != 1)
{
printf("can't find ds18b20.\n");
return -3;
}
strncat(w1_path, ID, sizeof(w1_path)-strlen(w1_path));
strncat(w1_path, "/w1_slave", 50-strlen(w1_path));
if ( (fd = open(w1_path, O_RDONLY)) < 0)
{
printf("open %s failure.\n", w1_path);
return -4;
}
if ( read(fd, buf, sizeof(buf)) < 0)
{
printf("read %s failure.", w1_path);
return -5;
}
close(fd);
ptr = strstr(buf, "t=");
if ( ptr == NULL)
{
printf("get temperature failure.\n");
return -6;
}
*temp = atof(ptr + 2)/1000;
return 0;
}