蓝桥杯_DS18B20温度传感器---新手入门级别超级详细解析

目录

一、引言

DS18B20的原理图

单总线简介:

​编辑暂存器简介:

DS18B20的温度转换与读取流程

二、代码配置

maic文件

疑问

关于不同格式化输出符号的使用

为什么要rd_temperature()/16.0?

onewire.h文件

这个配置为什么要先读low,如果反过来读会怎么样?


一、引言

DS18B20是单线接口数字温度传感器,测量范围是-55°C~+125°C,-10°C~+85°C的范围精度是±0.5°C,还是精度很高的呢。

DS18B20的原理图

外部结构长这样

|-----|------------------------------|
| 符号 | 说明 |
| GND | 接地 |
| DQ | 数据输入/输出引脚。当工作在寄生电源模式时用来提供电源。 |
| VDD | 可选的VDD引脚。工作与寄生电源模式是VDD必须接地。 |

这是内部结构

DS18B20包括很多东西,有寄生电源电路,64位ROM和单线接口电路、暂存器、EEPROM、8位CRC生成器和温度传感器。寄生电源电路可以实现外部电源供电和单线寄生供电,64位ROM中存放的48位序列号用于识别同一单线上连接的多个DS18B20,以实现多点测温。

单总线简介:

单总线是一种通用数据总线他只有一根通信线:DQ,单总线只需要一根通信线即可实现数据的双向传输。

单总线的具体时序:

初始化:

主机将总线拉低至少480us,然后释放总线,当DS18B20探测到I/O引脚上的上升沿侯,等待15~60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线

发送一位数据:

主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60u

接收一位:

主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us

具体的单总线完整的操作时序如下:

温度变换:初始化→跳过ROM →开始温度变换:

温度读取:初始化→跳过ROM →读暂存器→连续的读操作

其中ROM命令如下:

暂存器简介:

其中的暂存器很重要,有九个字节,最上面的两个字节是温度低位和高位。如下图

在LSB,MSB中BIT15~BIT11是符号位,控制符号;BIT10~BIT4是整数位;BIT3~BIT0是小数位。

这是一些例子说明,前五位是符号位,5个都为0是正数,5个都为1是负数,其他的就按二进制,十六进制常规操作算。

DS18B20的温度转换与读取流程

1 初始化总线

2 写入字节0xcc,跳过rom。

3 写入字节0x44,进行温度转换。

4 初始化总线

5 写入字节0xcc,跳过rom。

6 写入字节0xbe,读取高速暂存器。(将后面的high和low的值存放到这里面)

7 读取暂存器的第0字节,即温度数据的LSB(low)。

8 读取暂存器的第1字节,即温度数据的MSB(high)。

9 返回high+low的值


二、代码配置

maic文件

cs 复制代码
#include "bsp_seg.h"
#include "Timer0.h"
#include "bsp_key.h"
#include "STDIO.H"
#include <STC15F2K60S2.H>
#include "bsp_init.h"
#include "bsp_led.h"
#include "bsp_1302.h"
#include "bsp_onewire.h"


/* 函数声明 */
//三个主体循环,基本上不变
void	Key_Proc(void);//按键处理
void	Seg_Proc(void);//显示处理
void	Led_Proc(void);//LED处理

/* 全局变量声明 */
//显示专用,基本上永远不变
unsigned char seg_buf[8];//放置字符串转换后的段码到数组
unsigned char seg_string[10];//放置字符串
unsigned char pos = 0;//中断显示专用
//LED显示专用,基本上永远不变
unsigned char ucLed;
//按键专用,基本上永远不变
unsigned char Key_Value;//读取按键的数值存储变量
unsigned char Key_Down,Key_Old;

//按键和显示函数减速专用,基本上永远不变
unsigned int Key_Slow_Down;//按键减速
unsigned int Seg_Slow_Down;	

//DS1302专用,当使用DS1302时,基本不变
unsigned char ucRtc[3] = {23,59,55};//设置RTC时间


unsigned int ms_count;
unsigned char s_count;

unsigned char Running_State;//记录运行状态

