一、DS18B20传感器介绍
每个DS18B20出厂的时候都烧录了一个唯一的64为产品序列号在ROM中。ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个传感器。
1、DS18B20一线协议
所有的单总线器件都要求用严格的信号时序,以保证数据的完整性。DS18B20共有6种信号类型:复位脉冲、应答脉冲、写0/1、读0/1。所有的这些信号除了应答信号。都由主机发出同步信号,并且所有发送的命令和数据都是字节的低位在前(LSB)。
(1)复位脉冲和应答脉冲
单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少480us,以产生复位脉冲。接着主机释放总线,4.7K的上拉电阻将单总线拉高,延时15 ~ 60us,并进入接收模式(Rx)。接着DS18B20模块拉低总线60 ~ 240us,以产生低电平应答脉冲,若产生了低电平应答脉冲,再延时240us。
(2)写时序
写时序包括写0时序和写1时序。所有写时序至少要60us,且在两次独立的写时序之间至少要1us的恢复时间,两种写时序均起始于拉低总线。
写0时序:主机输出低电平,延时60us,然后释放总线2us; 写1时序:主机输出低电平,延时2us,然后再释放总线,延时60us。
从机在总线拉低30us后,读取电平。若为低电平,表示写0,若为高电平,表示写1。
(2)读时序
单总线器件仅在主机发出读时序时,才向主机传数据,主机发出读数据命令后,必须马上产生读时序,一遍从机能够传输数据。所有的读时序至少需要60us,且两次独立的读时序之间至少需要1us的恢复时间。
当总线控制器把数据线从高电平拉到低电平时,读时序开始,数据线必须至少保持1us,然后总线被释放。DS18B20通过拉高或拉低总线来传输“1”或“0”。当逻辑“0”结束后,总线被释放,通过上拉电阻回到上升状态,从DS18B20输出的数据在读时序的下降沿出现后15us内有效6.因此,总线控制器在读时序开始后必须停止把I/O口驱动为低电平,以读取I/O状态。
2.DS18B20工作流程
先进行温度转换,再进行温度读取; 温度转换:初始化 -> 跳过ROM -> 温度转换 温度读取:初始化 -> 跳过ROM -> 读寄存器 -> 读低八位 -> 读高八位
(1)初始化
单总线上的所有通信都是以初始化序列开始,主机发出初始化信号后等待从设备的应答信号,以确认从设备是否能正常工作。
(2)ROM操作命令
总线主机检测到DS18B20存在之后,便可以发出ROM操作命令,这些命令如下。但是我们一般不关心ROM中的16位产品序列号,通常会发送0xCC跳过ROM的相关操作。
(3)存储器操作命令
接下来发送相应的高速暂存存储器命令,命令表如下。其中0x44命令通知DS18B20传感器开始进行温度变换(温度采样),而0xBE命令则将开始读出DS18B20的采样值。
(4)数据处理
DS18B20的高速暂存存储器由9个字节组成。当执行温度转换命令(0x44)后,经转换的温度以二字节补码形式存于高速暂存存储器的前两个字节。 如果我们只关心温度值的话,则只需读前两个字节就可以了。其中Byte[0]为温度值得低字节,而Byte[1]为温度值得高字节。这16位数据的格式如下: 其中BIT[3:0]为温度值的小数部分,而BIT[10:4]为温度值的整数部分,BIT[15:11]为符号位,如果为0表示温度为正值,如果为-1则表示温度为负值。
二、DS18B20温度采样实现
1、硬件连接
DS18B20传感器工作电压为3~5.5V,本人将DS18B20的I/O口接到开发板的GPIO管脚PA5上,其实任意一个GPIO管脚都可以的,只需修改一下代码即可(后面说)。
2、代码实现
ds18b20.h
#ifndef INC_DS18B20_H_
#define INC_DS18B20_H_
#include "main.h"
uint8_t DS18B20_Reset(void);
void DS18B20_Writte_Byte(uint8_t byte);
uint8_t DS18B20_Read_Bit(void);
uint8_t DS18B20_Read_Byte(void);
uint8_t DS18B20_SampleData(float *temperature);
#endif
ds18b20.c
#include "ds18b20.h"
typedef struct w1_gpio_S
{
GPIO_TypeDef *group;
uint16_t pin;
}w1_gpio_s;
static w1_gpio_s W1DS =
{
.group = GPIOA,
.pin = GPIO_PIN_5,
};
#define W1DS_Input() \
{\
GPIO_InitTypeDef GPIO_InitStruct = {0};\
GPIO_InitStruct.Pin = W1DS.pin;\
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;\
GPIO_InitStruct.Pull = GPIO_PULLUP;\
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;\
HAL_GPIO_Init(W1DS.group, &GPIO_InitStruct);\
}
#define W1DS_Output() \
{\
GPIO_InitTypeDef GPIO_InitStruct = {0};\
GPIO_InitStruct.Pin = W1DS.pin;\
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;\
GPIO_InitStruct.Pull = GPIO_NOPULL;\
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;\
HAL_GPIO_Init(W1DS.group, &GPIO_InitStruct);\
}
#define W1DS_Write(x) HAL_GPIO_WritePin(W1DS.group, W1DS.pin,\
(x==1)?GPIO_PIN_SET : GPIO_PIN_RESET)
#define W1DQ_Read() HAL_GPIO_ReadPin(W1DS.group, W1DS.pin)
uint8_t DS18B20_Reset(void)
{
uint8_t retry = 0;
uint8_t rv;
W1DS_Output();
W1DS_Write(1);
delay_us(2);
W1DS_Write(0);
delay_us(480);
W1DS_Write(1);
delay_us(60);
W1DS_Input();
while(W1DQ_Read() && retry < 240)
{
retry++;
delay_us(1);
}
if(retry >= 240)
{
rv = 1;
}
delay_us(240);
W1DS_Output();
W1DS_Write(1);
delay_us(240);
return rv;
}
void DS18B20_Writte_Byte(uint8_t byte)
{
uint8_t i;
W1DS_Output();
for(i = 0; i< 8; i++)
{
if(byte & 0x01)
{
W1DS_Write(0);
delay_us(2);
W1DS_Write(1);
delay_us(60);
}
else
{
W1DS_Write(0);
delay_us(80);
W1DS_Write(1);
delay_us(2);
}
byte >>= 1;
delay_us(2);
}
}
uint8_t DS18B20_Read_Bit(void)
{
uint8_t rv = 0;
W1DS_Output();
W1DS_Write(0);
delay_us(2);
W1DS_Write(1);
delay_us(2);
W1DS_Input();
if(W1DQ_Read())
{
rv = 1;
}
delay_us(60);
W1DS_Output();
W1DS_Write(1);
return rv;
}
uint8_t DS18B20_Read_Byte(void)
{
uint8_t bit;
uint8_t i, Byte = 0;
for(i = 0; i < 8; i++)
{
bit = DS18B20_Read_Bit();
printf("read bit:%d\r\n", bit);
if(bit)
{
Byte |= (1 << i);
}
delay_us(2);
}
printf("Byte:%d\r\n", Byte);
return Byte;
}
uint8_t DS18B20_Start_Convet(void)
{
if(DS18B20_Reset())
{
printf("Initialization failed!\r\n");
return -1;
}
DS18B20_Writte_Byte(0xCC);
DS18B20_Writte_Byte(0x44);
return 0;
}
uint8_t DS18B20_SampleData(float *temperature)
{
uint8_t Byte[2];
uint8_t sign;
uint16_t temp;
if(!temperature)
return -1;
if(DS18B20_Start_Convet())
{
printf("Convet Temperature failed!\r\n");
return -2;
}
if(DS18B20_Reset())
{
printf("Initialization failed!\r\n");
return -3;
}
DS18B20_Writte_Byte(0xCC);
DS18B20_Writte_Byte(0xBE);
Byte[0] = DS18B20_Read_Byte();
Byte[1] = DS18B20_Read_Byte();
printf("Byte[0]:%d\r\n", Byte[0]);
printf("Byte[1]:%d\r\n", Byte[1]);
if(Byte[1] > 7)
{
sign = 0;
temp = ~(Byte[1]<<8 | Byte[0]) + 1;
}
else
{
sign = 1;
temp = Byte[1]<<8 | Byte[0];
}
*temperature = (temp>>4) + (temp & 0x0F) * 0.0625;
if(!sign)
{
*temperature = - *temperature;
}
return 0;
}
main.c
while(1)
{
if(DS18B20_SampleData(&temperature) < 0)
{
printf("ERROR:DS18B20 Sample Data failure\r\n");
}
else
{
printf("DS18B20 Sample Temperature:%.3f\r\n", temperature);
}
HAL_Delay(3000);
}
个人总结
在写之前借鉴了一下别人的代码,然后自己看datasheet自己写了代码,出现了不少错,其中有一个隐藏而致命的,让我找了很久,就是写一个字节最后的移位这,byte >>= 1;和读一个字节Byte |= (1 << i);这,少写了一个等号,也算是一个小坑吧,另外在写驱动的时候,一定先看懂数据手册的时序图,和一些对存储器的操作。
|