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