[FPGA] Verilog 燃氣竈控制器的設計與實現

燃氣竈控制器的設計與實現

1、引述

本次實驗所用可編程器件型號爲MAXII EPM1270T144C5(其引腳表見本人另外一博文,連接爲 https://www.cnblogs.com/RDJLM/p/12075584.html),經過可編程實驗板實現一個基本的模擬燃氣竈。html

2、設計課題的基本要求

一、 燃氣竈的控制按鍵有三個:點火/關閉按鍵 BTN七、火力調節按鍵 BTN6(火力增大) 和 BTN5(火力減少)git

二、 用 8×8 雙色點陣模擬顯示燃氣竈的竈眼,用如圖 1 所示的四個點陣顯示狀態分別表示火力的四個檔位,從左到右依次爲微火、小火、中火和大火,點陣沒有任何顯示錶示熄火狀態編程

 

三、 燃氣竈上電時竈眼應處於熄火狀態,在熄火狀態下按一下按鍵 BTN7 點燃燃氣竈進入燃燒狀態,在燃燒狀態下按一下按鍵 BTN7 則熄滅燃氣竈進入熄滅狀態spa

四、 燃氣竈在熄滅狀態下,按一下按鍵 BTN7 進入燃燒狀態時的初始火力爲小火設計

五、 在燃燒狀態下用按鍵 BTN6(火力增大)和 BTN5(火力減少)改變火力的大小, 每按一次按鍵火力增大或減少一檔(對應的點陣顯示改變一檔);在大火狀態下按鍵BTN6 不起效,在微火狀態下按鍵 BTN5 不起效,並且在大火狀態下按 BTN6 或微火狀態下按 BTN5 要有報警聲,提示火力已到極限code

3、系統設計

一、設計思路:1)點陣的圖案顯示經過快速掃描達到人眼可以定格看到穩定的圖案htm

             2)點陣顯示圖案的轉換經過狀態機實現blog

             3)蜂鳴器聲音的產生經過按下按鍵產生一個方波脈衝實現,聲音的頻率則需根據自定義的音調換算成頻率接口

             4)爲貼近生活,引入計數器功能,實現倒計時結束,燃氣竈自動關閉的效果進程

             5)爲方便使用者明瞭的知道使用時燃氣竈火焰的大小狀態,引入 LCD 液晶屏功能,在不一樣的火焰狀態下對應顯示不一樣的英文顯示

二、整體框圖



三、分塊設計:1)狀態機:狀態轉移狀況的羅列

          2)點陣顯示

         3)產生方波脈衝控制蜂鳴器

         4)數碼管顯示

         5)倒計時器

         6)LCD1602液晶屏顯示

4、功能說明

一、燃氣竈上電時竈眼應處於熄火狀態,在熄火狀態下按一下按鍵 BTN7 點燃燃氣竈進入燃燒狀態,在燃燒狀態下按一下按鍵 BTN7 則熄滅燃氣竈進入熄滅狀態;燃氣竈在熄滅狀態下,按一下按鍵 BTN7 進入燃燒狀態時的初始火力爲小火;在燃燒狀態下用按鍵 BTN6(火力增大)和 BTN5(火力減少)改變火力的大小, 每按一次按鍵火力增大或減少一檔(對應的點陣顯示改變一檔),順序爲從微火、小火、中火、大火依次增大;在大火狀態下按鍵BTN6 不起效,在微火狀態下按鍵 BTN5 不起效,並且在大火狀態下按 BTN6 或微火狀態下按 BTN5 有報警聲,提示火力已到極限。

二、倒計時器設定的計時時間爲20s到0s,按下BTN3倒計時器開始倒計時,倒計時過程當中,再按一下BTN3實現計時暫停,再按一下BTN3則會又開始倒計時;任意時候按下復位鍵(BTN2)會立馬復位到初始設定的20s;當倒計時到0時,不管燃氣竈處於何種燃燒狀態,都會立馬自動熄滅。

三、燃氣竈上電後向上撥動撥碼開關sw7,在LCD1602液晶屏上會出現「Welcome to use The Gas stove」的英文提示;在不一樣的燃燒狀態下會對應顯示不一樣的提示語,微火、小火、中火、大火分別對應「Fire Level Micro Fire」、「Fire Level Little Fire」、「Fire Level Medium Fire 」、「Fire Level Big Fire」。

 

 5、模塊代碼

一、LCD1602液晶屏

這裏要鄭重感謝一下方清歡大佬的技術與理論支持,關於這部分的解釋詳見方清歡大佬的博文(連接:https://www.cnblogs.com/Clouds42/p/11938079.html)

下面就來看一下在燃氣竈不一樣的火焰狀態下對應液晶屏顯示不一樣文字的效果圖吧!

   1)熄滅狀態顯示歡迎使用提示語

   2)微火狀態

   3)小火狀態

   4)中火狀態


  

   5)大火狀態

 

二、狀態機中的內容顯示(包含點陣顯示、蜂鳴器的方波脈衝產生和液晶屏顯示內容的賦值)

蜂鳴器的音調可調,從低音到中音到高音,每一個音段裏包含do ri mi fa so la xi do這7個音調可供選擇

(各音調的不一樣頻率複製見本人另外一博文,連接爲 https://www.cnblogs.com/RDJLM/p/12075661.html)

always@(posedge clk) //狀態機內容的顯示
begin
     case(sc)
         s0: //不顯示,關機狀態
             begin
                 row_1<=" Welcome to use "; //對液晶屏顯示的內容進行賦值
                 row_2<=" The Gas stove  ";
                 G_col<=8'b0;
                 R_col<=8'b0;
                 row<=8'b11111111;
               end
         s1: //顯示小火
             begin
                 row_1<="   Fire Level   ";
                 row_2<="   Little Fire  ";
                 G_col<=8'b0;
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00000000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00011000; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b00100100; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b00100100; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00011000; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00000000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
                 endcase    
             end
         s2: //顯示中火
             begin
                 row_1<="   Fire Level   ";
                 row_2<="   Medium Fire  ";
                 G_col<=8'b0;
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00011000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00100100; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b01011010; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b01011010; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00100100; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00011000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
                 endcase
             end
         s3: //顯示大火,大火中的橙色採用紅、綠色掃描疊加實現
             begin
                 row_1<="   Fire Level   ";
                 row_2<="    Big Fire    ";
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00011000;G_col<=8'b00011000;end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b010:begin row<=8'b11111011;R_col<=8'b01011010;G_col<=8'b01000010;end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b100:begin row<=8'b11101111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b101:begin row<=8'b11011111;R_col<=8'b01011010;G_col<=8'b01000010;end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00011000;G_col<=8'b00011000;end
                 endcase
             end
         s4: //顯示微火
             begin
                 row_1<="   Fire Level   ";
                 row_2<="   Micro Fire   ";
                 G_col<=8'b0;
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00000000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00000000; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b00011000; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b00011000; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00000000; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00000000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
                 endcase
             end
         s5: //大火報警
             begin
                 row_1<="   Fire Level   ";
                 row_2<="    Big Fire    ";
                 if(BTN6)
                     begin
                     cnt2<=cnt2+1;
                         if(cnt2==8'd253)
                             begin
                             cnt2<=0;
                             beep<=~beep;
                             end
                         else
                         beep<=beep;
                     end
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00011000;G_col<=8'b00011000;end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b010:begin row<=8'b11111011;R_col<=8'b01011010;G_col<=8'b01000010;end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b100:begin row<=8'b11101111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b101:begin row<=8'b11011111;R_col<=8'b01011010;G_col<=8'b01000010;end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00011000;G_col<=8'b00011000;end
                 endcase
             end
         s6: //微火報警
         begin
             row_1<="   Fire Level   ";
             row_2<="   Micro Fire   ";
             G_col<=8'b0;
             if(BTN5)
                 begin
                 cnt2<=cnt2+1;
                     if(cnt2==8'd253) //最高音的do(H7)
                         begin
                         cnt2<=0;
                         beep<=~beep;
                     end
                     else
                         beep<=beep;
                 end
             case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00000000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00000000; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b00011000; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b00011000; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00000000; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00000000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
             endcase
         end
     endcase
end

三、倒計時器模塊

always@(posedge clk_1hz or posedge rst_n) //倒計時器功能進程 
begin
     if(rst_n)
         begin
         ud<=4'b0000;
         td<=3'b010;
         end
     else if(flag)
         begin
             if(ud==4'b0000&&td==3'b000)
                 begin
                 ud<=4'b0000;
                 td<=3'b000;
                 end
             else if(ud==4'b0000)
                 begin
                 ud<=4'b1001;
                 td<=td-1;
                 end
             else 
                 ud<=ud-1;
         end    
     else
         begin
         ud<=ud;
         td<=td;
         end
end

四、數碼管顯示模塊

always@(posedge clk) //數碼管掃描時鐘產生進程
begin
     if(count2==2000)
         begin
         count2<=0;
         clk_scan<=~clk_scan;
         end
     else
         count2<=count2+1;
end
always @(posedge clk_scan)
begin 
     if(cnt3==2'b11)
         cnt3<=2'b00;
     else
         cnt3<=cnt3+1;
end
always @(ud or td or cnt3)
begin
     if(sc==s0)
     DS<=8'b1111_1111;
     else if(cnt3==2'b01)
     begin
         DS<=8'b1111_1110; 
         case(ud) //倒計時器個位數顯示
             4'b0000:begin duan=8'b0011_1111;end
             4'b0001:begin duan=8'b0000_0110;end
             4'b0010:begin duan=8'b0101_1011;end
             4'b0011:begin duan=8'b0100_1111;end
             4'b0100:begin duan=8'b0110_0110;end
             4'b0101:begin duan=8'b0110_1101;end
             4'b0110:begin duan=8'b0111_1101;end
             4'b0111:begin duan=8'b0000_0111;end
             4'b1000:begin duan=8'b0111_1111;end
             4'b1001:begin duan=8'b0110_1111;end
         endcase
     end
     else if(cnt3==2'b10)
     begin
         DS<=8'b1111_1101; 
         case(td) //倒計時器十位數顯示
             3'b000:duan=8'b0011_1111;
             3'b001:duan=8'b0000_0110;
             3'b010:duan=8'b0101_1011;
             3'b011:duan=8'b0100_1111;
             3'b100:duan=8'b0110_0110;
             3'b101:duan=8'b0110_1101;
             3'b110:duan=8'b0111_1101;
         endcase
     end
     else if(cnt3==2'b11)
     DS<=8'b1111_1111;
end

五、按鍵消抖模塊

module debounce1(clk,rst,key,key_pulse1); //消抖模塊1
parameter N  =  1; //要消除的按鍵的數量
input clk;
input rst;
input [N-1:0] key; //輸入的按鍵                    
output [N-1:0] key_pulse1; //按鍵動做產生的脈衝    
reg [N-1:0] key_rst_pre; //定義一個寄存器型變量存儲上一個觸發時的按鍵值
reg [N-1:0] key_rst; //定義一個寄存器變量儲存儲當前時刻觸發的按鍵值
wire [N-1:0] key_edge; //檢測到按鍵由高到低變化是產生一個高脈衝
//利用非阻塞賦值特色,將兩個時鐘觸發時按鍵狀態存儲在兩個寄存器變量中
always @(posedge clk or posedge rst)
     begin
         if(rst) 
         begin
             key_rst <= {N{1'b1}}; //初始化時給key_rst賦值全爲1,{}中表示N個1
             key_rst_pre <= {N{1'b1}};
         end
         else 
         begin
             key_rst <= key; //第一個時鐘上升沿觸發以後key的值賦給key_rst,同時key_rst的值賦給key_rst_pre
             key_rst_pre <= key_rst; //非阻塞賦值。至關於通過兩個時鐘觸發,key_rst存儲的是當前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值
         end    
     end
assign key_edge = (~key_rst_pre) & key_rst; //脈衝邊沿檢測。當key檢測到上升沿時,key_edge產生一個時鐘週期的高電平
reg[13:0] cnt; //產生延時所用的計數器,系統時鐘1MHz,要延時15ms左右時間,至少須要14位計數器     
//產生20ms延時,當檢測到key_edge有效是計數器清零開始計數
always@(posedge clk or posedge rst)
begin
     if(rst)
         cnt <= 14'h0;
     else if(key_edge)
         cnt <= 14'h0;
     else
         cnt <= cnt + 1'h1;
end  
reg [N-1:0] key_sec_pre; //延時後檢測電平寄存器變量
reg [N-1:0] key_sec; //延時後檢測key,若是按鍵狀態變高產生一個時鐘的高脈衝。若是按鍵狀態是低的話說明按鍵無效
always@(posedge clk or posedge rst)
begin
     if(rst) 
         key_sec <= {N{1'b1}};                
     else if (cnt==14'h3a98)
         key_sec <= key;  
end
always@(posedge clk or posedge rst)
begin
     if(rst)
         key_sec_pre <= {N{1'b1}};
     else                   
         key_sec_pre <= key_sec;             
end      
assign key_pulse1 = (~key_sec_pre) & key_sec;      
endmodule

6、完整代碼

module Gasstove(clk,rst,rst_n,BTN7,BTN6,BTN5,BTN3,row,R_col,G_col,beep,duan,DS,_RST,LCD_E,LCD_RS,LCD_DATA); //定義燃氣竈模塊
input clk,rst,rst_n,BTN7,BTN6,BTN5,BTN3,_RST; //BTN7作總開關,BTN6爲加大火力鍵,BTN5爲減少火力鍵,BTN3控制計時開始or暫停,rst賦BTN4,rst_n賦BTN2
output row,R_col,G_col,beep,duan,DS,LCD_E,LCD_RS,LCD_DATA;
reg [7:0] row,R_col,G_col; //點陣的行、紅燈列和綠燈列顯示
reg [2:0] cnt1; //計數器1,用於點陣的行、列掃描
reg [7:0] cnt2; //計數器2,用於產生蜂鳴器的音調頻率
reg [1:0] cnt3; //計數器3,用於分位顯示數碼管
reg [2:0] sc; //state_current表示現態
reg [2:0] sn; //state_next表示次態
reg beep; //蜂鳴器
reg flag; //啓動暫停標誌
reg clk_1hz; //1HZ(1s)時鐘信號
reg clk_scan; //數碼管掃描時鐘
reg [3:0] ud; //數碼管個位數(unit's digit)
reg [2:0] td; //數碼管十位數(ten's digit)
reg [7:0] duan; //數碼管段碼
reg [7:0] DS; //數碼管位碼
reg LCD_RS; //LCD1602液晶屏數據指令寄存器選擇控制端
reg [7:0] LCD_DATA; //LCD1602液晶屏八位並行數據
reg [127:0] row_1; //LCD1602液晶屏第一行顯示內容(最多可顯示16個字符)
reg [127:0] row_2; //LCD1602液晶屏第二行顯示內容(最多可顯示16個字符)
integer count; //1HZ時鐘計數器
integer count2; //掃描時鐘計數器
parameter s0=3'b0; //熄滅狀態
parameter s1=3'b001; //小火狀態
parameter s2=3'b010; //中火狀態
parameter s3=3'b011; //大火狀態
parameter s4=3'b100; //微火狀態
parameter s5=3'b101; //大火下的報警狀態
parameter s6=3'b110; //微火下的報警狀態
wire key_pulse1; //消抖後的BTN7
wire key_pulse2; //消抖後的BTN6
wire key_pulse3; //消抖後的BTN5
wire key_pulse4; //消抖後的BTN3
debounce1  u1 (                    //例化消抖模塊1,爲BTN7按鍵消抖        
                       .clk (clk),
                       .rst (rst),
                       .key (BTN7),
                       .key_pulse1 (key_pulse1)
                             ) ;
debounce2  u2 (                    //例化消抖模塊2,爲BTN6按鍵消抖           
                       .clk (clk),
                       .rst (rst),
                       .key (BTN6),
                       .key_pulse2 (key_pulse2)
                             ) ;
debounce3  u3 (                    //例化消抖模塊3,爲BTN5按鍵消抖           
                       .clk (clk),
                       .rst (rst),
                       .key (BTN5),
                       .key_pulse3 (key_pulse3)
                             ) ;
debounce4  u4 (                    //例化消抖模塊4,爲BTN3按鍵消抖           
                       .clk (clk),
                       .rst (rst),
                       .key (BTN3),
                       .key_pulse4 (key_pulse4)
                             ) ;
parameter TIME_500HZ=2000; //工做週期,可編程器件實驗板MAXII EPM1270T144C5選擇時鐘晶振爲1MHz
reg[19:0]cnt_500hz;
parameter TIME_20MS=20000; //須要20ms以達上電穩定(初始化)
reg[19:0]cnt_20ms;
parameter IDLE=8'h00;
parameter SET_FUNCTION=8'h01;
parameter DISP_OFF=8'h03;
parameter DISP_CLEAR=8'h02;
parameter ENTRY_MODE=8'h06;
parameter DISP_ON=8'h07;
parameter ROW1_ADDR=8'h05;
parameter ROW1_0=8'h04;
parameter ROW1_1=8'h0C;
parameter ROW1_2=8'h0D;
parameter ROW1_3=8'h0F;
parameter ROW1_4=8'h0E;
parameter ROW1_5=8'h0A;
parameter ROW1_6=8'h0B;
parameter ROW1_7=8'h09;
parameter ROW1_8=8'h08;
parameter ROW1_9=8'h18;
parameter ROW1_A=8'h19;
parameter ROW1_B=8'h1B;
parameter ROW1_C=8'h1A;
parameter ROW1_D=8'h1E;
parameter ROW1_E=8'h1F;
parameter ROW1_F=8'h1D;
parameter ROW2_ADDR=8'h1C;
parameter ROW2_0=8'h14;
parameter ROW2_1=8'h15;
parameter ROW2_2=8'h17;
parameter ROW2_3=8'h16;
parameter ROW2_4=8'h12;
parameter ROW2_5=8'h13;
parameter ROW2_6=8'h11;
parameter ROW2_7=8'h10;
parameter ROW2_8=8'h30;
parameter ROW2_9=8'h31;
parameter ROW2_A=8'h33;
parameter ROW2_B=8'h32;
parameter ROW2_C=8'h36;
parameter ROW2_D=8'h37;
parameter ROW2_E=8'h35;
parameter ROW2_F=8'h34;
reg[5:0]c_state; //current state,當前狀態
reg[5:0]n_state; //next state,下一狀態
always@(posedge clk or negedge _RST)
begin
     if(!_RST)
         cnt_20ms<=1'b0;
     else if(cnt_20ms==TIME_20MS-1'b1)
         cnt_20ms<=cnt_20ms;
     else
         cnt_20ms<=cnt_20ms+1'b1 ;
end
wire delay_done=(cnt_20ms==TIME_20MS-1'b1)?1'b1:1'b0; //上電延時完畢
always@(posedge clk or negedge _RST)
begin   
     if(!_RST)
         cnt_500hz<=1'b0;
     else if(delay_done)
         if(cnt_500hz==TIME_500HZ-1'b1)
             cnt_500hz<=1'b0;
         else
             cnt_500hz<=cnt_500hz+1'b1;
     else
         cnt_500hz<=1'b0;
end
assign LCD_E=(cnt_500hz>(TIME_500HZ-1'b1)/2)?1'b0:1'b1; //使能端,每一個工做週期一次降低沿,執行一次命令
wire write_flag=(cnt_500hz==TIME_500HZ-1'b1)?1'b1:1'b0; //每到一個工做週期,write_flag置高一週期
always@(posedge clk or negedge _RST)
begin 
     if(!_RST)
         c_state<=IDLE;
     else if(write_flag) //每個工做週期改變一次狀態
         c_state<=n_state;
     else
         c_state<=c_state;
end
always@(*)
begin
     case(c_state)
         IDLE:n_state=SET_FUNCTION;
         SET_FUNCTION:n_state=DISP_OFF;
         DISP_OFF:n_state=DISP_CLEAR;
         DISP_CLEAR:n_state=ENTRY_MODE;
         ENTRY_MODE:n_state=DISP_ON;
         DISP_ON:n_state=ROW1_ADDR;
         ROW1_ADDR:n_state=ROW1_0;
         ROW1_0:n_state=ROW1_1;
         ROW1_1:n_state=ROW1_2;
         ROW1_2:n_state=ROW1_3;
         ROW1_3:n_state=ROW1_4;
         ROW1_4:n_state=ROW1_5;
         ROW1_5:n_state=ROW1_6;
         ROW1_6:n_state=ROW1_7;
         ROW1_7:n_state=ROW1_8;
         ROW1_8:n_state=ROW1_9;
         ROW1_9:n_state=ROW1_A;
         ROW1_A:n_state=ROW1_B;
         ROW1_B:n_state=ROW1_C;
         ROW1_C:n_state=ROW1_D;
         ROW1_D:n_state=ROW1_E;
         ROW1_E:n_state=ROW1_F;
         ROW1_F:n_state=ROW2_ADDR;
         ROW2_ADDR:n_state=ROW2_0;
         ROW2_0:n_state=ROW2_1;
         ROW2_1:n_state=ROW2_2;
         ROW2_2:n_state=ROW2_3;
         ROW2_3:n_state=ROW2_4;
         ROW2_4:n_state=ROW2_5;
         ROW2_5:n_state=ROW2_6;
         ROW2_6:n_state=ROW2_7;
         ROW2_7:n_state=ROW2_8;
         ROW2_8:n_state=ROW2_9;
         ROW2_9:n_state=ROW2_A;
         ROW2_A:n_state=ROW2_B;
         ROW2_B:n_state=ROW2_C;
         ROW2_C:n_state=ROW2_D;
         ROW2_D:n_state=ROW2_E;
         ROW2_E:n_state=ROW2_F;
         ROW2_F:n_state=ROW1_ADDR; //循環到1-1進行掃描顯示
         default:;
     endcase
end
always@(posedge clk or negedge _RST)
begin
     if(!_RST)
         LCD_DATA<=1'b0;
     else if(write_flag)
         case(n_state)
             IDLE:LCD_DATA<=8'hxx;
             SET_FUNCTION:LCD_DATA<=8'h38; //8'b0011_1000,工做方式設置:DL=1(DB4,8位數據接口),N=1(DB3,兩行顯示),L=0(DB2,5x8點陣顯示).
             DISP_OFF:LCD_DATA<=8'h08; //8'b0000_1000,顯示開關設置:D=0(DB2,顯示關),C=0(DB1,光標不顯示),D=0(DB0,光標不閃爍)
             DISP_CLEAR:LCD_DATA<=8'h01; //8'b0000_0001,清屏
             ENTRY_MODE:LCD_DATA<=8'h06; //8'b0000_0110,進入模式設置:I/D=1(DB1,寫入新數據光標右移),S=0(DB0,顯示不移動)
             DISP_ON:LCD_DATA<=8'h0c; //8'b0000_1100,顯示開關設置:D=1(DB2,顯示開),C=0(DB1,光標不顯示),D=0(DB0,光標不閃爍)
             ROW1_ADDR:LCD_DATA<=8'h80; //8'b1000_0000,設置DDRAM地址:00H->1-1,第一行第一位
             //將輸入的row_1以每8-bit拆分,分配給對應的顯示位
             ROW1_0:LCD_DATA<=row_1[127:120];
             ROW1_1:LCD_DATA<=row_1[119:112];
             ROW1_2:LCD_DATA<=row_1[111:104];
             ROW1_3:LCD_DATA<=row_1[103: 96];
             ROW1_4:LCD_DATA<=row_1[ 95: 88];
             ROW1_5:LCD_DATA<=row_1[ 87: 80];
             ROW1_6:LCD_DATA<=row_1[ 79: 72];
             ROW1_7:LCD_DATA<=row_1[ 71: 64];
             ROW1_8:LCD_DATA<=row_1[ 63: 56];
             ROW1_9:LCD_DATA<=row_1[ 55: 48];
             ROW1_A:LCD_DATA<=row_1[ 47: 40];
             ROW1_B:LCD_DATA<=row_1[ 39: 32];
             ROW1_C:LCD_DATA<=row_1[ 31: 24];
             ROW1_D:LCD_DATA<=row_1[ 23: 16];
             ROW1_E:LCD_DATA<=row_1[ 15:  8];
             ROW1_F:LCD_DATA<=row_1[  7:  0];
             ROW2_ADDR:LCD_DATA<=8'hc0; //8'b1100_0000,設置DDRAM地址:40H->2-1,第二行第一位
             ROW2_0:LCD_DATA<=row_2[127:120];
             ROW2_1:LCD_DATA<=row_2[119:112];
             ROW2_2:LCD_DATA<=row_2[111:104];
             ROW2_3:LCD_DATA<=row_2[103: 96];
             ROW2_4:LCD_DATA<=row_2[ 95: 88];
             ROW2_5:LCD_DATA<=row_2[ 87: 80];
             ROW2_6:LCD_DATA<=row_2[ 79: 72];
             ROW2_7:LCD_DATA<=row_2[ 71: 64];
             ROW2_8:LCD_DATA<=row_2[ 63: 56];
             ROW2_9:LCD_DATA<=row_2[ 55: 48];
             ROW2_A:LCD_DATA<=row_2[ 47: 40];
             ROW2_B:LCD_DATA<=row_2[ 39: 32];
             ROW2_C:LCD_DATA<=row_2[ 31: 24];
             ROW2_D:LCD_DATA<=row_2[ 23: 16];
             ROW2_E:LCD_DATA<=row_2[ 15:  8];
             ROW2_F:LCD_DATA<=row_2[  7:  0];
         endcase
     else
         LCD_DATA<=LCD_DATA;
end
always@(posedge clk or negedge _RST)
begin
     if(!_RST)
         LCD_RS<=1'b0; //爲0時輸入指令,爲1時輸入數據
     else if(write_flag)
         //當狀態爲七個指令任意一個,將RS置爲指令輸入狀態
         if((n_state==SET_FUNCTION)||(n_state==DISP_OFF)||(n_state==DISP_CLEAR)||(n_state==ENTRY_MODE)||(n_state==DISP_ON)||(n_state==ROW1_ADDR)||(n_state==ROW2_ADDR))
             LCD_RS<=1'b0; 
         else
             LCD_RS<=1'b1;
     else
         LCD_RS<=LCD_RS;
end
initial //初始設定數碼管顯示20
begin
     ud=4'b0000;
     td=3'b010;
end
always@(posedge clk or posedge rst) //計數器用於點陣顯示
begin
     if(rst)
         cnt1<= 3'b0;
     else
         cnt1<=cnt1+1'b1;
end
always@(posedge clk or posedge rst_n) //BTN3產生標誌信號,BTN3控制計時開始or暫停
begin
     if(rst_n)
         flag = 1'b0;
     else if(key_pulse4)
         flag = ~flag;
     else
         flag = flag;
end
always@(posedge clk) //1HZ時鐘進程
begin
     if(count==500000) 
         begin
         clk_1hz=~clk_1hz;
         count<=0;
         end
     else
         count<=count+1'b1;
end
always@(posedge clk_1hz or posedge rst_n) //倒計時器功能進程 
begin
     if(rst_n)
         begin
         ud<=4'b0000;
         td<=3'b010;
         end
     else if(flag)
         begin
             if(ud==4'b0000&&td==3'b000)
                 begin
                 ud<=4'b0000;
                 td<=3'b000;
                 end
             else if(ud==4'b0000)
                 begin
                 ud<=4'b1001;
                 td<=td-1;
                 end
             else 
                 ud<=ud-1;
         end    
     else
         begin
         ud<=ud;
         td<=td;
         end
end
always@(posedge clk) //數碼管掃描時鐘產生進程
begin
     if(count2==2000)
         begin
         count2<=0;
         clk_scan<=~clk_scan;
         end
     else
         count2<=count2+1;
end
always @(posedge clk_scan)
begin 
     if(cnt3==2'b11)
         cnt3<=2'b00;
     else
         cnt3<=cnt3+1;
end
always @(ud or td or cnt3)
begin
     if(sc==s0)
     DS<=8'b1111_1111;
     else if(cnt3==2'b01)
     begin
         DS<=8'b1111_1110; 
         case(ud) //倒計時器個位數顯示
             4'b0000:begin duan=8'b0011_1111;end
             4'b0001:begin duan=8'b0000_0110;end
             4'b0010:begin duan=8'b0101_1011;end
             4'b0011:begin duan=8'b0100_1111;end
             4'b0100:begin duan=8'b0110_0110;end
             4'b0101:begin duan=8'b0110_1101;end
             4'b0110:begin duan=8'b0111_1101;end
             4'b0111:begin duan=8'b0000_0111;end
             4'b1000:begin duan=8'b0111_1111;end
             4'b1001:begin duan=8'b0110_1111;end
         endcase
     end
     else if(cnt3==2'b10)
     begin
         DS<=8'b1111_1101; 
         case(td) //倒計時器十位數顯示
             3'b000:duan=8'b0011_1111;
             3'b001:duan=8'b0000_0110;
             3'b010:duan=8'b0101_1011;
             3'b011:duan=8'b0100_1111;
             3'b100:duan=8'b0110_0110;
             3'b101:duan=8'b0110_1101;
             3'b110:duan=8'b0111_1101;
         endcase
     end
     else if(cnt3==2'b11)
     DS<=8'b1111_1111;
end
always @(posedge clk or posedge rst) 
begin
     if(rst)
         begin
         sc<=s0;
         end
     else
         begin
         sc<=sn; //狀態轉移設定,不按下復位鍵時採用非阻塞賦值將次態賦給現態
         end
end
always@(key_pulse1 or key_pulse2 or key_pulse3) //採用狀態機描述七個狀態的轉移
begin
    case(sc) //對每個狀態可能的次態進行列舉,本質上就是狀態轉移圖的代碼化
     s0: 
         begin
             if(key_pulse1)
                 sn=s1; //採用阻塞賦值,將目標狀態賦給次態
             else if(key_pulse2)
                 sn=s0;
             else if(key_pulse3)
                 sn=s0;
             else if(ud==4'b0000&&td==3'b000)
                 sn=s0;
             else
                 sn=s0;
         end
     s1:
         begin
             if(key_pulse1)
                 sn=s0;
             else if(key_pulse2)
                 sn=s2;
             else if(key_pulse3)
                 sn=s4;
             else if(ud==4'b0000&&td==3'b000)
                 sn=s0;
             else
                 sn=s1;
         end
     s2:
         begin
             if(key_pulse1)
                 sn=s0;
             else if(key_pulse2)
                 sn=s3;
             else if(key_pulse3)
                 sn=s1;
             else if(ud==4'b0000&&td==3'b000)
                 sn=s0;
             else
                 sn=s2;
         end
     s3:
         begin
             if(key_pulse1)
                 sn=s0;
             else if(key_pulse2)
                 sn=s5;
             else if(key_pulse3)
                 sn=s2;
             else if(ud==4'b0000&&td==3'b000)
                 sn=s0;
             else
                 sn=s3;
         end
     s4:
         begin
             if(key_pulse1)
                 sn=s0;
             else if(key_pulse2)
                 sn=s1;
             else if(key_pulse3)
                 sn=s6;
             else if(ud==4'b0000&&td==3'b000)
                 sn=s0;
             else
                 sn=s4;
         end
     s5: //大火報警
         begin
             if(key_pulse1)
                 sn=s0;
             else if(key_pulse2)
                 sn=s5;
             else if(key_pulse3)
                 sn=s2;
             else if(ud==4'b0000&&td==3'b000)
                 sn=s0;
             else
                 sn=s5;
         end
     s6: //微火報警
         begin 
              if(key_pulse1)
                 sn=s0;
             else if(key_pulse2)
                 sn=s1;
             else if(key_pulse3)
                 sn=s6;
             else if(ud==4'b0000&&td==3'b000)
                 sn=s0;
             else 
                 sn=s6;
         end
     endcase
end
always@(posedge clk) //狀態機內容的顯示
begin
     case(sc)
         s0: //不顯示,關機狀態
             begin
                 row_1<=" Welcome to use "; //對液晶屏顯示的內容進行賦值
                 row_2<=" The Gas stove  ";
                 G_col<=8'b0;
                 R_col<=8'b0;
                 row<=8'b11111111;
               end
         s1: //顯示小火
             begin
                 row_1<="   Fire Level   ";
                 row_2<="   Little Fire  ";
                 G_col<=8'b0;
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00000000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00011000; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b00100100; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b00100100; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00011000; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00000000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
                 endcase    
             end
         s2: //顯示中火
             begin
                 row_1<="   Fire Level   ";
                 row_2<="   Medium Fire  ";
                 G_col<=8'b0;
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00011000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00100100; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b01011010; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b01011010; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00100100; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00011000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
                 endcase
             end
         s3: //顯示大火,大火中的橙色採用紅、綠色掃描疊加實現
             begin
                 row_1<="   Fire Level   ";
                 row_2<="    Big Fire    ";
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00011000;G_col<=8'b00011000;end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b010:begin row<=8'b11111011;R_col<=8'b01011010;G_col<=8'b01000010;end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b100:begin row<=8'b11101111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b101:begin row<=8'b11011111;R_col<=8'b01011010;G_col<=8'b01000010;end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00011000;G_col<=8'b00011000;end
                 endcase
             end
         s4: //顯示微火
             begin
                 row_1<="   Fire Level   ";
                 row_2<="   Micro Fire   ";
                 G_col<=8'b0;
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00000000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00000000; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b00011000; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b00011000; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00000000; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00000000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
                 endcase
             end
         s5: //大火報警
             begin
                 row_1<="   Fire Level   ";
                 row_2<="    Big Fire    ";
                 if(BTN6)
                     begin
                     cnt2<=cnt2+1;
                         if(cnt2==8'd253)
                             begin
                             cnt2<=0;
                             beep<=~beep;
                             end
                         else
                         beep<=beep;
                     end
                 case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00011000;G_col<=8'b00011000;end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b010:begin row<=8'b11111011;R_col<=8'b01011010;G_col<=8'b01000010;end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b100:begin row<=8'b11101111;R_col<=8'b10111101;G_col<=8'b10000001;end
                     3'b101:begin row<=8'b11011111;R_col<=8'b01011010;G_col<=8'b01000010;end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00100100;G_col<=8'b00100100;end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00011000;G_col<=8'b00011000;end
                 endcase
             end
         s6: //微火報警
         begin
             row_1<="   Fire Level   ";
             row_2<="   Micro Fire   ";
             G_col<=8'b0;
             if(BTN5)
                 begin
                 cnt2<=cnt2+1;
                     if(cnt2==8'd253) //最高音的do(H7)
                         begin
                         cnt2<=0;
                         beep<=~beep;
                     end
                     else
                         beep<=beep;
                 end
             case(cnt1)
                     3'b000:begin row<=8'b11111110;R_col<=8'b00000000; end
                     3'b001:begin row<=8'b11111101;R_col<=8'b00000000; end
                     3'b010:begin row<=8'b11111011;R_col<=8'b00000000; end 
                     3'b011:begin row<=8'b11110111;R_col<=8'b00011000; end
                     3'b100:begin row<=8'b11101111;R_col<=8'b00011000; end
                     3'b101:begin row<=8'b11011111;R_col<=8'b00000000; end
                     3'b110:begin row<=8'b10111111;R_col<=8'b00000000; end
                     3'b111:begin row<=8'b01111111;R_col<=8'b00000000; end
             endcase
         end
     endcase
end
endmodule

module debounce1(clk,rst,key,key_pulse1); //消抖模塊1
parameter N  =  1; //要消除的按鍵的數量
input clk;
input rst;
input [N-1:0] key; //輸入的按鍵                    
output [N-1:0] key_pulse1; //按鍵動做產生的脈衝    
reg [N-1:0] key_rst_pre; //定義一個寄存器型變量存儲上一個觸發時的按鍵值
reg [N-1:0] key_rst; //定義一個寄存器變量儲存儲當前時刻觸發的按鍵值
wire [N-1:0] key_edge; //檢測到按鍵由高到低變化是產生一個高脈衝
//利用非阻塞賦值特色,將兩個時鐘觸發時按鍵狀態存儲在兩個寄存器變量中
always @(posedge clk or posedge rst)
     begin
         if(rst) 
         begin
             key_rst <= {N{1'b1}}; //初始化時給key_rst賦值全爲1,{}中表示N個1
             key_rst_pre <= {N{1'b1}};
         end
         else 
         begin
             key_rst <= key; //第一個時鐘上升沿觸發以後key的值賦給key_rst,同時key_rst的值賦給key_rst_pre
             key_rst_pre <= key_rst; //非阻塞賦值。至關於通過兩個時鐘觸發,key_rst存儲的是當前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值
         end    
     end
assign key_edge = (~key_rst_pre) & key_rst; //脈衝邊沿檢測。當key檢測到上升沿時,key_edge產生一個時鐘週期的高電平
reg[13:0] cnt; //產生延時所用的計數器,系統時鐘1MHz,要延時15ms左右時間,至少須要14位計數器     
//產生20ms延時,當檢測到key_edge有效是計數器清零開始計數
always@(posedge clk or posedge rst)
begin
     if(rst)
         cnt <= 14'h0;
     else if(key_edge)
         cnt <= 14'h0;
     else
         cnt <= cnt + 1'h1;
end  
reg [N-1:0] key_sec_pre; //延時後檢測電平寄存器變量
reg [N-1:0] key_sec; //延時後檢測key,若是按鍵狀態變高產生一個時鐘的高脈衝。若是按鍵狀態是低的話說明按鍵無效
always@(posedge clk or posedge rst)
begin
     if(rst) 
         key_sec <= {N{1'b1}};                
     else if (cnt==14'h3a98)
         key_sec <= key;  
end
always@(posedge clk or posedge rst)
begin
     if(rst)
         key_sec_pre <= {N{1'b1}};
     else                   
         key_sec_pre <= key_sec;             
end      
assign key_pulse1 = (~key_sec_pre) & key_sec;      
endmodule

module debounce2(clk,rst,key,key_pulse2); //消抖模塊2
parameter N  =  1; //要消除的按鍵的數量
input clk;
input rst;
input [N-1:0] key; //輸入的按鍵                    
output [N-1:0] key_pulse2; //按鍵動做產生的脈衝    
reg [N-1:0] key_rst_pre; //定義一個寄存器型變量存儲上一個觸發時的按鍵值
reg [N-1:0] key_rst; //定義一個寄存器變量儲存儲當前時刻觸發的按鍵值
wire [N-1:0] key_edge; //檢測到按鍵由高到低變化是產生一個高脈衝
//利用非阻塞賦值特色,將兩個時鐘觸發時按鍵狀態存儲在兩個寄存器變量中
always @(posedge clk or posedge rst)
begin
     if(rst) 
         begin
             key_rst <= {N{1'b1}}; //初始化時給key_rst賦值全爲1,{}中表示N個1
             key_rst_pre <= {N{1'b1}};
         end
     else 
         begin
             key_rst <= key; //第一個時鐘上升沿觸發以後key的值賦給key_rst,同時key_rst的值賦給key_rst_pre
             key_rst_pre <= key_rst; //非阻塞賦值。至關於通過兩個時鐘觸發,key_rst存儲的是當前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值
         end    
     end
assign key_edge = (~key_rst_pre) & key_rst; //脈衝邊沿檢測。當key檢測到上升沿時,key_edge產生一個時鐘週期的高電平
reg[13:0] cnt; //產生延時所用的計數器,系統時鐘1MHz,要延時15ms左右時間,至少須要14位計數器     
//產生20ms延時,當檢測到key_edge有效是計數器清零開始計數
always@(posedge clk or posedge rst)
begin
     if(rst)
         cnt <= 14'h0;
     else if(key_edge)
         cnt <= 14'h0;
     else
          cnt <= cnt + 1'h1;
end  
reg [N-1:0] key_sec_pre; //延時後檢測電平寄存器變量
reg [N-1:0] key_sec; //延時後檢測key,若是按鍵狀態變高產生一個時鐘的高脈衝。若是按鍵狀態是低的話說明按鍵無效
always@(posedge clk or posedge rst)
begin
     if(rst) 
         key_sec <= {N{1'b1}};                
     else if (cnt==14'h3a98)
         key_sec <= key;  
end
always@(posedge clk or posedge rst)
begin
     if(rst)
         key_sec_pre <= {N{1'b1}};
     else                   
         key_sec_pre <= key_sec;             
end      
assign key_pulse2 = (~key_sec_pre) & key_sec;      
endmodule

module debounce3(clk,rst,key,key_pulse3); //消抖模塊1
parameter N  =  1; //要消除的按鍵的數量
input clk;
input rst;
input [N-1:0] key; //輸入的按鍵                    
output [N-1:0] key_pulse3; //按鍵動做產生的脈衝    
reg [N-1:0] key_rst_pre; //定義一個寄存器型變量存儲上一個觸發時的按鍵值
reg [N-1:0] key_rst; //定義一個寄存器變量儲存儲當前時刻觸發的按鍵值
wire [N-1:0] key_edge; //檢測到按鍵由高到低變化是產生一個高脈衝
//利用非阻塞賦值特色,將兩個時鐘觸發時按鍵狀態存儲在兩個寄存器變量中
always @(posedge clk or posedge rst)
begin
     if(rst) 
         begin
             key_rst <= {N{1'b1}}; //初始化時給key_rst賦值全爲1,{}中表示N個1
             key_rst_pre <= {N{1'b1}};
         end
     else
         begin
             key_rst <= key; //第一個時鐘上升沿觸發以後key的值賦給key_rst,同時key_rst的值賦給key_rst_pre
             key_rst_pre <= key_rst; //非阻塞賦值。至關於通過兩個時鐘觸發,key_rst存儲的是當前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值
         end    
     end
assign key_edge = (~key_rst_pre) & key_rst; //脈衝邊沿檢測。當key檢測到上升沿時,key_edge產生一個時鐘週期的高電平
reg[13:0] cnt; //產生延時所用的計數器,系統時鐘1MHz,要延時15ms左右時間,至少須要14位計數器     
//產生20ms延時,當檢測到key_edge有效是計數器清零開始計數
always@(posedge clk or posedge rst)
begin
     if(rst)
         cnt <= 14'h0;
     else if(key_edge)
         cnt <= 14'h0;
     else
         cnt <= cnt + 1'h1;
end  
reg [N-1:0] key_sec_pre; //延時後檢測電平寄存器變量
reg [N-1:0] key_sec; //延時後檢測key,若是按鍵狀態變高產生一個時鐘的高脈衝。若是按鍵狀態是低的話說明按鍵無效
always@(posedge clk or posedge rst)
begin
     if(rst) 
         key_sec <= {N{1'b1}};                
     else if (cnt==14'h3a98)
         key_sec <= key;  
end
always@(posedge clk or posedge rst)
begin
     if(rst)
          key_sec_pre <= {N{1'b1}};
     else                   
         key_sec_pre <= key_sec;             
end      
assign key_pulse3 = (~key_sec_pre) & key_sec;      
endmodule

module debounce4(clk,rst,key,key_pulse4); //消抖模塊1
parameter N  =  1; //要消除的按鍵的數量
input clk;
input rst;
input [N-1:0] key; //輸入的按鍵                    
output [N-1:0] key_pulse4; //按鍵動做產生的脈衝    
reg [N-1:0] key_rst_pre; //定義一個寄存器型變量存儲上一個觸發時的按鍵值
reg [N-1:0] key_rst; //定義一個寄存器變量儲存儲當前時刻觸發的按鍵值
wire [N-1:0] key_edge; //檢測到按鍵由高到低變化是產生一個高脈衝
//利用非阻塞賦值特色,將兩個時鐘觸發時按鍵狀態存儲在兩個寄存器變量中
always @(posedge clk or posedge rst)
begin
     if(rst) 
         begin
             key_rst <= {N{1'b1}}; //初始化時給key_rst賦值全爲1,{}中表示N個1
             key_rst_pre <= {N{1'b1}};
         end
     else
         begin
             key_rst <= key; //第一個時鐘上升沿觸發以後key的值賦給key_rst,同時key_rst的值賦給key_rst_pre
             key_rst_pre <= key_rst; //非阻塞賦值。至關於通過兩個時鐘觸發,key_rst存儲的是當前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值
         end    
end
assign key_edge = (~key_rst_pre) & key_rst; //脈衝邊沿檢測。當key檢測到上升沿時,key_edge產生一個時鐘週期的高電平
reg[13:0] cnt; //產生延時所用的計數器,系統時鐘1MHz,要延時15ms左右時間,至少須要14位計數器     
//產生20ms延時,當檢測到key_edge有效是計數器清零開始計數
always@(posedge clk or posedge rst)
begin
     if(rst)
          cnt <= 14'h0;
     else if(key_edge)
         cnt <= 14'h0;
     else
         cnt <= cnt + 1'h1;
end  
reg [N-1:0] key_sec_pre; //延時後檢測電平寄存器變量
reg [N-1:0] key_sec; //延時後檢測key,若是按鍵狀態變高產生一個時鐘的高脈衝。若是按鍵狀態是低的話說明按鍵無效
always@(posedge clk or posedge rst)
begin
     if(rst) 
         key_sec <= {N{1'b1}};                
     else if (cnt==14'h3a98)
         key_sec <= key;  
end
always@(posedge clk or posedge rst)
begin
     if(rst)
         key_sec_pre <= {N{1'b1}};
     else                   
         key_sec_pre <= key_sec;             
end      
assign key_pulse4 = (~key_sec_pre) & key_sec;      
endmodule

7、總結

       到這裏整個模擬燃氣竈就完成啦!本人編程水平、時間有限,這篇文章到這裏就要結束啦,歡迎廣大讀者評論留言,更歡迎你們指出本人的不足,但願能經過交流自身獲得提升。最後感謝你們的耐心閱讀!

相關文章
相關標籤/搜索