在實(shí)際情況中很多傳感器并不會(huì)用到很復(fù)雜的通信協(xié)議,反而簡(jiǎn)單的數(shù)據(jù)傳輸機(jī)制能夠大大節(jié)省成本且滿足實(shí)際需要。紅外傳感器和DS18B20是典型的單總線傳感器,本期通過(guò)這兩類傳感器的工作原理和操作實(shí)例來(lái)認(rèn)識(shí)單總線。
單總線能夠極大程度節(jié)約管腳資源,只用一根管腳即可進(jìn)行通訊。這種通訊方式在傳感器中運(yùn)用較多,傳感器采集到的數(shù)據(jù)通過(guò)一根數(shù)據(jù)線直接傳到單片機(jī)。
先導(dǎo)知識(shí):
紅外線經(jīng)常使用在紅外遙控中,可以通過(guò)非接觸的方式來(lái)控制家中的一些電氣設(shè)備,既然可以通過(guò)遙控控制設(shè)備,中間一定存在數(shù)據(jù)傳輸。遙控中的發(fā)射器將內(nèi)部按鍵的數(shù)值和命令通過(guò)紅外線發(fā)射出去,電器中有紅外接收器,接收器接收到后可以解析命令進(jìn)行不同的動(dòng)作。
紅外線是光的一種,光的本質(zhì)是電磁波,其傳播本質(zhì)上是一種粒子振動(dòng)。廣義上,光是指所有的電磁波譜。狹義上的光是人類眼睛可以看見(jiàn)的一種電磁波,也稱可見(jiàn)光。
1.光的波長(zhǎng):是指波在一個(gè)振動(dòng)周期內(nèi)傳播的距離。光的波長(zhǎng)由光的頻率以及傳播的介質(zhì)決定,光通過(guò)不同介質(zhì)的時(shí)候,頻率不變而波長(zhǎng)發(fā)生改變。
2.光的顏色:是由它的波長(zhǎng)來(lái)決定的,各種顏色有各自的波長(zhǎng),人的眼睛能看到的可見(jiàn)光按波長(zhǎng)從長(zhǎng)到短排列,依次為紅、橙、黃、綠、青、藍(lán)、紫。紅光到紫光,波長(zhǎng)逐漸變小,紅外線是比紅光波長(zhǎng)還長(zhǎng)的非可見(jiàn)光。
高于絕對(duì)零度(-273.15℃)的物質(zhì)都可以產(chǎn)生紅外線?,F(xiàn)代物理學(xué)稱之為熱射線。我們把紅光之外的輻射叫做紅外線(紫光之外是紫外線),人的肉眼不可見(jiàn)。
3.無(wú)線遠(yuǎn)程遙控技術(shù):又稱為遙控技術(shù),是指實(shí)現(xiàn)對(duì)被控目標(biāo)的遙遠(yuǎn)控制,在工業(yè)控制、航空航天、家電領(lǐng)域應(yīng)用廣泛。
4.紅外遙控:是一種無(wú)線、非接觸控制技術(shù),具有抗干擾能力強(qiáng),信息傳輸可靠,功耗低,成本低,易實(shí)現(xiàn)等顯著優(yōu)點(diǎn),被諸多電子設(shè)備特別是家用電器廣泛采用,并越來(lái)越多的應(yīng)用到計(jì)算機(jī)和手機(jī)系統(tǒng)中。
5.紅外通訊:就是通過(guò)紅外線傳輸數(shù)據(jù)。發(fā)射器發(fā)出紅外信號(hào),接收器接收到信號(hào)進(jìn)行解析。
6.紅外遙控器:遙控器是利用一個(gè)紅外發(fā)光二極管,以紅外光為載體來(lái)將按鍵信息傳遞給接收端的設(shè)備。紅外光對(duì)于人眼是不可見(jiàn)的,因此使用紅外遙控器不會(huì)影響人的視覺(jué)(可以打開(kāi)手機(jī)攝像頭,遙控器對(duì)著攝像頭按,可以看到遙控器發(fā)出的紅外光)。
7.信號(hào)調(diào)制:日常生活環(huán)境中有很多紅外光源,太陽(yáng)、蠟燭火光、白熾燈、甚至是我們的身體。這些紅外光源都可能會(huì)對(duì)我們的接收設(shè)備產(chǎn)生干擾,為了屏蔽干擾,只接收有效信息,我們就需要用到調(diào)制。通過(guò)調(diào)制我們可以把指定的數(shù)字信號(hào)轉(zhuǎn)換為特定頻率的紅外光進(jìn)行發(fā)送,調(diào)制載波頻率一般在30khz到60khz之間,大多數(shù)使用的是38kHz。
8.紅外接受器:紅外線接收器是一種可以接收紅外信號(hào)并能獨(dú)立完成從紅外線接收到輸出與TTL電平信號(hào)兼容的器件,體積和普通的塑封三極管差不多,適合于各種紅外線遙控和紅外線數(shù)據(jù)傳輸。
9.信號(hào)解調(diào):解調(diào)就是將模擬信號(hào)(光信號(hào))轉(zhuǎn)換成數(shù)字信號(hào)。紅外接收器接收到外部發(fā)射器傳過(guò)來(lái)的紅外信號(hào)后,會(huì)按照固定的協(xié)議去解析信號(hào),并轉(zhuǎn)換成數(shù)字信號(hào)輸出。
NEC協(xié)議詳解:
NEC協(xié)議是典型的紅外協(xié)議,其實(shí)紅外協(xié)議有很多,如ITT、NRC17(Nokia)、Sharp等,NEC協(xié)議使用的最多的一種。在傳出過(guò)程中主要傳輸8 位地址碼和8 位命令碼,地址碼主要標(biāo)識(shí)遙控可以解析的設(shè)備,不同的遙控器控制不同的設(shè)備,不同發(fā)命令碼對(duì)應(yīng)不同的按鍵;該協(xié)議會(huì)完整發(fā)射兩次地址碼(第一次地址碼,第二次是地址反碼,這里起到了一個(gè)校驗(yàn)的作用,兩次命令碼同理)和命令碼,以提高可靠性;脈沖時(shí)間長(zhǎng)短可調(diào)制;典型的為38KHz 載波頻率;位時(shí)間 1.12ms(表示邏輯0) 或 2.25ms(表示邏輯1)
紅外遙控實(shí)例講解
電路分析:整個(gè)紅外傳感器只使用PG8與MCU進(jìn)行傳輸
實(shí)驗(yàn)?zāi)康模喊聪逻b控按鍵,主機(jī)通過(guò)紅外接收器接收到信號(hào)(模擬信號(hào))并解碼(數(shù)字信號(hào)),識(shí)別出按鍵的命令碼,打印出對(duì)應(yīng)的按鍵符號(hào)
將PG8設(shè)為中斷模式,下降沿和上升沿均可觸發(fā)中斷。第一次檢測(cè)到下降沿后觸發(fā)中斷并檢查是否是下降沿且是否達(dá)到要求的時(shí)長(zhǎng),如果滿足等待第二次上升沿觸發(fā),檢查是否是上升沿且是否達(dá)到要求的時(shí)長(zhǎng)。之后的每一個(gè)波形都會(huì)觸發(fā)兩次中斷檢測(cè)時(shí)間,用來(lái)確定是邏輯1還是邏輯0,連續(xù)檢測(cè)64次
實(shí)驗(yàn)步驟:
1.配置RCC
2.配置PG8為外部中斷模式
3.配置中斷
4.編寫代碼
//main.c
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
/*配置systick為100us中斷一次 */
/*原來(lái)默認(rèn)是1ms觸發(fā)一次中斷,現(xiàn)在要100us中斷一次*/
/*所以只需將計(jì)數(shù)值縮小10倍即可,因此除數(shù)值由1000變?yōu)榱?0000 */
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/10000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
printf("nr");
printf("nr-------------------------------------------------");
printf("nr FS-STM32開(kāi)發(fā)板 IR紅外接收是u按程序");
printf("nr 請(qǐng)將紅外接收頭連接到開(kāi)發(fā)板對(duì)應(yīng)的接口");
printf("nr 然后用紅外遙控進(jìn)行控制,注意串口輸出");
printf("nr-------------------------------------------------");
printf("nr----------------- 協(xié)議如下 ----------------------");
printf("nr 首先是引導(dǎo)碼:開(kāi)始拉低9ms,接著一個(gè)4.5ms的高脈沖");
printf("nr 引導(dǎo)碼的作用是通知接收器準(zhǔn)備接收數(shù)據(jù)");
printf("nr 引導(dǎo)碼之后是4個(gè)字節(jié)的二進(jìn)制碼,其中前兩個(gè)字節(jié)是:");
printf("nr 遙控識(shí)別碼ID,第一個(gè)為正碼,第二個(gè)為反碼,");
printf("nr 后兩字節(jié)是鍵值,第一個(gè)為正碼,第二個(gè)為反碼.");
printf("nr 最后有可能持續(xù)按鍵,上述數(shù)據(jù)發(fā)送完后則發(fā)送");
printf("nr 9ms低,2ms高的脈沖");
printf("nr---------------- 載波為38kHz --------------------");
printf("nr傳輸一個(gè)邏輯1需要2.25ms(560us低電平+1680us高電平)");
printf("nr傳輸一個(gè)邏輯0需要1.125ms(560us低電平+560us高電平)");
printf("nr-------------------------------------------------");
printf("nr----- 本實(shí)驗(yàn)在中斷中檢測(cè)接收IR紅外線數(shù)據(jù) ------nr");
while (1)
{
Remote_Infrared_KeyDeCode();
}
}
void HAL_SYSTICK_Callback(void)
{
if(GlobalTimingDelay100us != 0)
{
GlobalTimingDelay100us--;
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//中斷處理函數(shù),上升沿和下降沿均可觸發(fā)
//中斷檢測(cè)時(shí)間用于確定每一個(gè)脈沖代表的含義
Remote_Infrared_KEY_ISR();
}
//RemoteInfrared.h
#include "stm32f4xx_hal.h"
#defineRemote_Infrared_DAT_INPUT HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8)
typedef struct _Remote_Infrared_data_struct //定義紅外線接收到的數(shù)據(jù)結(jié)構(gòu)體類型
{
uint8_t bKeyCodeNot; //按鍵的ASIIC碼值——反碼
uint8_t bKeyCode; //shift鍵按下標(biāo)志
uint8_t bIDNot; //斷碼標(biāo)志位——反碼
uint8_t bID; //新鍵標(biāo)志位
}Remote_Infrared_data_struct;
typedef union _Remote_Infrared_data_union //定義紅外線接收到的數(shù)據(jù)聯(lián)合體類型
{
Remote_Infrared_data_struct RemoteInfraredDataStruct;
uint32_t uiRemoteInfraredData;
}Remote_Infrared_data_union;
void Remote_Infrared_KEY_ISR(void);
uint8_t Remote_Infrared_KeyDeCode(void);
//RemoteInfrared.c
#include "RemoteInfrared.h"
#define REPEAT_KEY 0xEE
extern __IO uint32_t GlobalTimingDelay100us;
extern __IO uint32_t GlobalTimingDelay100usTx;
__IO uint32_t FlagGotKey = 0;
__IO Remote_Infrared_data_union RemoteInfrareddata;
/************************************************************************
//紅外處理接收
-------------------------協(xié)議--------------------------
開(kāi)始拉低9ms,接著是一個(gè)4.5ms的高脈沖,通知器件開(kāi)始傳輸數(shù)據(jù)了
接著是發(fā)送4個(gè)8位二進(jìn)制碼,第一二個(gè)是遙控器識(shí)別碼(REMOTE_ID),第一個(gè)為
正碼(0),第二個(gè)為反碼(255),接著兩個(gè)數(shù)據(jù)是鍵值,第一個(gè)為正碼,
第二個(gè)為反碼,發(fā)送完后40ms,遙控器再發(fā)送一個(gè)9ms低,2ms高的脈沖,
表示按鍵的次數(shù),出現(xiàn)一次則證明只按下了一次,如果出現(xiàn)多次,則可
以認(rèn)為是持續(xù)按下該鍵
*名稱: Remote_Infrared_KEY_ISR(INT11_vect )
*功能: INT0中斷服務(wù)程序
*參數(shù): 無(wú)
*返回: 無(wú)
*************************************************************************/
// 檢測(cè)脈沖寬度最長(zhǎng)為5ms
const uint32_t TIME_DELAY_6MS = 60;
const uint32_t TIME_DELAY_10MS = 100;
void Remote_Infrared_KEY_ISR(void)
{
static __IO uint8_t bBitCounter = 0; //記錄中斷進(jìn)入的次數(shù)
static __IO uint32_t bKeyCode = 0;
bBitCounter++;
if(bBitCounter == 1) //為1表示第一次進(jìn)來(lái),就需要檢測(cè)波形
{
if(Remote_Infrared_DAT_INPUT)
//如果讀管腳是高電平,值為真,則無(wú)效
//因?yàn)橄冗M(jìn)來(lái)的必須是9ms的低電平
{
bBitCounter = 0;
}
else
{
GlobalTimingDelay100us = TIME_DELAY_10MS;
//將GlobalTimingDelay100us值設(shè)為100,因?yàn)樵撝得?00us減1
//100us觸發(fā)一次中斷,減1,從100減到0剛好10ms
//當(dāng)然正常情況下不會(huì)減到0,因?yàn)?ms左右就會(huì)再次觸發(fā)中斷
}
}
· else if(bBitCounter == 2) // 4.5ms高脈沖
{
if(Remote_Infrared_DAT_INPUT)//高脈沖為真
{
//高脈沖觸發(fā)中斷,先檢查上次GlobalTimingDelay100us剩余值
//理想狀態(tài)下為10,當(dāng)然不可能那么精確
//若以我們給定了一個(gè)范圍,剩余值在這個(gè)范圍就認(rèn)為滿足時(shí)間
if((GlobalTimingDelay100us > 2) && (GlobalTimingDelay100us < 18))
{
//之后就可以GlobalTimingDelay100us賦新值60檢查高脈沖時(shí)間
GlobalTimingDelay100us = TIME_DELAY_6MS;
}
else
{
bBitCounter = 0;
//printf(".");
}
}
else
{
bBitCounter = 0;
}
}
else if(bBitCounter == 3) // 4.5ms的高脈沖
{
if(Remote_Infrared_DAT_INPUT)
{
bBitCounter = 0;
}
else
{ //正常情況下GlobalTimingDelay100us值剩余15
//給定范圍是5-20,則滿足4.5ms的高脈沖
if((GlobalTimingDelay100us > 5) && (GlobalTimingDelay100us < 20)) //起始碼 4.5ms
{
//到這里,引導(dǎo)碼(起始碼)滿足條件,正確
GlobalTimingDelay100us = TIME_DELAY_6MS;
}
else if((GlobalTimingDelay100us > 32) && (GlobalTimingDelay100us < 46)) //重復(fù)碼 2.25ms
{
//范圍在32-46,則高電平滿足了重復(fù)碼的時(shí)間
bBitCounter = 0;//自動(dòng)持續(xù)動(dòng)作,進(jìn)入次數(shù)變?yōu)?
RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
//RemoteInfrareddata.uiRemoteInfraredData = REPEAT_KEY;
bBitCounter = 0;
FlagGotKey = 1;
}
else
{
bBitCounter = 0; //既不是起始碼也不是重復(fù)碼,回5到初始值
//printf("%d&", GlobalTimingDelay100us);
}
}
}
else if(bBitCounter > 3 && bBitCounter < 68) //接收32位數(shù)據(jù),共檢測(cè)64次
{
//在高電平中斷中檢測(cè)低電平是否合格,低電平中斷中檢測(cè)高電平是否合格
if(Remote_Infrared_DAT_INPUT)//檢測(cè)數(shù)據(jù)脈沖低電平的時(shí)間
//不管邏輯0還是邏輯1,前半段脈沖都是560us低電平
{
if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58))
{
GlobalTimingDelay100us = TIME_DELAY_6MS;//低電平合格,為GlobalTimingDelay100us重新賦值
}
else
{
bBitCounter = 0;
//printf("#");
}
}
else //低電平中斷中檢測(cè)高電平
{
if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58)) // '0' 0.56ms×óóò
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
bKeyCode < <= 1; // MSB First
bKeyCode += 0x00;
}
else if((GlobalTimingDelay100us > 40) && (GlobalTimingDelay100us < 48)) //'1' 1.685ms×óóò
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
bKeyCode < <= 1; // MSB First
bKeyCode += 0x01;
}
else
{
bBitCounter = 0;
}
}
if(bBitCounter == 67)
{
RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
bBitCounter = 0;
FlagGotKey = 1;
//printf("KeyCode = 0x%X", bKeyCode);
}
}
else
{
bBitCounter = 0;
//printf("KeyCode = 0x%X", bKeyCode);
}
}
/************************************************************************
*名稱: unsigned char Remote_Infrared_KeyDeCode(unsigned char bKeyCode)
*功能: PS2鍵盤解碼程序
*參數(shù): bKeyCode 鍵盤碼
*返回: 按鍵的ASIIC碼
************************************************************************/
uint8_t Remote_Infrared_KeyDeCode(void)
{
//uint8_t Key = 0xFF;
if (FlagGotKey == 1)//通碼,為1時(shí)表示有按鍵按下
{
FlagGotKey = 0;//還原為0以便下次判斷
//檢查地址碼和命令碼的反碼取反后是否與原來(lái)相同
if((RemoteInfrareddata.RemoteInfraredDataStruct.bID == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bIDNot)
&& (RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCodeNot))
{
printf("nr IR Receive KeyCode = 0x%02X, ", RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode);
switch(RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode)
{
case 0:
printf("ERROR ");
break;
case 0xA2:
printf("CH- ");
break;
case 0X62:
printf("CH ");
break;
case 0XE2:
printf("CH+ ");
break;
case 0X22:
printf("|< < ");
break;
case 0X02:
printf(" >?>| ");
break;
case 0XC2:
printf("PLAY/PAUSE");
break;
case 0XE0:
printf("VOL- ");
break;
case 0XA8:
printf("VOL+ ");
break;
case 0X90:
printf("EQ ");
break;
case 0X98:
printf("100+ ");
break;
case 0XB0:
printf("200+ ");
break;
case 0x68:
printf("0 ");
break;
case 0x30:
printf("1 ");
break;
case 0x18:
printf("2 ");
break;
case 0x7A:
printf("3 ");
break;
case 0x10:
printf("4 ");
break;
case 0x38:
printf("5 ");
break;
case 0x5A:
printf("6 ");
break;
case 0x42:
printf("7 ");
break;
case 0x4A:
printf("8 ");
break;
case 0x52:
printf("9 ");
break;
default:
printf("Unknown key!");
}
}
else
{
printf("nr ERR 0x%08X", RemoteInfrareddata.uiRemoteInfraredData);
}
}
return RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode;
}
DS18B20溫度傳感器
獨(dú)特的單總線接口方式,DS18B20在與微處理器連接時(shí)僅需要一條口線即可實(shí),現(xiàn)微處理器與DS18B20的雙向通訊。大大提高了系統(tǒng)的抗干擾性。測(cè)溫范圍 -55℃~+125℃,精度為±0.5℃。支持多點(diǎn)組網(wǎng)功能,多個(gè)DS18B20可以并聯(lián)在唯一的三線上,最多只能并聯(lián)8個(gè),實(shí)現(xiàn)多點(diǎn)測(cè)溫,如果數(shù)量過(guò)多,會(huì)使供電電源電壓過(guò)低,從而造成信號(hào)傳輸?shù)牟环€(wěn)定。工作電源: 3.05.5V/DC (可以數(shù)據(jù)線寄生電源)。在使用中不需要任何外圍元件。測(cè)量結(jié)果以912位數(shù)字量方式串行傳送。
DS18B20硬件連接
DS18B20通信類型
單總線是一種半雙工通信方式。DS18B20共有6種信號(hào)類型:復(fù)位脈沖、應(yīng)答脈沖、寫0、寫1、讀0和讀1。所有這些信號(hào),除了應(yīng)答脈沖以外,都由主機(jī)發(fā)出同步信號(hào)。并且發(fā)送所有的命令和數(shù)據(jù)都是字節(jié)的低位在前。單片機(jī)對(duì)傳感器操作的時(shí)候首先會(huì)給傳感器發(fā)送一個(gè)復(fù)位脈沖,傳感器會(huì)恢復(fù)一個(gè)應(yīng)答脈沖,如果得不到這樣一個(gè)應(yīng)答脈沖,傳感器就認(rèn)為它壞了或者不存在,每一個(gè)脈沖都有規(guī)定的格式。
1.復(fù)位脈沖
單總線上的所有通信都是以初始化序列開(kāi)始。主機(jī)輸出低電平,保持低電平時(shí)間至少480 us,一般是480 us-960us之間,以產(chǎn)生復(fù)位脈沖。接著主機(jī)釋放總線,4.7K的上拉電阻將單總線拉高,延時(shí)15~60 us,并進(jìn)入接收模式(Rx)等待傳感器回復(fù)。接著DS18B20拉低總線60~240 us,以產(chǎn)生低電平應(yīng)答脈沖。
2.寫時(shí)序
寫時(shí)序包括寫0時(shí)序和寫1時(shí)序。所有寫時(shí)序至少需要60us,且在2次獨(dú)立的寫時(shí)序之間至少需要1us的恢復(fù)時(shí)間,兩種寫時(shí)序均起始于主機(jī)拉低總線。
a.寫0時(shí)序:主機(jī)輸出低電平,延時(shí)60us,然后釋放總線,延時(shí)2us。
b.寫1時(shí)序:主機(jī)輸出低電平,延時(shí)2us,然后釋放總線,延時(shí)60us。
3.讀時(shí)序
單總線器件僅在主機(jī)發(fā)出讀時(shí)序時(shí),才向主機(jī)傳輸數(shù)據(jù),所以,在主機(jī)發(fā)出讀數(shù)據(jù)命令后,必須馬上產(chǎn)生讀時(shí)序,以便從機(jī)能夠傳輸數(shù)據(jù)。所有讀時(shí)序至少需要60us,且在2次獨(dú)立的讀時(shí)序之間至少需要1us的恢復(fù)時(shí)間。每個(gè)讀時(shí)序都由主機(jī)發(fā)起,至少拉低總線1us。主機(jī)在讀時(shí)序期間必須釋放總線,并且在時(shí)序起始后的15us之內(nèi)采樣總線狀態(tài)。
典型的讀時(shí)序過(guò)程為:主機(jī)輸出低電平延時(shí)2us,然后主機(jī)轉(zhuǎn)入輸入模式延時(shí)12us,然后讀取單總線當(dāng)前的電平,然后延時(shí)50us。
DS18B20溫度讀取過(guò)程
①?gòu)?fù)位
②發(fā)SKIP ROM命令(0XCC)
③發(fā)開(kāi)始轉(zhuǎn)換命令(0X44)
④復(fù)位
⑤發(fā)送SKIP ROM命令(0XCC)
⑥發(fā)讀存儲(chǔ)器命令(0XBE)
⑦連續(xù)讀出兩個(gè)字節(jié)數(shù)據(jù)(即溫度)
⑧結(jié)束。
DS18B20溫度數(shù)據(jù)存儲(chǔ)
轉(zhuǎn)化后得到的11位數(shù)據(jù),存儲(chǔ)在18B20的兩個(gè)8比特的RAM中, MSB的前面5位是符號(hào)位,如果測(cè)得的溫度大于0, 這5位為0,只要將測(cè)到的數(shù)值乘于0.0625即可得到實(shí)際溫度;如果溫度小于0,這5位為1,測(cè)到的數(shù)值需要取反再乘于0.0625即可得到實(shí)際溫度。
例如+125℃的數(shù)字輸出為07D0H(000000111 11010000),-25.0625℃的數(shù)字輸出為FE6FH
DS18B20溫度采集實(shí)驗(yàn)
實(shí)驗(yàn)步驟:
1.配置RCC
2.配置PG6管腳為輸出模式
3編寫代碼
//main.c
#include "ds18b20.h"
int mian(){
int16_t temperature;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM6_Init();
printf("this is DS18B20 testn");
if(!DS18B20_Init())
{
printf(" DS18B20 is heren");
}else{
printf(" DS18B20 is not heren");
}
while(1){
temperature = DS18B20_Get_Temp();
if(temperature< 0)
{
printf("-");//??ê??oo?
temperature=-temperature;//×a?a?yêy
}
printf("temperature = %d.%dn",temperature/10,temperature%10);
HAL_Delay(1000);
}
}
//ds18b20.h
#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f4xx_hal.h"
//IO方向設(shè)置
#define DS18B20_IO_IN() {GPIOG- >MODER&=~(3< (6*2));GPIOG- >MODER|=0< (6*2);} //PG6輸入模式
#define DS18B20_IO_OUT() {GPIOG- >MODER&=~(3< (6*2));GPIOG- >MODER|=1< (6*2);} //PG6輸出模式
//操作函數(shù)
#defineDS18B20_OUT_LOW HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET) //數(shù)據(jù)端口PG6
#defineDS18B20_OUT_HIGH HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET) //數(shù)據(jù)端口PG6
#defineDS18B20_DQ_IN HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_6) //數(shù)據(jù)端口PG6
uint8_t DS18B20_Init(void); //初始化DS18B20
short DS18B20_Get_Temp(void); //獲取溫度
void DS18B20_Start(void); //開(kāi)始溫度轉(zhuǎn)換
void DS18B20_Write_Byte(uint8_t dat); //寫入一個(gè)字節(jié)
uint8_t DS18B20_Read_Byte(void); //讀出一個(gè)字節(jié)
uint8_t DS18B20_Read_Bit(void); //讀出一個(gè)位
uint8_t DS18B20_Check(void); //檢測(cè)是否存在DS18B20
void DS18B20_Reset(void); //復(fù)位DS18B20
#endif
//ds18b20.c
#include "ds18b20.h"
uint32_t usctick = 0;
uint32_t time_delay = 0;
extern TIM_HandleTypeDef htim6;
//延時(shí)nus
//nus為要延時(shí)的us數(shù)
//nus:0~190887435(最大值即2^32/fac_us@fac_us=168)
static uint8_t fac_us = 168; //主時(shí)鐘為168M, 在1us內(nèi)ticks會(huì)減168次
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick- >LOAD;//LOAD的值
ticks=nus*fac_us; //nus需要的節(jié)拍數(shù)
told=SysTick- >VAL; //剛進(jìn)入時(shí)的計(jì)數(shù)器的值
while(1)
{
tnow=SysTick- >VAL;
if(tnow!=told)
{
if(tnow< told)
//計(jì)數(shù)器遞減
tcnt+=told-tnow;
else
tcnt+=reload-tnow+told;
told=tnow;
if(tcnt >=ticks)break;
//時(shí)間超過(guò)或等于要延時(shí)的時(shí)間,則退出.
}
}
}
//復(fù)位DS18B20
void DS18B20_Reset(void)
{
DS18B20_IO_OUT(); //設(shè)置為輸出模式
DS18B20_OUT_LOW ; //拉低
delay_us(650); //延時(shí)650us
DS18B20_OUT_HIGH ; //拉高
delay_us(20); //延時(shí)20us
}
//等待DS18B20的回應(yīng)
//返回1:未檢測(cè)到DS18B20存在
//返回0:存在
uint8_t DS18B20_Check(void)
{
uint8_t retry=0;
DS18B20_IO_IN(); //設(shè)置為輸入模式
//等待DS18B20拉低總線回應(yīng),如果超過(guò)200us未拉低,則認(rèn)為未回應(yīng)
//DS18B20_DQ_IN == 1表示管腳仍為高電平
while ((DS18B20_DQ_IN == 1) && (retry< 200))
{
retry++;
delay_us(1);
};
if(retry >=200)
return 1; //DS18B20超時(shí)未拉低總線
else
retry=0; //DS18B20及時(shí)拉低總線
while ( (DS18B20_DQ_IN == 0 ) && ( retry < 240) ) //測(cè)試?yán)涂偩€的時(shí)間是否在240us內(nèi)
{
retry++;
delay_us(1);
};
if(retry >=240)
return 1; //超多240us錯(cuò)誤
return 0; //正確回應(yīng)
}
//從DS18B20讀取一個(gè)位
//返回值:1/0
uint8_t DS18B20_Read_Bit(void)
{
uint8_t data;
DS18B20_IO_OUT(); //設(shè)置為輸出
DS18B20_OUT_LOW ; //輸出低電平
delay_us(3); //延時(shí)3us
DS18B20_OUT_HIGH ; //拉高DQ
DS18B20_IO_IN(); //設(shè)置為輸入
delay_us(12); //延時(shí)12us
if(DS18B20_DQ_IN)
data=1;
else
data=0;
delay_us(50);
return data;
}
//從DS18B20讀取一個(gè)字節(jié)
//返回值:讀到的數(shù)據(jù),先讀數(shù)據(jù)的低位
uint8_t DS18B20_Read_Byte(void)
{
uint8_t i,j,dat;
dat=0;
for (i=0;i< 8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<
評(píng)論