新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 基于FPGA的DS18B20數(shù)字溫度傳感器測溫實(shí)例

基于FPGA的DS18B20數(shù)字溫度傳感器測溫實(shí)例

作者: 時(shí)間:2023-11-28 來源:古月居 收藏

1、DS18B20數(shù)字溫度傳感器


本文將使用三段式狀態(tài)機(jī)(Moore型)的寫法來對DS18B20進(jìn)行測溫操作,以便了解DS18B20和熟悉三段式狀態(tài)機(jī)的寫法。

本文引用地址:http://m.butianyuan.cn/article/202311/453375.htm


1.1、概述


溫度傳感器(temperature transducer)是指能感受溫度并轉(zhuǎn)換成可用輸出信號的傳感器, 是各種傳感器中最常用的一種。隨著現(xiàn)代儀器的發(fā)展,微型化、集成化、數(shù)字化正成為傳感器發(fā)展的一個重要方向。
美國DALLAS半導(dǎo)體公司推出的數(shù)字化溫度傳 感器DS18B20采用單總線協(xié)議,即與接口僅需占用一個I/O端口,無須任何外部元件,直接 將環(huán)境溫度轉(zhuǎn)化成數(shù)字信號,以數(shù)字碼方式串行輸出,從而大大簡化了傳感器與的接口設(shè)計(jì)。
引腳如下圖:



1.2、結(jié)構(gòu)組成


DS18B20測量溫度范圍為-55~+125℃,精度為±0.5℃。現(xiàn)場(實(shí)時(shí))溫度直接以“單總線” 的數(shù)字方式傳輸,大大提高了系統(tǒng)的抗干擾性。它能直接讀出被測溫度,并且可根據(jù)實(shí)際要求通過簡單的編程實(shí)現(xiàn)9~l2位的數(shù)字值讀數(shù)方式。它工作在3~5.5V的電壓范圍,采用多種封裝形式,從而使系統(tǒng)設(shè)計(jì)靈活、方便,設(shè)定分辨率及用戶設(shè)定的報(bào)警溫度存儲在EEPROM中,掉電后 依然保存。其內(nèi)部結(jié)構(gòu)如圖:



高速緩存器的結(jié)構(gòu)框圖如下:



由上圖可知DS18B20的高速緩存器共有9個8位寄存器,其中溫度數(shù)據(jù)低位(LSB)對應(yīng)字節(jié) 地址0,溫度數(shù)據(jù)高位(MSB)對應(yīng)字節(jié)地址1,以此類推,配置寄存器的字節(jié)地址為4。溫度數(shù)據(jù)存放的格式如下圖:



DS18B20在出廠時(shí)默認(rèn)配置溫度數(shù)據(jù)為12位,其中最高位為符號位,即溫度值共11位,最低四位為小數(shù)位。在讀取溫度數(shù)據(jù)時(shí),一次會讀2字節(jié)共16位,讀完后將低11位的二進(jìn)制 數(shù)轉(zhuǎn)化為十進(jìn)制數(shù)后再乘以0.0625得到所測的實(shí)際溫度值。
另外還需要判斷溫度的正負(fù),前5 個數(shù)字為符號位,這5位同時(shí)變化,我們只需要判斷其中任何一位就可以了。前5位為1時(shí),讀 取的溫度為負(fù)值,則測到的數(shù)值需要取反加1再乘以0.0625才可得到實(shí)際溫度值。前5位為0時(shí), 讀取的溫度為正值,只要將測得的數(shù)值乘以0.0625即可得到實(shí)際溫度值。
高速緩存器中第四個字節(jié)即為配置寄存器,用戶通過改變 R1 和 R0 的值來配置 DS18B20 的分辨率,上電默認(rèn)為 R1=1 以及 R0=1(12 位分辨率)。需要注意的是轉(zhuǎn)換時(shí)間與分辨率時(shí)間是有關(guān)系的。另外寄存器中最高位和低 5 位作 為內(nèi)部使用而保留使用,不可被寫入。轉(zhuǎn)換時(shí)間與位數(shù)關(guān)系如下表所示:



1.3、通訊步驟


