文章下方附学习资源请自主发放
再有5天就是“黄金”假期~放两天假,上两天班(学)!不管怎样样,该做哪些就做哪些。学习便学习,工作便工作,走路便走路,喝水便喝水。
明天我们一起完成一个比较完整的作品,基于DS1818BB2020和LabVIEW的多点体温检测系统。我重点介绍实现多点DS1818BB2020气温驱动模块的思路,具体实现你们可以阅读源码。驱动源码参考了不少资料,在此谢谢这些乐于分享的程序员。分享,传递,沉淀,这仍然都是我们坚持的信念。
关于DS1818BB2020的特点、工作原理、时序等,请参考相关资料:
一、多点气温检测系统构架
多点气温检测系统框图如图1所示。编号为1#~8#的DS1818BB2020联接到8051单片机的P0口,每位DS1818BB2020占P0口的一个I/O,1#对应P0.1,2#对应P0.1,......,8#对应P0.7。8051单片机周期读取多点气温,通过并口上报到LabVIEW上位机。
图1多点气温检测系统框图
在我们的事例中,只实现了3点气温。因为我们采用了模块化编程,要扩充到8路只需改动2个地方(猜猜是那里)。图2给出了仿真电路图。我们在并口仿真电路图上降低了3个DS1818BB2020,分别接P0.0、P0.1和P0.2。
图2多点测温仿真电路图
二、DB1818BB2020多点气温驱动模块设计思路
网上有好多单个DS1818BB2020气温驱动程序源码,可惜的是那些源码难以直接使用,由于源码里DS1818BB2020初始化函数、读气温函数、写DS1818BB2020函数等代码绑定到了固定的I/O引脚(如P1.0),读和写都是基于单个I/O实现。以至于代码难以复用。
提供了多个DS1818BB2020挂在单一总线的多点测温方案linux uart驱动编写,你们可以去研究研究。我们明天使用另外的思路。
思路来源: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寄存器的BITMASK。表1给出了P0.0~P0.7的位网段(BITMASK)。
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和相关的驱动函数(DS1818BB2020初始化、读字节、写字节、读体温)。下边简略概述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)DS1818BB2020初始化函数
//初始化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;
}
初始化函数供读操作、写操作前调用。也可以单独调用来判定DS1818BB2020是否存在。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元发放:嵌入式物联网学习资料(头条)
读气温驱动函数主要完成以下操作:
①调用ds18b20_init()判定DS1818BB2020是否存在。
②存在,使用ds18b20_start_convert()函数让DS1818BB2020进行体温转换;等待一定时间后,读气温字节,并把气温字节转换为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位是符号位学linux有前途吗,11111表示是负的体温,以进制存储。所以先取反+1得到绝对值,再减去帧率,最后弄成正数。代码如下:
if((temp & 0xF800) == 0xF800) //负温度
{
temperature = ((~temp)+1)*0.0625;
temperature = -temperature;
}
③LOW_BYTE和HIGH_BYTE对应于图3中的LSBBYTE,MSBBYTE。注意,DS1818BB2020先传输LSB字节,且是最高位先传输(LSbFirst)。
图3气温数据
④温度读取函数有瑕疵。气温转换的代码不管传感存在是否,就会进行。当传感不存在时,仍然返回0,埋了一个深坑~~试一试,改进代码。
(提示:不存在返回250,超量程了就是设备不存在)。在主程序再做处理。
⑤is_ds10b20_exist原先是针对单个DS1818BB2020测温设计的。此处实在是鸡肋。你能把它用上去吗?提示:结合第④点。
3、(并口)数据传输合同
我们直接使用C51编程入门(二十三)并口编程入门--并口应用合同(二)里设计的合同。每位传感上报的数据包括1字节的设备号、4字节的体温(float)。三个传感的数据一起“打包”上报,如下。
1#设备号(1B)
1#气温(4B)
2#设备号(1B)
2#气温(4B)
3#设备号(1B)
3#气温(4B)
主函数如下。主函数所在的.c源码不仅降低#include"ds18b20.h"并另存为新文件名外,其它内容与C51编程入门(二十三)并口编程入门--并口应用合同(二)的一模一样linux uart驱动编写,未作任何更改~(难能可贵..)
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所示。实现将三个气温和当前时间戳储存到一个表格。
图4saveTemperature.vi程序框图
程序说明如下:
①创建文件路径,使用了应用程序目录,实现将程序储存到程序目录下。目标文件由文件名和当前日期(年月日)组成。这样实现三天一个文件。
.xls扩充名指定文件为表格。
②打开/创建文件,并设置文件表针到文件末尾,即从文件末尾新增数据。这样,就不会覆盖旧数据。
③调用低格写入文件,巧妙地通过低格将数据讲到表格里。低格字符为:
%.1ft%.1ft%.1ft%sn
三个%.1f对应三个气温值,存为1位小数的浮点数据。t是制表符,联通到下一个表格单元。%s为字符串,这儿对应着时间戳字符串。
n是换行,保证下一次数据储存到表格末尾的新的一行。
2、串口解释单个传感数据的子VI(getReceiveData.vi)
程序框图如图5所示。说明如下:
①先读取1个字节数据,并调用强制类型转换函数转换为U8数据。此为设备号redhat linux 下载,1个字节。
②再读取4个字节数据,并调用强制类型转换函数转换为SGL数据。此为气温数据,4个字节。注意,不能转换为DBL数据,由于LabVIEW的DBL为64位,8个字节,类型不匹配。
图5getReceiveData.vi程序框图
右图为LabVIEW主程序框图。须要注意的是,初始化并口时,禁用并口的启用停止符选项(F常量联接的选项)。
图6主程序框图
三、运行结果
LabVIEW上位机运行后,立刻收到了好多数据(这种都是缓冲在笔记本并口缓存里)。假如想要遗弃掉,可以在步入while循环前清空串口缓冲区。
使用ds18b20.h时,应注意设置(更改):
1.DS18B20_PORT宏定义,改为实际使用的PORT(P0、P1、P2、P3)
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.注意,DS1818BB2020上电气温转换结果默认为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>= 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.rn";
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;
}
}
}
}