快捷搜索:  汽车  科技

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)图2 多点测温仿真电路图在我们的例子中,只实现了3点温度。由于我们采用了模块化编程,要扩展到8路只需改动2个地方(猜猜是哪里)。图2给出了仿真电路图。我们在串口仿真电路图上增加了3个DS18B20,分别接P0.0、P0.1和P0.2。一、多点温度测量系统架构多点温度测量系统框图如图1所示。编号为1#~8#的DS18B20连接到8051单片机的P0口,每个DS18B20占P0口的一个I/O,1#对应P0.1,2#对应P0.1, ...... 8#对应P0.7。8051单片机周期读取多点温度,通过串口上报到LabVIEW上位机。图1 多点温度测量系统框图

文章下方附学习资源 请自主领取

再有5天就是“黄金”假期~放七天假,上七天班(学)!不管怎么样,该做什么就做什么。学习便学习,工作便工作,走路便走路,吃饭便吃饭。


今天我们一起完成一个比较完整的作品,基于DS18B20和LabVIEW的多点温度测量系统。我重点介绍实现多点DS18B20温度驱动模块的思路,具体实现大家可以阅读源码。驱动源码参考了不少资料,在此感谢那些乐于分享的程序员。分享,传递,沉淀,这一直都是我们坚持的信念。

关于DS18B20的特性、工作原理、时序等,请参考相关资料:

  • DS18B20官方手册:https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
  • DS18B20的复位(初始化)、读时序、写时序:https://blog.csdn.net/qq_17017545/article/details/82120467
  • DS18B20多点测温方案(多个DS18B20挂在一根总线上):https://blog.csdn.net/redeemer_Qi/article/details/108854687

一、多点温度测量系统架构

多点温度测量系统框图如图1所示。编号为1#~8#的DS18B20连接到8051单片机的P0口,每个DS18B20占P0口的一个I/O,1#对应P0.1,2#对应P0.1, ...... 8#对应P0.7。8051单片机周期读取多点温度,通过串口上报到LabVIEW上位机。

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(1)

图1 多点温度测量系统框图

在我们的例子中,只实现了3点温度。由于我们采用了模块化编程,要扩展到8路只需改动2个地方(猜猜是哪里)。图2给出了仿真电路图。我们在串口仿真电路图上增加了3个DS18B20,分别接P0.0、P0.1和P0.2。

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(2)

图2 多点测温仿真电路图

二、DB18B20多点温度驱动模块设计思路

网上有很多单个DS18B20温度驱动程序源码,可惜的是这些源码无法直接使用,因为源码里DS18B20初始化函数、读温度函数、写DS18B20函数等代码绑定到了固定的I/O引脚(如P1.0),读和写都是基于单个I/O实现。以至于代码无法复用。

https://blog.csdn.net/redeemer_Qi/article/details/108854687提供了多个DS18B20挂在单一总线的多点测温方案,大家可以去研究研究。我们今天使用另外的思路。

思路来源:Arduino里的I/O读写函数( digitalRead,digitalWrite)是通过指定pin序号来实现数字引脚的读写操作的。在分析这两个函数的原型时,发现它们是通过PORT和BIT_MASK来对整个PORT的寄存器操作实现的。举例来说,我们要写1到P0.0,则对P0寄存器进行以下操作:

P0 = P0|0x01; //或写成:P0|=0x01;

某位或1,就能置该位为1。

对P0.0写0,则是:

P0 = P0&(~0x01); //或写为 P0 &= ~0x01;

某位与0,则该位清零。某位与1,该位保持不变。

P0是PORT, 0x01是P0.0在P0寄存器的BIT MASK。表1给出了P0.0~P0.7的位掩码(BIT MASK)。

I/O

位掩码(二进制)

位掩码(十六进制)

P0.0

0000_0001b

0x01

P0.1

0000_0010b

0x02

P0.2

0000_0100b

0x04

P0.3

0000_1000b

0x08

P0.4

0001_0000b

0x10

P0.5

0010_0000b

0x20

P0.6

0100_0000b

0x40

P0.7

1000_0000b

0x80

练习:使用位掩码对P0.5操作,写1和清零。

//你的答案

前面解决了写I/O。哪如何实现读取某个IO的状态呢?使用位掩码~正确。

