基于FPGA的DS18B20數字溫度傳感器測溫實(shí)例
1、DS18B20數字溫度傳感器
本文將使用三段式狀態(tài)機(Moore型)的寫(xiě)法來(lái)對DS18B20進(jìn)行測溫操作,以便了解DS18B20和熟悉三段式狀態(tài)機的寫(xiě)法。
1.1、概述
溫度傳感器(temperature transducer)是指能感受溫度并轉換成可用輸出信號的傳感器, 是各種傳感器中最常用的一種。隨著(zhù)現代儀器的發(fā)展,微型化、集成化、數字化正成為傳感器發(fā)展的一個(gè)重要方向。
美國DALLAS半導體公司推出的數字化溫度傳 感器DS18B20采用單總線(xiàn)協(xié)議,即與FPGA接口僅需占用一個(gè)I/O端口,無(wú)須任何外部元件,直接 將環(huán)境溫度轉化成數字信號,以數字碼方式串行輸出,從而大大簡(jiǎn)化了傳感器與FPGA的接口設計。
引腳如下圖:
1.2、結構組成
DS18B20測量溫度范圍為-55~+125℃,精度為±0.5℃?,F場(chǎng)(實(shí)時(shí))溫度直接以“單總線(xiàn)” 的數字方式傳輸,大大提高了系統的抗干擾性。它能直接讀出被測溫度,并且可根據實(shí)際要求通過(guò)簡(jiǎn)單的編程實(shí)現9~l2位的數字值讀數方式。它工作在3~5.5V的電壓范圍,采用多種封裝形式,從而使系統設計靈活、方便,設定分辨率及用戶(hù)設定的報警溫度存儲在EEPROM中,掉電后 依然保存。其內部結構如圖:
高速緩存器的結構框圖如下:
由上圖可知DS18B20的高速緩存器共有9個(gè)8位寄存器,其中溫度數據低位(LSB)對應字節 地址0,溫度數據高位(MSB)對應字節地址1,以此類(lèi)推,配置寄存器的字節地址為4。溫度數據存放的格式如下圖:
DS18B20在出廠(chǎng)時(shí)默認配置溫度數據為12位,其中最高位為符號位,即溫度值共11位,最低四位為小數位。FPGA在讀取溫度數據時(shí),一次會(huì )讀2字節共16位,讀完后將低11位的二進(jìn)制 數轉化為十進(jìn)制數后再乘以0.0625得到所測的實(shí)際溫度值。
另外還需要判斷溫度的正負,前5 個(gè)數字為符號位,這5位同時(shí)變化,我們只需要判斷其中任何一位就可以了。前5位為1時(shí),讀 取的溫度為負值,則測到的數值需要取反加1再乘以0.0625才可得到實(shí)際溫度值。前5位為0時(shí), 讀取的溫度為正值,只要將測得的數值乘以0.0625即可得到實(shí)際溫度值。
高速緩存器中第四個(gè)字節即為配置寄存器,用戶(hù)通過(guò)改變 R1 和 R0 的值來(lái)配置 DS18B20 的分辨率,上電默認為 R1=1 以及 R0=1(12 位分辨率)。需要注意的是轉換時(shí)間與分辨率時(shí)間是有關(guān)系的。另外寄存器中最高位和低 5 位作 為內部使用而保留使用,不可被寫(xiě)入。轉換時(shí)間與位數關(guān)系如下表所示:
1.3、通訊步驟
如何操作 DS18B20 去進(jìn)行對溫度的轉換以及讀取呢?步驟如下:
1. 初始化
1-Wire 總線(xiàn)上的所有事件都必須以初始化為開(kāi)始。初始化序列由總線(xiàn)上的主設備發(fā)出的復位脈沖以及緊跟著(zhù)從設備回應的存在脈沖構成。該存在脈沖是讓總線(xiàn)主設備知道 DS18B20 在總線(xiàn)上并準備好運行。所有具體時(shí)序都在1.4章節介紹。
2. ROM命令
當初始化完成之后,就可以執行 ROM 命令。這些命令是對每個(gè)設備的 64 位 ROM 編 碼進(jìn)行操作的,當總線(xiàn)上連接有多個(gè)設備時(shí),可以通過(guò)這些命令識別各個(gè)設備??偣舶?有 5 種 ROM 命令,每個(gè)命令的長(cháng)度都是 8bit。
搜索 ROM[F0h]:當系統上電初始化后,主設備可識別該總線(xiàn)上所有的從設備的 ROM 編碼,這樣就可以使得主設備確定總線(xiàn)上的從設備的類(lèi)型以及數量。
讀 ROM[33h] :該命令允許主設備讀取 DS18B20 的 64 位 ROM 編碼,只有在總線(xiàn)上只有一個(gè) DS18B20 時(shí)才能使用這個(gè)命令。如果總線(xiàn)上存在多個(gè)從設備,發(fā)送此命令,則當所有從設 備都會(huì )回應時(shí),將會(huì )引起數據沖突。
匹配 ROM[55h] :該匹配 ROM 命令之后接著(zhù)發(fā)出 64 位 ROM 編碼,使主設備在多點(diǎn)總線(xiàn)上定位一只特定的 DS18B20。只有和 64 位 ROM 序列完全匹配的 DS18B20 才會(huì )做出響應??偩€(xiàn)上的其 他從設備都將等待下一個(gè)復位脈沖。此命令在總線(xiàn)上有單個(gè)或多個(gè)器件時(shí)都可以使用。
跳過(guò) ROM[CCh] :這條命令可以不用提供 64 位 ROM 編碼就進(jìn)行下一步操作,在單點(diǎn)總線(xiàn)(一個(gè) DS18B20 傳感器)情況下可以節省時(shí)間。如果總線(xiàn)上不止一個(gè)從設備,在跳過(guò) ROM 命令 之后跟著(zhù)發(fā)一條讀命令,則所有從設備將會(huì )同時(shí)執行溫度轉換,總線(xiàn)上就會(huì )發(fā)生數據沖突。
警報搜索[ECh] :該命令的操作與跳過(guò) ROM 命令基本相同,但是不同的是只有溫度高于 TH 或低于 TL (達到報警條件)的從設備才會(huì )響應。只要不掉電,警報狀態(tài)將一直保持,直到溫度不在警報范圍內為止。
3. 功能命令
當總線(xiàn)上的主設備通過(guò) ROM 命令確定了哪個(gè) DS18B20 可以進(jìn)行通信時(shí),主設備就可 以向其中一個(gè)從設備發(fā)送功能命令。這些命令可以使得主設備操控從設備進(jìn)行一系列的操作。
溫度轉換[44h]: 此命令為初始化單次溫度轉換,溫度轉換完后,轉換的溫度數據會(huì )寄存在高速緩存器 的 byte0(溫度數據低八位)和 byte1(溫度數據高八位)中,之后 DS18B20 恢復到低功耗 的閑置狀態(tài)。如果總線(xiàn)在該命令后發(fā)出讀時(shí)隙,若 DS18B20 正在進(jìn)行溫度轉換則會(huì )響應 “0”,若完成了溫度轉換則響應“1”。如果是用的“寄生電源”供電模式,則在命令發(fā) 出后應立即強制拉高總線(xiàn),拉高時(shí)間應大于時(shí)序要求。
寫(xiě)入暫存器[4Eh] :該命令使得主設備向高速緩存器寫(xiě)入 3 個(gè)字節的數據。第一個(gè)字節寫(xiě)入高速緩存器的 byte2 中(TH 寄存器),第二個(gè)字節的數據寫(xiě)入 byte3 中(TL 寄存器),第三個(gè)字節的數據寫(xiě)入 byte4 中(配置寄存器)。所有的數據都是由低位到高位的順序寫(xiě)入。復位可隨時(shí)中斷寫(xiě)入。
讀取高速緩存器[BEh] :讀取高速緩存器里的值,從 byte0(溫度低八位)開(kāi)始一直讀到 byte8(CRC 校驗),每個(gè)字節的數據從低位開(kāi)始傳送。若是不想讀取這么多數據則在讀取數據時(shí)隨時(shí)可以通過(guò)復位來(lái)終止。
復制高速緩存器[48h] :該命令是將高速緩存器中的 TH(byte2)、TL(byte3)以及配置寄存器(byte4)里的 值拷貝到非易失性的存儲器 EEPROM 里。如果總線(xiàn)控制器在這條命令之后跟著(zhù)發(fā)出讀時(shí) 隙,而 DS18B20 又正在忙于把暫存器拷貝到 EEPROM 存儲器,DS18B20 就會(huì )輸出一個(gè) “0”,如果拷貝結束的話(huà),DS18B20 則輸出“1”。如果設備采用“寄生電源”供電模 式,則在該命令發(fā)送后,必須立即強制拉高總線(xiàn)至少 10ms。
召回 EEPROM[B8h] :該命令將溫度報警觸發(fā)值(TH 和 TL)及配置寄存器的數據從 EEPROM 中召回至高速 緩存器中。這個(gè)操作會(huì )在上電后自動(dòng)執行一次,所以在上電期間暫存器中一直會(huì )存在有效 的數據。若在召回命令之后啟動(dòng)讀時(shí)隙,若 DS18B20 正在進(jìn)行召回 EEPROM 則會(huì )響應 “0”,若召回完成則響應“1”。
讀取供電模式[B4h] :該命令可以讀取總線(xiàn)上的 DS18B20 是否是由“寄生電源”供電。在讀取數據時(shí)序中 “0”表示“寄生電源供”模式供電,“1”表示外部電源供電。
1.4、總線(xiàn)時(shí)序
初始化—復位和存在脈沖
與 DS18B20 所有的通信都是由初始化開(kāi)始的,初始化由主設備發(fā)出的復位脈沖及 DS18B20 響應的存在脈沖組成。如下圖 所示。當 DS18B20 響應復位信號的存在脈沖 后,則其向主設備表明其在該總線(xiàn)上,并且已經(jīng)做好了執行命令的準備。 在初始化狀態(tài),總線(xiàn)上的主設備通過(guò)拉低 1-Wire 總線(xiàn)最少 480us 來(lái)表示發(fā)送復位脈 沖。發(fā)送完之后,主設備要釋放總線(xiàn)進(jìn)入接收模式。當總線(xiàn)釋放后,上拉電阻將 1- Wire 總線(xiàn)拉至高電平。當 DS18B20 檢測到該上升沿信號后,其等待 15us 至 60us 后將總線(xiàn) 拉低 60us 至 240us 來(lái)實(shí)現發(fā)送一個(gè)存在脈沖。
寫(xiě)時(shí)隙
主設備通過(guò)寫(xiě)時(shí)隙將命令寫(xiě)入 DS18B20 中,寫(xiě)時(shí)隙有兩種情況:寫(xiě)“1”和寫(xiě)“0”時(shí) 隙。主設備通過(guò)寫(xiě) 1 時(shí)隙來(lái)向 DS18B20 中寫(xiě)入邏輯 1,通過(guò)寫(xiě) 0 時(shí)隙來(lái)向 DS18B20 中寫(xiě)入 邏輯 0。當主設備將總線(xiàn)從高電平拉至低電平時(shí),啟動(dòng)寫(xiě)時(shí)隙,所有的寫(xiě)時(shí)隙持續時(shí)間最 少為 60us,每個(gè)寫(xiě)時(shí)隙間的恢復時(shí)間最少為 1us。 當總線(xiàn)(DQ)拉低后,DS18B20 在 15us 至 60us 之間對總線(xiàn)進(jìn)行采樣,如果采的 DQ 為高電平則發(fā)生寫(xiě) 1,如果為低電平則發(fā)生寫(xiě) 0,如下圖所示(圖中的總線(xiàn)控制器即為主設備)。 如果要產(chǎn)生寫(xiě) 1 時(shí)隙,必須先將總線(xiàn)拉至邏輯低電平然后釋放總線(xiàn),允許總線(xiàn)在寫(xiě) 隙開(kāi)始后 15us 內上拉至高電平。若要產(chǎn)生寫(xiě) 0 時(shí)隙,必須將總線(xiàn)拉至邏輯低電平并保持不 變最少 60us。
讀時(shí)隙
當我們發(fā)送完讀取供電模式[B4h]或讀高速緩存器[BEh]命令時(shí),必須及時(shí)地生成讀時(shí)隙,只有在讀時(shí)隙 DS18B20 才能向主設備傳送數據。每個(gè)讀時(shí)隙最小必須有 60us 的持續 時(shí)間以及每個(gè)讀時(shí)隙間至少要有 1us 的恢復時(shí)間。當主設備將總線(xiàn)從高電平拉至低電平超 過(guò) 1us,啟動(dòng)讀時(shí)隙,如下圖所示。當啟動(dòng)讀時(shí)隙后,DS18B20 將會(huì )向主設備發(fā)送“0”或者“1”。DS18B20 通過(guò)將總線(xiàn) 拉高來(lái)發(fā)送 1,將總線(xiàn)拉低來(lái)發(fā)送 0 。當讀時(shí)隙完成后,DQ 引腳將通過(guò)上拉電阻將總線(xiàn)拉高至高電平的閑置狀態(tài)。從 DS18B20 中輸出的數據在啟動(dòng)讀時(shí)隙后的 15us 內有效,所以,主設備在讀時(shí)隙開(kāi)始后的 15us 內必須釋放總線(xiàn),并且對總線(xiàn)進(jìn)行采樣。
2、采用三段式狀態(tài)機測試
接下來(lái)將采用三段式狀態(tài)機對DS18B20進(jìn)行測溫操作。
2.1、整體設計
因為本文只寫(xiě)DS18B20的驅動(dòng),不涉及到其他模塊(如數碼管),所以模塊框圖如下:
信號說(shuō)明如下:
sys_clk:系統時(shí)鐘,50M
rst_n:低電平有效的復位信號
dq:?jiǎn)慰偩€(xiàn)(雙向信號)
temp_data:輸出的有效數據,位寬20
sign:輸出給數碼管的正負信號,1表示數據為負數;0表示數據為正數
根據上面對DS18B20的介紹,可以概括出整個(gè)的測溫流程如下:
2.2、狀態(tài)機設計
三段式狀態(tài)機的概念可以參考:狀態(tài)機(一段式、二段式、三段式)、摩爾型(Moore)和米勒型(Mealy)
當知道了 DS18B20 的控制流程之后,我們可以借助狀態(tài)機來(lái)進(jìn)一步了解它是如何跳轉的:
下面對各狀態(tài)說(shuō)明:
INIT1:每次操作前都需要進(jìn)行初始化操作。在這個(gè)狀態(tài)主機會(huì )使用一個(gè)計數器從0計數到1000us。一開(kāi)始就先拉低總線(xiàn)500us,然后釋放總線(xiàn);在570us處采集總線(xiàn)電平,若為0,則說(shuō)明總線(xiàn)進(jìn)行了響應,初始化完成。
WR_CMD:在這個(gè)狀態(tài)一起發(fā)送跳過(guò)ROM和溫度轉換指令。使用一個(gè)計數器計時(shí),使用另一個(gè)計數器則對發(fā)送的數據個(gè)數計數,當成功發(fā)送16個(gè)數據后,發(fā)送命令完成
WAIT:這個(gè)狀態(tài)為延時(shí)狀態(tài),滿(mǎn)足發(fā)送溫度轉換指令后的等待時(shí)間750ms。使用一個(gè)計數器計時(shí),時(shí)間滿(mǎn)足750ms則說(shuō)明計時(shí)完成。
INIT2:第二次操作前的初始化操作。所有操作同INIT1,不贅述。
RD_CMD:在這個(gè)狀態(tài)一起發(fā)送跳過(guò)ROM和讀取溫度指令。使用一個(gè)計數器計時(shí),使用另一個(gè)計數器則對發(fā)送的數據個(gè)數計數,當成功發(fā)送16個(gè)數據后,發(fā)送命令完成
RD_DATA:在這個(gè)狀態(tài)讀取從機返回的16位溫度數據。使用一個(gè)計數器計時(shí),使用另一個(gè)計數器則對讀取的數據個(gè)數計數,使用一個(gè)寄存器寄存讀取到的溫度數據,當成功讀取16個(gè)數據后,接收命令完成
需要注意:信號線(xiàn)dq是一個(gè)雙向信號,所以使用時(shí)要用使用三態(tài)門(mén)的方法來(lái)操作,具體方法參考:如何規范地使用雙向(inout)信號?
2.3、Verilog代碼
根據上面的狀態(tài)分析圖,編寫(xiě)Verilog代碼如下:(這里就不寫(xiě)分析了,注釋已經(jīng)寫(xiě)得很詳細了,如果你看到了這邊文章且這段代碼又不懂的地方,可以評論給我)
//================================================================== //--3段式狀態(tài)機(Moore)實(shí)現的DS18B20驅動(dòng) //================================================================== //------------<模塊及端口聲明>---------------------------------------- module ds18b20_dri( input clk , //系統時(shí)鐘,50M input rst_n , //低電平有效的復位信號 inout dq , //單總線(xiàn)(雙向信號) output reg [19:0] temp_data , // 轉換后得到的溫度值 output reg sign // 符號位 ); //------------<參數定義>---------------------------------------------- //狀態(tài)機狀態(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í)間參數定義 localparam T_INIT = 1000 , //初始化最大時(shí)間,單位us T_WAIT = 780_000 ; //轉換等待延時(shí),單位us //命令定義 localparam WR_CMD_DATA = 16'h44cc, //跳過(guò) ROM 及溫度轉換命令,低位在前 RD_CMD_DATA = 16'hbecc; //跳過(guò) ROM 及讀取溫度命令,低位在前 //------------<reg定義>---------------------------------------------- reg [5:0] cur_state ; //現態(tài) reg [5:0] next_state ; //次態(tài) reg [4:0] cnt ; //50分頻計數器,1Mhz(1us) reg dq_out ; //雙向總線(xiàn)輸出 reg dq_en ; //雙向總線(xiàn)輸出使能,1則輸出,0則高阻態(tài) reg flag_ack ; //從機響應標志信號 reg clk_us ; //us時(shí)鐘 reg [19:0] cnt_us ; //us計數器,最大可表示1048ms reg [3:0] bit_cnt ; //接收數據計數器 reg [15:0] data_temp ; //讀取的溫度數據寄存 reg [15:0] data ; //未處理的原始溫度數據 //------------<wire定義>---------------------------------------------- wire dq_in ; //雙向總線(xiàn)輸入 //================================================================== //===========================<main code>=========================== //================================================================== //----------------------------------------------------------------------- //--雙向端口使用方式 //----------------------------------------------------------------------- assign dq_in = dq; //高阻態(tài)的話(huà),則把總線(xiàn)上的數據賦給dq_in assign dq = dq_en ? dq_out : 1'bz; //使能1則輸出,0則高阻態(tài) //----------------------------------------------------------------------- //--us時(shí)鐘生成,因為時(shí)序都是以us為單位,所以生成一個(gè)1us的時(shí)鐘會(huì )比較方便 //----------------------------------------------------------------------- //50分頻計數 always @(posedge clk or negedge rst_n)begin if(!rst_n) cnt <= 5'd0; else if(cnt == 5'd24) //每25個(gè)時(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í)鐘反轉 else clk_us <= clk_us; end //----------------------------------------------------------------------- //--三段式狀態(tài)機 //----------------------------------------------------------------------- //狀態(tài)機第一段:同步時(shí)序描述狀態(tài)轉移 always @(posedge clk_us or negedge rst_n)begin if(!rst_n) cur_state <= INIT1; else cur_state <= next_state; end //狀態(tài)機第二段:組合邏輯判斷狀態(tài)轉移條件,描述狀態(tài)轉移規律以及輸出 always @(*)begin next_state = INIT1; case(cur_state) INIT1 :begin if(cnt_us == T_INIT && flag_ack) //滿(mǎn)足初始化時(shí)間且接收到了從機的響應信號 next_state = WR_CMD; //跳轉到寫(xiě)狀態(tài) else next_state = INIT1; //不滿(mǎn)足則保持原有狀態(tài) end WR_CMD :begin if(bit_cnt == 4'd15 && cnt_us == 20'd62) //寫(xiě)完了16個(gè)數據,寫(xiě)跳過(guò)ROM和寫(xiě)溫度轉換命令 next_state = WAIT; //跳轉到等待狀態(tài),等待溫度轉換完成 else next_state = WR_CMD; //不滿(mǎn)足則保持原有狀態(tài) end WAIT :begin if(cnt_us == T_WAIT) //等待時(shí)間結束 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) //寫(xiě)完了16個(gè)數據,寫(xiě)跳過(guò)ROM和寫(xiě)讀取溫度轉換命令 next_state = RD_DATA; //跳轉到讀取溫度數據狀態(tài) else next_state = RD_CMD; end RD_DATA :begin if(bit_cnt == 4'd15 && cnt_us == 20'd62) //讀取完了16個(gè)數據 next_state = INIT1; //跳轉到初始化狀態(tài),開(kāi)始新一輪溫度采集 else next_state = RD_DATA; end default:next_state = INIT1; //默認初始化狀態(tài) endcase end //狀態(tài)機第三段:時(shí)序邏輯描述輸出 always @(posedge clk_us or negedge rst_n)begin if(!rst_n)begin //默認輸出 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í)間計數到最大值(初始化時(shí)間) cnt_us <= 20'd0; //計數器清零 flag_ack <= 1'b0; //從機響應標志信號拉低 end else begin //沒(méi)有計數到最大值 cnt_us <= cnt_us + 1'd1; //計數器計數 if(cnt_us <= 20'd499)begin //小于500us時(shí) dq_en <= 1'b1; //控制總線(xiàn) dq_out <= 1'b0; //輸出0,即拉低總線(xiàn) end else begin //在500us處 dq_en <= 1'b0; //釋放總線(xiàn),等待從機響應 if (cnt_us == 20'd570 && !dq_in) //在570us處采集總線(xiàn)電平,如果為0則說(shuō)明從機響應了 flag_ack <= 1'b1; //拉高從機響應標志信號 end end end WR_CMD :begin if(cnt_us == 20'd62)begin //一個(gè)寫(xiě)時(shí)隙周期63us,滿(mǎn)足計時(shí)條件則 cnt_us <= 20'd0; //清空計數器 dq_en <= 1'b0; //釋放總線(xiàn) if(bit_cnt == 4'd15) //如果數據已經(jīng)寫(xiě)了15個(gè) bit_cnt <= 4'd0; //則清空 else //沒(méi)寫(xiě)15個(gè) bit_cnt <= bit_cnt + 1'd1; //則數據計數器+1,代表寫(xiě)入了一個(gè)數據 end else begin //一個(gè)寫(xiě)時(shí)隙周期63us未完成 cnt_us <= cnt_us + 1'd1; //計數器一直計數 if(cnt_us <= 20'd1)begin //0~1us(每?jì)蓚€(gè)寫(xiě)數據之間需要間隔2us) dq_en <= 1'b1; //拉低總線(xiàn) dq_out <= 1'b0; end else begin if (WR_CMD_DATA[bit_cnt] == 1'b0)begin //需要寫(xiě)入的數據為0 dq_en <= 1'b1; //拉低總線(xiàn) dq_out <= 1'b0; // end else if(WR_CMD_DATA[bit_cnt] == 1'b1)begin dq_en <= 1'b0; //需要寫(xiě)入的數據為1 dq_out <= 1'b0; //釋放總線(xiàn) end end end end WAIT :begin //等待溫度轉換完成 dq_en <= 1'b1; //拉低總線(xiàn)兼容寄生電源模式 dq_out <= 1'b1; if(cnt_us == T_WAIT) //計數完成 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 //寫(xiě)16個(gè)數據,時(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位溫度數據 if(cnt_us == 20'd62)begin //一個(gè)讀時(shí)隙周期63us,滿(mǎn)足計時(shí)條件則 cnt_us <= 20'd0; //清空計數器 dq_en <= 1'b0; //釋放總線(xiàn) if(bit_cnt == 4'd15)begin //如果數據已經(jīng)讀取了15個(gè) bit_cnt <= 4'd0; //則清空 data <= data_temp; //臨時(shí)的數據賦值給data end else begin //如果數據沒(méi)有讀取15個(gè) bit_cnt <= bit_cnt + 1'd1; //則數據計數器+1,意味著(zhù)讀取了一個(gè)數據 data <= data; end end else begin //一個(gè)讀時(shí)隙周期還沒(méi)結束 cnt_us <= cnt_us + 1'd1; //計數器累加 if(cnt_us <= 20'd1)begin //0~1us(每?jì)蓚€(gè)讀數據之間需要間隔2us) dq_en <= 1'b1; //拉低總線(xiàn) dq_out <= 1'b0; end else begin //2us后 dq_en <= 1'b0; //釋放總掉線(xiàn) if (cnt_us == 20'd10) //在10us處讀取總線(xiàn)電平 data_temp <= {dq,data_temp[15:1]}; //讀取總線(xiàn)電平 end end end default:; endcase end end //----------------------------------------------------------------------- //--12位溫度數據處理 //----------------------------------------------------------------------- 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; //標志位為正 temp_data <= data[10:0] * 11'd625 /7'd100; //12位溫度數據處理 end else if(data[15])begin //最高位為1則溫度為負 sign <= 1'b1; //標志位為負 temp_data <= (~data[10:0] + 1'b1)* 11'd625 /7'd100; //12位溫度數據處理 end end end endmodule
2.4、調試 因為通訊過(guò)程涉及到從機的響應,我又找不到相應的器件模型,仿真就不搞了。 直接使用signaltap抓下波形: 上圖中: 狀態(tài)機開(kāi)始運行,進(jìn)入INTI1的初始化狀態(tài) dht11_en拉高同時(shí)dht11_out為0,說(shuō)明主機拉低了總線(xiàn) 主機拉低總線(xiàn)后在500us處釋放了總線(xiàn),總線(xiàn)被上拉電阻拉高 在528us處總線(xiàn)被從機拉低,直至637us從機才釋放了總線(xiàn),說(shuō)明從機發(fā)送了響應 在570us處因為總線(xiàn)被從機拉低,所以拉高了響應信號flag_ack,直到進(jìn)入下個(gè)狀態(tài)才將flag_ack拉低 上圖中: 狀態(tài)機從INTI1的初始化狀態(tài)跳轉到寫(xiě)入ROM和溫度轉換命令的狀態(tài)WR_CMD bit_cnt從0計數到F,說(shuō)明寫(xiě)入了16個(gè)數據;與此同時(shí),總線(xiàn)上也在分別寫(xiě)入“0”和“1” 上圖中: 狀態(tài)機從WAIT狀態(tài)跳轉到初始化狀態(tài)INIT2 cnt_us計數器從780000清零,說(shuō)明此時(shí)延時(shí)了780ms的時(shí)間以便完成溫度轉換 上圖中: 狀態(tài)機從狀態(tài)RD_CMD跳轉到數據讀取狀態(tài)RD_DATA bit_cnt從0計數到F,說(shuō)明讀取了16個(gè)數據;與此同時(shí),總線(xiàn)上也在分別輸出“0”和“1” 上圖中: data是直接從DS18B20溫度寄存器中讀取的數據0000000111111001 temp_data是處理后發(fā)送給數碼管顯示的數據3156,對應攝氏度31.56 3、上板調試 添加數碼管顯示模塊,編譯工程,板卡顯示如下: 和用調試軟件抓取的結果一致。 4、參考 DS18B20—Dalas Semiconductor FPGA Verilog 開(kāi)發(fā)實(shí)戰指南—基于 Intel Cyclone IV—野火電子
評論