当前位置: 首页 > news >正文

STM32基础驱动系列-DS18B20

昨天更了一篇DHT11的驱动,这个探测器的温湿度准确性都很差。DS18B20的温度精确度相比于DHT11高得多。下面是性能对比。

ds18b20.h

#ifndef __DS18B20_H #define __DS18B20_H #ifdef __cplusplus extern "C" { #endif #include "main.h" #include <stdint.h> typedef struct { GPIO_TypeDef *GPIOx; uint16_t GPIO_Pin; } DS18B20_t; typedef enum { DS18B20_OK = 0, DS18B20_ERROR = 1, DS18B20_NO_DEVICE = 2, DS18B20_CRC_ERROR = 3 } DS18B20_Status_t; /* 初始化 DS18B20 */ void DS18B20_Init(DS18B20_t *dev, GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); /* 检测 DS18B20 是否在线 */ DS18B20_Status_t DS18B20_Check(DS18B20_t *dev); /* 启动温度转换 */ DS18B20_Status_t DS18B20_StartConvert(DS18B20_t *dev); /* 读取温度,单位:摄氏度 */ DS18B20_Status_t DS18B20_ReadTemperature(DS18B20_t *dev, float *temperature); /* 启动转换并等待,然后读取温度 */ DS18B20_Status_t DS18B20_GetTemperature(DS18B20_t *dev, float *temperature); #ifdef __cplusplus } #endif #endif

ds18b20.c

#include "ds18b20.h" /* * DS18B20 命令 */ #define DS18B20_CMD_SKIP_ROM 0xCC #define DS18B20_CMD_CONVERT_T 0x44 #define DS18B20_CMD_READ_SCRATCHPAD 0xBE /* * 内部函数声明 */ static void DS18B20_DelayUs(uint32_t us); static void DS18B20_SetOutput(DS18B20_t *dev); static void DS18B20_SetInput(DS18B20_t *dev); static void DS18B20_WriteBit(DS18B20_t *dev, uint8_t bit); static uint8_t DS18B20_ReadBit(DS18B20_t *dev); static void DS18B20_WriteByte(DS18B20_t *dev, uint8_t data); static uint8_t DS18B20_ReadByte(DS18B20_t *dev); static uint8_t DS18B20_Reset(DS18B20_t *dev); static uint8_t DS18B20_CRC8(uint8_t *data, uint8_t len); /* * 初始化 */ void DS18B20_Init(DS18B20_t *dev, GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) { dev->GPIOx = GPIOx; dev->GPIO_Pin = GPIO_Pin; DS18B20_SetInput(dev); /* * 初始化 DWT 微秒延时 * 适用于 Cortex-M3/M4,例如 STM32F103、F405、F407 */ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; } /* * 微秒延时 */ static void DS18B20_DelayUs(uint32_t us) { uint32_t startTick = DWT->CYCCNT; uint32_t delayTicks = us * (HAL_RCC_GetHCLKFreq() / 1000000U); while ((DWT->CYCCNT - startTick) < delayTicks) { } } /* * 设置 DQ 为输出开漏 */ static void DS18B20_SetOutput(DS18B20_t *dev) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = dev->GPIO_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(dev->GPIOx, &GPIO_InitStruct); } /* * 设置 DQ 为输入 */ static void DS18B20_SetInput(DS18B20_t *dev) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = dev->GPIO_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(dev->GPIOx, &GPIO_InitStruct); } /* * 复位 DS18B20 * 返回 1 表示检测到设备 * 返回 0 表示没有设备 */ static uint8_t DS18B20_Reset(DS18B20_t *dev) { uint8_t presence = 0; DS18B20_SetOutput(dev); HAL_GPIO_WritePin(dev->GPIOx, dev->GPIO_Pin, GPIO_PIN_RESET); DS18B20_DelayUs(480); DS18B20_SetInput(dev); DS18B20_DelayUs(70); /* * DS18B20 存在时,会把总线拉低 */ if (HAL_GPIO_ReadPin(dev->GPIOx, dev->GPIO_Pin) == GPIO_PIN_RESET) { presence = 1; } else { presence = 0; } DS18B20_DelayUs(410); return presence; } /* * 写 1 bit */ static void DS18B20_WriteBit(DS18B20_t *dev, uint8_t bit) { DS18B20_SetOutput(dev); if (bit) { /* * 写 1:先拉低 1~15us,然后释放总线 */ HAL_GPIO_WritePin(dev->GPIOx, dev->GPIO_Pin, GPIO_PIN_RESET); DS18B20_DelayUs(6); HAL_GPIO_WritePin(dev->GPIOx, dev->GPIO_Pin, GPIO_PIN_SET); DS18B20_DelayUs(64); } else { /* * 写 0:拉低至少 60us */ HAL_GPIO_WritePin(dev->GPIOx, dev->GPIO_Pin, GPIO_PIN_RESET); DS18B20_DelayUs(60); HAL_GPIO_WritePin(dev->GPIOx, dev->GPIO_Pin, GPIO_PIN_SET); DS18B20_DelayUs(10); } } /* * 读 1 bit */ static uint8_t DS18B20_ReadBit(DS18B20_t *dev) { uint8_t bit = 0; DS18B20_SetOutput(dev); HAL_GPIO_WritePin(dev->GPIOx, dev->GPIO_Pin, GPIO_PIN_RESET); DS18B20_DelayUs(6); DS18B20_SetInput(dev); DS18B20_DelayUs(9); if (HAL_GPIO_ReadPin(dev->GPIOx, dev->GPIO_Pin) == GPIO_PIN_SET) { bit = 1; } else { bit = 0; } DS18B20_DelayUs(55); return bit; } /* * 写 1 byte,低位先发 */ static void DS18B20_WriteByte(DS18B20_t *dev, uint8_t data) { uint8_t i; for (i = 0; i < 8; i++) { DS18B20_WriteBit(dev, data & 0x01); data >>= 1; } } /* * 读 1 byte,低位先读 */ static uint8_t DS18B20_ReadByte(DS18B20_t *dev) { uint8_t i; uint8_t data = 0; for (i = 0; i < 8; i++) { if (DS18B20_ReadBit(dev)) { data |= (1 << i); } } return data; } /* * CRC8 校验 * 多项式:x^8 + x^5 + x^4 + 1 */ static uint8_t DS18B20_CRC8(uint8_t *data, uint8_t len) { uint8_t crc = 0; uint8_t i, j; for (i = 0; i < len; i++) { crc ^= data[i]; for (j = 0; j < 8; j++) { if (crc & 0x01) { crc = (crc >> 1) ^ 0x8C; } else { crc >>= 1; } } } return crc; } /* * 检查设备是否存在 */ DS18B20_Status_t DS18B20_Check(DS18B20_t *dev) { if (DS18B20_Reset(dev)) { return DS18B20_OK; } else { return DS18B20_NO_DEVICE; } } /* * 启动温度转换 */ DS18B20_Status_t DS18B20_StartConvert(DS18B20_t *dev) { if (!DS18B20_Reset(dev)) { return DS18B20_NO_DEVICE; } DS18B20_WriteByte(dev, DS18B20_CMD_SKIP_ROM); DS18B20_WriteByte(dev, DS18B20_CMD_CONVERT_T); return DS18B20_OK; } /* * 读取温度 * 注意:调用这个函数前,需要先调用 DS18B20_StartConvert() * 并等待转换完成。 */ DS18B20_Status_t DS18B20_ReadTemperature(DS18B20_t *dev, float *temperature) { uint8_t scratchpad[9]; uint8_t i; int16_t raw; if (temperature == NULL) { return DS18B20_ERROR; } if (!DS18B20_Reset(dev)) { return DS18B20_NO_DEVICE; } DS18B20_WriteByte(dev, DS18B20_CMD_SKIP_ROM); DS18B20_WriteByte(dev, DS18B20_CMD_READ_SCRATCHPAD); for (i = 0; i < 9; i++) { scratchpad[i] = DS18B20_ReadByte(dev); } /* * 校验 scratchpad 前 8 个字节的 CRC * 第 9 个字节是 CRC */ if (DS18B20_CRC8(scratchpad, 8) != scratchpad[8]) { return DS18B20_CRC_ERROR; } /* * 温度原始值: * scratchpad[0] = LSB * scratchpad[1] = MSB */ raw = (int16_t)((scratchpad[1] << 8) | scratchpad[0]); /* * 默认 12-bit 分辨率,温度单位是 0.0625℃ */ *temperature = (float)raw * 0.0625f; return DS18B20_OK; } /* * 启动转换,等待,然后读取温度 * 12-bit 分辨率最大转换时间 750ms */ DS18B20_Status_t DS18B20_GetTemperature(DS18B20_t *dev, float *temperature) { DS18B20_Status_t status; status = DS18B20_StartConvert(dev); if (status != DS18B20_OK) { return status; } HAL_Delay(750); status = DS18B20_ReadTemperature(dev, temperature); return status; }

