写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。
标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。
点击此处进入学习日记的总目录
2023.04.24
一、元件:DS18B20
——数字温度传感器(单总线)
-
DS18B20
介绍
-
引脚及应用电路
-
内部结构框图
-
存储器结构
-
单总线介绍
-
单总线电路规范
-
单总线时序结构
主机先拉低再拉高,从机接着拉低再拉高,代表初始化。
主机先拉低,如果一直拉低,代表发送0;如果立即抬起来,代表发送1
从机将会在主机拉低后30us后读取电平
主机在发送时会先拉低15us,在这15us中从机保持低或拉高,主机在15us末尾读取电平
低位在前 -
DS18B20操作流程
ROM指令(ROM Commands):
- 搜索ROM(Search ROM):该指令用于搜索总线上连接的所有DS18B20设备的唯一ROM代码。通过发送搜索设备指令,可以获取总线上所有DS18B20设备的地址,以便后续与特定设备进行通信。
- 读取ROM(Read ROM):该指令用于从DS18B20传感器中读取设备的唯一ROM代码。通过发送读取ROM指令,可以获取特定DS18B20设备的地址。
- 匹配ROM(Match ROM):使用该指令可以将总线上的通信限定为特定的DS18B20设备。发送匹配ROM指令后,只有匹配的设备会对后续的指令做出响应。
- 跳过ROM(Skip ROM):使用该指令可以忽略总线上所有设备的地址,直接与总线上唯一一个DS18B20设备进行通信。适用于只连接一个DS18B20设备的情况。
- 使能温度报警搜索(Alarm Search):该指令用于在总线上启动温度报警搜索过程。可以通过发送该指令,识别温度超过设定阈值的DS18B20设备。
功能指令(Function Commands):
- 读取温度(Convert T):该指令用于启动温度转换过程。在执行该指令后,DS18B20开始测量温度,并将结果存储在内部寄存器中。
- 写入温度设定值(Write Scratchpad):该指令用于将温度设定值写入DS18B20传感器的内部寄存器。可以使用该指令设置温度报警阈值等参数。
- 读取温度设定值(Read Scratchpad):该指令用于从DS18B20传感器的内部寄存器中读取温度设定值和其他配置参数。
- 复制温度设定值(Copy Scratchpad):该指令用于将写入的温度设定值从内部寄存器复制到DS18B20的EEPROM中。通过执行该指令,可以确保温度设定值在掉电后仍然有效。
- 读取电源供应(Read Power Supply):该指令用于读取DS18B20传感器的电源供应状态,以确定传感器是否正常供电。
- 写入温度报警阈值(Set Alarm Temp):使用该指令可以将温度报警的上限和下限阈值写入DS18B20的报警寄存器,以便在温度超出设定范围时触发报警。
- DS18B20数据帧
- 温度存储格式
二、51:DS18B20温度传感器
1. 单总线函数
- 寻找引脚,很明显引脚是
P37
- 编写单总线初始化函数
/**
* @brief 单总线初始化
* @param 无
* @retval 从机响应位,0为响应,1为未响应
*/
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char AckBit;
OneWire_DQ=1;
OneWire_DQ=0;
i = 247;while (--i); //Delay 500us
OneWire_DQ=1;
i = 32;while (--i); //Delay 70us
AckBit=OneWire_DQ;
i = 247;while (--i); //Delay 500us
return AckBit;
}
- 首先,定义了一个无符号字符型变量 i 和 AckBit。i 用于计数,AckBit 用于存储从机的响应位。
- 设置单总线引脚 OneWire_DQ 为高电平(逻辑1)。
- 将单总线引脚 OneWire_DQ 设置为低电平(逻辑0),这个动作用于发出初始化信号。
- 使用一个循环延时大约 500 微秒,具体延时的时间取决于循环的执行次数。这个延时是为了等待总线上的设备完成初始化过程。
- 将单总线引脚 OneWire_DQ 设置为高电平,释放总线。
- 使用一个循环延时大约 70 微秒,等待从机设备的响应。
- 将从机设备的响应位读取到 AckBit 变量中。
- 再次使用一个循环延时大约 500 微秒,确保总线上的设备完成响应过程。
- 返回 AckBit 变量,表示从机的响应状态。如果 AckBit 的值为 0,则表示从机有响应;如果 AckBit 的值为 1,则表示从机无响应。
这个函数的目的是初始化单总线,并检测从机设备是否有响应。根据返回值可以判断单总线上是否存在有效的从机设备。
这里的延时都是软件生成出来的
由于程序中不需要进入函数,而进入函数需要4us,所以图片中延时54us
- 编写单总线发送一位函数
/**
* @brief 单总线发送一位
* @param Bit 要发送的位
* @retval 无
*/
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
OneWire_DQ=0;
i = 4;while (--i); //Delay 10us
OneWire_DQ=Bit;
i = 24;while (--i); //Delay 50us
OneWire_DQ=1;
}
- 首先,定义了一个无符号字符型变量 i。
- 将单总线引脚 OneWire_DQ 设置为低电平(逻辑0),用于开始发送数据。
- 使用一个循环延时大约 10 微秒,具体延时的时间取决于循环的执行次数。这个延时是为了保持数据在总线上的稳定时间。
- 将要发送的位数据 Bit 设置到单总线引脚 OneWire_DQ 上。
- 使用一个循环延时大约 50 微秒,具体延时的时间取决于循环的执行次数。这个延时是为了保持数据在总线上的稳定时间。
- 将单总线引脚 OneWire_DQ 设置为高电平(逻辑1),表示数据发送完毕。
该函数用于将一位数据发送到单总线上,具体发送的是逻辑0还是逻辑1由参数 Bit 决定。注意,在发送完数据后,需要等待一段时间以确保数据在总线上被接收。
- 编写单总线接收一位函数
/**
* @brief 单总线接收一位
* @param 无
* @retval 读取的位
*/
unsigned char OneWire_ReceiveBit(void)
{
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i = 2;while (--i); //Delay 5us
OneWire_DQ=1;
i = 2;while (--i); //Delay 5us
Bit=OneWire_DQ;
i = 24;while (--i); //Delay 50us
return Bit;
}
- 首先,定义了一个无符号字符型变量 i 和 Bit。i 用于计数,Bit 用于存储接收到的位数据。
- 将单总线引脚 OneWire_DQ 设置为低电平(逻辑0),用于准备接收数据。
- 使用一个循环延时大约 5 微秒,具体延时的时间取决于循环的执行次数。这个延时是为了等待数据的稳定时间。
- 将单总线引脚 OneWire_DQ 设置为高电平(逻辑1),表示准备接收数据。
- 使用一个循环延时大约 5 微秒,具体延时的时间取决于循环的执行次数。这个延时是为了保持数据在总线上的稳定时间。
- 将从单总线引脚 OneWire_DQ 上读取的位数据存储到变量 Bit 中。
- 使用一个循环延时大约 50 微秒,具体延时的时间取决于循环的执行次数。这个延时是为了保持数据在总线上的稳定时间。
- 返回变量 Bit,表示接收到的位数据。
该函数用于从单总线上接收一位数据,它通过读取单总线引脚上的电平状态来获取数据。在接收完数据后,需要等待一段时间以确保数据在总线上稳定并被正确读取。
- 编写单总线发送一个字节
/**
* @brief 单总线发送一个字节
* @param Byte 要发送的字节
* @retval 无
*/
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
这是一个用于在单总线上发送一个字节数据的函数。下面是对该函数的解释:
-
首先,定义了一个无符号字符型变量 i,用于循环计数。
-
使用一个循环,循环次数为 8,表示一个字节有 8 位。
-
在每次循环中,通过位运算和逻辑与操作,从要发送的字节 Byte 中取出当前位(最低位开始),并将其作为参数传递给函数
OneWire_SendBit()
来发送该位数据。 -
循环结束后,表示所有的 8 位数据都已发送完成。
该函数用于将一个字节的数据按位发送到单总线上。它会遍历字节的每一位,从最低位开始发送。在发送每一位数据时,会调用函数 OneWire_SendBit()
来实现单个位的发送。
- 编写单总线接收一个字节
/**
* @brief 单总线接收一个字节
* @param 无
* @retval 接收的一个字节
*/
unsigned char OneWire_ReceiveByte(void)
{
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
}
return Byte;
}
- 首先,定义了一个无符号字符型变量 i 和 Byte。i 用于循环计数,Byte 用于存储接收到的字节数据,初始值为 0x00。
- 使用一个循环,循环次数为 8,表示一个字节有 8 位。
- 在每次循环中,通过调用函数
OneWire_ReceiveBit()
接收单总线上的一位数据。如果返回值为真(非零),表示接收到的位数据为逻辑1,将该位设置到字节变量 Byte 中,使用位运算和逻辑或操作。 - 循环结束后,表示所有的 8 位数据都已接收完成。
- 返回变量 Byte,表示接收到的一个字节数据。
该函数用于从单总线上按位接收一个字节的数据。它会遍历字节的每一位,从最低位开始接收。在接收每一位数据时,会调用函数 OneWire_ReceiveBit()
来实现单个位的接收,并根据接收到的位数据将其设置到字节变量 Byte 中。
2. DS18B20函数
- 首先,找到需要使用的指令,宏定义为较为易懂的单词
//DS18B20指令
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
- 编写开始 DS18B20 温度转换的函数
/**
* @brief DS18B20开始温度变换
* @param 无
* @retval 无
*/
void DS18B20_ConvertT(void)
{
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T);
}
- 首先,调用函数
OneWire_Init()
进行单总线初始化,确保单总线准备就绪。 - 调用函数
OneWire_SendByte()
发送命令字节DS18B20_SKIP_ROM
。这个命令字节用于跳过对 DS18B20 的 ROM 码操作,直接对所有 DS18B20 进行温度转换。 - 调用函数
OneWire_SendByte()
发送命令字节DS18B20_CONVERT_T
。这个命令字节用于启动 DS18B20 的温度转换过程。
该函数的目的是通过单总线与 DS18B20 通信,发送相应的命令字节来开始温度转换过程。在函数执行完毕后,DS18B20 将会开始进行温度转换。
- 编写读取 DS18B20 温度的函数
/**
* @brief DS18B20读取温度(此处用补码判断判断温度正负)
* @param 无
* @retval 温度数值
*/
float DS18B20_ReadT(void)
{
unsigned char TLSB,TMSB;
int Temp;
float T;
OneWire_Init();
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
TLSB=OneWire_ReceiveByte();
TMSB=OneWire_ReceiveByte();
Temp=(TMSB<<8)|TLSB;
T=Temp/16.0;
return T;
}
- 首先,定义了无符号字符型变量 TLSB 和 TMSB 用于存储温度的低位字节和高位字节,定义了整型变量 Temp 用于存储合并后的温度值,定义了浮点型变量 T 用于存储最终的温度数值。
- 调用函数
OneWire_Init()
进行单总线初始化,确保单总线准备就绪。 - 调用函数
OneWire_SendByte()
发送命令字节DS18B20_SKIP_ROM
,用于跳过对 DS18B20 的 ROM 码操作(因为只有一个从机)。 - 调用函数
OneWire_SendByte()
发送命令字节DS18B20_READ_SCRATCHPAD
,用于读取 DS18B20 的温度数据。 - 调用函数
OneWire_ReceiveByte()
两次分别接收温度的低位字节和高位字节,并将其分别存储到变量 TLSB 和 TMSB 中。 - 将高位字节 TMSB 左移 8 位,并与低位字节 TLSB 进行按位或操作,得到合并后的温度值 Temp。
- 将温度值 Temp 除以 16.0(等同于右移四位,因为右四位是小数),得到最终的温度数值 T。
- 返回温度数值 T。
该函数的目的是通过单总线与 DS18B20 通信,发送相应的命令字节来读取 DS18B20 的温度数据,并将其转换为浮点型的温度数值。函数执行过程中,首先进行单总线初始化,然后发送命令字节读取温度数据,并将接收到的字节数据进行合并和转换,最后返回转换后的温度数值。
3. main函数
float T;
void main()
{
DS18B20_ConvertT(); //上电先转换一次温度,防止第一次读数据错误
Delay(1000); //等待转换完成
LCD_Init();
LCD_ShowString(1,1,"Temperature:");
while(1)
{
DS18B20_ConvertT(); //转换温度
T=DS18B20_ReadT(); //读取温度
if(T<0) //如果温度小于0
{
LCD_ShowChar(2,1,'-'); //显示负号
T=-T; //将温度变为正数
}
else //如果温度大于等于0
{
LCD_ShowChar(2,1,'+'); //显示正号
}
LCD_ShowNum(2,2,T,3); //显示温度整数部分
LCD_ShowChar(2,5,'.'); //显示小数点
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4);//显示温度小数部分
}
}
-
首先,定义了一个浮点型变量 T 用于存储温度值。
-
在
main()
函数中,首先调用函数DS18B20_ConvertT()
进行一次温度转换,这是为了防止第一次读取数据时出现错误。 -
然后,通过调用延时函数
Delay()
来等待转换完成,延时时间为 1000 毫秒。 -
初始化 LCD 屏幕,通过调用函数
LCD_Init()
实现。 -
在 LCD 屏幕上显示字符串 “Temperature:”,通过调用函数
LCD_ShowString()
来实现。 -
进入一个无限循环,在循环中进行温度的读取和显示操作。
-
在循环中,首先调用函数
DS18B20_ConvertT()
来进行温度转换。 -
然后,通过调用函数
DS18B20_ReadT()
来读取温度值,并将其存储到变量 T 中。 -
判断温度是否小于 0,如果是,则显示负号,并将温度 T 取负值。
-
如果温度大于等于 0,则显示正号。
-
使用函数
LCD_ShowNum()
将温度的整数部分显示在 LCD 屏幕上。 -
在小数点位置显示小数点符号。
-
使用函数
LCD_ShowNum()
将温度的小数部分显示在 LCD 屏幕上。 -
无限循环会持续进行温度的读取和显示,实现实时的温度监测。
该主程序的功能是通过 DS18B20 温度传感器读取温度数据,并使用 LCD 屏幕进行显示。在程序开始时,先进行一次温度转换,然后进入循环,循环中不断进行温度转换和读取,并将温度值显示在 LCD 屏幕上。同时,根据温度值的正负显示相应的符号。循环会持续执行,实现实时的温度监测和显示。