為了加深對于底層驅動開發的認識和了解,樓主決定自撸常見子產品,傳感器的驅動程式。-------立貼為證。
DS18B20是一款單總線可程式設計分辨率的數字溫度計,詳細内容可見中英文datasheet,筆者不在贅述。
很早就接觸到的溫度傳感器,也相信每一個曾學習過嵌入式開發的人都用過,筆者在STM32F4上自碼DS18B20驅動,有些小小心得:
1.初始化時序要注意,筆者親測,在MCU控制單總線為低電平240us即可(資料手冊上要求至少480us)釋放總線,等待60us後即可檢測到到DS18B20傳回的拉低單總線信号,此處,需注意至少應在此等待120us,否則可能會導緻溫度傳感器無法正常工作。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DN1cTMzQTNxITNxMDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
2.初學者需注意時序,對于DS18B20的操作都必需經過三步:初始化,ROM指令(多為跳過指令0xCC),DS18B20功能指令。再次強調對其的每一個操作必須經過這三步,可閱讀code加深了解。
3.在讀取DS18B20時,注意順序,DS18B20先發送低位,在位元組讀取時應當注意。
4.初學者應嘗試實作對于DS18B20内部ROM的8位系列号(28H),和48位唯一序列号進行讀取,以及修改溫度傳感器内部EEPROM的過溫、低溫報警值。(可參考筆者code)
廢話不多說,上碼:
#include <ds18b20.h>
#include "delay.h"
#include "usart.h"
//ds18b20初始化
void init_ds18b20( void )
{
init_onewire_out();
GPIO_ResetBits(GPIOG,GPIO_Pin_9);
delay_us(480);
init_onewire_in();
delay_us(60);
if( !DQ_In)
{
delay_us(120);
}
}
//ds18b20 檢測
void chack_ds18b20( void )
{
init_onewire_out();
GPIO_ResetBits(GPIOG,GPIO_Pin_9);
delay_us(240);
init_onewire_in();
delay_us(60);
if( !DQ_In)
{
delay_us(80);
if( !DQ_In )
printf("檢測到DS18B20!\r\n");
}
}
//設定為主裝置寫總線,從裝置讀總線
void init_onewire_out( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
}
//設定為主裝置讀取總線,從裝置寫總線
void init_onewire_in( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通輸入模式
// GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
}
void ds18b20_write_byte( u8 data )
{
u8 i;
u8 j=0;
init_onewire_out();
for(i=0;i<8;i++)
{
j=data & 0x01;
if(j)
{
DQ_Out=0; //寫1
delay_us(15);
DQ_Out=1;
delay_us(60);
}
else
{
DQ_Out=0; //寫0
delay_us(60);
DQ_Out=1;
delay_us(1);
}
data = data>>1;
}
}
//讀取DS18B20 的一位
u8 ds18b20_read_bit( void )
{
u8 bit;
init_onewire_out();
DQ_Out=0;
delay_us(2);
DQ_Out=1;
init_onewire_in();
delay_us(12);
if(DQ_In)
bit=1;
else
bit=0;
delay_us(50);
return bit;
}
//讀ds18b20的位元組
u8 ds18b20_read_byte ( void )
{
u8 data=0;
u8 i;
u8 j=0;
for(i=0;i<8;i++)
{
j=ds18b20_read_bit();
if(j) //注意順序即可,ds18b20先發送地位到總線上
j=j<<i;
data |=j;
}
return data;
}
//擷取ds18b20的系列碼和48位唯一序列号
void ds18b20_read_rom_number()
{
u32 number=0;
u8 data,i,serial_num,ds18b20_crc;
init_ds18b20();
ds18b20_write_byte(0x33);
serial_num = ds18b20_read_byte();
for(i=0;i<6;i++)
{
data = ds18b20_read_byte();
number |= data;
number = number<<8;
}
ds18b20_crc = ds18b20_read_byte();
printf("系列号是:%d\r\n",serial_num);
printf("序列号是:%d\r\n",number);
printf("CRC校驗為:%d\r\n",ds18b20_crc);
}
//開啟ds18b20溫度轉換
void tem_chage( void )
{
init_ds18b20();
ds18b20_write_byte(0xcc); //忽略rom指令
ds18b20_write_byte(0x44); //開啟轉換
}
short get_temp( void )
{
int temp=0;
u8 i,TH,TL;
short tem;
tem_chage();
delay_us(10);
init_ds18b20();
ds18b20_write_byte(0xcc); //忽略rom指令
ds18b20_write_byte(0xbe); //讀取溫度轉換值
TL=ds18b20_read_byte();
TH=ds18b20_read_byte();
if(TH > 7) //通過判讀存儲器的高五位的0,1來判斷溫度的正負,
{
temp = 0; //為負
TH =~TH;
TL =~TL;
}
else
temp = 1; //為正
tem = TH;
tem =tem<<8;
tem =tem+TL;
tem = (double)tem * 0.625;
if(temp)
return tem;
else
return -tem;
}
void ds18b20_return_TH_TL_CONF( void )
{
char data,data_TH,data_TL,CONF;
init_ds18b20();
ds18b20_write_byte(0xcc); //忽略rom指令
ds18b20_write_byte(0xbe); //讀取溫度轉換值
data = ds18b20_read_byte();
data = ds18b20_read_byte();
data_TH = ds18b20_read_byte();
data_TL = ds18b20_read_byte();
CONF =ds18b20_read_byte();
printf("過溫報警的溫度為:%d℃\r\n",data_TH);
printf("低溫報警的溫度為:%d℃\r\n",-(data_TL-128));
CONF &=0x60 ;
CONF =CONF>>5;
switch (CONF) {
case 0:
printf("ds18b20的測量精度為9位,精度為0.5℃\r\n");
break;
case 1:
printf("ds18b20的測量精度為10位,精度為0.25℃\r\n");
break;
case 2:
printf("ds18b20的測量精度為11位,精度為0.125℃\r\n");
break;
case 3:
printf("ds18b20的測量精度為12位,精度為0.0625℃\r\n");
break;
default:
printf("error!!\r\n");
break;
}
}
//設定溫度報警值和配置精度,TH過溫報警值(TH>0),TL低溫報警值(TL為負數 ),mode配置模式0,1,2,3
//mode=0 精度為9位 00011111 dat=31
//mode=1 精度為10位 00111111 dat=63
//mode=2 精度為11位 01011111 dat=95
//mode=3 精度為12位 01111111 dat =127
void ds18b20_write_TH_TL_CONF(u8 TH,u8 TL,u8 mode)
{
u8 dat;
switch (mode){
case 0:
dat=31;
break;
case 1:
dat=63;
break;
case 2:
dat=95;
break;
case 3:
dat=127;
break;
default:
printf("mode error!!\r\n");
dat=127;
break;
}
TL=TL+128;
init_ds18b20();
ds18b20_write_byte(0xcc); //忽略rom指令
ds18b20_write_byte(0x4e); //寫入暫存寄存器 ,過溫和低溫報警值
ds18b20_write_byte(TH); //寫入20°為過溫報警值
ds18b20_write_byte(TL); //寫入-20°為低溫報警值
ds18b20_write_byte(dat); //寫入精度
init_ds18b20();
ds18b20_write_byte(0xcc); //忽略rom指令
ds18b20_write_byte(0x48); //将寫入的暫存寄存器拷入EEPROM
}
void ds18b20_chack_self( void )
{
chack_ds18b20();
ds18b20_read_rom_number();
ds18b20_return_TH_TL_CONF();
}
筆者用序列槽助手得到結果如下: