# 前言
最近再搞农业物联网的项目,用到了 ZigBee 协议栈,需求之一是要监测土壤温度的数据,便采购了「 善学坊 」家的「 YTWD-A1 」传感器,其本质是 DS18B20 芯片,但网上关于这个芯片的数据读取方式多是 51 单片机或 STM32 相关的代码,经过自己的一番踩坑和实验,终于在 ZigBee 协议栈下写好了这个芯片的驱动,便在此记录下来,希望能帮助到有需要的小伙伴~

# 相关代码
#ifndef HAL_DS18B20_H | |
#define HAL_DS18B20_H | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/** @brief YTWD GPIO. */ | |
#define HAL_DS18B20_PORT 1 //!< Port1. | |
#define HAL_DS18B20_PIN 2 //!< Pin2. | |
/** @brief DHT11 Data. */ | |
typedef struct { | |
unsigned char ok; //!< Is ok? | |
unsigned char fg; | |
unsigned char xs; //!< xiaoshu. | |
unsigned char zs; //!< zhengshu. | |
} halDS18B20Data_t; | |
/** | |
* @fn halDS18B20Init | |
* | |
* @brief Init. DS18B20. | |
*/ | |
unsigned char halDS18B20Init(void); | |
/** | |
* @fn halDHT11GetData | |
* | |
* @brief Get data from DS18B20. | |
* | |
* @return xs&zs value if ok is 1. | |
*/ | |
halDS18B20Data_t halDS18B20GetData(void); | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif /* #ifndef HAL_DS18B20_H */ |
#include "hal_ds18b20.h" | |
#include "hal_delay.h" | |
#include "cc2530_ioctl.h" | |
#include "hal_lcd.h" | |
#include <stdio.h> | |
/* Boolean value. */ | |
#define HAL_DS18B20_FALSE 0 | |
#define HAL_DS18B20_TRUE 1 | |
/* Delay Functions. */ | |
#define HAL_DS18B20_DELAY_US(x) delayUsIn32Mhz((x)) | |
#define HAL_DS18B20_DELAY_MS(x) delayMs(SYSCLK_32MHZ ,(x)) | |
/* Set DS18B20 GPIO mode. */ | |
#define HAL_DS18B20_IO_OUTPUT() CC2530_IOCTL(HAL_DS18B20_PORT, HAL_DS18B20_PIN, CC2530_OUTPUT) | |
#define HAL_DS18B20_IO_PULLDOWN() CC2530_IOCTL(HAL_DS18B20_PORT, HAL_DS18B20_PIN, CC2530_INPUT_PULLDOWN) | |
#define HAL_DS18B20_IO_PULLUP() CC2530_IOCTL(HAL_DS18B20_PORT, HAL_DS18B20_PIN, CC2530_INPUT_PULLUP) | |
/* Set DS18B20 GPIO Level. */ | |
#define HAL_DS18B20_IO_SET(port, pin, level) do { \ | |
if(level) CC2530_GPIO_SET(port, pin); \ | |
else CC2530_GPIO_CLEAR(port, pin); \ | |
} while(0) | |
#define HAL_DS18B20_IO_SET_LO() HAL_DS18B20_IO_SET(HAL_DS18B20_PORT, HAL_DS18B20_PIN, 0) | |
#define HAL_DS18B20_IO_SET_HI() HAL_DS18B20_IO_SET(HAL_DS18B20_PORT, HAL_DS18B20_PIN, 1) | |
/* Get DS18B20 GPIO Status. */ | |
#define HAL_DS18B20_IO_GET(port, pin) CC2530_GPIO_GET(port, pin) | |
#define HAL_DS18B20_IO() HAL_DS18B20_IO_GET(HAL_DS18B20_PORT, HAL_DS18B20_PIN) | |
static uint8_t ReadOneChar(void); | |
static void WriteOneChar(uint8_t dat); | |
unsigned char halDS18B20Init(void) | |
{ | |
unsigned char x = 0; | |
HAL_DS18B20_IO_OUTPUT(); | |
//DQ 先置高 | |
HAL_DS18B20_IO_SET_HI(); | |
HAL_DS18B20_DELAY_US(74); // 稍延时 | |
// 发送复位脉冲 | |
HAL_DS18B20_IO_SET_LO(); | |
HAL_DS18B20_DELAY_US(750); // 延时(>480us) | |
// 拉高数据线 | |
HAL_DS18B20_IO_SET_HI(); | |
HAL_DS18B20_DELAY_US(50); // 等待(15~60us) | |
HAL_DS18B20_IO_PULLUP(); | |
x = HAL_DS18B20_IO(); // 用 X 的值来判断初始化有没有成功,18B20 存在的话 X=0,否则 X=1 | |
// if (x == 0) HalLcdWriteString("finddddd", 3); | |
// else HalLcdWriteString("nooooo", 3); | |
HAL_DS18B20_DELAY_US(100); | |
return x; | |
} | |
halDS18B20Data_t halDS18B20GetData(void) | |
{ | |
uint8_t tempL, tempH, xiaoshu1, xiaoshu2; | |
halDS18B20Data_t ds18b20Dat = { .ok = HAL_DS18B20_FALSE, .fg = 1 }; | |
halDS18B20Init(); | |
HAL_DS18B20_DELAY_US(15); | |
WriteOneChar(0xcc); // 跳过读序列号的操作 | |
WriteOneChar(0x44); // 启动温度转换 | |
HAL_DS18B20_DELAY_US(1000); // 转换需要一点时间,延时 | |
halDS18B20Init(); // 初始化 | |
HAL_DS18B20_DELAY_US(15); | |
WriteOneChar(0xcc); // 跳过读序列号的操作 | |
WriteOneChar(0xbe); // 读温度寄存器(头两个值分别为温度的低位和高位) | |
tempL = ReadOneChar(); // 读出温度的低位 LSB | |
tempH = ReadOneChar(); // 读出温度的高位 MSB | |
if(tempH & 0xf8) // 最高 5 位为 1 时温度是负 | |
{ | |
tempL = ~tempL; // 补码转换,取反加一 | |
tempH = ~tempH+1; | |
ds18b20Dat.fg = 0; // 读取温度为负时 fg=0 | |
} | |
// 温度为负时貌似有问题,后面再改 | |
ds18b20Dat.zs = tempL/16+tempH*16; // 整数部分 | |
xiaoshu1 = (tempL&0x0f)*10/16; // 小数第一位 | |
xiaoshu2 = (tempL&0x0f)*100/16%10; // 小数第二位 | |
ds18b20Dat.xs = xiaoshu1*10+xiaoshu2; // 小数两位 | |
// 进行数据校验 | |
if ((!ds18b20Dat.fg && ds18b20Dat.zs <= 55) || (ds18b20Dat.fg && ds18b20Dat.zs <= 125)) | |
{ | |
ds18b20Dat.ok = HAL_DS18B20_TRUE; | |
} | |
halDS18B20Init(); | |
return ds18b20Dat; | |
} | |
// 主机数据线先从高拉至低电平 1us 以上,再使数据线升为高电平,从而产生读信号 | |
static uint8_t ReadOneChar(void) | |
{ | |
uint8_t i = 0; // 每个读周期最短的持续时间为 60us,各个读周期之间必须有 1us 以上的高电平恢复期 | |
uint8_t dat = 0; | |
for(i = 8; i>0; i--) // 一个字节有 8 位 | |
{ | |
HAL_DS18B20_IO_OUTPUT(); | |
dat>>=1; | |
HAL_DS18B20_IO_SET_LO(); | |
HAL_DS18B20_DELAY_US(2); | |
HAL_DS18B20_IO_SET_HI(); | |
HAL_DS18B20_IO_PULLUP(); | |
HAL_DS18B20_DELAY_US(12); | |
if(HAL_DS18B20_IO()) | |
dat |= 0x80; | |
HAL_DS18B20_DELAY_US(50); | |
} | |
return dat; | |
} | |
// 数据线从高电平拉至低电平,产生写起始信号。15us 之内将所需写的位送到数据线上 | |
static void WriteOneChar(uint8_t dat) | |
{ | |
HAL_DS18B20_IO_OUTPUT(); | |
uint8_t i = 0; | |
for(i = 8; i > 0; i--) // 在 15~60us 之间对数据线进行采样,如果是高电平就写 1,低写 0 发生。 | |
{ | |
HAL_DS18B20_IO_SET_LO(); // 在开始另一个写周期前必须有 1us 以上的高电平恢复期 | |
HAL_DS18B20_DELAY_US(1); | |
if (dat&0x01) HAL_DS18B20_IO_SET_HI(); | |
else HAL_DS18B20_IO_SET_LO(); | |
HAL_DS18B20_DELAY_US(68); | |
HAL_DS18B20_IO_SET_HI(); | |
dat >>= 1; | |
} | |
HAL_DS18B20_DELAY_US(40); | |
} |
# 参考链接
- 液体温度传感器 YTWD-A1
- DS18B20 数字温度传感器干货
- 温度传感器 DS18B20 原理,附 STM32 例程代码
- 51 单片机 DS18B20 温度传感器详解