天天看點

【FPGA】DS18B20溫度顯示

目錄

專用名詞

一. 溫度傳感器介紹

1.2 DS18B20結構

二. DS18B20工作流程

2.1 三步驟

2.2 初始化序列

2.3 ROM指令

2.4 功能ROM指令

2.5 讀寫時序

三 代碼設計

3.1 狀态跳轉——工作流程

3.2 狀态跳轉條件

3.3 代碼

四 上闆驗證

五 總結

專用名詞

CONFIGURATION REGISTER 配置寄存器
scrathpad memory 緩沖存儲器、高速暫存存儲器、暫時存儲器
​
​      

一. 溫度傳感器介紹

DS18B20 預設溫度資料是 12bit(補碼),最高位為符号位(1:負溫,0:正溫),低 4bit為小數位,低 11bit 轉化為十進制後乘以 0.0625 即為實際溫度值。      

DS18B20 是一款常用的數字式溫度傳感器,輸出數字信号來表示溫度值,具有體積小,精度高等特點。DS18B20 的溫度測量範圍為-55℃~+125℃;精度為±0.5℃;輸出溫度值位寬可程式設計(9—12bit);單總線接口允許多個裝置挂在同一總線,可以用于部署分布式溫度采集系統;可配置的溫度轉換時間,最大溫度轉換時間為 750ms。

DS18B20的傳輸序列:

1. 初始化
2. ROM指令
3. 功能指令
​
當需要讀取 DS18B20 的溫度轉換結果時,ROM 指令發送完後,需要回到步驟 1,再次發送初始化序列,然後發送功能指令。      

1.2 DS18B20結構

【FPGA】DS18B20溫度顯示

高速緩存器的結構框圖如下:

【FPGA】DS18B20溫度顯示
高速緩存器一共有九個八位寄存器,位元組0和位元組1是存儲溫度的,從低位元組LSB到高位元組MSB,這兩個位元組不可以更改,隻能讀出。認配置溫度資料為12位,其中最高位為符号位,即溫度值共11位,最低四位為小數位。FPGA在讀取溫度資料時,一次會讀2位元組共16位,讀完後将低11位的二進制數轉化為十進制數後再乘以0.0625得到所測的實際溫度值。另外還需要判斷溫度的正負,前5個數字為符号位,這5位同時變化,我們隻需要判斷其中任何一位就可以了。前5位為1時,讀取的溫度為負值,則測到的數值需要取反加1再乘以0.0625才可得到實際溫度值。前5位為0時,讀取的溫度為正值,隻要将測得的數值乘以0.0625即可得到實際溫值。高溫出發值 TH 和低溫觸發值 TL 都是要滿足一定條件才會響應,需要自己設定。
【FPGA】DS18B20溫度顯示

由上圖可知DS18B20的高速緩存器共有9個8位寄存器,其中溫度資料低位(LSB)對應位元組 位址0,溫度資料高位(MSB)對應位元組位址1,以此類推,配置寄存器的位元組位址為4。溫度資料存放的格式如下圖:

【FPGA】DS18B20溫度顯示
【FPGA】DS18B20溫度顯示

DS18B20在出廠時預設配置溫度資料為12位,其中最高位為符号位,即溫度值共11位,最低四位為小數位。FPGA在讀取溫度資料時,一次會讀2位元組共16位,讀完後将低11位的二進制 數轉化為十進制數後再乘以0.0625得到所測的實際溫度值。

另外還需要判斷溫度的正負,前5 個數字為符号位,這5位同時變化,我們隻需要判斷其中任何一位就可以了。前5位為1時,讀 取的溫度為負值,則測到的數值需要取反加1再乘以0.0625才可得到實際溫度值。前5位為0時, 讀取的溫度為正值,隻要将測得的數值乘以0.0625即可得到實際溫度值。

高速緩存器中第四個位元組即為配置寄存器,使用者通過改變 R1 和 R0 的值來配置 DS18B20 的分辨率,上電預設為 R1=1 以及 R0=1(12 位分辨率)。需要注意的是轉換時間與分辨率時間是有關系的。另外寄存器中最高位和低 5 位作 為内部使用而保留使用,不可被寫入。轉換時間與位數關系如下表所示:

【FPGA】DS18B20溫度顯示

二. 單總線協定

2.1 單總線傳輸原理

由于DS18B20溫度傳感器采用單總線的方式進行通信,是以我們先簡略的介紹一下單總線

通信的原理。

單總線傳輸的定義:顧名思義,即主機和從機用一根總線進行通信,是一種半雙工的通信

方式,單線=時鐘線+資料線+控制線(+電源線)。理想狀況下一條總線上的從器件數量幾乎不

受數量限制。

 單總線優劣勢:

單總線技術具有線路簡單,硬體開銷少,成本低廉,便于總線擴充和維護等優點。但由于隻有一根總線,驅動能力一般較差,不能接過多的從器件,實際使用中,一般最多隻能接8個從器件;抗幹擾能力較差,一般隻能在中短距離的低速傳輸中使用;軟體設計複雜,事物往往有兩面性,硬體部分的簡單往往需要軟體在複雜度上做出犧牲。

 資料傳輸:

   單總線通信協定為確定資料的完整性,定義了如下幾種單線信号類型:複位脈沖、存在脈

沖、寫0、寫1、讀0和讀1。所有這些信号,除存在脈沖外,都是由總線控制器發出的。

   單總線協定中主機和從機(DS18B20)間的任何通訊都需要以初始化序列開始

2.2 資料傳輸三步驟

1. 初始化序列
2. rom指令
3. 功能指令      

2.3 初始化序列

【FPGA】DS18B20溫度顯示

 即初始化序列分為三個步驟:

1.主機發送複位脈沖 至少480us

2.上拉電阻将總線拉高 15~60us

3.DS18B20發送存在脈沖應答主機 60~240us

初始化—複位和存在脈沖

單總線上的所有事件都必須以初始化為開始。初始化序列由總線上的主裝置發出的複位脈沖以及緊跟着從裝置回應的存在脈沖構成。該存在脈沖是讓總線主裝置知道 DS18B20 在總線上并準備好運作

與 DS18B20 所有的通信都是由初始化開始的,初始化由主裝置發出的複位脈沖及 DS18B20 響應的存在脈沖組成。如下圖 所示。當 DS18B20 響應複位信号的存在脈沖 後,則其向主裝置表明其在該總線上,并且已經做好了執行指令的準備。 在初始化狀态,總線上的主裝置通過拉低 1-Wire 總線最少 480us 來表示發送複位脈 沖。發送完之後,主裝置要釋放總線進入接收模式。當總線釋放後,上拉電阻将 1- Wire 總線拉至高電平。當 DS18B20 檢測到該上升沿信号後,其等待 15us 至 60us 後将總線 拉低 60us 至 240us 來實作發送一個存在脈沖。

【FPGA】DS18B20溫度顯示

2.4 ROM指令

用于對 64 位的 ROM 操作;

F0H:搜尋ROM 
    當系統上電初始化後,主裝置可識别該總線上所有的從裝置的 ROM 編碼,這樣就可以使得主裝置确定總線上的從裝置的類型以及數量。 
33H:讀 ROM  
    該指令允許主裝置讀取 DS18B20 的 64 位 ROM 編碼,隻有在總線上隻有一個 DS18B20 時才能使用這個指令。如果總線上存在多個從裝置,發送此指令,則當所有從設 備都會回應時,将會引起資料沖突。
55H:比對 ROM 
    該比對 ROM 指令之後接着發出 64 位 ROM 編碼,使主裝置在多點總線上定位一隻特定的 DS18B20。隻有和 64 位 ROM 序列完全比對的 DS18B20 才會做出響應。總線上的其 他從裝置都将等待下一個複位脈沖。此指令在總線上有單個或多個器件時都可以使用。
CCH:跳過 ROM
    這條指令可以不用提供 64 位 ROM 編碼就進行下一步操作,在單點總線(一個 DS18B20 傳感器)情況下可以節省時間。如果總線上不止一個從裝置,在跳過 ROM 指令 之後跟着發一條讀指令,則所有從裝置将會同時執行溫度轉換,總線上就會發生資料沖突。
ECH:報警搜尋
    該指令的操作與跳過 ROM 指令基本相同,但是不同的是隻有溫度高于 TH 或低于 TL (達到報警條件)的從裝置才會響應。隻要不掉電,警報狀态将一直保持,直到溫度不在警報範圍内為止。
​
如果系統中隻有一個 DS18B20,就不需要讀取 ROM 與比對 ROM 操作,可以直接發送跳過 ROM(CCH)指令,然後就可以開始測量溫度。      

2.5 功能ROM指令

用于主機向DS18B20 的暫存器寫/讀資料,發溫度轉換以及讀取供電模式;

44H:溫度轉換 
    此指令為初始化單次溫度轉換,溫度轉換完後,轉換的溫度資料會寄存在高速緩存器 的 byte0(溫度資料低八位)和 byte1(溫度資料高八位)中,之後 DS18B20 恢複到低功耗 的閑置狀态。如果總線在該指令後發出讀時隙,若 DS18B20 正在進行溫度轉換則會響應 “0”,若完成了溫度轉換則響應“1”。如果是用的“寄生電源”供電模式,則在指令發 出後應立即強制拉高總線,拉高時間應大于時序要求。
4EH:寫暫存寄存器
    該指令使得主裝置向高速緩存器寫入 3 個位元組的資料。第一個位元組寫入高速緩存器的 byte2 中(TH 寄存器),第二個位元組的資料寫入 byte3 中(TL 寄存器),第三個位元組的資料寫入 byte4 中(配置寄存器)。所有的資料都是由低位到高位的順序寫入。複位可随時中斷寫入。
48H:複制 RAM 
    該指令是将高速緩存器中的 TH(byte2)、TL(byte3)以及配置寄存器(byte4)裡的 值拷貝到非易失性的存儲器 EEPROM 裡。如果總線控制器在這條指令之後跟着發出讀時 隙,而 DS18B20 又正在忙于把暫存器拷貝到 EEPROM 存儲器,DS18B20 就會輸出一個 “0”,如果拷貝結束的話,DS18B20 則輸出“1”。如果裝置采用“寄生電源”供電模 式,則在該指令發送後,必須立即強制拉高總線至少 10ms。
BEH:讀暫存寄存器
    從 byte0(溫度低八位)開始一直讀到 byte8(CRC 校驗),每個位元組的資料從低位開始傳送。若是不想讀取這麼多資料則在讀取資料時随時可以通過主機發送複位脈沖來停止讀取。
B8H:重調 EEPROM 
    該指令将溫度報警觸發值(TH 和 TL)及配置寄存器的資料從 EEPROM 中召回至高速 緩存器中。這個操作會在上電後自動執行一次,是以在上電期間暫存器中一直會存在有效 的資料。若在召回指令之後啟動讀時隙,若 DS18B20 正在進行召回 EEPROM 則會響應 “0”,若召回完成則響應“1”。
​
B4H:讀供電方式
    該指令可以讀取總線上的 DS18B20 是否是由“寄生電源”供電。在讀取資料時序中 “0”表示“寄生電源供”模式供電,“1”表示外部電源供電。
    