如何操作 DS18B20 去進(jìn)行對溫度的轉(zhuǎn)換以及讀取呢?步驟如下:
1. 初始化
1-Wire 總線上的所有事件都必須以初始化為開始。初始化序列由總線上的主設(shè)備發(fā)出的復(fù)位脈沖以及緊跟著從設(shè)備回應(yīng)的存在脈沖構(gòu)成。該存在脈沖是讓總線主設(shè)備知道 DS18B20 在總線上并準(zhǔn)備好運(yùn)行。所有具體時(shí)序都在1.4章節(jié)介紹。


2. ROM命令


當(dāng)初始化完成之后,就可以執(zhí)行 ROM 命令。這些命令是對每個設(shè)備的 64 位 ROM 編 碼進(jìn)行操作的,當(dāng)總線上連接有多個設(shè)備時(shí),可以通過這些命令識別各個設(shè)備??偣舶?有 5 種 ROM 命令,每個命令的長度都是 8bit。
搜索 ROM[F0h]:當(dāng)系統(tǒng)上電初始化后,主設(shè)備可識別該總線上所有的從設(shè)備的 ROM 編碼,這樣就可以使得主設(shè)備確定總線上的從設(shè)備的類型以及數(shù)量。
讀 ROM[33h] :該命令允許主設(shè)備讀取 DS18B20 的 64 位 ROM 編碼,只有在總線上只有一個 DS18B20 時(shí)才能使用這個命令。如果總線上存在多個從設(shè)備,發(fā)送此命令,則當(dāng)所有從設(shè) 備都會回應(yīng)時(shí),將會引起數(shù)據(jù)沖突。
匹配 ROM[55h] :該匹配 ROM 命令之后接著發(fā)出 64 位 ROM 編碼,使主設(shè)備在多點(diǎn)總線上定位一只特定的 DS18B20。只有和 64 位 ROM 序列完全匹配的 DS18B20 才會做出響應(yīng)??偩€上的其 他從設(shè)備都將等待下一個復(fù)位脈沖。此命令在總線上有單個或多個器件時(shí)都可以使用。
跳過 ROM[CCh] :這條命令可以不用提供 64 位 ROM 編碼就進(jìn)行下一步操作,在單點(diǎn)總線(一個 DS18B20 傳感器)情況下可以節(jié)省時(shí)間。如果總線上不止一個從設(shè)備,在跳過 ROM 命令 之后跟著發(fā)一條讀命令,則所有從設(shè)備將會同時(shí)執(zhí)行溫度轉(zhuǎn)換,總線上就會發(fā)生數(shù)據(jù)沖突。
警報(bào)搜索[ECh] :該命令的操作與跳過 ROM 命令基本相同,但是不同的是只有溫度高于 TH 或低于 TL (達(dá)到報(bào)警條件)的從設(shè)備才會響應(yīng)。只要不掉電,警報(bào)狀態(tài)將一直保持,直到溫度不在警報(bào)范圍內(nèi)為止。


3. 功能命令


