STM32、单总线、驱动DS18B20(温度传感器)HAL库&标准库通用

 代码:

 !!!只要代码的就复制走,需要搞懂原理的看代码后面的

 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 传感器。

  • byte & (0x01 << i):
  • 0x01 是一个二进制数 00000001。
  • 0x01 << i 表示将 0x01 左移 i 位。
  • byte & (0x01 << i) 通过按位与操作提取 byte 的第 i 位。
  • DS18B20_WriteBit:
  • 这个函数将提取出的第 i 位写入 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语言用法,不深入讲解。

  • for(i = 0; i < 8; i++): 循环 8 次,每次读取 1 位数据。
  • *byte >>= 1;: 将 byte 的值右移一位。右移操作会将当前最低位丢弃,并在最高位填充 0。
  • if(DS18B20_ReadBit()): 调用 DS18B20_ReadBit 函数读取一位数据。如果读取到的位是 1,则进入 if 语句块。
  • *byte |= 0x80;: 将 byte 的最高位置为 1。0x80 是一个 8 位二进制数,最高位为 1,其余位为 0(即 10000000)。

该函数通过循环 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;
    }
}

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值