接通信協定之一線協定--DHT11_7 un的部落格-CSDN部落格
ds18b20簡介
一線協定的溫度傳感器,工作在3~5.5v電壓範圍,測量溫度範圍為-55~125℃精度為±0.5℃。ds18b20采用多種封裝形式進而設計靈活,友善。與傳統的熱敏電阻等測溫元件相比,他是一種新型的體積小,适用電壓寬,接口簡單的數字化溫度傳感器。
每個ds18b20晶片在出廠時,都固化燒錄了一個唯一的64位産品序列号在其ROM中,它可以看作是ds19b20的位址序列碼。64位ROM的排列是:前8位是産品家族碼。接着48位時ds18b20的序列号,最後8位是前面56位的循環備援檢驗碼(CRC=X8+X5+X4+X1)。ROM作用是使每一個ds18b20都各不相同,這樣就實作一個總線挂接多個ds18b20。
ds18b20一線協定
所有的單總線器件要求采用嚴格的信号時序,以保證資料的完整性。ds18b20共有6種信号類型:複位脈沖、應答信号、寫0/1,讀0/1。所有的這些信号,除了應答脈沖以外,都是有主機發出同步信号,并且發送所有指令和資料都是位元組的地位在前LSB
複位脈沖和應答信号
單總線上所有的通信都是初始化序列開始。主機輸出低電平,保持低電平時間至少480us,以産生複位脈沖。接着主機釋放總線,4.7k的上拉電阻将單總線拉高,延時15~60us并進入接收模式(Rx)。接着ds18b20拉低總線60~240us,以産生低電平應答脈沖信号,若為低電平,在岩石480us。
寫時序
寫時序包括寫0時序和寫1時序。所有寫時序至少需要60us,且在2次獨立的寫時序之間至少需要1us的恢複時間,兩種時序均起始于主機拉低總線。
寫0時序:主機輸出低電平,延時60us,然後釋放總線,延時2us;
寫1時序:主機輸出低電平,延時2us,然後釋放總線,延時60us;
讀時序
單總線器件僅在主機發出讀時序,才向主機傳輸資料,是以,在主機發送讀資料指令,必須立刻馬上昌盛讀時序,以便從主機能夠傳送資料。所有讀時序至少需要60us,且在2次獨立的讀時序之間至少需要1us的回複恢複時間。
當總線控制器把資料線從高電平拉低到低電平時,讀時序開始,資料線必須至少保持1us,然後總線被釋放。ds18b20通過拉高拉低總線上來傳輸“1”或“0”。當傳輸邏輯"0"結束後,總線被釋放,通過上拉電阻回到上升沿狀态,從ds18b20輸出的資料在讀時序的下降沿出現15us内有效。是以,總線控制器讀時序開始後必須停止把I/O口驅動為低點15us,讀取I/O口狀态。
DS18B20工作流程
DS18B20工作過程中的協定為:初始化,ROM操作指令,存儲器操作指令,處理資料。具體如下
1、初始化
單總線上的所有通信都是以初始化序列開始,Master發出初始化信号後等待從裝置的應答信号,已确定從裝置是否存在并能正常工作。
2、ROM操作指令
總線主機檢測到ds18b20的存在後,便可以發出ROM操作指令之一,這些指令如下表所示。一般我們不關心ROM中的16位産品序列号,通常會發送0xCC跳過ROM的相關操作。
3、存儲器操作指令
ROM指令操作完成之後,接下來可以發送相應的高速暫存存儲器操作指令,這些指令如下表所示,其中0x44指令将通知DS18B20溫度傳感器開始采樣,而0xBE指令則開始讀出DS18B20的采樣值。
4、資料處理
DS18B20的高速暫存存儲器9個位元組組成。當溫度轉換指令(0x44)釋出後,經轉換所得的溫度值以二位元組補碼形式存放在高速暫存存儲器前兩個位元組。
接着單片機可以發送讀暫存存儲器指令(0xBH)讀出存儲器裡的值,存儲器裡的9個位元組的存儲結構如下圖所示
如果我們隻關心采樣溫度值的話,則隻需要讀前兩個位元組即可,其中Byte[0]為溫度值的低位元組,而Byte[1]為溫度的高位元組,這樣16位資料的格式如下
其中BIT[3:0]為溫度值的小部分,而BIT[10:4]為溫度值的整數部分,BIT[15:11]則為符号位,如果0則溫度值為正值,如果為1則溫度值為負值。
代碼實作
建立編寫DS18B20的驅動源檔案ds18b20.c
#include "tim.h"
#include "gpio.h"
#include "main.h"
typedef struct w1_gpio_s
{
GPIO_TypeDef *group;
uint16_t pin;
} w1_gpio_t;
static w1_gpio_t W1Dat = /* IO pin connected to PA5 */
{
.group = GPIOA,
.pin = GPIO_PIN_5,
};
#define W1DQ_Input() \
{ \
GPIO_InitTypeDef GPIO_InitStruct = {0}; \
GPIO_InitStruct.Pin = W1Dat.pin; \
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; \
GPIO_InitStruct.Pull = GPIO_PULLUP; \
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \
HAL_GPIO_Init(W1Dat.group, &GPIO_InitStruct); \
}
#define W1DQ_Output() \
{ \
GPIO_InitTypeDef GPIO_InitStruct = {0}; \
GPIO_InitStruct.Pin = W1Dat.pin; \
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; \
GPIO_InitStruct.Pull = GPIO_NOPULL; \
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \
HAL_GPIO_Init(W1Dat.group, &GPIO_InitStruct); \
}
#define W1DQ_Write(x) HAL_GPIO_WritePin(W1Dat.group, W1Dat.pin, \
(x==1)?GPIO_PIN_SET:GPIO_PIN_RESET)
#define W1DQ_Read() HAL_GPIO_ReadPin(W1Dat.group, W1Dat.pin)
/* Master issues reset pulse and DS18B20s respond with presence pulse */
uint8_t DS18B20_Reset(void)
{
uint8_t rv = 0;
uint8_t retry;
/* Setup W1 DQ pin as output and high level*/
W1DQ_Output();
W1DQ_Write(1);
delay_us(2);
/* Reset pulse by pulling the DQ pin low >=480us */
W1DQ_Write(0);
delay_us(480);
/* Master releases bus to high. When DS18B20 detects this rising edge, it waits 15µs to 60µs */
W1DQ_Write(1);
delay_us(60);
/* Then DS18B20 transmits a presence pulse by pulling the W1 bus low for 60µs to 240µs */
while( W1DQ_Read() && retry <240)
{
retry++;
delay_us(1);
}
if(retry >= 240)
rv = 1;
delay_us(240);
/* Master Rx time must >= 480us */
W1DQ_Output();
W1DQ_Write(1);
delay_us(240);
return rv;
}
void DS18B20_WriteByte(uint8_t byte)
{
uint8_t i = 0;
W1DQ_Output();
for(i=0; i<8; i++)
{
/* Write 1: pull low <= 15us, Write 0: pull low 15~60us*/
W1DQ_Write(0);
delay_us(10);
/* DS18B20 bit sent by LSB (lower bit first) */
if( byte & 0x1 )
W1DQ_Write(1);
else
W1DQ_Write(0);
/* Write 1/0 slot both >= 60us, hold the level for 60us */
delay_us(60);
/* Release W1 bus to high */
W1DQ_Write(1);
delay_us(2);
/* Prepare for next bit */
byte >>= 1;
}
}
uint8_t DS18B20_ReadByte(void)
{
uint8_t i = 0;
uint8_t byte = 0;
for(i=0; i<8; i++)
{
/* Read time slot is initiated by master pulling the W1 bus
* low for minimum of 1µs and then releasing the bus */
W1DQ_Output();
W1DQ_Write(0);
delay_us(2);
W1DQ_Write(1);
delay_us(2);
/* After master initiates read time slot, DS18B20 will begin
* transmitting a 1 or 0 on bus */
W1DQ_Input();
/* DS18B20 bit sent by LSB (lower bit first) */
if( W1DQ_Read() )
{
byte |= 1<<i;
}
/* Read slot for >= 60us */
delay_us(60);
/* Release W1 bus to high */
W1DQ_Output();
W1DQ_Write(1);
delay_us(2);
}
return byte;
}
static inline int DS18B20_Start_Convert(void)
{
/* Master issues reset pulse and DS18B20s respond with presence pulse */
if( 0 != DS18B20_Reset() )
return -1;
/* Master issues Skip ROM command */
DS18B20_WriteByte(0xCC);
/* Master issues Convert T command. */
DS18B20_WriteByte(0x44);
return 0;
}
static inline int DS18B20_Start_Read(uint8_t *buf, int bytes)
{
/* Master issues reset pulse and DS18B20s respond with presence pulse */
if( 0 != DS18B20_Reset() )
return -1;
/* Master issues Skip ROM command */
DS18B20_WriteByte(0xCC);
/* Master issues Read Scratchpad command. */
DS18B20_WriteByte(0xBE);
buf[0] = DS18B20_ReadByte(); /* Temperature LSB */
buf[1] = DS18B20_ReadByte(); /* Temperature MSB */
/*Don't care followed 7 bytes data */
return 0;
}
int DS18B20_SampleData(float *temperature)
{
uint8_t byte[2];
uint8_t sign;
uint16_t temp;
if( !temperature )
return -1;
if( 0 != DS18B20_Start_Convert() )
return -2;
if( 0 != DS18B20_Start_Read(byte, 2) )
return -3;
/* Temperature byte[0] is LSB, byte[1] is MSB, total 16 bit:
* Byte[0]: bit[3:0]: decimal bits, bit[7:4]: integer bits
* bYTE[1]: bit[2:0]: integer bits, bit[7:3]: sign bits
*/
if( byte[1]> 0x7 ) /* bit[7:3] is 1 */
{
temp = ~(byte[1]<<8|byte[0]) + 1; //補碼
sign=0; //溫度為負
}
else
{
sign=1;//溫度為正
temp = byte[1]<<8 | byte[0];
}
/* byte[1]的低三位和byte[0]的高四位組成溫度值的整數部分,
* 而byte[0]的低四位為小數精度部分,且精度系數為0.0625 */
*temperature = (temp>>4) + (temp&0xF)*0.0625 ;
if( !sign )
{
*temperature = -*temperature;
}
return 0;
}
寫ds18b20的頭檔案 ds18b20.h
#ifndef INC_DS18B20_H_
#define INC_DS18B20_H_
extern int DS18B20_SampleData(float *temperature);
#endif /* INC_DS18B20_H_ */