當(dāng)總線上的主設(shè)備通過 ROM 命令確定了哪個 DS18B20 可以進(jìn)行通信時(shí),主設(shè)備就可 以向其中一個從設(shè)備發(fā)送功能命令。這些命令可以使得主設(shè)備操控從設(shè)備進(jìn)行一系列的操作。
溫度轉(zhuǎn)換[44h]: 此命令為初始化單次溫度轉(zhuǎn)換,溫度轉(zhuǎn)換完后,轉(zhuǎn)換的溫度數(shù)據(jù)會寄存在高速緩存器 的 byte0(溫度數(shù)據(jù)低八位)和 byte1(溫度數(shù)據(jù)高八位)中,之后 DS18B20 恢復(fù)到低功耗 的閑置狀態(tài)。如果總線在該命令后發(fā)出讀時(shí)隙,若 DS18B20 正在進(jìn)行溫度轉(zhuǎn)換則會響應(yīng) “0”,若完成了溫度轉(zhuǎn)換則響應(yīng)“1”。如果是用的“寄生電源”供電模式,則在命令發(fā) 出后應(yīng)立即強(qiáng)制拉高總線,拉高時(shí)間應(yīng)大于時(shí)序要求。
寫入暫存器[4Eh] :該命令使得主設(shè)備向高速緩存器寫入 3 個字節(jié)的數(shù)據(jù)。第一個字節(jié)寫入高速緩存器的 byte2 中(TH 寄存器),第二個字節(jié)的數(shù)據(jù)寫入 byte3 中(TL 寄存器),第三個字節(jié)的數(shù)據(jù)寫入 byte4 中(配置寄存器)。所有的數(shù)據(jù)都是由低位到高位的順序?qū)懭搿?fù)位可隨時(shí)中斷寫入。
讀取高速緩存器[BEh] :讀取高速緩存器里的值,從 byte0(溫度低八位)開始一直讀到 byte8(CRC 校驗(yàn)),每個字節(jié)的數(shù)據(jù)從低位開始傳送。若是不想讀取這么多數(shù)據(jù)則在讀取數(shù)據(jù)時(shí)隨時(shí)可以通過復(fù)位來終止。
復(fù)制高速緩存器[48h] :該命令是將高速緩存器中的 TH(byte2)、TL(byte3)以及配置寄存器(byte4)里的 值拷貝到非易失性的存儲器 EEPROM 里。如果總線控制器在這條命令之后跟著發(fā)出讀時(shí) 隙,而 DS18B20 又正在忙于把暫存器拷貝到 EEPROM 存儲器,DS18B20 就會輸出一個 “0”,如果拷貝結(jié)束的話,DS18B20 則輸出“1”。如果設(shè)備采用“寄生電源”供電模 式,則在該命令發(fā)送后,必須立即強(qiáng)制拉高總線至少 10ms。
召回 EEPROM[B8h] :該命令將溫度報(bào)警觸發(fā)值(TH 和 TL)及配置寄存器的數(shù)據(jù)從 EEPROM 中召回至高速 緩存器中。這個操作會在上電后自動執(zhí)行一次,所以在上電期間暫存器中一直會存在有效 的數(shù)據(jù)。若在召回命令之后啟動讀時(shí)隙,若 DS18B20 正在進(jìn)行召回 EEPROM 則會響應(yīng) “0”,若召回完成則響應(yīng)“1”。
讀取供電模式[B4h] :該命令可以讀取總線上的 DS18B20 是否是由“寄生電源”供電。在讀取數(shù)據(jù)時(shí)序中 “0”表示“寄生電源供”模式供電,“1”表示外部電源供電。


1.4、總線時(shí)序


初始化—復(fù)位和存在脈沖
與 DS18B20 所有的通信都是由初始化開始的,初始化由主設(shè)備發(fā)出的復(fù)位脈沖及 DS18B20 響應(yīng)的存在脈沖組成。如下圖 所示。當(dāng) DS18B20 響應(yīng)復(fù)位信號的存在脈沖 后,則其向主設(shè)備表明其在該總線上,并且已經(jīng)做好了執(zhí)行命令的準(zhǔn)備。 在初始化狀態(tài),總線上的主設(shè)備通過拉低 1-Wire 總線最少 480us 來表示發(fā)送復(fù)位脈 沖。發(fā)送完之后,主設(shè)備要釋放總線進(jìn)入接收模式。當(dāng)總線釋放后,上拉電阻將 1- Wire 總線拉至高電平。當(dāng) DS18B20 檢測到該上升沿信號后,其等待 15us 至 60us 后將總線 拉低 60us 至 240us 來實(shí)現(xiàn)發(fā)送一個存在脈沖。



寫時(shí)隙
主設(shè)備通過寫時(shí)隙將命令寫入 DS18B20 中,寫時(shí)隙有兩種情況:寫“1”和寫“0”時(shí) 隙。主設(shè)備通過寫 1 時(shí)隙來向 DS18B20 中寫入邏輯 1,通過寫 0 時(shí)隙來向 DS18B20 中寫入 邏輯 0。當(dāng)主設(shè)備將總線從高電平拉至低電平時(shí),啟動寫時(shí)隙,所有的寫時(shí)隙持續(xù)時(shí)間最 少為 60us,每個寫時(shí)隙間的恢復(fù)時(shí)間最少為 1us。 當(dāng)總線(DQ)拉低后,DS18B20 在 15us 至 60us 之間對總線進(jìn)行采樣,如果采的 DQ 為高電平則發(fā)生寫 1,如果為低電平則發(fā)生寫 0,如下圖所示(圖中的總線控制器即為主設(shè)備)。 如果要產(chǎn)生寫 1 時(shí)隙,必須先將總線拉至邏輯低電平然后釋放總線,允許總線在寫 隙開始后 15us 內(nèi)上拉至高電平。若要產(chǎn)生寫 0 時(shí)隙,必須將總線拉至邏輯低電平并保持不 變最少 60us。



