STM32的DS18B20單總線上掛載多路採集
DS18B20中文資料手冊及程式程式碼下載連結:
https://pan.baidu.com/s/1IHSgMN9AQzW3L7jO3_PKlA
提取碼:yhki
DS18B20單總線上多路採集的關鍵在於讀取ROM指令,通過ROM指令讀取指定的DS18B20溫度。
64 位(激)光刻只讀儲存器
每隻DS18B20 都有一個唯一儲存在ROM 中的64 位編碼。最前面8 位是單線系列編碼:28h。接著的48 位是一個唯一的序列號。最後8 位是以上56 位的CRC 編碼。CRC的詳細解釋見CRC 發生器節。64位ROM和ROM操作控制區允許DS18B20作為單匯流排器件並按照詳述於單匯流排系統節的單匯流排協議工作。
ROM 指令
一旦匯流排控制器探測到一個存在脈衝,它就發出一條ROM指令。如果總線上掛有多隻DS18B20,這些指令將基於器件獨有的64 位ROM 片序列碼使得匯流排控制器選出特定要進行操作的器件。這些指令同樣也可以使匯流排控制器識別有多少隻,什麼型號的器件掛在總線上,同樣,它們也可以識別哪些器件已經符合報警條件。ROM指令有5條,都是8 位長度。匯流排控制器在發起一條DS18B20功能指令之前必須先發出一條ROM指令。ROM指令操作流程圖見圖11。
Search ROM [F0h] (搜尋ROM 指令)
當系統上電初始化的時候,匯流排控制器必須通過識別總線上所有ROM片序列碼去得到從機的數目和型號。匯流排控制器通過搜尋ROM指令多次迴圈搜尋ROM編碼,以確認所有從機器件。如果總線上只有一隻從機,那麼可以用較為簡單的讀取ROM 指令(見下文)代替搜尋ROM 指令。
READ ROM [33h] (讀取ROM 指令)
只有在總線上存在單隻DS18B20的時候才能使用這條命令。該命令允許匯流排控制器在不使用搜索ROM 指令的情況下讀取從機的64 位片序列碼。如果總線上有不止一隻從機,當所有從機試圖同時傳送訊號時就會發生資料衝突。
MATH ROM [55h] (匹配ROM 指令)
匹配ROM 指令,後跟64 位ROM 編碼序列,讓匯流排控制器在多點總線上定位一隻特定的DS18B20。只有和64 位ROM 片序列碼完全匹配的DS18B20 才能響應隨後的儲存器操作指令;所有和64位ROM片序列碼不匹配的從機都將等待復位脈衝。
以下是STM32的DS18B20多路採集,時序程式碼和普通讀單個的程式碼一致,添加了讀取ID和讀取多個DS18B20的程式。
核心程式碼如下:
DS18B20.c檔案程式碼:
#include "DS18B20.h"
#include "Delay.h"
#include "stdio.h" // printf用
#define DS18B20_GPIO_NUM GPIO_Pin_5
#define DS18B20_GPIO_X GPIOC
#define RCC_APB2Periph_DS18B20_GPIO_X RCC_APB2Periph_GPIOC
#define DS18B20_DQ_OUT_Low GPIO_ResetBits(DS18B20_GPIO_X,DS18B20_GPIO_NUM)
#define DS18B20_DQ_OUT_High GPIO_SetBits(DS18B20_GPIO_X,DS18B20_GPIO_NUM)
#define DS18B20_DQ_IN GPIO_ReadInputDataBit(DS18B20_GPIO_X,DS18B20_GPIO_NUM)
#define MaxSensorNum 8
unsigned char DS18B20_ID[MaxSensorNum][8]; // 存檢測到的感測器DS18B20_ID的陣列,前面的維數代表單根線感測器數量上限
unsigned char DS18B20_SensorNum; // 檢測到的感測器數量(從1開始,例如顯示1代表1個,8代表8個)
// 配置DS18B20用到的I/O口
void DS18B20_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_DS18B20_GPIO_X, ENABLE);
GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);
GPIO_SetBits(DS18B20_GPIO_X, DS18B20_GPIO_NUM);
}
// 引腳輸入
void DS18B20_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);
}
// 引腳輸出
void DS18B20_Mode_Out(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);
}
// 復位,主機給從機發送復位脈衝
void DS18B20_Rst(void)
{
DS18B20_Mode_Out();
DS18B20_DQ_OUT_Low; // 產生至少480us的低電平復位訊號
Delay_us(480);
DS18B20_DQ_OUT_High; // 在產生復位訊號後,需將匯流排拉高
Delay_us(15);
}
// 檢測從機給主機返回的應答脈衝。從機接收到主機的復位訊號後,會在15~60us後給主機發一個應答脈衝
u8 DS18B20_Answer_Check(void)
{
u8 delay = 0;
DS18B20_Mode_IPU(); // 主機設定為上拉輸入
// 等待應答脈衝(一個60~240us的低電平訊號 )的到來
// 如果100us內,沒有應答脈衝,退出函式,注意:從機接收到主機的復位訊號後,會在15~60us後給主機發一個存在脈衝
while (DS18B20_DQ_IN&&delay < 100)
{
delay++;
Delay_us(1);
}
// 經過100us後,如果沒有應答脈衝,退出函式
if (delay >= 100)//Hu200
return 1;
else
delay = 0;
// 有應答脈衝,且存在時間不超過240us
while (!DS18B20_DQ_IN&&delay < 240)
{
delay++;
Delay_us(1);
}
if (delay >= 240)
return 1;
return 0;
}
// 從DS18B20讀取1個位
u8 DS18B20_Read_Bit(void)
{
u8 data;
DS18B20_Mode_Out();
DS18B20_DQ_OUT_Low; // 讀時間的起始:必須由主機產生 >1us <15us 的低電平訊號
Delay_us(2);
DS18B20_DQ_OUT_High;
Delay_us(12);
DS18B20_Mode_IPU();// 設定成輸入,釋放匯流排,由外部上拉電阻將匯流排拉高
if (DS18B20_DQ_IN)
data = 1;
else
data = 0;
Delay_us(50);
return data;
}
// 從DS18B20讀取2個位
u8 DS18B20_Read_2Bit(void)//讀二位 子程式
{
u8 i;
u8 dat = 0;
for (i = 2; i > 0; i--)
{
dat = dat << 1;
DS18B20_Mode_Out();
DS18B20_DQ_OUT_Low;
Delay_us(2);
DS18B20_DQ_OUT_High;
DS18B20_Mode_IPU();
Delay_us(12);
if (DS18B20_DQ_IN) dat |= 0x01;
Delay_us(50);
}
return dat;
}
// 從DS18B20讀取1個位元組
u8 DS18B20_Read_Byte(void) // read one byte
{
u8 i, j, dat;
dat = 0;
for (i = 0; i < 8; i++)
{
j = DS18B20_Read_Bit();
dat = (dat) | (j << i);
}
return dat;
}
// 寫1位到DS18B20
void DS18B20_Write_Bit(u8 dat)
{
DS18B20_Mode_Out();
if (dat)
{
DS18B20_DQ_OUT_Low;// Write 1
Delay_us(2);
DS18B20_DQ_OUT_High;
Delay_us(60);
}
else
{
DS18B20_DQ_OUT_Low;// Write 0
Delay_us(60);
DS18B20_DQ_OUT_High;
Delay_us(2);
}
}
// 寫1位元組到DS18B20
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_Mode_Out();
for (j = 1; j <= 8; j++)
{
testb = dat & 0x01;
dat = dat >> 1;
if (testb)
{
DS18B20_DQ_OUT_Low;// 寫1
Delay_us(10);
DS18B20_DQ_OUT_High;
Delay_us(50);
}
else
{
DS18B20_DQ_OUT_Low;// 寫0
Delay_us(60);
DS18B20_DQ_OUT_High;// 釋放匯流排
Delay_us(2);
}
}
}
//初始化DS18B20的IO口,同時檢測DS的存在
u8 DS18B20_Init(void)
{
DS18B20_GPIO_Config();
DS18B20_Rst();
return DS18B20_Answer_Check();
}
// 從ds18b20得到溫度值,精度:0.1C,返回溫度值(-550~1250),Temperature1返回浮點實際溫度
float DS18B20_Get_Temp(u8 i)
{
//u8 flag;
u8 j;//匹配的位元組
u8 TL, TH;
short Temperature;
float Temperature1;
DS18B20_Rst();
DS18B20_Answer_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0x44);// convert
DS18B20_Rst();
DS18B20_Answer_Check();
// DS18B20_Write_Byte(0xcc);// skip rom
//匹配ID,i為形參
DS18B20_Write_Byte(0x55);
for (j = 0; j < 8; j++)
{
DS18B20_Write_Byte(DS18B20_ID[i][j]);
}
DS18B20_Write_Byte(0xbe);// convert
TL = DS18B20_Read_Byte(); // LSB
TH = DS18B20_Read_Byte(); // MSB
if (TH & 0xfc)
{
//flag=1;
Temperature = (TH << 8) | TL;
Temperature1 = (~Temperature) + 1;
Temperature1 *= 0.0625;
}
else
{
//flag=0;
Temperature1 = ((TH << 8) | TL)*0.0625;
}
return Temperature1;
}
// 自動搜尋ROM
void DS18B20_Search_Rom(void)
{
u8 k, l, chongtuwei, m, n, num;
u8 zhan[5];
u8 ss[64];
u8 tempp;
l = 0;
num = 0;
do
{
DS18B20_Rst(); //注意:復位的延時不夠
Delay_us(480); //480、720
DS18B20_Write_Byte(0xf0);
for (m = 0; m < 8; m++)
{
u8 s = 0;
for (n = 0; n < 8; n++)
{
k = DS18B20_Read_2Bit();//讀兩位資料
k = k & 0x03;
s >>= 1;
if (k == 0x01)//01讀到的資料為0 寫0 此位為0的器件響應
{
DS18B20_Write_Bit(0);
ss[(m * 8 + n)] = 0;
}
else if (k == 0x02)//讀到的資料為1 寫1 此位為1的器件響應
{
s = s | 0x80;
DS18B20_Write_Bit(1);
ss[(m * 8 + n)] = 1;
}
else if (k == 0x00)//讀到的資料為00 有衝突位 判斷衝突位
{
//如果衝突位大於棧頂寫0 小於棧頂寫以前資料 等於棧頂寫1
chongtuwei = m * 8 + n + 1;
if (chongtuwei > zhan[l])
{
DS18B20_Write_Bit(0);
ss[(m * 8 + n)] = 0;
zhan[++l] = chongtuwei;
}
else if (chongtuwei < zhan[l])
{
s = s | ((ss[(m * 8 + n)] & 0x01) << 7);
DS18B20_Write_Bit(ss[(m * 8 + n)]);
}
else if (chongtuwei == zhan[l])
{
s = s | 0x80;
DS18B20_Write_Bit(1);
ss[(m * 8 + n)] = 1;
l = l - 1;
}
}
else
{
//沒有搜尋到
}
}
tempp = s;
DS18B20_ID[num][m] = tempp; // 儲存搜尋到的ID
}
num = num + 1;// 儲存搜尋到的個數
} while (zhan[l] != 0 && (num < MaxSensorNum));
DS18B20_SensorNum = num;
//printf("DS18B20_SensorNum=%d\r\n",DS18B20_SensorNum);
}
DS18B20.h檔案程式碼:
#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f10x.h"
u8 DS18B20_Init(void);
u8 DS18B20_Read_Byte(void);
u8 DS18B20_Read_Bit(void);
u8 DS18B20_Answer_Check(void);
void DS18B20_GPIO_Config(void);
void DS18B20_Mode_IPU(void);
void DS18B20_Mode_Out(void);
void DS18B20_Rst(void);
void DS18B20_Search_Rom(void);
void DS18B20_Write_Byte(u8 dat);
float DS18B20_Get_Temp(u8 i);
#endif
main.c檔案程式碼:
#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"//strlen、memset用到
#include "USART.h"
#include "Delay.h"
#include "DS18B20.h"
extern unsigned char DS18B20_ID[8][8];//檢測到的感測器ID存陣列
extern unsigned char DS18B20_SensorNum;
int main(void)
{
u8 num=0;
USART1_init(9600);
while(DS18B20_Init())//初始化DS18B20,兼檢測18B20
{
printf("DS18B20 Check Failed!\r\n");
}
printf("DS18B20 Ready!\r\n");
while(1)
{
DS18B20_Search_Rom();
printf("DS18B20_SensorNum:%d\r\n",DS18B20_SensorNum);
for(num=0;num<DS18B20_SensorNum;num++)
{
printf("ID:%02x%02x%02x%02x%02x%02x%02x%02x TM:%.2f\r\n",DS18B20_ID[num][0],DS18B20_ID[num][1],DS18B20_ID[num][2],DS18B20_ID[num][3],DS18B20_ID[num][4],DS18B20_ID[num][5],DS18B20_ID[num][6],DS18B20_ID[num][7],DS18B20_Get_Temp(num));
}
printf("\r\n");
Delay_s(2);
}
}
執行結果如圖: