[FPGA] Verilog 燃气灶控制器的设计与实现

燃气灶控制器的设计与实现

一、引述

本次实验所用可编程器件型号为MAXII EPM1270T144C5(其引脚表见本人另一博文,链接为 https://www.cnblogs.com/RDJLM/p/12075584.html),通过可编程实验板实现一个基本的模拟燃气灶。

二、设计课题的基本要求

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液晶屏

这里要郑重感谢一下方清欢大佬的技术与理论支持,关于这部分的解释详见方清欢大佬的博文(链接:https://www.cnblogs.com/Clouds42/p/11938079.html)

下面就来看一下在燃气灶不同的火焰状态下对应液晶屏显示不同文字的效果图吧!

   1)熄灭状态显示欢迎使用提示语

   2)微火状态

   3)小火状态

   4)中火状态

  

   5)大火状态

2、状态机中的内容显示(包含点阵显示、蜂鸣器的方波脉冲产生和液晶屏显示内容的赋值)

蜂鸣器的音调可调,从低音到中音到高音,每个音段里包含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

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.cnblogs.com/RDJLM/p/12075569.html

时间: 2024-10-17 03:56:26

[FPGA] Verilog 燃气灶控制器的设计与实现的相关文章

基于MVC4+EasyUI的Web开发框架形成之旅--MVC控制器的设计

自从上篇<基于MVC4+EasyUI的Web开发框架形成之旅--总体介绍>总体性的概括,得到很多同行的关注和支持,不过上一篇主要是介绍一个总体的界面效果和思路,本系列的文章将逐步介绍其中的细节,本文主要介绍整个Web开发框架中的MVC控制器的设计.在设计之初,我就希望尽可能的减少代码,提高编程模型的统一性.因此希望能够以基类继承的方式,和我Winform开发框架一样,尽可能通过基类,而不是子类的重复代码来实现各种通用的操作. 1.登录控制的控制器基类设计 我们知道,一般我们创建一个MVC的控制

基于Verilog HDL整数乘法器设计与仿真验证

基于Verilog HDL整数乘法器设计与仿真验证 1.预备知识 整数分为短整数,中整数,长整数,本文只涉及到短整数.短整数:占用一个字节空间,8位,其中最高位为符号位(最高位为1表示为负数,最高位为0表示为正数),取值范围为-127~127. 负数的表示方法为正值的求反又加1.例如: 8’b0000_0100; //表示值:4,正值求反为:8’b1111_1011:再加1表示为:8’b1111_1100,这样便得到了-4的表示方法为:8’b1111_1100. 同理,负值变成正值的方法为:负值

[连载]《C#通讯(串口和网络)框架的设计与实现》- 8.总体控制器的设计

目       录 第八章           总体控制器的设计... 2 8.1           总控制器的职能... 2 8.2           组装和释放部件... 3 8.3           事件响应... 5 8.4           小结... 9 第八章     总体控制器的设计 有了IO部分.设备驱动部分.显示部分.数据导出部分和服务组件部分等,在这些已经存在的接口上构建一个集成各部分的总控制器,协调各部分有序工作.事件响应和控制数据流向. 另外,这个总控制器还负责

(转)基于MVC4+EasyUI的Web开发框架形成之旅--MVC控制器的设计

http://www.cnblogs.com/wuhuacong/p/3284628.html 自从上篇<基于MVC4+EasyUI的Web开发框架形成之旅--总体介绍>总体性的概括,得到很多同行的关注和支持,不过上一篇主要是介绍一个总体的界面效果和思路,本系列的文章将逐步介绍其中的细节,本文主要介绍整个Web开发框架中的MVC控制器的设计.在设计之初,我就希望尽可能的减少代码,提高编程模型的统一性.因此希望能够以基类继承的方式,和我Winform开发框架一样,尽可能通过基类,而不是子类的重复

FPGA Verilog HDL 系列实例--------步进电机驱动控制

[连载] FPGA Verilog HDL 系列实例 Verilog HDL 之 步进电机驱动控制 步进电机的用途还是非常广泛的,目前打印机,绘图仪,机器人等等设备都以步进电机为动力核心.那么,下面我们就了解下什么是步进电机,它是怎么控制的. 一.步进电机相关知识简介 1.步进电机概述 步进电机是一种能够将电脉冲信号转换成角位移或线位移的机电元件,它实际上是一种单相或多相同步电动机.单相步进电动机有单路电脉冲驱动,输出功率一般很小,其用途为微小功率驱动.多相步进电动机有多相方波脉冲驱动,用途很广

[FPGA]Verilog 60s秒表计时器(最大可计时间长达9min)

[FPGA]Verilog 60s秒表计时器 1.引述 这次的实验来自于本人本科课程数电结课时的自选题目.由于这次上传是后知后觉,学校已将小脚丫板子回收,所以在这篇文章中没法贴出代码结果的效果图了,但最终效果已经过测试,可放心食用.那么下面就贴上代码并略加讲解供大家参考. 2.分频模块 我们要实现一个秒表,自然要将实验板中的时钟脉冲clk分频为一个周期为1s的脉冲,已知小脚丫板子的晶振为12MHz.下面贴上分频模块的代码. module divide # ( //parameter是verilo

FPGA的时钟质量对设计的影响

小梅哥编写,未经许可严禁用于任何商业用途 近期,一直在调试使用Verilog编写的以太网发送摄像头数据到电脑的工程(以下简称以太网图传).该工程基于今年设计的一款FPGA教学板AC620.AC620上有一个百兆以太网接口和一个通用CMOS摄像头接口,因此非常适合实现以太网图传功能.CMOS摄像头接口没有什么好说的,就是IO而已,这里先重点介绍下以太网接口. 以太网接口使用了一片10/100M自适应以太网收发器(PHY),型号为RTL8201.该芯片和FPGA采用标准的MII接口进行连接.什么是M

FPGA的FIR抽取滤波器设计

摘 要:本文介绍了FIR抽取滤波器的工作原理,重点阐述了用XC2V1000实现FIR抽取滤波器的方法,并给出了仿真波形和设计特点. 关键词:FIR抽取滤波器:流水线操作:FPGA 用FPGA实现抽取滤波器比较复杂,主要是因为在FPGA中缺乏实现乘法运算的有效结构,现在,FPGA中集成了硬件乘法器,使FPGA在数字信号处理方面有了长足的进步.本文介绍了一种采用Xilinx公司的XC2V1000实现FIR抽取滤波器的设计方法. 具体实现 结构设计 基于抽取滤波器的工作原理,本文采用XC2V1000实

【设计开发】 玩转FPGA (DE2-115) - 1602 LCD接口设计

一.前言 闲来无事,在X宝上搜寻了一套DE2-115的FPGA开发板以丰富业余生活.虽附带光盘里包含各外设及组件驱动及接口样例,但本着"不捣腾不痛快"的折腾精神,将DE2-115的各个外设重新整理并玩转一遍. DE2-115全貌见下图,LCD1602是一个简单.实用的显示交互界面:DE2-115中的1602不支持中文字库. 二.接口说明 1602 LCD模块接口如下图所示,对几个信号做简要说明: #4 (RS) - DB内容指示信号:0-当前DB数据为命令数据:1-当前DB数据为显示数