讀時(shí)隙

當(dāng)我們發(fā)送完讀取供電模式[B4h]或讀高速緩存器[BEh]命令時(shí),必須及時(shí)地生成讀時(shí)隙,只有在讀時(shí)隙 DS18B20 才能向主設(shè)備傳送數(shù)據(jù)。每個讀時(shí)隙最小必須有 60us 的持續(xù) 時(shí)間以及每個讀時(shí)隙間至少要有 1us 的恢復(fù)時(shí)間。當(dāng)主設(shè)備將總線從高電平拉至低電平超 過 1us,啟動讀時(shí)隙,如下圖所示。當(dāng)啟動讀時(shí)隙后,DS18B20 將會向主設(shè)備發(fā)送“0”或者“1”。DS18B20 通過將總線 拉高來發(fā)送 1,將總線拉低來發(fā)送 0 。當(dāng)讀時(shí)隙完成后,DQ 引腳將通過上拉電阻將總線拉高至高電平的閑置狀態(tài)。從 DS18B20 中輸出的數(shù)據(jù)在啟動讀時(shí)隙后的 15us 內(nèi)有效,所以,主設(shè)備在讀時(shí)隙開始后的 15us 內(nèi)必須釋放總線,并且對總線進(jìn)行采樣。


2、采用三段式狀態(tài)機(jī)測試


接下來將采用三段式狀態(tài)機(jī)對DS18B20進(jìn)行測溫操作。
2.1、整體設(shè)計(jì)
因?yàn)楸疚闹粚慏S18B20的驅(qū)動,不涉及到其他模塊(如數(shù)碼管),所以模塊框圖如下:


信號說明如下:
sys_clk:系統(tǒng)時(shí)鐘,50M

rst_n:低電平有效的復(fù)位信號

dq:單總線(雙向信號)

temp_data:輸出的有效數(shù)據(jù),位寬20

sign:輸出給數(shù)碼管的正負(fù)信號,1表示數(shù)據(jù)為負(fù)數(shù);0表示數(shù)據(jù)為正數(shù)


根據(jù)上面對DS18B20的介紹,可以概括出整個的測溫流程如下:



2.2、狀態(tài)機(jī)設(shè)計(jì)


三段式狀態(tài)機(jī)的概念可以參考:狀態(tài)機(jī)(一段式、二段式、三段式)、摩爾型(Moore)和米勒型(Mealy)
當(dāng)知道了 DS18B20 的控制流程之后,我們可以借助狀態(tài)機(jī)來進(jìn)一步了解它是如何跳轉(zhuǎn)的:


下面對各狀態(tài)說明:


INIT1:每次操作前都需要進(jìn)行初始化操作。在這個狀態(tài)主機(jī)會使用一個計(jì)數(shù)器從0計(jì)數(shù)到1000us。一開始就先拉低總線500us,然后釋放總線;在570us處采集總線電平,若為0,則說明總線進(jìn)行了響應(yīng),初始化完成。
WR_CMD:在這個狀態(tài)一起發(fā)送跳過ROM和溫度轉(zhuǎn)換指令。使用一個計(jì)數(shù)器計(jì)時(shí),使用另一個計(jì)數(shù)器則對發(fā)送的數(shù)據(jù)個數(shù)計(jì)數(shù),當(dāng)成功發(fā)送16個數(shù)據(jù)后,發(fā)送命令完成
WAIT:這個狀態(tài)為延時(shí)狀態(tài),滿足發(fā)送溫度轉(zhuǎn)換指令后的等待時(shí)間750ms。使用一個計(jì)數(shù)器計(jì)時(shí),時(shí)間滿足750ms則說明計(jì)時(shí)完成。
INIT2:第二次操作前的初始化操作。所有操作同INIT1,不贅述。
RD_CMD:在這個狀態(tài)一起發(fā)送跳過ROM和讀取溫度指令。使用一個計(jì)數(shù)器計(jì)時(shí),使用另一個計(jì)數(shù)器則對發(fā)送的數(shù)據(jù)個數(shù)計(jì)數(shù),當(dāng)成功發(fā)送16個數(shù)據(jù)后,發(fā)送命令完成
RD_DATA:在這個狀態(tài)讀取從機(jī)返回的16位溫度數(shù)據(jù)。使用一個計(jì)數(shù)器計(jì)時(shí),使用另一個計(jì)數(shù)器則對讀取的數(shù)據(jù)個數(shù)計(jì)數(shù),使用一個寄存器寄存讀取到的溫度數(shù)據(jù),當(dāng)成功讀取16個數(shù)據(jù)后,接收命令完成
需要注意:信號線dq是一個雙向信號,所以使用時(shí)要用使用三態(tài)門的方法來操作,具體方法參考:如何規(guī)范地使用雙向(inout)信號?