void main()
{
	 Cls_Peripheral();
	 Timer0Init();		//1毫秒@12.000MHz
	 EA = 1;
		
	Set_Rtc(ucRtc);//设置RTC时间,23-59-55
	
	
	 while(1)
	 {
		Key_Proc();//按键处理
		Seg_Proc();//显示处理
		Led_Proc();
	 }


}



/* Timer0 interrupt routine */
void tm0_isr() interrupt 1 
{
  if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
  if(++Seg_Slow_Down == 10) Seg_Slow_Down = 0;	
	
	if(++ms_count == 1000) //记录·运行时间 
	{
		s_count++;
		ms_count = 0;
	}
	
		Seg_Disp(seg_buf,  pos);
   if(++pos ==8) pos = 0;
		Led_Disp(ucLed);//LED显示
}
/* Key_Proc */
void	Key_Proc(void)//按键处理,底层数据变更
{
	if(Key_Slow_Down) return;
	Key_Slow_Down = 1;
	
	Key_Value = Key_Read();//读取按键按下的编号
	
	Key_Down = Key_Value & (Key_Old ^ Key_Value);//^异或(0000^0101)= 0101   0101 & 0101 = 0101//如果按键发生了下降沿的变化,输出结果和本次按键数值相同
																					//^异或(0101^0101)= 0000   0101 & 0000 = 0000//如果按键发生了下降沿的变化,输出结果和本次按键数值相同
	Key_Old = Key_Value;

	if(Key_Down)//如果捕捉到下降沿跳变
	{
			if(++Running_State == 3) 
		Running_State = 0;//保证Running_State在0-2之间翻滚

	}
	
}

/* Seg_Proc */
//Seg_Proc,准备数码管要显示的内容,Seg_Tran把字符串转成数码管段码,存进seg_buf[]
void	Seg_Proc(void)//显示处理,显示信息生成
{
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;
	
	switch(Running_State)
	{
		
		case 0:
		//读取18B20的数值	
		//seg_string是一个字符数组,理解为"数码管要显示的文字内容"
		
		sprintf(seg_string, "----%04.2f",rd_temperature()/16.0);//这个代码的效果是当ucRtc是[23,59,55],seg_string ="23-59-55"
		break;
		%d
		%2d
		%02d
		%2.2f
		%02.2f
		
		
		
		case 1:
		Read_RTC(ucRtc);//读取1302内部	
		//seg_string是一个字符数组,理解为"数码管要显示的文字内容"
		
		sprintf(seg_string, "%02d-%02d-%02d",(unsigned int)ucRtc[0],(unsigned int)ucRtc[1],(unsigned int)ucRtc[2]);//这个代码的效果是当ucRtc是[23,59,55],seg_string ="23-59-55"
		break;
		case 2:
	  sprintf(seg_string, "-----%03d",(unsigned int)s_count);
		break;
	}
	
		//seg_buf是一个存储段码的数组。因为数码管不能直接显示"字符",它要的是"段码"-告诉它点亮哪几段
	Seg_Tran(seg_string,  seg_buf);//Seg_Tran作用------把字符串seg_string转换成段码,放入数组seg_buf中

}

void	Led_Proc(void)
{
	switch(Running_State)
	{
		case 0:
			ucLed = 0x03;//让L1,L2两个亮		0000 0011
			break;
		case 1:
			ucLed =0x0c;//让L3,L4两个亮		0000 1100
			break;
		case 2:
			ucLed =0x30;//让L5,L6两个亮		0011 0000
			break;
	}
	
}
//温度,时钟,系统运行时

疑问

sprintf(seg_string, "----%04.2f",rd_temperature()/16.0);

break; 在这个代码里有"----%04.2f",为什么这里要这样写呢,引起我的思考

关于不同格式化输出符号的使用

%d:输出一个十进制整数,无特别格式限制。

%2d:输出一个整数,占用至少2个字符宽度,右对齐。若数字不足2位,用空格补在左侧

%02d:输出一个整数,占用2 位宽度,不足的用 0 补左边。

%2.2f:整体宽度至少 2 位。小数点后保留2 位小数。整数部分和小数点也算在宽度里,但如果不够宽度会自动扩展。示例:printf("%2.2f", 3.1); → 输出:3.10。实际宽度超过 2 位(共 4 位),所以宽度不限制实际输出。