暫存器位元組 0 和位元組 1 是隻讀的,用于存儲傳感器測量的溫度值      

2.6 讀寫時序

【FPGA】DS18B20溫度顯示

寫時隙:

主裝置通過寫時隙将指令寫入 DS18B20 中,寫時隙有兩種情況:寫“1”和寫“0”時 隙。主裝置通過寫 1 時隙來向 DS18B20 中寫入邏輯 1,通過寫 0 時隙來向 DS18B20 中寫入 邏輯 0。當主裝置将總線從高電平拉至低電平時,啟動寫時隙,所有的寫時隙持續時間最 少為 60us,每個寫時隙間的恢複時間最少為 1us。 當總線(DQ)拉低後,DS18B20 在 15us 至 60us 之間對總線進行采樣,如果采的 DQ 為高電平則發生寫 1,如果為低電平則發生寫 0,如下圖所示(圖中的總線控制器即為主裝置)。 如果要産生寫 1 時隙,必須先将總線拉至邏輯低電平然後釋放總線,允許總線在寫 隙開始後 15us 内上拉至高電平。若要産生寫 0 時隙,必須将總線拉至邏輯低電平并保持不 變最少 60us。

讀時隙:

當我們發送完讀取供電模式[B4h]或讀高速緩存器[BEh]指令時,必須及時地生成讀時隙,隻有在讀時隙 DS18B20 才能向主裝置傳送資料。每個讀時隙最小必須有 60us 的持續 時間以及每個讀時隙間至少要有 1us 的恢複時間。當主裝置将總線從高電平拉至低電平超 過 1us,啟動讀時隙,如下圖所示。當啟動讀時隙後,DS18B20 将會向主裝置發送“0”或者“1”。DS18B20 通過将總線 拉高來發送 1,将總線拉低來發送 0 。當讀時隙完成後,DQ 引腳将通過上拉電阻将總線拉高至高電平的閑置狀态。從 DS18B20 中輸出的資料在啟動讀時隙後的 15us 内有效,是以,主裝置在讀時隙開始後的 15us 内必須釋放總線,并且對總線進行采樣。

三 代碼設計

3.1 狀态跳轉——工作流程

DS18B20的典型溫度讀取過程為:初始化➔發跳過ROM指令(CCH)➔發開始轉換指令(44H)➔延時➔初始化➔發送跳過ROM指令(CCH)➔發讀存儲器指令(BEH)➔連續讀出兩個位元組資料(即溫度)➔結束或開始下一循環。

【FPGA】DS18B20溫度顯示
【FPGA】DS18B20溫度顯示

主狀态機

就是用來表示溫度傳感器的工作流程的,首先是初始化

即初始化序列分為三個步驟:
1.主機發送複位脈沖 至少480us
2.上拉電阻将總線拉高即 釋放總線 15~60us
3.DS18B20發送存在脈沖應答主機 60~240us      

初始化完成之後,主機就可以向從機讀寫資料。主機向從機發送跳過ROM指令,跳過ROM指令對單總線上僅有一個從機的情況下可以節省很多時間,不用提供64位ROM編碼就可以進行下一步操作。

條件:

當從機接收到應答,且計數器記到60us進行采樣,slave_ack == 1'b0 方可發送rom指令
即從機接收應答到發送rom指令是,跳過ROm指令發出 M_ACK2M_ROMS      

本次實驗包含三個指令:

CMD_ROMS  = 8'hCC,//跳過ROM指令
CMD_CONT  = 8'h44,//溫度轉化
CMD_RTMP  = 8'hBE;//讀暫存器      

發送完跳過ROM指令之後進入發送功能指令

包括溫度轉換和讀取溫度暫存器指令,需要我們标記區分,是以需要一個flag标志,當m_wait2m_rest階段,flag為1,當m_rtmp2m_idle階段,則flag為0      

若m_roms2m_cont,表示轉換溫度指令,當跳過rom指令寫入完成,即當從狀态機完成資料寫入并且從狀态機處于S_DONE,且标志flag為0,将rom指令寫入之後,進入溫度轉換

溫度轉換也需要向暫存器寫入資料,即溫度最開始的LSB到MSB兩個位元組的資料,CMD_CONT = 8'h44指令寫入後,即當從狀态機完成資料寫入并且從狀态機處于S_DONE,進入等待狀态

等待狀态即等待溫度轉換完成,需要等待750ms,之後直接進入idle狀态或者主機發送複位脈沖狀态,之後再次進行初始化序列,完成後發送rom指令,再進行讀取溫度寄存器指令      

若m_roms2m_rcmd,表示讀溫度暫存器指令,即當從狀态機完成資料寫入并且從狀态機處于S_DONE,當跳過rom指令寫完後且flag為1,進入讀溫度指令。

讀溫度指令寫需要進行寫資料操作,當CMD_RTMP = 8'hBE寫入之後,即當從狀态機完成資料寫入并且從狀态機處于S_DONE,進入讀取溫度值,讀溫度值則進行讀操作,讀取溫度完成後進入idle狀态

從狀态機:

主機想從機進行寫資料(寫操作),或從機向主機發送資料(讀操作)

從機開啟條件:

assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
             m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);        

即當主機處于M_ROMS|| M_CONT || M_RCMD || M_RTMP時,從機開啟進入總線拉低狀态,s_low

assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;//拉低2us      

即當主機處于M_ROMS|| M_CONT || M_RCMD ,從機近些寫操作,向從機寫入八比特資料,

寫資料是一比特一比特的資料寫入,寫入一個比特需要60us,進入釋放總線狀态,S_RELE,一個位元組沒有寫完則繼續進行寫入,釋放總線 3us (至少1us),即s_rele2s_low,然後主機拉低總線至少1us,進行寫“0”或寫“1”,再進入釋放總線狀态,直到一個位元組資料寫完,從機從s_rele2s_done,釋放總線 3us (至少1us)。

讀溫度暫存器:當主機處于M_RTMP,從機進行讀操作,主機拉低總線至少1us,進行讀資料,讀1bit需要60us,直到2位元組資料讀完(低位元組資料LSB到高位元組MSB)一共16位。

計數器

計數器主要記主狀态機狀态轉移時間,和從狀态機的讀寫時間,以及資料位數

設定時間參數:

  parameter   TIME_1US  = 50,      //基本時間1us
                //主狀态機延時
                TIME_RST  = 500,     //複位脈沖 500us
                TIME_REL  = 20,      //主機釋放總線 20us
                TIME_PRE  = 200,     //主機接收存在脈沖 200us
                TIME_WAIT = 750000,  //主機發完溫度轉換指令 等待750ms
                //從狀态機的延時
                TIME_LOW  = 2,       //主機拉低總線 2us
                TIME_RW   = 60,      //主機讀、寫1bit 60us
                TIME_REC  = 3;       
                //主機讀寫完1bit釋放總線 3us包括   rele_low,rele_done      

bit計數器主要用于記寫入一個位元組資料或讀出兩個位元組資料

   assign add_cnt_bit = s_state_c == S_RELE && end_cnt1; // 從機處于RELE狀态,時隙恢複時間,表明1 bit 資料傳輸完成
    assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RTMP)?16-1:8-1);      

slave_ack:采樣傳感器的存在脈沖

   always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            slave_ack <= 1'b1;
        end     //接收應答狀态      計數器計到60us  進行采樣
        else if(m_state_c == M_RACK && cnt0 == 60 && end_cnt_1us)begin 
            slave_ack <= dq_din;
        end 
    end       

輸出信号

   //dq_out_en     
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            dq_out_en <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out_en <= 1'b1;      //輸出 dq_out
        end 
        else if(m_rest2m_rele | s_send2s_rele | s_low2s_samp)begin  
            dq_out_en <= 1'b0;      //不輸出 dq_out
        end 
    end 
​
    //dq_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dq_out <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out <= 1'b0;
        end 
        else if(s_low2s_send)begin 
            dq_out <= wr_data[cnt_bit];
        end 
    end      

溫度采集轉換并輸出:

//orign_data    溫度采集
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            orign_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt1 == 12 && end_cnt_1us)begin //溫度采集,在時隙起始後15us内采樣
            orign_data[cnt_bit] <= dq_din; 
        end 
    end
​
//temp_data     溫度判斷
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            temp_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin 
            if(orign_data[15])                          //判斷正負溫度,決定是否需要做補碼-原碼轉化
                temp_data <= ~orign_data[10:0] + 1'b1;  //負溫 則取反加1 
            else 
                temp_data <= orign_data[10:0];          //正溫
        end 
    end
​
/*
    實際的溫度值為 temp_data * 0.0625;
    為了保留4位小數精度,将實際溫度值放大了10000倍,
    即 temp_data * 625;
​
*/
    assign temp_data_w = temp_data * 625;       //組合邏輯計算十進制溫度
​
//temp_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out <= 0;
        end 
        else if(m_state_c == M_RTMP && s_rele2s_done)begin 
            temp_out <= temp_data_w;
        end 
    end 
​
//temp_out_vld                                     訓示溫度值輸出有效
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out_vld <= 0;
        end 
        else begin 
            temp_out_vld <= m_state_c == M_RTMP && s_rele2s_done;
        end 
    end       

狀态跳轉條件

主狀态機:

   assign m_idle2m_rest = m_state_c == M_IDLE && (1'b1);       //進入m_idle狀态,下一個時鐘周期上升沿将進入複位狀态
    assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);
    assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);
    assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0  && slave_ack == 1'b0);
    assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);
    assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);
    assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);
    assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);
    assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);
    assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);      

從狀态機:

   assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);     
​
    assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;   
    assign s_low2s_samp  = s_state_c == S_LOW  && (m_state_c == M_RTMP && end_cnt1); 
​
    assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);       
    assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);       
    assign s_rele2s_low  = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);   
    assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);         

3.2 代碼

3.2 狀态跳轉條件

主狀态機狀态跳轉條件:

   assign m_idle2m_rest = m_state_c == M_IDLE && (1'b1);       //進入m_idle狀态,下一個時鐘周期上升沿将進入複位狀态
    assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);
    assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);
    assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0  && slave_ack == 1'b0);
    assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);
    assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);
    assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);
    assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);
    assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);
    assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);      

從狀态機:

   assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);     
​
    assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;   
    assign s_low2s_samp  = s_state_c == S_LOW  && (m_state_c == M_RTMP && end_cnt1); 
​
    assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);       
    assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);       
    assign s_rele2s_low  = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);   
    assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);         

3.3 代碼

ds18_driver:

module ds18_driver(
    input                       clk          ,
    input                       rst_n        ,
    input                       dq_din        ,
    output  reg                 dq_out       ,
    output  reg                 dq_out_en    , 
    output  reg                 temp_sign    ,//溫度值符号位 0:正 1:負溫
    output  reg   [23:0]        temp_out     ,
    output  reg                 temp_out_vld 
);