2.3、Verilog代碼


根據(jù)上面的狀態(tài)分析圖,編寫Verilog代碼如下:(這里就不寫分析了,注釋已經(jīng)寫得很詳細(xì)了,如果你看到了這邊文章且這段代碼又不懂的地方,可以評論給我)


//==================================================================
//--3段式狀態(tài)機(jī)(Moore)實(shí)現(xiàn)的DS18B20驅(qū)動
//==================================================================
 
//------------<模塊及端口聲明>----------------------------------------
module ds18b20_dri(
	input 				clk			,		//系統(tǒng)時(shí)鐘,50M
	input				rst_n		,       //低電平有效的復(fù)位信號	
	
	inout				dq			,		//單總線(雙向信號)
	output reg [19:0]   temp_data  	,   	// 轉(zhuǎn)換后得到的溫度值
    output reg          sign 				// 符號位
);
 
//------------<參數(shù)定義>----------------------------------------------
//狀態(tài)機(jī)狀態(tài)定義
localparam	INIT1		= 6'b000001 ,
			WR_CMD      = 6'b000010 ,
			WAIT  		= 6'b000100 ,
			INIT2  		= 6'b001000 ,
			RD_CMD  	= 6'b010000 ,
			RD_DATA  	= 6'b100000 ;
		
//時(shí)間參數(shù)定義
localparam	T_INIT = 1000		,			//初始化最大時(shí)間,單位us
			T_WAIT = 780_000	;			//轉(zhuǎn)換等待延時(shí),單位us
	
//命令定義	
localparam 	WR_CMD_DATA = 16'h44cc, 		//跳過 ROM 及溫度轉(zhuǎn)換命令,低位在前
			RD_CMD_DATA = 16'hbecc; 		//跳過 ROM 及讀取溫度命令,低位在前
					
//------------<reg定義>----------------------------------------------		
reg	[5:0]	cur_state	;					//現(xiàn)態(tài)
reg	[5:0]	next_state	;					//次態(tài)
reg	[4:0]	cnt			;					//50分頻計(jì)數(shù)器,1Mhz(1us)
reg			dq_out		;					//雙向總線輸出
reg			dq_en		;					//雙向總線輸出使能,1則輸出,0則高阻態(tài)
reg			flag_ack	;					//從機(jī)響應(yīng)標(biāo)志信號
reg			clk_us		;					//us時(shí)鐘
reg [19:0]	cnt_us		;					//us計(jì)數(shù)器,最大可表示1048ms
reg [3:0]	bit_cnt		;					//接收數(shù)據(jù)計(jì)數(shù)器
reg [15:0]	data_temp	;					//讀取的溫度數(shù)據(jù)寄存
reg [15:0]	data		;					//未處理的原始溫度數(shù)據(jù)
				
//------------<wire定義>----------------------------------------------				
wire		dq_in		;					//雙向總線輸入
 
//==================================================================
//===========================<main  code>===========================
//==================================================================
 