uchar value = PORT & BIT_MASK; //非零表示输入高电平,全零表示输入低电平。 if(value): //高电平 do something else: //低电平 do something

例如 if(P0 & 0x04)就能读取到P0.2的输入状态。请分析为什么?

我们设计了ds18b20.h,在该头文件里定义了PORT、BIT_MASK和相关的驱动函数(DS18B20初始化、读字节、写字节、读温度)。下面简要概述ds18b20.h。

1、PORT和BIT_MASK

使用宏定义了PORT和BIT_MASK。

//三个DS18B20,分别接到P0.1 P0.2 P0.3 //P0口最多连接8个DS18B20 #define DS18B20_PORT P0 #define ds18b20_1_mask 0x01 //sensor no. 1 #define ds10b20_2_mask 0x02 //sensor no. 2 #define ds10b20_3_mask 0x04 //sensor no. 3 #define ds10b20_4_mask 0x08 //sensor no. 4 #define ds10b20_5_mask 0x10 //sensor no. 5

ds18b20_get_mask( ) 函数实现了传感器编号到BIT_MASK的映射。例如, 1的dq引脚接到Px.0, 掩码为0x01。

// 由序号获得ds18b20的引脚mask // no: 1 2 3 uchar ds18b20_get_mask(uchar no) { uchar pin_mask; switch(no) { case 1: {pin_mask = ds18b20_1_mask; break;} case 2: {pin_mask = ds10b20_2_mask; break;} case 3: {pin_mask = ds10b20_3_mask; break;} default: break; } return pin_mask; }

2、重要驱动函数

(1)DS18B20初始化函数

//初始化ds18b20 uchar ds18b20_init(uchar sensor_no) { uchar pin_mask; uchar ack; pin_mask= ds18b20_get_mask(sensor_no); DS18B20_PORT |= pin_mask; //置1 delay_10xus(1); //延时10us DS18B20_PORT &= ~pin_mask; //清零 delay_10xus(90);//拉低900us DS18B20_PORT |= pin_mask; //置1 delay_10xus(8); //80us后读ds18b20的响应 ack = DS18B20_PORT & pin_mask; //读引脚 delay_10xus(50); return ack; }

初始化函数供读操作、写操作前调用。也可以单独调用来判断DS18B20是否存在。ACK为0表示传感器应答,ACK为1表示传感器未应答(多次未应答可视为传感器不存在或损坏)。

(2)读温度驱动函数

// 读温度函数,返回浮点类型温度 float ds18b20_read_temperature(uchar sensor_no) { uchar low_byte = 0; uchar hight_byte = 0; int temp = 0; float temperature = 0; if(ds18b20_init(sensor_no) == 0) // 温度传感器应答了 { is_ds10b20_exist = 1; ds18b20_start_convert(sensor_no); //开始转换 ds18b20_start_read(sensor_no); //开始读取 low_byte = ds18b20_read_byte(sensor_no); //读温度的低八位 hight_byte = ds18b20_read_byte(sensor_no); //读温度的高八位 temp = (hight_byte<<8)|low_byte; } else { is_ds10b20_exist = 0; } if((temp & 0xF800) == 0xF800) //负温度 { temperature = ((~temp) 1)*0.0625; temperature = -temperature; } else { temperature = temp * 0.0625; //12位的温度分辨率为0.0625℃ } return temperature; }

嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!

无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。

点击这里找小助理0元领取:嵌入式物联网学习资料(头条)

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(3)

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(4)

读温度驱动函数主要完成以下操作:

① 调用ds18b20_init()判断DS18B20是否存在。

② 存在,使用ds18b20_start_convert()函数让DS18B20进行温度转换;等待一定时间后,读温度字节,并把温度字节转换为float数据,再返回。

③ 不存在,置 is_ds10b20_exist为0。

关于读温度函数,有几点要说明:

① 温度字节可以认为是A/D的数字量输出,其量化单位q就是温度分辨率。12位的是0.0625℃。

DS18B20默认是12位分辨率,可软件配置为9、10、11、12位,分辨率分别为0.5、0.25、0.125、0.0625℃。