//狀态機參數

    localparam  M_IDLE = 9'b0_0000_0001,//空閑狀态
                M_REST = 9'b0_0000_0010,//複位
                M_RELE = 9'b0_0000_0100,//釋放總線 -- ds18b20等待
                M_RACK = 9'b0_0000_1000,//接收應答 -- 主機接收存在脈沖
                M_ROMS = 9'b0_0001_0000,//rom指令  -- 跳過rom指令
                M_CONT = 9'b0_0010_0000,//轉化
                M_WAIT = 9'b0_0100_0000,//等待     -- 12bit分辨率下的溫度轉化時間
                M_RCMD = 9'b0_1000_0000,//讀指令   -- 讀暫存器指令
                M_RTMP = 9'b1_0000_0000;//讀溫度   -- 産生讀時隙 -- 接收2位元組帶符号位的補碼溫度值

    localparam  S_IDLE = 6'b00_0001,
                S_LOW  = 6'b00_0010,//拉低總線 -- 時隙的開始
                S_SEND = 6'b00_0100,//發送     -- 15us内
                S_SAMP = 6'b00_1000,//采樣     -- 在15us内
                S_RELE = 6'b01_0000,//釋放     -- 時隙的恢複時間
                S_DONE = 6'b10_0000;

    parameter   TIME_1US  = 50,      //基本時間1us
                //主狀态機延時
                TIME_RST  = 500,     //複位脈沖 500us
                TIME_REL  = 20,      //主機釋放總線 20us
                TIME_PRE  = 200,     //主機接收存在脈沖 200us
                TIME_WAIT = 750000,  //主機發完溫度轉換指令 等待750ms
                //從狀态機的延時
                TIME_LOW  = 2,       //主機拉低總線 2us
                TIME_RW   = 60,      //主機讀、寫1bit 60us
                TIME_REC  = 3;       //主機讀寫完1bit釋放總線 3us
    
    localparam  CMD_ROMS  = 8'hCC,//跳過ROM指令
                CMD_CONT  = 8'h44,//溫度轉化
                CMD_RTMP  = 8'hBE;//讀暫存器

//信号定義

    reg     [8:0]       m_state_c   ;//主狀态機
    reg     [8:0]       m_state_n   ;

    reg     [5:0]       s_state_c   ;//從狀态機
    reg     [5:0]       s_state_n   ;

    reg     [5:0]       cnt_1us     ;//1us計數器
    wire                add_cnt_1us ;
    wire                end_cnt_1us ;

    reg     [19:0]      cnt0        ;//複位脈沖、釋放、存在脈沖、等待750ms
    wire                add_cnt0    ;
    wire                end_cnt0    ;
    reg     [19:0]      X           ;

    reg     [5:0]       cnt1        ;//計數從狀态機每個狀态多少us
    wire                add_cnt1    ;
    wire                end_cnt1    ;
    reg     [5:0]       Y           ;   

    reg     [4:0]       cnt_bit     ;
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;

    reg                 slave_ack   ;//接收存在脈沖
    reg                 flag        ;//0:發溫度轉換指令 1:發溫度讀取指令
    reg     [7:0]       wr_data     ;
    reg     [15:0]      orign_data  ;//采樣溫度值寄存器
    
    reg     [10:0]      temp_data   ;
    wire    [23:0]      temp_data_w ;//組合邏輯計算實際溫度值 十進制

    wire                m_idle2m_rest   ;
    wire                m_rest2m_rele   ;
    wire                m_rele2m_rack   ;
    wire                m_rack2m_roms   ;
    wire                m_roms2m_cont   ;
    wire                m_roms2m_rcmd   ;
    wire                m_cont2m_wait   ;
    wire                m_wait2m_rest   ;
    wire                m_rcmd2m_rtmp   ;
    wire                m_rtmp2m_idle   ;

    wire                s_idle2s_low    ;
    wire                s_low2s_send    ;
    wire                s_low2s_samp    ;
    wire                s_send2s_rele   ;
    wire                s_samp2s_rele   ;
    wire                s_rele2s_low    ;
    wire                s_rele2s_done   ;


//狀态機設計
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            m_state_c <= M_IDLE;
        end 
        else begin 
            m_state_c <= m_state_n;
        end 
    end

    always @(*)begin 
        case(m_state_c)
            M_IDLE:begin 
                if(m_idle2m_rest)
                    m_state_n = M_REST;
                else 
                    m_state_n = m_state_c;
            end 
            M_REST:begin 
                if(m_rest2m_rele)
                    m_state_n = M_RELE;
                else 
                    m_state_n = m_state_c;
            end
            M_RELE:begin 
                if(m_rele2m_rack)
                    m_state_n = M_RACK;
                else 
                    m_state_n = m_state_c;
            end 
            M_RACK:begin 
                if(m_rack2m_roms)
                    m_state_n = M_ROMS;
                else 
                    m_state_n = m_state_c;
            end 
            M_ROMS:begin 
                if(m_roms2m_cont)
                    m_state_n = M_CONT;
                else if(m_roms2m_rcmd)
                    m_state_n = M_RCMD;
                else 
                    m_state_n = m_state_c;
            end 
            M_CONT:begin 
                if(m_cont2m_wait)
                    m_state_n = M_WAIT;
                else 
                    m_state_n = m_state_c;
            end 
            M_WAIT:begin 
                if(m_wait2m_rest)
                    m_state_n = M_REST;
                else 
                    m_state_n = m_state_c;
            end 
            M_RCMD:begin 
                if(m_rcmd2m_rtmp)
                    m_state_n = M_RTMP;
                else 
                    m_state_n = m_state_c;
            end 
            M_RTMP:begin 
                if(m_rtmp2m_idle)
                    m_state_n = M_IDLE;
                else 
                    m_state_n = m_state_c;
            end         
            default:m_state_n = M_IDLE;
        endcase 
    end

    assign m_idle2m_rest = m_state_c == M_IDLE && (1'b1);       //進入m_idle狀态,下一個時鐘周期上升沿将進入複位狀态
    assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);
    assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);
    assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0  && slave_ack == 1'b0);
    assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);
    assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);
    assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);
    assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);
    assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);
    assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);

//從狀态機
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            s_state_c <= S_IDLE;
        end 
        else begin 
            s_state_c <= s_state_n;
        end 
    end

    always @(*)begin 
        case(s_state_c)
            S_IDLE:begin 
                if(s_idle2s_low)
                    s_state_n = S_LOW;
                else 
                    s_state_n = s_state_c;
            end  
            S_LOW :begin 
                if(s_low2s_send)
                    s_state_n = S_SEND;
                else if(s_low2s_samp)
                    s_state_n = S_SAMP;
                else 
                    s_state_n = s_state_c;
            end  
            S_SEND:begin 
                if(s_send2s_rele)
                    s_state_n = S_RELE;
                else 
                    s_state_n = s_state_c;
            end  
            S_SAMP:begin 
                if(s_samp2s_rele)
                    s_state_n = S_RELE;
                else 
                    s_state_n = s_state_c;
            end  
            S_RELE:begin 
                if(s_rele2s_low)
                    s_state_n = S_LOW;
                else if(s_rele2s_done)
                    s_state_n = S_DONE;
                else 
                    s_state_n = s_state_c;
            end  
            S_DONE:begin               //進入s_done狀态,下一個時鐘周期上升沿将回到初始狀态
                    s_state_n = S_IDLE;
            end  
            default:s_state_n = S_IDLE;
        endcase 
    end

    assign s_idle2s_low  = s_state_c == S_IDLE && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);     

    assign s_low2s_send  = s_state_c == S_LOW  && (m_state_c == M_ROMS || 
                            m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;   
    assign s_low2s_samp  = s_state_c == S_LOW  && (m_state_c == M_RTMP && end_cnt1); 

    assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);       
    assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);       
    assign s_rele2s_low  = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);   
    assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);       

//計數器
    always @(posedge clk or negedge rst_n)begin //1us計數
        if(!rst_n)begin
            cnt_1us <= 0;
        end 
        else if(add_cnt_1us)begin 
            if(end_cnt_1us)begin 
                cnt_1us <= 0;
            end
            else begin 
                cnt_1us <= cnt_1us + 1;
            end 
        end
    end 
    assign add_cnt_1us = m_state_c != M_IDLE;   //非IDLE狀态持續計數
    assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US-1;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            cnt0 <= 0;
        end 
        else if(add_cnt0)begin 
            if(end_cnt0)begin 
                cnt0 <= 0;
            end
            else begin 
                cnt0 <= cnt0 + 1;
            end 
        end
    end                   
    assign add_cnt0 = (m_state_c == M_REST || m_state_c == M_RELE || m_state_c == M_RACK || m_state_c == M_WAIT) && end_cnt_1us;
    assign end_cnt0 = add_cnt0 && cnt0 == X-1;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            X <= 0;
        end 
        else if(m_state_c == M_REST)begin //       複位:500us (480us)
            X <= TIME_RST;
        end 
        else if(m_state_c == M_RELE)begin //       釋放總線:20us (15-60us 内)
            X <= TIME_REL;
        end 
        else if(m_state_c == M_RACK)begin //       接收應答:200us (60-240us)
            X <= TIME_PRE;
        end 
        else if(m_state_c == M_WAIT)begin //       等待:750ms (等待轉換完成)
            X <= TIME_WAIT;
        end
    end

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt1 <= 0;
        end 
        else if(add_cnt1)begin 
            if(end_cnt1)begin 
                cnt1 <= 0;
            end
            else begin 
                cnt1 <= cnt1 + 1;
            end 
        end
    end 
    assign add_cnt1 = (s_state_c == S_LOW || s_state_c == S_SEND || 
                       s_state_c == S_SAMP || s_state_c == S_RELE) && end_cnt_1us;
    assign end_cnt1 = add_cnt1 && cnt1 == Y-1;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            Y <= 0;
        end 
        else if(s_state_c == S_LOW)begin 
            Y <= TIME_LOW;                  //      主機拉低總線 2us    (大于1us)
        end
        else if(s_state_c == S_SEND || s_state_c == S_SAMP)begin 
            Y <= TIME_RW;                   //      主機讀寫1bit 60us   ()
        end 
        else begin                      
            Y <= TIME_REC;                  //      主機讀寫完1bit釋放總線 3us (至少1us)
        end 
    end 

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_bit <= 0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1;
            end 
        end
    end 
    assign add_cnt_bit = s_state_c == S_RELE && end_cnt1; // 從機處于RELE狀态,時隙恢複時間,表明1 bit 資料傳輸完成
    assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RTMP)?16-1:8-1);

//slave_ack  采樣傳感器的存在脈沖
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            slave_ack <= 1'b1;
        end     //接收應答狀态      計數器計到60us  進行采樣
        else if(m_state_c == M_RACK && cnt0 == 60 && end_cnt_1us)begin 
            slave_ack <= dq_din;
        end 
    end 

    always @(posedge clk or negedge rst_n)begin //指令發送标志 (區分溫度轉換和溫度讀取指令)
        if(!rst_n)begin 
            flag <= 0;
        end 
        else if(m_wait2m_rest)begin 
            flag <= 1'b1;
        end 
        else if(m_rtmp2m_idle)begin 
            flag <= 1'b0;
        end 
    end