//-----------------------------------------------------------------------
//--雙向端口使用方式
//-----------------------------------------------------------------------
assign	dq_in = dq;							//高阻態(tài)的話,則把總線上的數(shù)據(jù)賦給dq_in
assign	dq =  dq_en ? dq_out : 1'bz;		//使能1則輸出,0則高阻態(tài)
//-----------------------------------------------------------------------
//--us時(shí)鐘生成,因?yàn)闀r(shí)序都是以us為單位,所以生成一個1us的時(shí)鐘會比較方便
//-----------------------------------------------------------------------
//50分頻計(jì)數(shù)
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt <= 5'd0;
	else if(cnt == 5'd24)					//每25個時(shí)鐘500ns清零
		cnt <= 5'd0;
	else
		cnt <= cnt + 1'd1;
end
//生成1us時(shí)鐘
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
		clk_us <= 1'b0;
	else  if(cnt == 5'd24)					//每500ns
		clk_us <= ~clk_us;                  //時(shí)鐘反轉(zhuǎn)
	else
		clk_us <= clk_us;
end
//-----------------------------------------------------------------------
//--三段式狀態(tài)機(jī)
//-----------------------------------------------------------------------
//狀態(tài)機(jī)第一段:同步時(shí)序描述狀態(tài)轉(zhuǎn)移
always @(posedge clk_us or negedge rst_n)begin
	if(!rst_n)		
		cur_state <= INIT1;	
	else
		cur_state <= next_state;
end
//狀態(tài)機(jī)第二段:組合邏輯判斷狀態(tài)轉(zhuǎn)移條件,描述狀態(tài)轉(zhuǎn)移規(guī)律以及輸出
always @(*)begin
	next_state = INIT1;	
	case(cur_state)
		INIT1		:begin
			if(cnt_us == T_INIT && flag_ack)				//滿足初始化時(shí)間且接收到了從機(jī)的響應(yīng)信號	
				next_state = WR_CMD;						//跳轉(zhuǎn)到寫狀態(tài)
			else	
				next_state = INIT1;							//不滿足則保持原有狀態(tài)
		end	
		WR_CMD       :begin	
			if(bit_cnt == 4'd15 && cnt_us == 20'd62)		//寫完了16個數(shù)據(jù),寫跳過ROM和寫溫度轉(zhuǎn)換命令	
				next_state = WAIT;							//跳轉(zhuǎn)到等待狀態(tài),等待溫度轉(zhuǎn)換完成 
			else	
				next_state = WR_CMD;						//不滿足則保持原有狀態(tài)
		end	
		WAIT  :begin	
			if(cnt_us == T_WAIT)							//等待時(shí)間結(jié)束
				next_state = INIT2;	
			else	
				next_state = WAIT;	
		end	
		INIT2  :begin	
			if(cnt_us == T_INIT && flag_ack)				//再進(jìn)行初始化,時(shí)序同INIT1
				next_state = RD_CMD;
			else
				next_state = INIT2;
		end
		RD_CMD  :begin
			if(bit_cnt == 4'd15 && cnt_us == 20'd62)		//寫完了16個數(shù)據(jù),寫跳過ROM和寫讀取溫度轉(zhuǎn)換命令	
				next_state = RD_DATA;						//跳轉(zhuǎn)到讀取溫度數(shù)據(jù)狀態(tài)
			else	
				next_state = RD_CMD;	
		end	
		RD_DATA  :begin	
			if(bit_cnt == 4'd15 && cnt_us == 20'd62)		//讀取完了16個數(shù)據(jù)
				next_state = INIT1;							//跳轉(zhuǎn)到初始化狀態(tài),開始新一輪溫度采集
			else	
				next_state = RD_DATA;	
		end			
		default:next_state = INIT1;							//默認(rèn)初始化狀態(tài)
	endcase
end	
//狀態(tài)機(jī)第三段:時(shí)序邏輯描述輸出
always @(posedge clk_us or negedge rst_n)begin
	if(!rst_n)begin											//默認(rèn)輸出
		dq_en <= 1'b0;
		dq_out <= 1'b0;
		flag_ack <= 1'b0;
		cnt_us <= 20'd0;
		bit_cnt <= 4'd0;
	end
	else begin 	
		case(cur_state)
			INIT1	:begin
				if(cnt_us == T_INIT)begin					//時(shí)間計(jì)數(shù)到最大值(初始化時(shí)間)
					cnt_us <= 20'd0;						//計(jì)數(shù)器清零
					flag_ack <= 1'b0;						//從機(jī)響應(yīng)標(biāo)志信號拉低
				end
				else begin									//沒有計(jì)數(shù)到最大值
					cnt_us <= cnt_us + 1'd1;				//計(jì)數(shù)器計(jì)數(shù)
					if(cnt_us <= 20'd499)begin				//小于500us時(shí)
						dq_en <= 1'b1;						//控制總線
						dq_out <= 1'b0;						//輸出0,即拉低總線
					end
					else begin								//在500us處
						dq_en <= 1'b0;						//釋放總線,等待從機(jī)響應(yīng)						
						if (cnt_us == 20'd570 && !dq_in)	//在570us處采集總線電平,如果為0則說明從機(jī)響應(yīng)了
							flag_ack <= 1'b1;				//拉高從機(jī)響應(yīng)標(biāo)志信號
					end	
				end
			end
			WR_CMD	:begin
				if(cnt_us == 20'd62)begin						//一個寫時(shí)隙周期63us,滿足計(jì)時(shí)條件則
					cnt_us <= 20'd0;							//清空計(jì)數(shù)器
					dq_en <= 1'b0;								//釋放總線
					if(bit_cnt == 4'd15)						//如果數(shù)據(jù)已經(jīng)寫了15個
						bit_cnt <= 4'd0;						//則清空
					else										//沒寫15個
						bit_cnt <= bit_cnt + 1'd1;				//則數(shù)據(jù)計(jì)數(shù)器+1,代表寫入了一個數(shù)據(jù)
				end	
				else begin										//一個寫時(shí)隙周期63us未完成
					cnt_us <= cnt_us + 1'd1;					//計(jì)數(shù)器一直計(jì)數(shù)
					if(cnt_us <= 20'd1)begin					//0~1us(每兩個寫數(shù)據(jù)之間需要間隔2us)
						dq_en <= 1'b1;							//拉低總線
						dq_out <= 1'b0;
					end
					else begin					
						if (WR_CMD_DATA[bit_cnt] == 1'b0)begin	//需要寫入的數(shù)據(jù)為0
							dq_en <= 1'b1;						//拉低總線
							dq_out <= 1'b0;						//								
						end
						else if(WR_CMD_DATA[bit_cnt] == 1'b1)begin
							dq_en <= 1'b0;						//需要寫入的數(shù)據(jù)為1
							dq_out <= 1'b0;						//釋放總線						
						end
					end	
				end		
			end		
			WAIT	:begin										//等待溫度轉(zhuǎn)換完成
				dq_en <= 1'b1;									//拉低總線兼容寄生電源模式
				dq_out <= 1'b1;									
				if(cnt_us == T_WAIT)							//計(jì)數(shù)完成
					cnt_us <= 20'd0;
				else
					cnt_us <= cnt_us + 1'd1;
			end	
			INIT2	:begin										//第二次初始化,時(shí)序同INIT1
				if(cnt_us == T_INIT)begin						
					cnt_us <= 20'd0;
					flag_ack <= 1'b0;
				end
				else begin
					cnt_us <= cnt_us + 1'd1;
					if(cnt_us <= 20'd499)begin
						dq_en <= 1'b1;						
						dq_out <= 1'b0;
					end
					else begin
						dq_en <= 1'b0;												
						if (cnt_us == 20'd570 && !dq_in)
							flag_ack <= 1'b1;
					end	
				end
			end	
			RD_CMD	:begin										//寫16個數(shù)據(jù),時(shí)序同WR_CMD
				if(cnt_us == 20'd62)begin
					cnt_us <= 20'd0;
					dq_en <= 1'b0;
					if(bit_cnt == 4'd15)
						bit_cnt <= 4'd0;
					else
						bit_cnt <= bit_cnt + 1'd1;
				end
				else begin
					cnt_us <= cnt_us + 1'd1;
					if(cnt_us <= 20'd1)begin
						dq_en <= 1'b1;							
						dq_out <= 1'b0;
					end
					else begin					
						if (RD_CMD_DATA[bit_cnt] == 1'b0)begin
							dq_en <= 1'b1;						
							dq_out <= 1'b0;														
						end
						else if(RD_CMD_DATA[bit_cnt] == 1'b1)begin
							dq_en <= 1'b0;						
							dq_out <= 1'b0;												
						end
					end	
				end
			end	
			RD_DATA	:begin										//讀16位溫度數(shù)據(jù)
				if(cnt_us == 20'd62)begin						//一個讀時(shí)隙周期63us,滿足計(jì)時(shí)條件則
					cnt_us <= 20'd0;							//清空計(jì)數(shù)器
					dq_en <= 1'b0;								//釋放總線
					if(bit_cnt == 4'd15)begin					//如果數(shù)據(jù)已經(jīng)讀取了15個
						bit_cnt <= 4'd0;						//則清空
						data <= data_temp;						//臨時(shí)的數(shù)據(jù)賦值給data
					end
					else begin									//如果數(shù)據(jù)沒有讀取15個
						bit_cnt <= bit_cnt + 1'd1;				//則數(shù)據(jù)計(jì)數(shù)器+1,意味著讀取了一個數(shù)據(jù)
						data <= data;
					end
				end
				else begin										//一個讀時(shí)隙周期還沒結(jié)束
					cnt_us <= cnt_us + 1'd1;					//計(jì)數(shù)器累加
					if(cnt_us <= 20'd1)begin					//0~1us(每兩個讀數(shù)據(jù)之間需要間隔2us)
						dq_en <= 1'b1;							//拉低總線
						dq_out <= 1'b0;
					end
					else begin									//2us后
						dq_en <= 1'b0;							//釋放總掉線					
						if (cnt_us == 20'd10)					//在10us處讀取總線電平
							data_temp <= {dq,data_temp[15:1]};	//讀取總線電平
					end	
				end
			end
			default:;		
		endcase
	end
end
 
//-----------------------------------------------------------------------
//--12位溫度數(shù)據(jù)處理
//-----------------------------------------------------------------------
always @(posedge clk_us or negedge rst_n)begin
	if(!rst_n)begin													//初始狀態(tài)
		temp_data <= 20'd0;	
		sign  <= 1'b0;	
	end	
	else begin	
		if(!data[15])begin											//最高位為0則溫度為正
			sign  <= 1'b0;											//標(biāo)志位為正
			temp_data <= data[10:0] * 11'd625 /7'd100;				//12位溫度數(shù)據(jù)處理
		end	
		else if(data[15])begin										//最高位為1則溫度為負(fù)
			sign  <= 1'b1;											//標(biāo)志位為負(fù)
			temp_data <= (~data[10:0] + 1'b1)* 11'd625 /7'd100;		//12位溫度數(shù)據(jù)處理			
		end
	end
end
endmodule
2.4、調(diào)試


因?yàn)橥ㄓ嵾^程涉及到從機(jī)的響應(yīng),我又找不到相應(yīng)的器件模型,仿真就不搞了。
直接使用signaltap抓下波形:



上圖中:
狀態(tài)機(jī)開始運(yùn)行,進(jìn)入INTI1的初始化狀態(tài)
dht11_en拉高同時(shí)dht11_out為0,說明主機(jī)拉低了總線
主機(jī)拉低總線后在500us處釋放了總線,總線被上拉電阻拉高
在528us處總線被從機(jī)拉低,直至637us從機(jī)才釋放了總線,說明從機(jī)發(fā)送了響應(yīng)
在570us處因?yàn)榭偩€被從機(jī)拉低,所以拉高了響應(yīng)信號flag_ack,直到進(jìn)入下個狀態(tài)才將flag_ack拉低



上圖中:
狀態(tài)機(jī)從INTI1的初始化狀態(tài)跳轉(zhuǎn)到寫入ROM和溫度轉(zhuǎn)換命令的狀態(tài)WR_CMD
bit_cnt從0計(jì)數(shù)到F,說明寫入了16個數(shù)據(jù);與此同時(shí),總線上也在分別寫入“0”和“1”



上圖中:
狀態(tài)機(jī)從WAIT狀態(tài)跳轉(zhuǎn)到初始化狀態(tài)INIT2
cnt_us計(jì)數(shù)器從780000清零,說明此時(shí)延時(shí)了780ms的時(shí)間以便完成溫度轉(zhuǎn)換



上圖中:
狀態(tài)機(jī)從狀態(tài)RD_CMD跳轉(zhuǎn)到數(shù)據(jù)讀取狀態(tài)RD_DATA
bit_cnt從0計(jì)數(shù)到F,說明讀取了16個數(shù)據(jù);與此同時(shí),總線上也在分別輸出“0”和“1”



上圖中:
data是直接從DS18B20溫度寄存器中讀取的數(shù)據(jù)0000000111111001
temp_data是處理后發(fā)送給數(shù)碼管顯示的數(shù)據(jù)3156,對應(yīng)攝氏度31.56

3、上板調(diào)試

添加數(shù)碼管顯示模塊,編譯工程,板卡顯示如下:



和用調(diào)試軟件抓取的結(jié)果一致。

4、參考

DS18B20—Dalas Semiconductor
FPGA Verilog 開發(fā)實(shí)戰(zhàn)指南—基于 Intel Cyclone IV—野火電子




關(guān)鍵詞: FPGA DS18B2溫度傳感器

評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