1. DS18B20温度传感器的介绍
DS18B20是美信公司的一款温度传感器,树莓派可以通过1-Wire和DS18B20进行通信,最终将温度读出。1-Wire总线的硬件接口很简单,只需要把18B20的数据引脚和树莓派的一个IO口接上就可以通信。硬件的简单,随之而来的,就是软件时序的复杂。1-Wire总线的时序比较复杂(这里就不讲了),我们来看一下DS18B20的硬件原理图,如图下图所示。
2.代码流程分析
2.1获取实时温度值
先来看一张图:
我们要在/sys/bus/w1/devices/28-041731f7c0ff/w1_slave路径下获取实时温度,即t=12062。首先打开文件(用到open函数),然后读文件(用到read函数),在读到的数据中找出"t=12062"这个字符串(用到strstr函数),此时用指针保存该字符串的首地址,但"t="并不是我们需要的数据,所以此时我们应该将指针+2,直接指向实时温度数据。还应注意的是此时我们获得的数据是一个字符串,所以要用到atof函数(将字符串转成float类型),当然温度值肯定是两位数,所以还得除以1000。最终获得实时温度值。
这部分代码如下:
60 if((fd=open(path1,O_RDONLY))<0) //根据目标路径以只读形式打开文件,并加上错误判断
61 {
62 printf("open file %s failure: %s\n",path1,strerror(errno));//打开文件失败
63 return -3;
64 }
65
66 if(read(fd,buff,sizeof(buff))<0) //读取文件内容,并保存在buff中 同是加上错误判断
67 {
68 printf("read %s error :%s\n",path1,strerror(errno));//读文件失败
69 return -4;
70 }
71
72 ptr=strstr(buff,"t=")+2; // 跳过"t="字符串 所以加2 直接指向温度数据
73
74 if(!ptr)
75 {
76 printf("error:can not get temperature\n");// buff为空 获取温度失败
77 return -5;
78 }
79
80 temperature=atof(ptr)/1000;//atof()函数将字符串转成float型
81 printf("The sampling temperature of DS18B20 is %f \n",temperature);
2.2获取温度值存放路径
但是此时还是一个bug,因为每个DS18B20的产品序列号不一样,那么温度保存文件的路径也就会不一样,所以我们编写程序的时候,为了得到更广泛的适用性并不能直接用read()去读取相应路径下的文件。(这是一个工程师应该考虑的)
/sys/bus/w1/devices/这一部分路径都是固定的,定义一个数组存放该路径,打开该路径下的文件列表(opendir函数),然后while循环访问该文件列表,找到以"28-“开头的序列号(strstr函数),再将该序列号存入缓冲区(strcpy函数),然后将缓冲区中的数据连接在固定路径后(strncat函数),最后再次更新路径**添加上路径"w1_slave”😗*单总线从动设备(strncat函数)。
这部分代码如下:
29 if((dirp=opendir(path1))==NULL)
30 {
31 printf("opendir %s error :%s\n",path1,strerror(errno));//打开PATH1失败
32 return -1;
33 }
34 while((direntp=readdir(dirp))!=NULL)//循环访问文件列表
35 {
36 if(strstr(direntp->d_name,"28-"))//找到以"28-"开头的温度传感器序列号
37 {
38 strcpy(temp,direntp->d_name);//存入缓冲区
39 found=1;//返回找到标志符
40 }
41 }
42
43 closedir(dirp);//及时关闭文件夹
44
45 if(!found)//未找到ds18b20
46 {
47 printf("can not find ds18b20 in %s\n",path1);
48 return -2;
49 }
50
51 strncat (path1,temp,sizeof(temp)); //将序列号作为路径的一部分
52 strncat (path1,"/w1_slave",9); //再次更新路径(添加w1_slave):单总线从动设备
2.3用到的库函数
1、strstr()函数:
原型: extern char *strstr(char *str1, const char *str2);
作用:用于判断字符串str2是否是str1的子串。如果是,则该函数返回 str1字符串从 str2第一次出现的位置开始到 str1结尾的字符串;否则,返回NULL。
2、atof()函数:
原型: double atof(const char *nptr);
作用: 把字符串转换成浮点数
3、strcpy()函数:
原型:char *strcpy(char *dest, const char *src);
作用:把 src 所指向的字符串复制到 dest,该函数返回一个指向最终的目标字符串 dest 的指针。
4、strncat()函数:
因为strcat()函数无法检测第一个数组是否能够容纳第二个字符串,这时就要用到strncat()函数。
原型:char *strncat(char *dest,const char *src,int n);
作用:把src所指字符串的前n个字符添加到dest结尾处(覆盖dest结尾处‘\0’),并添加‘\0’
3.完整代码
1 #include <stdio.h> //头文件全部可以通过man+函数名找到
2 #include <string.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <dirent.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <stdlib.h>
10
11 #define PATH "/sys/bus/w1/devices/" //宏定义温度传感器的存放路径
12 #define PATH1_SIZE 100 //宏定义路径大小
13 #define BUFF_SIZE 100 //宏定义缓冲区大小
14
15
16 int main(int argc,char **argv)
17 {
18 char path1[PATH1_SIZE]=PATH; //局部变量保存温度传感器的路径
19 int fd=-1; //初始化文件描述符
20 DIR *dirp; //文件列表指针,opendir函数的返回类型,用来指定打开的文件夹
21 struct dirent *direntp; //系统调用的struct dirent的指针变量 readdir返回的结构体指针,用来指向打开文件夹的内容
22 char temp[15]; //缓冲区用来存放温度传感器的序列号
23 int found=0; //找到序列号的标志,=0 未找到 ,=1 找到
24 char buff[BUFF_SIZE]; //存放温度的缓冲区
25 char *ptr=NULL; //指向实时温度缓冲区的指针
26 float temperature=0.00; //实时温度数据
27
28
29 if((dirp=opendir(path1))==NULL)
30 {
31 printf("opendir %s error :%s\n",path1,strerror(errno));//打开PATH1失败
32 return -1;
33 }
34 while((direntp=readdir(dirp))!=NULL)//循环访问文件列表
35 {
36 if(strstr(direntp->d_name,"28-"))//找到以"28-"开头的温度传感器序列号
37 {
38 strcpy(temp,direntp->d_name);//存入缓冲区
39 found=1;//返回找到标志符
40 }
41 }
42
43 closedir(dirp);//及时关闭文件夹
44
45 if(!found)//未找到ds18b20
46 {
47 printf("can not find ds18b20 in %s\n",path1);
48 return -2;
49 }
50
51 strncat (path1,temp,sizeof(temp)); //将序列号作为路径的一部分
52 strncat (path1,"/w1_slave",9); //再次更新路径(添加w1_slave):单总线从动设备
53
54
55
56 /****************前面一部分为找到目标路径,后一部分是在目标路径下打开文件并读取部分文件内容***************************/
57
58
59
60 if((fd=open(path1,O_RDONLY))<0) //根据目标路径以只读形式打开文件,并加上错误判断
61 {
62 printf("open file %s failure: %s\n",path1,strerror(errno));//打开文件失败
63 return -3;
64 }
65
66 if(read(fd,buff,sizeof(buff))<0) //读取文件内容,并保存在buff中 同是加上错误判断
67 {
68 printf("read %s error :%s\n",path1,strerror(errno));//读文件失败
69 return -4;
70 }
71
72 ptr=strstr(buff,"t=")+2; // 跳过"t="字符串 所以加2 直接指向温度数据
73
74 if(!ptr)
75 {
76 printf("error:can not get temperature\n");// buff为空 获取温度失败
77 return -5;
78 }
79
80 temperature=atof(ptr)/1000;//atof()函数将字符串转成float型
81 printf("The sampling temperature of DS18B20 is %f \n",temperature);
82
83 return 0;//成功返回
84 }