//輸出信号
    //dq_out_en     
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            dq_out_en <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out_en <= 1'b1;      //輸出 dq_out
        end 
        else if(m_rest2m_rele | s_send2s_rele | s_low2s_samp)begin  
            dq_out_en <= 1'b0;      //不輸出 dq_out
        end 
    end 

    //dq_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dq_out <= 0;
        end 
        else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin 
            dq_out <= 1'b0;
        end 
        else if(s_low2s_send)begin 
            dq_out <= wr_data[cnt_bit];
        end 
    end

//wr_data   指令
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            wr_data <= 0;
        end 
        else if(m_rack2m_roms)begin 
            wr_data <= CMD_ROMS;
        end 
        else if(m_roms2m_cont)begin 
            wr_data <= CMD_CONT;
        end
        else if(m_roms2m_rcmd)begin 
            wr_data <= CMD_RTMP;
        end 
    end

//orign_data    溫度采集
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            orign_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt1 == 12 && end_cnt_1us)begin //溫度采集,在時隙起始後15us内采樣
            orign_data[cnt_bit] <= dq_din; 
        end 
    end

//temp_data     溫度判斷
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            temp_data <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin 
            if(orign_data[15])                          //判斷正負溫度,決定是否需要做補碼-原碼轉化
                temp_data <= ~orign_data[10:0] + 1'b1;  //負溫 則取反加1 
            else 
                temp_data <= orign_data[10:0];          //正溫
        end 
    end

/*
    實際的溫度值為 temp_data * 0.0625;
    為了保留4位小數精度,将實際溫度值放大了10000倍,
    即 temp_data * 625;

*/
    assign temp_data_w = temp_data * 625;

//temp_out
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out <= 0;
        end 
        else if(m_state_c == M_RTMP && s_rele2s_done)begin 
            temp_out <= temp_data_w;
        end 
    end 

//temp_out_vld                                     訓示溫度值輸出有效
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_out_vld <= 0;
        end 
        else begin 
            temp_out_vld <= m_state_c == M_RTMP && s_rele2s_done;
        end 
    end 

//temp_sign                                       (輸出至數位管顯示時使用)
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin 
            temp_sign <= 0;
        end 
        else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin 
            temp_sign <= orign_data[15];
        end 
    end 

endmodule

           

control:

module control( 
    input				clk		    ,
    input				rst_n	    ,

    input				din_sign    ,
    input		[23:0]  din		    ,//輸入24位十進制溫度值(*10000)
    input		    	din_vld 	,

    output  wire        dout_sign   ,
    output	wire[23:0]	dout	    ,//輸出每位數位管對應顯示的值
    output	reg	    	dout_vld	
);	
             
    //中間信号定義		 
    reg		[23:0]	din_r       ;
    reg             din_vld_r0  ;
    reg             din_vld_r1  ;

    wire    [7:0]   tmp_int_w   ;//整數部分
    reg     [7:0]   tmp_int_r   ;
    
    wire	[3:0]	tmp_int_w2  ;
    wire	[3:0]	tmp_int_w1  ;
    wire	[3:0]	tmp_int_w0  ;

    reg 	[3:0]	tmp_int_r2  ;
    reg 	[3:0]	tmp_int_r1  ;
    reg 	[3:0]	tmp_int_r0  ; 

    wire    [15:0]  tmp_dot_w   ;//小數部分
    reg     [15:0]  tmp_dot_r   ;

    wire	[3:0]	tmp_dot_w3  ;
    wire	[3:0]	tmp_dot_w0  ;
    wire	[3:0]	tmp_dot_w1  ;
    wire	[3:0]	tmp_dot_w2  ;

    reg 	[3:0]	tmp_dot_r3  ;
    reg 	[3:0]	tmp_dot_r0  ;
    reg 	[3:0]	tmp_dot_r1  ;
    reg 	[3:0]	tmp_dot_r2  ;

//din_r 
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            din_r <= 0;
        end 
        else if(din_vld)begin 
            din_r <= din;
        end 
    end

//din_vld_r0  din_vld_r1
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            din_vld_r0 <= 0;
            din_vld_r1 <= 0;
        end 
        else begin 
            din_vld_r0 <= din_vld;
            din_vld_r1 <= din_vld_r0;
        end 
    end

    assign tmp_int_w = din_r/10000;//拆分整數部分及小數部分
    assign tmp_dot_w = din_r%10000;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tmp_int_r <= 0;
        end 
        else if(din_vld_r0)begin 
            tmp_int_r <= tmp_int_w;
            tmp_dot_r <= tmp_dot_w;
        end 
    end

    assign tmp_int_w2 = tmp_int_r/100;//百位
    assign tmp_int_w1 = tmp_int_r/10%10 ;//十位
    assign tmp_int_w0 = tmp_int_r%10;//個位

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tmp_int_r2 <= 0;
            tmp_int_r1 <= 0;
            tmp_int_r0 <= 0;
        end 
        else if(din_vld_r1)begin 
            tmp_int_r2 <= tmp_int_w2;
            tmp_int_r1 <= tmp_int_w1;
            tmp_int_r0 <= tmp_int_w0;
        end 
    end

    assign tmp_dot_w0 = tmp_dot_r/1000;
    assign tmp_dot_w1 = tmp_dot_r/100%10;
    assign tmp_dot_w2 = tmp_dot_r/10%10;
    assign tmp_dot_w3 = tmp_dot_r%10;

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tmp_dot_r0 <= 0;
            tmp_dot_r1 <= 0;
            tmp_dot_r2 <= 0;
            tmp_dot_r3 <= 0;
        end 
        else if(din_vld_r1)begin 
            tmp_dot_r0 <= tmp_dot_w0;
            tmp_dot_r1 <= tmp_dot_w1;
            tmp_dot_r2 <= tmp_dot_w2;
            tmp_dot_r3 <= tmp_dot_w3;
        end 
    end

    assign dout = {tmp_int_r1,tmp_int_r0,tmp_dot_r0,tmp_dot_r1,tmp_dot_r2,tmp_dot_r3}; 

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dout_vld <= 1'b0;
        end 
        else begin 
            dout_vld <= din_vld_r1; 
        end 
    end

    assign dout_sign = din_sign;