%02.2f::整体至少 2 位宽(但不包含小数位数限制时会自动扩展)。小数点后保留 2 位。前面不足时补 0(但一般无效) 。

示例:printf("%02.2f", 3.1); → 输出:3.10

实际上宽度会扩展以容纳整个数字,0补位不会生效,因为 3.10 就已经超出了 2 的宽度

为什么要rd_temperature()/16.0?

假设温度传感器(比如DS18B20)的测量范围是 -55°C 到 +125°C ,但它内部存储温度数据时,把温度值放大了16倍,相当于把温度的小数部分用整数来记录。

onewire.h文件

cs 复制代码
#include "bsp_onewire.h"
#include <STC15F2K60S2.H>




void Delay_OneWire(unsigned int t)  //STC89C52RC
{
	t *=12;
	while(t--);
}


void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(5);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(5);
}


unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat >>= 1;
		DQ = 1;
		if(DQ)
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(5);
	}
	return dat;
}


bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;
  	Delay_OneWire(12);
  	DQ = 0;
  	Delay_OneWire(80);
  	DQ = 1;
  	Delay_OneWire(10); 
    initflag = DQ;     
  	Delay_OneWire(5);
  
  	return initflag;
}

//unsigned char check_1[7] = {0};
unsigned int rd_temperature(void)
{
	unsigned char low,high;
	
	init_ds18b20();
	Write_DS18B20(0xcc);//Ìø¹ýROM
	Write_DS18B20(0x44);//ת>>>>ζÈ

	init_ds18b20();
	Write_DS18B20(0xcc);//Ìø¹ýROM
	Write_DS18B20(0xbe);//¶ÁȡζÈ

	low = Read_DS18B20();
	high = Read_DS18B20();//¶ÁÈ¡¸ßÎ>>
	
	return (high<<8)|low;
}

unsigned int rd_temperature(void)

{

unsigned char low,high;

init_ds18b20();

Write_DS18B20(0xcc);//跳过ROM

Write_DS18B20(0x44);//转换温度

init_ds18b20();

Write_DS18B20(0xcc);//跳过ROM

Write_DS18B20(0xbe);//读取温度

low = Read_DS18B20();

high = Read_DS18B20();//读取高位

return (high<<8)|low;

}

low = Read_DS18B20();

high = Read_DS18B20();//读取高位

这个配置为什么要先读low,如果反过来读会怎么样?

因为ds18b20中,是先读低位再读高位,并且这里将high左移8位是为了正确对齐两个字节的二进制位,确保高字节占据16位整数的高8位,低字节占据低8位。这都是DS18B20数据格式的强制要求。如果反过来读,那得到的数据就是错误的。

这是我配置的效果,显示了环境的温度,如果想让温度变高,可以拿手指捏住温度传感器黑色帽子一样的,在右上角大家可以试试。

相关推荐
蓝斯4971 小时前
从.到实现一个Windows上的虚拟hid键盘设备
windows·单片机·计算机外设
熙芯XiChip2 小时前
硬实时与软实时RTOS核心区别
嵌入式硬件
伟大的前程2 小时前
unsigned int 定义的变量 在 电脑的 64 位系统中 和 32 位 cortex-m0 和 8 位的 51 单片机
单片机·嵌入式硬件
小渔村的拉线工2 小时前
1.HPM6E80 解析芯片整体工作原理和存储架构
单片机·嵌入式硬件·mcu·架构·hpm6e80
熙芯XiChip3 小时前
CAN与CANopen的本质区别
单片机
lilihewo3 小时前
Altium Designer 封装库的创建(分享pcb 3D封装库)
嵌入式硬件·51单片机
神罚天下ET4 小时前
典型arm位单片机启动流程(从上电到main.c)
c语言·arm开发·单片机
碧海银沙音频科技研究院5 小时前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
Zevalin爱灰灰6 小时前
模拟信号链电路——基于TL431的电压基准电路
单片机·嵌入式硬件
csdn杰哥6 小时前
瑞萨单片机开发教程[二十七] DTC 外部中断触发传输实验
单片机·嵌入式硬件·mongodb·瑞萨·ra6m5