主頁 > 後端開發 > [FPGA] Verilog 燃氣灶控制器的設計與實作

[FPGA] Verilog 燃氣灶控制器的設計與實作

2020-10-01 16:31:31 後端開發

燃氣灶控制器的設計與實作

一、引述

本次實驗所用可編程器件型號為MAXII EPM1270T144C5(其引腳表見本人另一博文:可編程實驗板EPM1270T144C5使用說明),通過可編程實驗板實作一個基本的模擬燃氣灶,

二、設計課題的基本要求

1、 燃氣灶的控制按鍵有三個:點火/關閉按鍵 BTN7、火力調節按鍵 BTN6(火力增大) 和 BTN5(火力減小)

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

 

3、 燃氣灶上電時灶眼應處于熄火狀態,在熄火狀態下按一下按鍵 BTN7 點燃燃氣灶進入燃燒狀態,在燃燒狀態下按一下按鍵 BTN7 則熄滅燃氣灶進入熄滅狀態

4、 燃氣灶在熄滅狀態下,按一下按鍵 BTN7 進入燃燒狀態時的初始火力為小火

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

三、系統設計

1、設計思路:1)點陣的圖案顯示通過快速掃描達到人眼能夠定格看到穩定的圖案

             2)點陣顯示圖案的轉換通過狀態機實作

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

             4)為貼近生活,引入計數器功能,實作倒計時結束,燃氣灶自動關閉的效果

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

2、總體框圖



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

          2)點陣顯示

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

         4)數碼管顯示

         5)倒計時器

         6)LCD1602液晶屏顯示

四、功能說明

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

2、倒計時器設定的計時時間為20s到0s,按下BTN3倒計時器開始倒計時,倒計時程序中,再按一下BTN3實作計時暫停,再按一下BTN3則會又開始倒計時;任意時候按下復位鍵(BTN2)會立馬復位到初始設定的20s;當倒計時到0時,無論燃氣灶處于何種燃燒狀態,都會立馬自動熄滅,

3、燃氣灶上電后向上撥動撥碼開關sw7,在LCD1602液晶屏上會出現“Welcome to use The Gas stove”的英文提示;在不同的燃燒狀態下會對應顯示不同的提示語,微火、小火、中火、大火分別對應“Fire Level Micro Fire”、“Fire Level Little Fire”、“Fire Level Medium Fire ”、“Fire Level Big Fire”,

 

 五、模塊代碼

1、LCD1602液晶屏

這里要鄭重感謝一下方清歡大佬的技術與理論支持,關于這部分的解釋詳見方清歡大佬的博文:[FPGA]淺談LCD1602字符型液晶顯示幕(Verilog)

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

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

   2)微火狀態

   3)小火狀態

   4)中火狀態


  

   5)大火狀態

 

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

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

(各音調的不同頻率參見本人另一博文:可編程實驗板EPM1270T144C5蜂鳴器音調頻率選擇 ,共包含了21個標準音調,讀者們也可修改這21個標準音調之外的值)

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

3、倒計時器模塊

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

4、數碼管顯示模塊

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

5、按鍵消抖模塊

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 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

七、總結

       到這里整個模擬燃氣灶就完成啦!本人編程水平、時間有限,這篇文章到這里就要結束啦,歡迎廣大讀者評論留言,更歡迎大家指出本人的不足,希望能通過交流自身得到提高,最后感謝大家的耐心閱讀!

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/145736.html

標籤:Verilog

上一篇:斐波那契數列列印輸出演算法改進

下一篇:Python例外處理

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more