endmodule
           

seg_driver:

module seg_driver(
    input                    clk     ,//時鐘信号
    input                    rst_n   ,//複位信号
    input                    din_sign,
    input        [23:0]      din     ,
    input                    din_vld ,

    output  reg  [5:0]       sel     ,
    output  reg  [7:0]       dig     
);

//參數定義
parameter       TIME_1MS = 25_000,

                ZERO     = 7'b100_0000,
                ONE      = 7'b111_1001,
                TWO      = 7'b010_0100,
                THREE    = 7'b011_0000,
                FOUR     = 7'b001_1001,
                FIVE     = 7'b001_0010,
                SIX      = 7'b000_0010,
                SEVEN    = 7'b111_1000,
                EIGHT    = 7'b000_0000,
                NINE     = 7'b001_0000,
                P        = 7'b000_1111,
                N        = 7'b011_1111;

//信号定義
reg     [19:0]      cnt_1ms;//掃描頻率計數器
wire                add_cnt_1ms;
wire                end_cnt_1ms;

reg     [3 :0]      disp_num;
reg                 dot;

//數位管掃描頻率計數
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_1ms <= 0;
   end
   else if(add_cnt_1ms) begin
      if(end_cnt_1ms)begin
         cnt_1ms <= 0;
      end
      else begin
         cnt_1ms <= cnt_1ms + 1;
      end
   end

end
assign add_cnt_1ms = 1'b1;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS - 1;


//seg_sel 數位管片選信号
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      sel <= 6'b111110;
   end
   else if(end_cnt_1ms) begin
      sel <= {sel[4:0],sel[5]};
   end
end

//譯碼
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      disp_num  <= 0;
   end
   else begin
      case(sel)
         6'b011111:begin disp_num <= din_sign?4'ha:4'hb;dot <= 1; end
         6'b101111:begin disp_num <= din[23:20];dot <= 1;end
         6'b110111:begin disp_num <= din[19:16];dot <= 0;end
         6'b111011:begin disp_num <= din[15:12];dot <= 1;end
         6'b111101:begin disp_num <= din[11:8] ;dot <= 1;end
         6'b111110:begin disp_num <= din[7 :4] ;dot <= 1;end
         default  :begin disp_num <= 4'hF ;end
      endcase
   end
end 


//segment 段選譯碼
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      dig <= 8'hff;
   end
   else begin//顯示小數點
      case(disp_num)
         4'h0:dig <= {dot,ZERO }  ;
         4'h1:dig <= {dot,ONE  }  ;
         4'h2:dig <= {dot,TWO  }  ;
         4'h3:dig <= {dot,THREE}  ;
         4'h4:dig <= {dot,FOUR }  ;
         4'h5:dig <= {dot,FIVE }  ;
         4'h6:dig <= {dot,SIX  }  ;
         4'h7:dig <= {dot,SEVEN}  ;
         4'h8:dig <= {dot,EIGHT}  ;
         4'h9:dig <= {dot,NINE }  ;
         4'ha:dig <= {dot,N    }  ;
         4'hb:dig <= {dot,P    }  ;
         default:dig <= 8'hff ;
      endcase
   end
end


endmodule
           

temp_top

module temp_top (
    input        clk            ,
    input        rst_n           ,
    inout        dq             ,

    output [5:0] sel            ,
    output [7:0] seg
);

//信号定義
    wire            dq_in       ;
    wire            dq_out      ;
    wire            dq_out_en   ;
    wire            temp_sign   ;
    wire    [23:0]  temp_out    ;
    wire            temp_out_vld;
    wire            dout_sign   ;
    wire    [23:0]  dout        ;
    wire            dout_vld    ;

    assign dq = dq_out_en?dq_out:1'bz;
    assign dq_in = dq;


//子產品例化
ds18_driver u_ds18_driver(
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .dq_din          (dq_in          ),//dq總線DS18B20輸出
    .dq_out         (dq_out         ),//dq總線FPGA輸出,DS18B20輸入
    .dq_out_en      (dq_out_en      ),//dq總線輸出使能控制信号
    .temp_sign      (temp_sign      ),//溫度的正負
    .temp_out       (temp_out       ),//輸出十進制溫度
    .temp_out_vld   (temp_out_vld   ) //溫度采集資料有效
);

control u_control(
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .din_sign       (temp_sign      ),
    .din            (temp_out       ),
    .din_vld        (temp_out_vld   ),
    .dout_sign      (dout_sign      ), 
    .dout           (dout           ),//輸出溫度值數位管對應位置的bcd碼
    .dout_vld       (dout_vld       )
);

seg_driver seg_driver(
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .din_sign       (temp_sign      ),
    .din            (dout           ),
    .din_vld        (dout_vld       ),
    .sel            (sel            ),
    .dig            (seg            )
);
endmodule //temp_top
           

四 上闆驗證

【FPGA】DS18B20溫度顯示

五 總結

多調試,多動手,畫好時序圖,畫好邏輯圖,注意狀态轉移條件。

繼續閱讀