使用HAL库驱动DS18B20温度传感器在STM32单总线通信
代码:
!!!只要代码的就复制走,需要搞懂原理的看代码后面的
DS18B20.h
#ifndef __DS18B20_H
#define __DS18B20_H
//修改引脚在这
#define DS18B20_PIN GPIO_PIN_1 //DS18B20引脚
#define DS18B20_PORT GPIOA //DS18B20端口
#define DS18B20_LOW HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET) //引脚输出低电平
#define DS18B20_HIGH HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET) //引脚输出高电平
#define DS18B20_IN HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN) //读取引脚电平
void DS18B20_Tem_Convert(void);
void DS18B20_Read_Tem(float* tem);
void DS18B20_MspInit(void);
#endif
DS18B20.c
#include "stm32f1xx_hal.h"
#include "DS18B20.h"
#include "Delay.h"
#include "UART.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
void DS18B20_MspInit(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = DS18B20_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL; // 添加这行以确保没有上拉或下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
}
uint8_t DS18B20_Init(void)
{
uint8_t AckBit;
DS18B20_HIGH;
DS18B20_LOW;
Delay_us(480);
DS18B20_HIGH;
Delay_us(60);
AckBit = DS18B20_IN;
Delay_us(420);
if(AckBit)
{
/*打印错误信息*/
return 1;
}
return 0;
}
void DS18B20_WriteBit(uint8_t bit)
{
DS18B20_LOW;
Delay_us(15);
if(bit)
{
DS18B20_HIGH;
}
else
{
DS18B20_LOW; /*可省略*/
}
Delay_us(45);
DS18B20_HIGH;
}
void DS18B20_WriteByte(uint8_t byte)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
DS18B20_WriteBit(byte & (0x01 << i));
}
}
uint8_t DS18B20_ReadBit(void)
{
uint8_t bit;
DS18B20_LOW;
Delay_us(5);
DS18B20_HIGH;
Delay_us(10);
if(DS18B20_IN)
{
bit = 1;
}
else
{
bit = 0;
}
Delay_us(45);
return bit;
}
void DS18B20_ReadByte(uint8_t *byte)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
*byte >>= 1; // 右移一位
if(DS18B20_ReadBit())
{
*byte |= 0x80; // 设置最高位
}
}
}
void DS18B20_Tem_Convert(void)
{
if(DS18B20_Init() == 0)
{
DS18B20_WriteByte(DS18B20_SKIP_ROM);
DS18B20_WriteByte(DS18B20_CONVERT_T);
}
else
{
/*打印错误信息*/
Printf("DS18B20_Init ERROR");
}
}
void DS18B20_Read_Tem(float* tem)
{
uint16_t temValue;
uint8_t tem_LSB, tem_MSB;
if(DS18B20_Init() == 0)
{
DS18B20_WriteByte(DS18B20_SKIP_ROM);
DS18B20_WriteByte(DS18B20_READ_SCRATCHPAD);
DS18B20_ReadByte(&tem_LSB);
DS18B20_ReadByte(&tem_MSB);
temValue = (tem_MSB << 8) | tem_LSB;
*tem = (float)temValue / 16;
}
else
{
/*打印错误信息*/
Printf("DS18B20_Init ERROR");
}
}
结合代码+数据手册时序图讲解:
头文件DS18B20.h
#ifndef __DS18B20_H
#define __DS18B20_H
//修改引脚在这
#define DS18B20_PIN GPIO_PIN_1 //DS18B20引脚
#define DS18B20_PORT GPIOA //DS18B20端口
#define DS18B20_LOW HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET) //引脚输出低电平
#define DS18B20_HIGH HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET) //引脚输出高电平
#define DS18B20_IN HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN) //读取引脚电平
void DS18B20_Tem_Convert(void);
void DS18B20_Read_Tem(float* tem);
void DS18B20_MspInit(void);
#endif
1、在DS18B20的头文件,定义了引脚端口,方便修改。
2、定义了输出高低电平和读取电平的宏定义
C文件DS18B20.c
#include "stm32f1xx_hal.h"
#include "DS18B20.h"
#include "Delay.h"
#include "UART.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
void DS18B20_MspInit(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = DS18B20_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; //开漏输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 添加这行以确保没有上拉或下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
}
1、包含的头文件:HAL库头文件 DS18B20头文件 Delay延时函数头文件 串口UART打印头文件
2、宏定义了三个DS1820 命令
(1)DS18B20_SKIP_ROM
(2)DS18B20_CONVERT_T
(3)DS18B20_READ_SCRATCHPAD
3、底层GPIO口初始化(看注释够了)
DS18B20初始化函数
uint8_t DS18B20_Init(void)
{
uint8_t AckBit; //判断DS18B20是否存在
DS18B20_HIGH;
DS18B20_LOW;
Delay_us(480);
DS18B20_HIGH;
Delay_us(60);
AckBit = DS18B20_IN;
Delay_us(420);
if(AckBit)
{
/*打印错误信息*/
return 1;
}
return 0;
}
1、图11为初始化时序图,看着代码的延时时间一一对应,很好理解,在延时60微妙后读取引脚电平状态给AckBit,再判断AckBit,如果为低电平,DS18B20存在,如果为高电平DS18B20不存在或者未响应。
向DS18B20写入一个位的函数
void DS18B20_WriteBit(uint8_t bit)
{
DS18B20_LOW;
Delay_us(15);
if(bit)
{
DS18B20_HIGH;
}
else
{
DS18B20_LOW; /*可省略*/
}
Delay_us(45);
DS18B20_HIGH;
}
1、下图为写入DS18B20一位数据的时序图,看着代码的延时时间一一对应,很好理解。设置低电平15微妙后,判断传入的bit值,设置相应的电平。(注:写入0的时候,低电平至少保持60微妙,所以总的延时事件加起来为60微妙,一一对应,很好理解)
向DS18B20写入一个字节的函数
void DS18B20_WriteByte(uint8_t byte)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
DS18B20_WriteBit(byte & (0x01 << i));
}
}
1、这个函数需要传入一个byte参数,八次循环的操作是将 byte 的每一位写入 DS18B20 传感器。
跟时序无关,纯纯C语言用法,不深入讲解。
从DS18B20读取一个位的函数
uint8_t DS18B20_ReadBit(void)
{
uint8_t bit;
DS18B20_LOW;
Delay_us(5);
DS18B20_HIGH;
Delay_us(10);
if(DS18B20_IN)
{
bit = 1;
}
else
{
bit = 0;
}
Delay_us(45);
return bit;
}
1、下图为写入DS18B20一位数据的时序图,看着代码的延时时间一一对应,很好理解。也是总共60微妙,先拉低5微妙,再拉高(释放)需要10微妙,之后再读取数据。(注:写入0的时候,低电平至少保持60微妙,所以总的延时事件加起来为60微妙,一一对应,很好理解)
从DS18B20读取一个字节的函数
void DS18B20_ReadByte(uint8_t *byte)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
*byte >>= 1; // 右移一位
if(DS18B20_ReadBit())
{
*byte |= 0x80; // 设置最高位
}
}
}
1、这个函数需要传入一个byte参数,用来储存读取的字节。跟时序无关,纯纯C语言用法,不深入讲解。
该函数通过循环 8 次,每次读取 1 位数据,并将其存储到 byte 中。每次读取到的位会被放置在 byte 的最高位,然后通过右移操作将其移到正确的位置。最终,byte 中将包含从 DS18B20 传感器读取的一个完整字节的数据。
DS18B20开始温度变换
void DS18B20_Tem_Convert(void)
{
if(DS18B20_Init() == 0)
{
DS18B20_WriteByte(DS18B20_SKIP_ROM);
DS18B20_WriteByte(DS18B20_CONVERT_T);
}
else
{
/*打印错误信息*/
Printf("DS18B20_Init ERROR");
}
}
1、DS18B20_SKIP_ROM :跳过ROM命令(0xCC)用于在单总线系统中跳过ROM选择步骤,直接对所连接的DS18B20传感器进行操作。这在只有一个传感器或不需要区分多个传感器时非常有用。DS18B20_CONVERT_T :温度转换命令。都是通过向DS18B20写入一个字节的函数写入命令。
DS18B20读取温度
void DS18B20_Read_Tem(float* tem)
{
uint16_t temValue;
uint8_t tem_LSB, tem_MSB;
if(DS18B20_Init() == 0)
{
DS18B20_WriteByte(DS18B20_SKIP_ROM);
DS18B20_WriteByte(DS18B20_READ_SCRATCHPAD);
DS18B20_ReadByte(&tem_LSB);
DS18B20_ReadByte(&tem_MSB);
temValue = (tem_MSB << 8) | tem_LSB;
*tem = (float)temValue / 16;
}
else
{
/*打印错误信息*/
Printf("DS18B20_Init ERROR");
}
}
1、DS18B20_READ_SCRATCHPAD :读取DS18B20传感器的暂存器(Scratchpad)的命令。
2、下面这两行代码分别从DS18B20传感器读取温度值的低字节(tem_LSB)和高字节(tem_MSB)。
DS18B20_ReadByte(&tem_LSB);
DS18B20_ReadByte(&tem_MSB);
3、这行代码将高字节左移8位,然后与低字节进行按位或操作,合并成一个16位的温度值(temValue)。
temValue = (tem_MSB << 8) | tem_LSB;
4、这行代码将16位的温度值除以16,转换为实际的温度值(单位:摄氏度),并存储在tem指针指向的变量中。
*tem = (float)temValue / 16;
不理解在评论问!!!
HAL库的Delay_us代码
void Delay_us(uint32_t us)
{
// 初始化延迟时间总数为0
uint32_t total = 0;
// 计算需要延迟的时间(以微秒为单位)
uint32_t target = (SystemCoreClock/1000000U) * us;
// 获取初始的SysTick计数值
int last = SysTick->VAL;
int now = last;
int diff = 0;
// 循环直到累计的延迟时间达到目标时间
while(1)
{
// 获取当前的SysTick计数值
now = SysTick->VAL;
// 计算两次读取之间的差值
diff = last - now;
// 如果差值大于0,表示时间有增加
if(diff > 0)
{
// 增加总延迟时间
total += diff;
}
// 如果差值小于0,表示时间有减少,需要加上最大计数值
else
{
total += diff + SysTick->LOAD;
}
// 如果总延迟时间大于目标时间,则退出循环
if(total > target)
{
return;
}
// 更新上次读取的SysTick计数值
last = now;
}
}
作者:肖誉誉誉誉誉