② 温度MSB字节的高5位是符号位, 11111表示是负的温度,以补码储存。所以先取反 1得到绝对值,再乘以分辨率,最后变成负数。代码如下:

if((temp & 0xF800) == 0xF800) //负温度 { temperature = ((~temp) 1)*0.0625; temperature = -temperature; }

③ LOW_BYTE和HIGH_BYTE对应于图3中的LSb BYTE MSB BYTE。注意,DS18B20先传输LSB字节,且是最低位先传输(LSb First)。

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(5)

图3 温度数据

④ 温度读取函数有瑕疵。温度转换的代码不管传感器存在是否,都会进行。当传感器不存在时,始终返回0,埋了一个大坑~~试一试,改进代码。

(提示:不存在返回250,超量程了就是设备不存在)。在主程序再做处理。

⑤ is_ds10b20_exist 原来是针对单个DS18B20测温设计的。此处实在是鸡肋。你能把它用起来吗?提示:结合第④点。

3、(串口)数据传输协议

我们直接使用C51编程入门(二十三)串口编程入门--串口应用协议(二)里设计的协议。每个传感器上报的数据包括1字节的设备号、4字节的温度(float)。三个传感器的数据一起“打包”上报,如下。

1#设备号(1B)

1#温度(4B)

2#设备号(1B)

2#温度(4B)

3#设备号(1B)

3#温度(4B)

主函数如下。主函数所在的.c源码除了增加#include"ds18b20.h"并另存为新文件名外,其它内容与C51编程入门(二十三)串口编程入门--串口应用协议(二)的一模一样,未作任何修改~(难能可贵..)

