【STM32CubeMX学习】1WIRE总线读取DS18B20温度

本文详细介绍了如何使用STM32作为主机通过1WIRE总线与DS18B20传感器进行通信,实现温度测量。内容包括1WIRE总线的工作原理、DS18B20的特性、读写时序以及相应的C代码实现。DS18B20具有-55℃~+125℃的测量范围,12位分辨率,并能通过设置转换精度调整分辨率。代码中展示了复位、应答、读写等关键步骤,以及读取温度数据的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、1WIRE总线介绍

        1WIRE总线只用一根线就可以完成读写操作,下面以STM32为主机,DS18B20为从机,介绍1WIRE总线的各种时序。

①复位脉冲:主机输出低电平,保持低电平时间至少480us,然后主机释放总线,延时15~60us,并进入接收模式。

②应答脉冲:从机发现总线有上升沿,拉低总线并保持60~240us,表示应答。

③写时序(低位先写):

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

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

④读时序:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,得到一位数据1/0(低位在前),然后延时50us。

2、DS18B20简介

温度测量范围:-55℃~+125℃。
通信协议:1WIRE总线,数据线接上拉电阻,使总线空闲时处于高电平。
转换精度:9~12位分辨率可调,默认为12位,即分辨率是0.0625。
转换时间:典型值200ms。

 

DS18B20的内部有64位的ROM单元和9字节的高速暂存器。64位ROM单元包含了DS18B20唯一的序列号,因为这里只使用了一个DS18B20,因此不必关心这个ROM的内容。需要关注的是9字节的高速暂存器:

byte0:温度数据的低8位。
byte1:温度数据的高8位。
byte2:TH用户字节,设置报警温度最高值。
byte3:TL用户字节,设置报警温度最低值。
byte4:保留。
byte5:保留。
byte6、7:配置寄存器,设置转换精度,默认12位。
byte8:CRC码

DS18B20的温度数据格式如下:

BIT0~3:小数部分。
BIT4~10:整数部分。
BIT11~15:00000为正温度,11111为负温度。

若温度问正,则直接将这个值乘0.0625,若温度为负,则将这个值取反加一,再乘0.0625。

3、代码实现读取温度

ds18b20.h

#ifndef __DS18B20_H
#define __DS18B20_H	

#include "main.h"

//IO方向设置
#define DS18B20_GPIO_MODE_Pos    GPIO_CRH_MODE9_Pos                           
#define DS18B20_IO_IN   {ONEWIRE_DQ_GPIO_Port->CRH&=0XFFFFFF0F;GPIOB->CRH|=8<<GPIO_CRH_MODE9_Pos;}
#define DS18B20_IO_OUT {ONEWIRE_DQ_GPIO_Port->CRH&=0XFFFFFF0F;GPIOB->CRH|=3<<GPIO_CRH_MODE9_Pos;}

//IO操作函数											   
#define DS18B20_DQ_LOW      HAL_GPIO_WritePin(ONEWIRE_DQ_GPIO_Port,ONEWIRE_DQ_Pin,GPIO_PIN_RESET)
#define DS18B20_DQ_HIGH     HAL_GPIO_WritePin(ONEWIRE_DQ_GPIO_Port,ONEWIRE_DQ_Pin,GPIO_PIN_SET)

#define DS18B20_DQ_READ     HAL_GPIO_ReadPin(ONEWIRE_DQ_GPIO_Port,ONEWIRE_DQ_Pin)

short DS18B20_Get_Temp(void);

#endif

ds18b20.c

读取步骤:复位 -> 发SKIP ROM命令(0XCC) -> 发开始转换命令(0X44) -> 延时 -> 复位 -> 发送 SKIP ROM 命令(0XCC) -> 发读存储器命令(0XBE) -> 连续读出两个字节温度数据 -> 结束。

#include "ds18b20.h"	

//复位DS18B20
void DS18B20_Rst(void)	   
{                 
	DS18B20_IO_OUT;//设置为输出
	DS18B20_DQ_LOW;//拉低DQ
	Delay_Us(500);//拉低500us
	DS18B20_DQ_HIGH;//拉高DQ
	Delay_Us(20);//20us
}

//等待DS18B20的应答,返回值:0-非应答,1-应答
uint8_t Wait_DS18B20_ACK(void) 	   
{   
	uint8_t retry=0;
	DS18B20_IO_IN;//设置为输入
  while(DS18B20_DQ_READ&&(retry<200))
	{
		retry++;
		Delay_Us(1);
	}	 
	if(retry>=200)return 0;//超过200us未读到低电平,无应答
	else retry=1;//200us内转为低电平
  while ((!DS18B20_DQ_READ)&&(retry<240))
	{
		retry++;
		Delay_Us(1);
	}
	if(retry>=240)return 0;//低电平超过240us,无应答	    
	return 1;//低电平在240以内,有应答
}

//从DS18B20读取一位,返回值:1/0
uint8_t DS18B20_Read_Bit(void) 
{
	uint8_t data;
	DS18B20_IO_OUT;//设置为输出
	DS18B20_DQ_LOW; 
	Delay_Us(2);
	DS18B20_DQ_HIGH; 
	DS18B20_IO_IN;//设置为输入
	Delay_Us(12);
	if(DS18B20_DQ_READ)data=1;
	else data=0;	 
	Delay_Us(50);           
	return data;
}

//从DS18B20读取一个字节,返回值:读到的数据
uint8_t DS18B20_Read_Byte(void)   
{        
	uint8_t i,j,dat;
	dat=0;
	for(i=0;i<8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
  }						    
	return dat;
}

//写一个字节到DS18B20,dat:要写入的字节
void DS18B20_Write_Byte(uint8_t dat)     
{             
  uint8_t j;
  uint8_t testb;
  DS18B20_IO_OUT;//设置为输出
  for (j=0;j<8;j++) 
	{
     testb=dat&0x01;
     dat=dat>>1;
     if(testb)//写1
     {
        DS18B20_DQ_LOW;
        Delay_Us(2);                            
        DS18B20_DQ_HIGH;
        Delay_Us(60);             
     }
     else//写0
     {
        DS18B20_DQ_LOW;
        Delay_Us(60);             
        DS18B20_DQ_HIGH;
        Delay_Us(2);                          
     }
  }
}
 
//开始温度转换
void DS18B20_Start(void)
{   						               
    DS18B20_Rst();	   
    Wait_DS18B20_ACK();	 
    DS18B20_Write_Byte(0xcc);//跳过64位ROM地址
    DS18B20_Write_Byte(0x44);//启动温度转换
}

//从ds18b20得到温度值,精度:0.1℃,返回值:温度值 (-550~1250) 
short DS18B20_Get_Temp(void)
{
    uint8_t TL,TH;
    short temp;
    DS18B20_Start ();//开始转换
    Delay_Ms(200); 
    DS18B20_Rst();
    Wait_DS18B20_ACK();	 
    DS18B20_Write_Byte(0xcc);  //跳过64位ROM地址
    DS18B20_Write_Byte(0xbe);  //读取暂存器数据	    
    TL=DS18B20_Read_Byte();  //LSB   
    TH=DS18B20_Read_Byte();  //MSB 
    
    temp = TH;//获得高八位
    temp<<=8;    
    temp+=TL;//获得底八位

    //分辨率为0.0625,这里比实际值大十倍
    if(TH>7)//温度为负 
        temp = -((~temp+1)*0.625); 
    else//温度为正
      temp = temp*0.625;  
    return temp;    
}

定义全局变量

short temperature;//温度值

在main函数中查询

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    temperature=DS18B20_Get_Temp();//获取温度值
    printf("temperature=%d.%d\r\n",temperature/10,temperature%10);
    Delay_Ms(1000);


  }
  /* USER CODE END 3 */

运行结果

 

### 使用STM32CubeMX配置STM32读取DS18B20温度传感器 #### 一、硬件连接 在使用STM32CubeMX配置STM32读取DS18B20温度传感器之前,需完成硬件连接。通常情况下,DS18B20的引脚定义如下: - VDD 连接到 STM32 的供电电压 (一般为 3.3V 或 5V)。 - GND 接地。 - DQ 数据线连接到 STM32 的 GPIO 引脚,并通过一个约 4.7kΩ 的上拉电阻连接至 VCC。 这种连接方式确保了单总线通信的有效性[^1]。 --- #### 二、STM32CubeMX 配置流程 以下是基于 STM32CubeMX 工具的具体配置步骤: 1. **创建新项目并选择 MCU** 启动 STM32CubeMX 软件,点击 “New Project”,然后选择目标 MCU 型号(例如 STM32F103C8T6),点击 “Start Project”。 2. **GPIO 配置** 将 DS18B20 的数据线所对应的 GPIO 设置为推挽输出模式(Push-pull Output Mode)。这一步是为了支持单总线协议下的信号传输[^1]。 3. **启用 USART 功能(可选)** 如果计划将读取温度数据显示在串口调试工具中,则需要配置 USART 外设。设置波特率(如 115200)、数据位、停止位等参数。 4. **时钟树调整** 根据实际需求调整系统时钟频率,推荐使用默认的 HSE 或 HSI 模式来满足实时性能要求。 5. **生成代码** 完成以上配置后,点击 “Generate Code” 来生成初始化代码框架。 --- #### 三、软件实现 以下是一个完整的 C 语言程序示例,展示如何利用 HAL 库读取 DS18B20 温度值并通过串口打印出来。 ```c #include "stm32f1xx_hal.h" #include <stdio.h> #include <string.h> #define ONE_WIRE_PIN GPIO_PIN_5 // 单总线使用的 GPIO 引脚编号 #define ONE_WIRE_PORT GPIOA // 单总线使用的端口号 // 函数声明 void OneWireReset(void); uint8_t OneWireReadBit(void); void OneWireWriteBit(uint8_t bit); void OneWireWriteByte(uint8_t byte_val); uint8_t OneWireReadByte(void); float ReadTemperature(void); int main(void) { HAL_Init(); // 初始化HAL库 SystemClock_Config(); // 配置系统时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟 GPIO_InitTypeDef GPIO_InitStruct; // 配置单总线引脚为推挽输出模式 GPIO_InitStruct.Pin = ONE_WIRE_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(ONE_WIRE_PORT, &GPIO_InitStruct); while (1) { float temperature = ReadTemperature(); printf("Current Temperature: %.2f°C\r\n", temperature); // 打印温度 HAL_Delay(1000); // 每秒更新一次 } } /** * @brief 发起单总线复位序列 */ void OneWireReset() { HAL_GPIO_WritePin(ONE_WIRE_PORT, ONE_WIRE_PIN, GPIO_PIN_RESET); HAL_Delay(1); // 下拉至少 480us HAL_GPIO_WritePin(ONE_WIRE_PORT, ONE_WIRE_PIN, GPIO_PIN_SET); HAL_Delay(1); // 上拉等待主机释放总线 } /** * @brief 从单总线读取一位数据 * @return 返回读取的一位数据 */ uint8_t OneWireReadBit() { uint8_t value; HAL_GPIO_WritePin(ONE_WIRE_PORT, ONE_WIRE_PIN, GPIO_PIN_RESET); HAL_DelayMicroseconds(2); // 创建采样时间窗口 HAL_GPIO_WritePin(ONE_WIRE_PORT, ONE_WIRE_PIN, GPIO_PIN_SET); if(HAL_GPIO_ReadPin(ONE_WIRE_PORT, ONE_WIRE_PIN)) value = 1; // 若检测到高电平则返回 '1' else value = 0; // 反之返回 '0' return value; } /** * @brief 向单总线写入一位数据 * @param bit 待写入的数据位 ('0' or '1') */ void OneWireWriteBit(uint8_t bit) { HAL_GPIO_WritePin(ONE_WIRE_PORT, ONE_WIRE_PIN, GPIO_PIN_RESET); if(bit){ HAL_DelayMicroseconds(10); // 写 '1': 短暂下拉 }else{ HAL_DelayMicroseconds(60); // 写 '0': 较长时间下拉 } HAL_GPIO_WritePin(ONE_WIRE_PORT, ONE_WIRE_PIN, GPIO_PIN_SET); HAL_DelayMicroseconds(10); // 总线恢复时间为 ~10 us } /** * @brief 向单总线写入一字节数据 * @param byte_val 待写入的一个字节 */ void OneWireWriteByte(uint8_t byte_val) { uint8_t i; for(i=0;i<8;i++) { // 对每一位依次处理 OneWireWriteBit(byte_val & 0x01); byte_val >>= 1; } } /** * @brief 从单总线读取一字节数据 * @return 返回读取到的一个字节 */ uint8_t OneWireReadByte() { uint8_t data = 0; uint8_t i; for(i=0;i<8;i++) { // 对每一位依次处理 data >>= 1; if(OneWireReadBit()) // 移位并将当前位加入结果 data |= 0x80; } return data; } /** * @brief 获取 DS18B20 当前测量的温度值 * @return 测量得到的浮点型温度值 */ float ReadTemperature() { uint8_t scratchpad[9]; // 存储寄存器数据缓冲区 float temp_celsius; OneWireReset(); // 开始复位序列 OneWireWriteByte(0xCC); // 跳过 ROM 地址匹配命令 OneWireWriteByte(0x44); // 发送温度转换命令 HAL_Delay(750); // 等待温度转换完成 (~750ms) OneWireReset(); // 再次发起复位 OneWireWriteByte(0xCC); // 继续跳过 ROM 地址匹配 OneWireWriteByte(0xBE); // 请求读取存储器内容 for(int i=0;i<9;i++) // 逐字节读取 Scratch Pad 寄存器 scratchpad[i] = OneWireReadByte(); int16_t raw_temp = ((scratchpad[1] << 8) | scratchpad[0]); // 提取原始温度数值 temp_celsius = (float)(raw_temp / 16.0); // 计算摄氏温度 return temp_celsius; } ``` --- #### 四、注意事项 1. **延时函数** `HAL_Delay()` 和 `HAL_DelayMicroseconds()` 是关键部分,用于控制单总线通信的时间间隔。如果未正确调用这些函数可能导致通信失败[^1]。 2. **电源管理** 确保 DS18B20 的供电稳定,尤其是在多设备共用同一根数据线的情况下可能需要额外考虑负载能力[^3]。 3. **错误处理机制** 实际应用中建议增加异常捕获逻辑,比如当无法成功获取温度时重试一定次数后再退出循环。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值