使用方法

#include "ds18b20.h" DS18B20_t ds18b20; float temperature = 0.0f; DS18B20_Status_t ds_status;

main函数的初始化部分加入

OLED_Init(); OLED_Clear(); OLED_ShowString(0, 0, (uint8_t *)"Temp:", 16); DS18B20_Init(&ds18b20, GPIOB, GPIO_PIN_12); ds_status = DS18B20_Check(&ds18b20); if (ds_status == DS18B20_OK){ OLED_ShowString(0, 4, (uint8_t *)"OK", 16); }

while中加入

while (1) { ds_status = DS18B20_GetTemperature(&ds18b20, &temperature); if (ds_status == DS18B20_OK) { /* * temperature 里面就是转换好的温度 * 比如 25.0625 * 可以直接显示到 OLED 或通过串口发送 */ OLED_ShowFloat(0, 2, temperature, 16); } else if (ds_status == DS18B20_NO_DEVICE) { /* * 没检测到 DS18B20 */ } else if (ds_status == DS18B20_CRC_ERROR) { /* * 数据 CRC 校验错误 */ } HAL_Delay(1000); }

效果

http://www.jsqmd.com/news/760086/

相关文章:

  • 高效便捷!macOS 这 5 款命令行工具免费易装,让操作更高效
  • Claude Code 终于能在手机上跑了:10k Star 开源 UI,浏览器一进就有
  • Cortex-M55 CTI架构与调试技术详解
  • 英伟达:离线策略蒸馏Lightning OPD
  • 从“看图识字“到“全能感知“!多模态大模型5年爆变史,Qwen系成“基础设施“!
  • Nemotron-Flash:低延迟LLM推理的混合架构设计
  • 避坑指南:在Ubuntu 20.04上从零搭建OpenPCDet+PointPillars_ROS环境(含CUDA 11.7、spconv2.x配置)
  • Tool Calling 的实现细节——Agent 如何决定调用哪个工具
  • YOLO训练入门(下)学习笔记(第四集)
  • 【AI模型】模型量化技术详解
  • 大模型代码生成与代理任务评估框架及优化实践
  • 2026年5月专业靠谱的全屋定制TOP5:基于全案交付与口碑验证的权威榜单 - 商业科技观察
  • 告别手动测试:深入解读Vector CANoe LIN一致性测试模块(ISO17987/J2602标准覆盖哪些内容?)
  • 2026树枝粉碎机品牌评分出炉!博尚9.8分领跑,全能配置+高性价比,市政/物业首选品牌 - 会飞的懒猪
  • 大模型输入的“灵魂”步骤:Embedding如何让0、1、2变得有“意义”?
  • 2026年5月全屋定制品牌权威盘点:精工智造如何定义家的品质 - 商业科技观察
  • 前端学习打卡 Day1:从0到1认识前端与HTML基础结构
  • 大语言模型逻辑验证框架:原理、实现与应用
  • 2026年5月全屋整装十大公认品牌——选对品牌,装好一个家 - 商业科技观察
  • 超表面技术在水下定位系统中的应用与优化
  • 前端已死?2026年,转型AI Agent工程师才是你的“续命”良方!
  • 基于Flutter的OpenClaw桌面控制台开发:架构设计与跨平台实践
  • 4J36低膨胀合金有哪些?符合国标的4J36低膨胀合金厂商推荐 - 品牌2026
  • CANoe诊断测试避坑指南:ISO 15765-2网络层时间参数(N_Ar, N_As, N_Br...)详解与实战监控
  • 2026年5月厨柜定制选购白皮书:从物理参数到精工交付的品质解码 - 商业科技观察
  • 利用Taotoken多模型能力为嵌入式系统设计文档寻找最优的生成模型
  • 告别Docker依赖!用tileserver-gl-light在Windows/Mac上5分钟搭建本地地图服务
  • 不只是建模:手把手教你用TCAD为GaN功率器件做‘虚拟实验’(DOE与参数校准篇)
  • GitHub汉化插件:3分钟告别英文界面,让中文开发者更高效
  • 别再手动配IP了!用Cloud-Init在OpenStack上5分钟搞定CentOS 7云主机初始化(附完整配置流程)