void main() { unsigned char ds18b20_no = 1; //设备号 unsigned char ds18b20_N = 3; //ds18b20总数 float temperature; //温度 uart_init(); while(1) { temperature = ds18b20_readTemperature(ds18b20_no); //读温度 sendTemperature(ds18b20_no temperature); //发送温度 ds18b20_no ; if(ds18b20_no > ds18b20_N)//已经读完所有点的温度 { ds18b20_no = 1; } delayMS(1000); //等待1s左右 } }

三、LabVIEW上位机程序改进

1、添加温度保存子VI(saveTemperature.vi),如图4所示。实现将三个温度和当前时间戳存储到一个表格。

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(6)

图4 saveTemperature.vi程序框图

程序说明如下:

① 创建文件路径,使用了应用程序目录,实现将程序存储到程序目录下。目标文件由文件名和当前日期(年月日)组成。这样实现一天一个文件。

.xls扩展名指定文件为表格。

② 打开/创建文件,并设置文件指针到文件末尾,即从文件末尾新增数据。这样,就不会覆盖旧数据。

③ 调用格式化写入文件,巧妙地通过格式化将数据写到表格里。格式化字符为:

%.1f\t%.1f\t%.1f\t%s\n

三个%.1f对应三个温度值,存为1位小数的浮点数据。\t是制表符,移动到下一个表格单元。%s为字符串,这里对应着时间戳字符串。

\n是换行,保证下一次数据存储到表格末尾的新的一行。

2、串口解释单个传感器数据的子VI(getReceiveData.vi)

程序框图如图5所示。说明如下:

① 先读取1个字节数据,并调用强制类型转换函数转换为U8数据。此为设备号,1个字节。

② 再读取4个字节数据,并调用强制类型转换函数转换为SGL数据。此为温度数据,4个字节。注意,不能转换为DBL数据,因为LabVIEW的DBL为64位,8个字节,类型不匹配。

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(7)

图5 getReceiveData.vi程序框图

下图为LabVIEW主程序框图。需要注意的是,初始化串口时,禁用串口的启用停止符选项(F常量连接的选项)。

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(8)

图6 主程序框图

三、运行结果

LabVIEW上位机运行后,立马收到了很多数据(这些都是缓冲在电脑串口缓存里)。如果想要丢弃掉,可以在进入while循环前清空串口缓冲区

使用ds18b20.h时,应注意设置(修改):

1. DS18B20_PORT宏定义,改为实际使用的PORT(P0、P1、P2、P3)

labview温控系统总结心得(C51编程入门基于DS18B20和LabVIEW的多点温度测量系统)(9)

2. 新增BIT_MASK, ds18b20.h只定义了5个,即ds18b20_1_mask到ds18b20_5_mask。

关于BIT_MASK,其实也无需预先定义宏。我们可根据sensor_no算出来,核心代码如下:

bit_mask = 0x01<<(sensor_no-1); //sensor_no = 1~8

3. 注意,DS18B20上电温度转换结果默认为85℃,第一次读到的温度始终是85。因此,我们在正式读取之前,应该调用一次读取温度函数(如在while循环前)。

四、结束语

串口程序编写教程到此告一个段落,希望相关文章对大家有所助益。原本计划继续写串口校验和和AT命令,后面视情况而定吧。如何在有限的时间里,完成更多的事情是一个值得研究和探讨的话题。如果您有感兴趣的主题,可后台发消息给我。

如果你觉得本篇文章有所帮助,请点赞、打赏。 分享,传递,沉淀。

附录:源代码

ds18b20驱动源码(ds18b20.h)

// ds18b20.h #ifndef _DS18B20_H #define _DS18B20_H #include "intrins.h" #include "reg51.h" float temperature = 0; bit is_ds10b20_exist = 0; //1: 存在 0:不存在 #define uchar unsigned char //三个DS18B20,分别接到P0.1 P0.2 P0.3 //P0口最多连接8个DS18B20 #define DS18B20_PORT P0 #define ds18b20_1_mask 0x01 //sensor no. 1 #define ds10b20_2_mask 0x02 //sensor no. 2 #define ds10b20_3_mask 0x04 //sensor no. 3 #define ds10b20_4_mask 0x08 //sensor no. 4 #define ds10b20_5_mask 0x10 //sensor no. 5 // 10us延时函数 void delay_10xus(uchar n) { //每个循环约10us左右 110次循环约1ms while(n--); } // 由序号获得ds18b20的引脚mask // no: 1 2 3 uchar ds18b20_get_mask(uchar no) { uchar pin_mask; switch(no) { case 1: {pin_mask = ds18b20_1_mask; break;} case 2: {pin_mask = ds10b20_2_mask; break;} case 3: {pin_mask = ds10b20_3_mask; break;} default: break; } return pin_mask; } //初始化ds18b20 uchar ds18b20_init(uchar sensor_no) { uchar pin_mask; uchar ack; pin_mask= ds18b20_get_mask(sensor_no); DS18B20_PORT |= pin_mask; //置1 delay_10xus(1); //延时10us DS18B20_PORT &= ~pin_mask; //清零 delay_10xus(90);//拉低900us DS18B20_PORT |= pin_mask; //置1 delay_10xus(8); //80us后读ds18b20的响应 ack = DS18B20_PORT & pin_mask; //读引脚 delay_10xus(50); return ack; } //从ds18b20读一个字节数据 //先接接收低位LSB bit uchar ds18b20_read_byte(uchar sensor_no) { unsigned char i = 0; unsigned char byte_rx = 0; uchar pin_mask = ds18b20_get_mask(sensor_no); DS18B20_PORT |= pin_mask; //置1 _nop_();_nop_(); //延时2us for(i=8; i>0; i--) { DS18B20_PORT &= ~pin_mask; //清零 byte_rx >>= 1; DS18B20_PORT |= pin_mask; //置1 _nop_();_nop_(); if(DS18B20_PORT & pin_mask) //读到1 { byte_rx |=0x80; } delay_10xus(30); DS18B20_PORT |= pin_mask; //置1 } return(byte_rx); } // 写一个字节到DS18B20 void ds18b20_write_byte(uchar c uchar sensor_no) { uchar i; uchar pin_mask = ds18b20_get_mask(sensor_no); for(i=0;i<8;i ) { DS18B20_PORT &= ~pin_mask; //清零、写0 _nop_(); if(c & 0x01) //判断是否是写1 { DS18B20_PORT |= pin_mask; //置1 } delay_10xus(5); //延时50us DS18B20_PORT |= pin_mask; //置1,释放总线 c >>= 1; //取下一位,准备发送 } } // 开始温度采集转换 void ds18b20_start_convert(uchar sensor_no) { ds18b20_init(sensor_no); ds18b20_write_byte(0xcc sensor_no); //SKIP ROM ds18b20_write_byte(0x44 sensor_no); //Convert command } // 开始读取温度 void ds18b20_start_read(uchar sensor_no) { ds18b20_init(sensor_no); ds18b20_write_byte(0xcc sensor_no); //SKIP ROM ds18b20_write_byte(0xbe sensor_no); //READ Command } // 读温度,返回浮点类型温度 float ds18b20_read_temperature(uchar sensor_no) { uchar low_byte = 0; uchar hight_byte = 0; int temp = 0; float temperature = 0; if(ds18b20_init(sensor_no) == 0) // 温度传感器应答了 { is_ds10b20_exist = 1; ds18b20_start_convert(sensor_no); //开始转换 ds18b20_start_read(sensor_no); //开始读取 low_byte = ds18b20_read_byte(sensor_no); //读温度的低八位 hight_byte = ds18b20_read_byte(sensor_no); //读温度的高八位 temp = (hight_byte<<8)|low_byte; } else { is_ds10b20_exist = 0; } if((temp & 0xF800) == 0xF800) //负温度 { temperature = ((~temp) 1)*0.0625; temperature = -temperature; } else { temperature = temp * 0.0625; } return temperature; } #endif

主程序.c源码

//uart_ds18b20_temperatureMonitor.c #include "uart.h" //#include"reg51.h" #include"ds18b20.h" sbit beeper_en = P2^0; sbit key_s1 = P1^0; char msg[] = "Welcome back.\r\n"; unsigned char uart_rx_buffer[2]; unsigned int count = 0; //函数定义 void delayMS(unsigned int nms); void keyScan(); //按键扫描 float ds18b20_readTemperature(unsigned char no); //读取DS18B20温度 void sendTemperature(unsigned char no float temperature); //发送温度函数 void main() { unsigned char ds18b20_no = 1; //设备号 unsigned char ds18b20_N = 3; //ds18b20总数 float temperature; //温度 uart_init(); while(1) { temperature = ds18b20_readTemperature(ds18b20_no); //读温度 sendTemperature(ds18b20_no temperature); //发送温度 ds18b20_no ; if(ds18b20_no > ds18b20_N)//已经读完所有点的温度 { ds18b20_no = 1; } delayMS(1000); //等待1s左右 } } void keyScan() { float temperature; if(key_s1 == 0) { delayMS(10); //消抖 if(key_s1 == 0) //按键按下,读取并上报1#地点的温度 { temperature = ds18b20_readTemperature(1); //读温度 sendTemperature(1 temperature); //发送温度 } } } //延时函数 void delayMS(unsigned int nms) { unsigned int i j; for(i=0;i<nms;i ) for(j=0;j<130;j ); } //读取DS18B20温度(模拟) float ds18b20_readTemperature(uchar senor_no) { float temperature; temperature = ds18b20_read_temperature(senor_no); return temperature; } //发送温度函数 void sendTemperature(unsigned char no float temperature) { uart_sendUchar(no); uart_sendFloat(temperature); } //串口中断函数 void isr_uart() interrupt 4 { static unsigned char rx_byte_count = 0; if(RI) //收到数据 { uart_rx_buffer[rx_byte_count] = SBUF; rx_byte_count ; RI = 0; if(rx_byte_count == 2) //接收到2个字节数据 { rx_byte_count = 0; //下面是命令解析及执行~魔幻的if else if(uart_rx_buffer[0] == 0x01 && uart_rx_buffer[1] == 0x00) { beeper_en = 1; //beeper off printf("执行命令:关闭蜂鸣器!"); } else if (uart_rx_buffer[0] == 0x01 && uart_rx_buffer[1] == 0x01) { beeper_en = 0; //beeper on printf("执行命令:开启蜂鸣器!"); } else if (uart_rx_buffer[0] == 0x02 && uart_rx_buffer[1] == 0x00) { count = 0; printf("执行命令:清零count!"); } else if (uart_rx_buffer[0] == 0xF0 && uart_rx_buffer[1] == 0x0F) { printf("将关闭串口,再见!"); TR1 = 0; } } } }

原文作者:快乐冻鱼

作品来源:轻松学单片机

来源链接:https://mp.weixin.qq.com/s/BYEnCq-0sDVz702K0GtCsg

猜您喜欢: