嵌入式开发学习(STC51-13-温度传感器)

内容

通过DS18B20温度传感器,在数码管显示检测到的温度值;

DS18B20介绍

简介

DS18B20是由DALLAS半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器;

与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器;

特点

DS18B20温度传感器具有如下特点:

  • 适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电;
  • 温范围-55℃~+125℃,在-10~+85℃时精度为±0.5℃;
  • 可编程的分辨率为9~12位,对应的可分辨温度分别为0.5℃、0.25℃、0.125℃和 0.0625℃,可实现高精度测温;
  • 在9位分辨率时最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms内把温度值转换为数字,速度更慢;
  • 测量结果直接输出数字温度信号,以"一根总线"串行传送给CPU,同时可传送CRC校验码,具有极强的抗干扰纠错能力;
  • 负压特性:电源极性接反时,芯片不会因发热而烧毁,但不能正常工作;

结构

DS18B20一共有三个管脚,当我们正对传感器切面(传感器型号字符那一面)时,传感器的管脚顺序是从左到右排列;

管脚1为GND,管脚2为数据DQ,管脚3为VDD;

如果把传感器插反,那么电源将短路,传感器就会发烫,很容易损坏,所以一定要注意传感器方向;

通常在开发板上都会标出传感器的凸起出,所以只需要把传感器凸起的方向对着开发板凸起方向插入即可;

DS18B20温度传感器的内部存储器包括一个高速的暂存器RAM和一个非易失性的可电擦除的EEPROM,后者存放高温度和低温度触发器TH、TL和配置寄存器;

配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下:

TMR1R011111

低五位一直都是"1",TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式,在DS18B20出厂时该位被设置为0,用户不需要去改动;

R1和R0用来设置DS18B20的精度(分辨率),可设置为9,10,11或12位,对应的分辨率温度是0.5℃,0.25℃,0.125℃和0.0625℃;

R0和R1配置如下图:
在这里插入图片描述
在初始状态下默认的精度是12位,即R0=1、R1=1;

高速暂存存储器由9个字节组成,其分配如下:
在这里插入图片描述
当温度转换命令(44H)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节;

存储由两个字节组成,高字节的前5位是符号位S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后,数据格式如下:
在这里插入图片描述
如果测得的温度大于0,这5位为‘0’,只要将测到的数值乘以0.0625(默认精度是12位)即可得到实际温度;
如果温度小于0,这5位为‘1’,测到的数值需要取反加1再乘以0.0625即可得到实际温度;

温度计算

温度与数据对应关系如下:
在这里插入图片描述
比如我们要计算+85度,数据输出十六进制是0X0550,因为高字节的高5位为0,表明检测的温度是正温度,0X0550对应的十进制为1360,将这个值乘以12位精度0.0625,所以可以得到+85度;

DS18B20使用

知道了怎么计算温度,接下来我们就来看看如何读取温度数据;

由于DS18B20是单总线器件,所有的单总线器件都要求采用严格的信号时序,以保证数据的完整性;

DS18B20时序包括如下几种:初始化时序、写(0和1)时序、 读(0和1)时序;

DS18B20发送所有的命令和数据都是字节的低位在前;

这里我们简单介绍这几个信号的时序:

初始化时序

初始化时序图如下:
在这里插入图片描述

单总线上的所有通信都是以初始化序列开始;

主机输出低电平,保持低电平时间至少480us(该时间的时间范围可以从480到960us),以产生复位脉冲;

接着主机释放总线,外部的上拉电阻将单总线拉高,延时15~60us,并进入接收模式;

接着DS18B20拉低总线60~240us,以产生低电平应答脉冲,若为低电平,还要做延时,其延时的时间从外部上拉电阻将单总线拉高算起最少要480us;

写时序

写时序图如下:
在这里插入图片描述
写时序包括写0时序和写1时序;

所有写时序至少需要60us,且在2次独立的写时序之间至少需要1us的恢复时间,两种写时序均起始于主机拉低总线;

写1时序:主机输出低电平,延时2us,然后释放总线,延时60us;

写0时序:主机输出低电平,延时60us,然后释放总线,延时2us;

读时序

读时序图如下:
在这里插入图片描述
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据;

所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间;

每个读时序都由主机发起,至少拉低总线1us;

主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态;

一般的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us;

完整过程

了解了单总线时序之后,我们来看看DS18B20的温度读取过程:
DS18B20的温度读取过程为:复位→发SKIP ROM命令(0XCC)→发开始转换命令(0X44)→延时→复位→发送SKIP ROM命令(0XCC)→发读存储器命令(0XBE)→连续读出两个字节数据(即温度)→结束;

原理图

在这里插入图片描述
由图可知,总线连接p37口,所以我们通过控制该io口的电位变化即可实现初始化以及读写时序;

思路

根据时序图编写初始化、读、写程序;(初始化包括复位和检测DS18B20是否存在)

编写检测DS18B20是否存在的程序(如果信号口一直为低电位,即判定为不存在);

按使用步骤,读取温度值,转换为十进制后,使其在数码管上显示;

编码

User

main.c

/** @Description: 通过DS18B20温度传感器,在数码管显示检测到的温度值*/
#include "public.h"
#include "smg.h"
#include "ds18b20.h"void main()
{u8 i = 0;int temp_value;u8 temp_buf[5];ds18b20_init(); // 初始化DS18B20while (1){i++;if (i % 50 == 0)								 // 间隔一段时间读取温度值,间隔时间要大于温度传感器转换温度时间(12位分辨率时转换时间为750ms)temp_value = ds18b20_read_temperture() * 10; // 保留温度值小数后一位if (temp_value < 0)								 // 负温度{temp_value = -temp_value;temp_buf[0] = 0x40; // 显示负号}elsetemp_buf[0] = 0x00;										  // 不显示temp_buf[1] = gsmg_code[temp_value / 1000];					  // 百位temp_buf[2] = gsmg_code[temp_value % 1000 / 100];			  // 十位temp_buf[3] = gsmg_code[temp_value % 1000 % 100 / 10] | 0x80; // 个位+小数点temp_buf[4] = gsmg_code[temp_value % 1000 % 100 % 10];		  // 小数点后一位smg_display(temp_buf, 4);}
}

Public

public.h

#ifndef _public_H
#define _public_H#include "reg52.h"typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;void delay_10us(u16 ten_us);
void delay_ms(u16 ms);#endif

public.c

#include "public.h"/*** @description: 延时函数,ten_us=1时,大约延时10us* @param {u16} ten_us 延时倍数* @return {*}*/
void delay_10us(u16 ten_us)
{while (ten_us--);
}/**ms延时函数,ms=1时,大约延时1ms**** @param {u16} ms 延时倍数* @return {*}*/
void delay_ms(u16 ms)
{u16 i, j;for (i = ms; i > 0; i--)for (j = 110; j > 0; j--);
}

App/ds18b20

ds18b20.h

#ifndef _ds18b20_H
#define _ds18b20_H#include "public.h"// 管脚定义
sbit DS18B20_PORT = P3 ^ 7; // DS18B20数据口定义// 函数声明
u8 ds18b20_init(void);
float ds18b20_read_temperture(void);#endif

ds18b20.c

#include "ds18b20.h"
#include "intrins.h"/*** @description: 复位DS18B20* @return {*}*/
void ds18b20_reset(void)
{DS18B20_PORT = 0; // 拉低DQdelay_10us(75);	  // 拉低750usDS18B20_PORT = 1; // DQ=1delay_10us(2);	  // 20US
}/*** @description: 检测DS18B20是否存在* @return {u8} 1:未检测到DS18B20的存在,0:存在*/
u8 ds18b20_check(void)
{u8 time_temp = 0;while (DS18B20_PORT && time_temp < 20) // 等待DQ为低电平{time_temp++;delay_10us(1);}if (time_temp >= 20)return 1; // 如果超时则强制返回1elsetime_temp = 0;while ((!DS18B20_PORT) && time_temp < 20) // 等待DQ为高电平{time_temp++;delay_10us(1);}if (time_temp >= 20)return 1; // 如果超时则强制返回1return 0;
}/*** @description: 从DS18B20读取一个位* @return {u8} 1/0*/
u8 ds18b20_read_bit(void)
{u8 dat = 0;DS18B20_PORT = 0;_nop_();_nop_();DS18B20_PORT = 1;_nop_();_nop_(); // 该段时间不能过长,必须在15us内读取数据if (DS18B20_PORT)dat = 1; // 如果总线上为1则数据dat为1,否则为0elsedat = 0;delay_10us(5);return dat;
}/*** @description: 从DS18B20读取一个字节* @return {u8} 一个字节数据*/
u8 ds18b20_read_byte(void)
{u8 i = 0;u8 dat = 0;u8 temp = 0;for (i = 0; i < 8; i++) // 循环8次,每次读取一位,且先读低位再读高位{temp = ds18b20_read_bit();dat = (temp << 7) | (dat >> 1);}return dat;
}/*** @description: 写一个字节到DS18B20* @param {u8} dat 要写入的字节* @return {*}*/
void ds18b20_write_byte(u8 dat)
{u8 i = 0;u8 temp = 0;for (i = 0; i < 8; i++) // 循环8次,每次写一位,且先写低位再写高位{temp = dat & 0x01; // 选择低位准备写入dat >>= 1;		   // 将次高位移到低位if (temp){DS18B20_PORT = 0;_nop_();_nop_();DS18B20_PORT = 1;delay_10us(6);}else{DS18B20_PORT = 0;delay_10us(6);DS18B20_PORT = 1;_nop_();_nop_();}}
}/*** @description: 开始温度转换* @return {*}*/
void ds18b20_start(void)
{ds18b20_reset();		  // 复位ds18b20_check();		  // 检查DS18B20ds18b20_write_byte(0xcc); // SKIP ROMds18b20_write_byte(0x44); // 转换命令
}/*** @description: 初始化DS18B20的IO口DQ,同时检测DS的存在* @return {u8} 1:不存在,0:存在*/
u8 ds18b20_init(void)
{ds18b20_reset();return ds18b20_check();
}/*** @description: 从ds18b20得到温度值* @return {float} 温度数据*/
float ds18b20_read_temperture(void)
{float temp;u8 dath = 0;u8 datl = 0;u16 value = 0;ds18b20_start(); // 开始转换ds18b20_reset(); // 复位ds18b20_check();ds18b20_write_byte(0xcc); // SKIP ROMds18b20_write_byte(0xbe); // 读存储器datl = ds18b20_read_byte(); // 低字节dath = ds18b20_read_byte(); // 高字节value = (dath << 8) + datl; // 合并为16位数据if ((value & 0xf800) == 0xf800) // 判断符号位,负温度{value = (~value) + 1;	  // 数据取反再加1temp = value * (-0.0625); // 乘以精度}else // 正温度{temp = value * 0.0625;}return temp;
}

App/smg

smg.h

#ifndef _smg_H
#define _smg_H#include "public.h"#define SMG_A_DP_PORT P0 // 使用宏定义数码管段码口// 定义数码管位选信号控制脚
sbit LSA = P2 ^ 2;
sbit LSB = P2 ^ 3;
sbit LSC = P2 ^ 4;extern u8 gsmg_code[17]; // 使“共阴极数码管显示0~F的段码数据”这个变量定义为外部可用void smg_display(u8 dat[], u8 pos);#endif

smg.c

#include "smg.h"// 共阴极数码管显示0~F的段码数据
u8 gsmg_code[17] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};/*** @description: 动态数码管显示函数* @param {u8} dat 要显示的数据* @param {u8} pos 从左开始第几个位置开始显示,范围1-8* @return {*}*/
void smg_display(u8 dat[], u8 pos)
{u8 i = 0;u8 pos_temp = pos - 1;for (i = pos_temp; i < 8; i++){switch (i) // 位选{case 0:LSC = 1;LSB = 1;LSA = 1;break;case 1:LSC = 1;LSB = 1;LSA = 0;break;case 2:LSC = 1;LSB = 0;LSA = 1;break;case 3:LSC = 1;LSB = 0;LSA = 0;break;case 4:LSC = 0;LSB = 1;LSA = 1;break;case 5:LSC = 0;LSB = 1;LSA = 0;break;case 6:LSC = 0;LSB = 0;LSA = 1;break;case 7:LSC = 0;LSB = 0;LSA = 0;break;}SMG_A_DP_PORT = dat[i - pos_temp]; // 传送段选数据delay_10us(100);							  // 延时一段时间,等待显示稳定SMG_A_DP_PORT = 0x00;						  // 消影}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:显示检测到的温度
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/24894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚函数表(vtable)

虚函数表&#xff08;通常简称为 vtable&#xff09;是 C 用于实现多态行为的一种机制。当一个类定义了虚函数或者继承了虚函数&#xff0c;编译器会为该类生成一个虚函数表。下面详细介绍虚函数表及其工作原理&#xff1a; 1. 什么是虚函数表&#xff1f; 虚函数表是一个存放…

关于Express 5

目录 1、概述 2、Express 5的变化 2.1 弃用或删除内容的列表&#xff1a; app.param&#xff08;name&#xff0c;fn&#xff09;名称中的前导冒号&#xff08;&#xff1a;&#xff09; app.del() app.param&#xff08;fn&#xff09; 复数方法名 res.json&#xff0…

Codeforces Round 890 (Div. 2) D. More Wrong(交互题 贪心/启发式 补写法)

题目 t(t<100)组样例&#xff0c;长为n(n<2000)的序列 交互题&#xff0c;每次你可以询问一个区间[l,r]的逆序对数&#xff0c;代价是 要在的代价内问出最大元素的位置&#xff0c;输出其位置 思路来源 neal Codeforces Round 890 (Div. 2) supported by Constructo…

springboot工程测试临时数据修改技巧

目录 properties临时属性测试注入 args临时参数测试注入 bean配置类属性注入&#xff08;Import&#xff09; SpringBootTest是一个注解&#xff0c;用于测试Spring Boot应用程序。它可用于指示Spring Boot测试应用程序的启动点&#xff0c;并为测试提供一个可用的Spring应用…

Godot 4 源码分析 - Path2D与PathFollow2D

学习演示项目dodge_the_creeps&#xff0c;发现里面多了一个Path2D与PathFollow2D 研究GDScript代码发现&#xff0c;它主要用于随机生成Mob var mob_spawn_location get_node(^"MobPath/MobSpawnLocation")mob_spawn_location.progress randi()# Set the mobs dir…

【C语言】初阶完结练习题

&#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&#xff1a;C语言初阶 ✨其他专栏&#xff1a;代码小游戏 &#x1f91d;希望作者的文章能对你有所帮助&#xff0c;有不足的地方请在评论…

Misc取证学习

文章目录 Misc取证学习磁盘取证工具veracryto挂载fat文件DiskGenius 磁盘取证例题[RCTF2019]disk 磁盘[](https://ciphersaw.me/ctf-wiki/misc/disk-memory/introduction/#_2)内存取证工具volatility 内存取证例题数字取证赛题0x01.从内存中获取到用户admin的密码并且破解密码 …

ffmpeg视频音频命令

视频音频合并 视频音频合并&#xff0c;以视频时间为主&#xff0c;音频短了循环 方法1&#xff1a;混音&#xff0c;视频权重0&#xff0c;volume调节音量&#xff0c;aloop无限循环&#xff0c;duration:first为第一个素材的长度 ffmpeg -i video.mp4 -i audio.mp3 -filter_…

P3754. [NOIP2002 提高组] 字串变换

本题思路 纯bfs&#xff0c;注意一个字符中有多个相同的可变字符即可。 代码 void solve() {string a,b; cin>>a>>b; // 读入起始串和结束串vector<pair<string,string>> mss; // 可能有一个字符串的多个变换规则&#xff0c;不能用map&#xff0c;…

如何搭建一个成功的家具小程序

家具行业近年来发展迅猛&#xff0c;越来越多的消费者开始选择在小程序商城上购买家具。因此&#xff0c;制作一款家具小程序商城成为了许多家具商家的必然选择。那么&#xff0c;如何制作一款个性化、功能齐全的家具小程序商城呢&#xff1f;下面将为大家介绍一种简单且高效的…

观察者模式(C++)

定义 定义对象间的一种一对多(变化)的依赖关系&#xff0c;以便当一个对象(Subject)的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并自动更新。 ——《设计模式》GoF 使用场景 一个对象&#xff08;目标对象&#xff09;的状态发生改变&#xff0c;所有的依赖对…

首页文章列表分页模板渲染(Go搭建qiucode.cn 之六)

笔者小站:秋码记录 距离上一篇已过去好久了,当然咯,秋码记录 也已换了本来的面貌,其中林林种种,不可道于外人乎!然而之所以会有第六篇,完全得益于那位订阅的粉丝,也是他唤起了我应该将Go搭建qiucode.cn写完,在此我由衷的感谢这位,且作为本专栏首位订阅者。 接上一篇,…

Pytorch Tutorial【Chapter 3. Simple Neural Network】

Pytorch Tutorial【Chapter 3. Simple Neural Network】 文章目录 Pytorch Tutorial【Chapter 3. Simple Neural Network】Chapter 3. Simple Neural Network3.1 Train Neural Network Procedure训练神经网络流程3.2 Build Neural Network Procedure 搭建神经网络3.3 Use Loss …

SpringBoot——使用@Value()注解获取yml字段为null

文章目录 使用Value注解获取yml字段当字段设为static时获取的为null 使用Value注解获取yml字段 在Spring Boot中&#xff0c;可以使用Value注解来读取和赋值YAML配置文件中的值到变量中。 如何读取YAML配置文件中的值并将其赋值给变量 示例代码: import org.springframework.…

【LeetCode】24.两两交换链表中的节点

题目 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xff1a…

python项目各个文件夹的解释

一个大的 Python 项目通常会被组织成多个文件夹&#xff0c;每个文件夹都有特定的功能和用途。 以下是常见的文件夹和它们可能的用途&#xff1a; src 或 source&#xff1a; 这通常是项目的主要源代码目录。您会在这里找到实际执行功能的 Python 脚本或模块。tests 或 testin…

SQL-每日一题【1193. 每月交易 I】

题目 Table: Transactions 编写一个 sql 查询来查找每个月和每个国家/地区的事务数及其总金额、已批准的事务数及其总金额。 以 任意顺序 返回结果表。 查询结果格式如下所示。 示例 1: 解题思路 1.题目要求我们查找每个月和每个国家/地区的事务数及其总金额、已批准的事务数…

HTML <script> 标签

实例 在 HTML 页面中插入一段 JavaScript: <script type="text/javascript"> document.write("Hello World!") </script>(在本页底部可以找到更多实例) 定义和用法 <script> 标签用于定义客户端脚本,比如 JavaScript。 script …

实验笔记之——Windows下的Android环境开发搭建

好久一段时间没有进行Android开发了&#xff0c;最新在用的电脑也没有了Android studio了。为此&#xff0c;本博文记录一下最近重新搭建Android开发的过程。本博文仅为本人学习记录用&#xff08;**别看&#xff09; 目录 安装Android Studio以及JDK JDK Android Studiio …

Java并发系列之七:ConcurrentHashMap

回顾HashMap 既然说到HashMap了&#xff0c;那么我们就先来简单总结一下HashMap的重点。 1.基本结构 HashMap存储的是存在映射关系的键值对&#xff0c;存储在被称为哈希表(数组链表/红黑树)的数据结构中。通过计算key的hashCode值来确定键值对在数组中的位置&#xff0c;假…