本次實驗所用可編程器件型號爲MAXII EPM1270T144C5(其引腳表見本人另外一博文,連接爲 https://www.cnblogs.com/RDJLM/p/12075584.html),經過可編程實驗板實現一個基本的模擬燃氣竈。html
一、 燃氣竈的控制按鍵有三個:點火/關閉按鍵 BTN七、火力調節按鍵 BTN6(火力增大) 和 BTN5(火力減少)git
二、 用 8×8 雙色點陣模擬顯示燃氣竈的竈眼,用如圖 1 所示的四個點陣顯示狀態分別表示火力的四個檔位,從左到右依次爲微火、小火、中火和大火,點陣沒有任何顯示錶示熄火狀態編程
三、 燃氣竈上電時竈眼應處於熄火狀態,在熄火狀態下按一下按鍵 BTN7 點燃燃氣竈進入燃燒狀態,在燃燒狀態下按一下按鍵 BTN7 則熄滅燃氣竈進入熄滅狀態spa
四、 燃氣竈在熄滅狀態下,按一下按鍵 BTN7 進入燃燒狀態時的初始火力爲小火設計
五、 在燃燒狀態下用按鍵 BTN6(火力增大)和 BTN5(火力減少)改變火力的大小, 每按一次按鍵火力增大或減少一檔(對應的點陣顯示改變一檔);在大火狀態下按鍵BTN6 不起效,在微火狀態下按鍵 BTN5 不起效,並且在大火狀態下按 BTN6 或微火狀態下按 BTN5 要有報警聲,提示火力已到極限code
一、設計思路:1)點陣的圖案顯示經過快速掃描達到人眼可以定格看到穩定的圖案htm
2)點陣顯示圖案的轉換經過狀態機實現blog
3)蜂鳴器聲音的產生經過按下按鍵產生一個方波脈衝實現,聲音的頻率則需根據自定義的音調換算成頻率接口
4)爲貼近生活,引入計數器功能,實現倒計時結束,燃氣竈自動關閉的效果進程
5)爲方便使用者明瞭的知道使用時燃氣竈火焰的大小狀態,引入 LCD 液晶屏功能,在不一樣的火焰狀態下對應顯示不一樣的英文顯示
二、整體框圖
三、分塊設計:1)狀態機:狀態轉移狀況的羅列
2)點陣顯示
3)產生方波脈衝控制蜂鳴器
4)數碼管顯示
5)倒計時器
6)LCD1602液晶屏顯示
一、燃氣竈上電時竈眼應處於熄火狀態,在熄火狀態下按一下按鍵 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」。
一、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
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
到這裏整個模擬燃氣竈就完成啦!本人編程水平、時間有限,這篇文章到這裏就要結束啦,歡迎廣大讀者評論留言,更歡迎你們指出本人的不足,但願能經過交流自身獲得提升。最後感謝你們的耐心閱讀!