module top (
input wire sys_clk ,
input wire sys_rst_n ,
input wire one ,
input wire half ,
output wire [03:00] led
);
wire one_out_w ;
wire half_out_w ;
wire po_cola ;
wire po_money ;
key_filter key_filter_insert(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.one ( one ) ,
.half ( half ) ,
.one_out ( one_out_w ) ,
.half_out ( half_out_w)
);
complex_fsm complex_fsm_insert (
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.money_one ( one_out_w ) ,
.money_half ( half_out_w) ,
.po_cola ( po_cola ) ,
.po_money ( po_money ) ,
.led ( led )
);
endmodule
![](https://img-blog.csdnimg.cn/4a15de93b04e4aed9ab32c22ec6bd8cb.png)
![](https://img-blog.csdnimg.cn/a85c3402cd474d5aac1232a146a3fdca.png)
![](https://img-blog.csdnimg.cn/ff32e19c2a774237b6aa5cd21b6788f1.png)
![](https://img-blog.csdnimg.cn/fd5196fcdfe3433691f48bb4ef7162da.png)
module key_filter
#(
parameter MAX_CNT_20MS = 20'd100_0000
)(
input wire sys_clk ,
input wire sys_rst_n ,
input wire one ,
input wire half ,
output reg one_out ,
output reg half_out
);
/*****************************************************
* 计数器的要素是什么?
* 1 开始计数的条件 什么时候开始计数
* 2 计数归零的条件 什么时候结束计数
* 3 计数寄存器的位宽
* 要点:
* 如何产生这两个条件是关键。
* ***************************************************/
// wire define
wire [01:00] key ;
wire nege ;
wire pose ;
wire add_cnt_20ms ;
wire end_cnt_20ms ;
// reg define
reg [19:00] cnt_20ms ;
reg [01:00] key_r1 ;
reg [01:00] key_r2 ;
reg add_cnt_flag ;
// key
assign key = {one, half} ;
// key_r1
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
key_r1 <= 1'b1 ;
end else begin
key_r1 <= key ;
end
end
// key_r2
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
key_r2 <= 1'b1 ;
end else begin
key_r2 <= key_r1 ;
end
end
// nege pose
assign nege = | ( ~key_r1 & key_r2 ) ; // 都需要按位操作
assign pose = | ( key_r1 & ~key_r2 ) ;
// add_cnt_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
add_cnt_flag <= 1'b0 ;
end else begin
if(nege) begin
add_cnt_flag <= 1'b1 ;
end else begin
if( pose || end_cnt_20ms ) begin
add_cnt_flag <= 1'b0 ;
end else begin
add_cnt_flag <= add_cnt_flag ;
end
end
end
end
// cnt_20ms add_cnt_20ms end_cnt_20ms
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_20ms <= 20'd0 ;
end else begin
if(add_cnt_20ms) begin
if(end_cnt_20ms) begin
cnt_20ms <= 20'd0 ;
end else begin
cnt_20ms <= cnt_20ms + 20'd1 ;
end
end else begin
cnt_20ms <= 20'd0 ;
end
end
end
assign add_cnt_20ms = add_cnt_flag ;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == ( MAX_CNT_20MS - 1'b1 ) ;
// one_out
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
one_out <= 1'b0 ;
end else begin
if(end_cnt_20ms) begin
one_out <= ~key_r2[1] ;
end else begin
one_out <= 1'b0 ;
end
end
end
// half_out
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
half_out <= 1'b0 ;
end else begin
if(end_cnt_20ms) begin
half_out <= ~key_r2[0] ;
end else begin
half_out <= 1'b0 ;
end
end
end
endmodule
// `timescale 1ns/1ns
//
// // Author : EmbedFire
// // Create Date : 2019/03/15
// // Module Name : key_filter
// // Project Name : key_filter
// // Target Devices: Altera EP4CE10F17C8N
// // Tool Versions : Quartus 13.0
// // Description : 按键消抖模块
// //
// // Revision : V1.0
// // Additional Comments:
// //
// // 实验平台: 野火_征途Pro_FPGA开发板
// // 公司 : http://www.embedfire.com
// // 论坛 : http://www.firebbs.cn
// // 淘宝 : https://fire-stm32.taobao.com
//
// module key_filter
// #(
// parameter CNT_MAX = 20'd999_999 //计数器计数最大值
// )
// (
// input wire sys_clk , //系统时钟50Mhz
// input wire sys_rst_n , //全局复位
// input wire key_in , //按键输入信号
// output reg key_flag //key_flag为1时表示消抖后检测到按键被按下
// //key_flag为0时表示没有检测到按键被按下
// );
// //********************************************************************//
// //****************** Parameter and Internal Signal *******************//
// //********************************************************************//
// //reg define
// reg [19:0] cnt_20ms ; //计数器
// //********************************************************************//
// //***************************** Main Code ****************************//
// //********************************************************************//
// //cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
// always@(posedge sys_clk or negedge sys_rst_n)
// if(sys_rst_n == 1'b0)
// cnt_20ms <= 20'b0;
// else if(key_in == 1'b1)
// cnt_20ms <= 20'b0;
// else if(cnt_20ms == CNT_MAX && key_in == 1'b0)
// cnt_20ms <= cnt_20ms;
// else
// cnt_20ms <= cnt_20ms + 1'b1;
// //key_flag:当计数满20ms后产生按键有效标志位
// //且key_flag在999_999时拉高,维持一个时钟的高电平
// always@(posedge sys_clk or negedge sys_rst_n)
// if(sys_rst_n == 1'b0)
// key_flag <= 1'b0;
// else if(cnt_20ms == CNT_MAX - 1'b1)
// key_flag <= 1'b1;
// else
// key_flag <= 1'b0;
// endmodule
module complex_fsm
#(
parameter CNT_1US = 6'd50 ,
CNT_1K = 10'd1000
)(
input wire sys_clk ,
input wire sys_rst_n ,
input wire money_one ,
input wire money_half ,
output reg po_cola ,
output reg po_money ,
output reg [03:00] led
);
// define wire signal
wire [01:00] money ;
wire clear ;
// define reg signal
reg [06:00] state ;
reg ctrl ;
reg [03:00] water_led ;
// counter
reg [05:00] cnt_1us ;
wire add_cnt_1us ;
wire end_cnt_1us ;
reg [09:00] cnt_1ms ;
wire add_cnt_1ms ;
wire end_cnt_1ms ;
reg [09:00] cnt_1s ;
wire add_cnt_1s ;
wire end_cnt_1s ;
reg [03:00] cnt_10s ;
wire add_cnt_10s ;
wire end_cnt_10s ;
// define localparam
localparam IDLE = 7'b0000001 ,
HALF = 7'b0000010 ,
ONE = 7'b0000100 ,
ONE_HALF = 7'b0001000 ,
TWO = 7'b0010000 ,
WATER_R = 7'b0100000 ,
WATER_C = 7'b1000000 ;
// money
// assign money = ( money_half ) ? 2'b01 : ( money_one ) ? 2'b10 : 2'b00 ;
assign money = {money_one,money_half} ;
// counter
// cnt_1us
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_1us <= 0 ;
end else begin
if(clear) begin
cnt_1us <= 0 ;
end else begin
if(add_cnt_1us) begin // 状态机在非 IDLE 状态下开始计数
if(end_cnt_1us) begin // 状态机在 状态转换后 与 计满后,需要清零,重新计数
cnt_1us <= 0 ;
end else begin
cnt_1us <= cnt_1us + 1'b1 ;
end
end else begin
cnt_1us <= 0 ; // 状态机在 IDLE 状态下计数值归零
end
end
end
end
assign clear = (state != IDLE && state != WATER_R && state != WATER_C && money != 2'b00) ;
assign add_cnt_1us = state != IDLE ;
assign end_cnt_1us = add_cnt_1us && cnt_1us == ( CNT_1US - 1'b1 );
//|| (state == WATER_R && end_cnt_10s) || (state == WATER_C && end_cnt_10s) 由于进入IDLE会归零,所以这两句不用写。
// cnt_1ms
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_1ms <= 0 ;
end else begin
if(clear) begin
cnt_1ms <= 0 ;
end else begin
if(add_cnt_1ms) begin
if(end_cnt_1ms) begin
cnt_1ms <= 0 ;
end else begin
cnt_1ms <= cnt_1ms + 1'b1 ;
end
end else begin
cnt_1ms <= cnt_1ms ; // 这里明显不能再归零了,需要保持。
end
end
end
end
assign add_cnt_1ms = end_cnt_1us ; // 级联
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == ( CNT_1K - 1'b1 ) ;
// cnt_1s
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_1s <= 0 ;
end else begin
if(clear) begin
cnt_1s <= 0 ;
end else begin
if(add_cnt_1s) begin
if(end_cnt_1s) begin
cnt_1s <= 0 ;
end else begin
cnt_1s <= cnt_1s + 1'b1 ;
end
end else begin
cnt_1s <= cnt_1s ;
end
end
end
end
assign add_cnt_1s = end_cnt_1ms ;
assign end_cnt_1s = add_cnt_1s && cnt_1s == ( CNT_1K - 1'b1 ) ;
// cnt_10s
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_10s <= 0 ;
end else begin
if(clear) begin
cnt_10s <= 0 ;
end else begin
if(add_cnt_10s) begin
if(end_cnt_10s) begin
cnt_10s <= 0 ;
end else begin
cnt_10s <= cnt_10s + 1'b1 ;
end
end else begin
cnt_10s <= cnt_10s ;
end
end
end
end
assign add_cnt_10s = end_cnt_1s ;
assign end_cnt_10s = add_cnt_10s && cnt_10s == ( 4'd10 - 4'b1 ) ;
// state
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
state <= IDLE ;
end else begin
case ( state )
IDLE : begin
if(money == 2'b01) begin
state <= HALF ;
end else begin
if(money == 2'b10) begin
state <= ONE ;
end else begin
state <= IDLE ;
end
end
end
HALF : begin
if(end_cnt_10s) begin
state <= IDLE ;
end else begin
if(money == 2'b01) begin
state <= ONE ;
end else begin
if(money == 2'b10) begin
state <= ONE_HALF ;
end else begin
state <= HALF ;
end
end
end
end
ONE : begin
if(end_cnt_10s) begin
state <= IDLE ;
end else begin
if(money == 2'b01) begin
state <= ONE_HALF ;
end else begin
if(money == 2'b10) begin
state <= TWO ;
end else begin
state <= ONE ;
end
end
end
end
ONE_HALF : begin
if(end_cnt_10s) begin
state <= IDLE ;
end else begin
if(money == 2'b01) begin
state <= TWO ;
end else begin
if(money == 2'b10) begin
state <= WATER_R ; // 正好 2.5 元 进入右流水的状态
end else begin
state <= ONE_HALF ;
end
end
end
end
TWO : begin
if(end_cnt_10s) begin
state <= IDLE ;
end else begin
if(money == 2'b01) begin
state <= WATER_R ; // 正好 2.5 元 进入右流水的状态
end else begin
if(money == 2'b10) begin
state <= WATER_C ;
end else begin
state <= TWO ;
end
end
end
end
WATER_R : begin
if(end_cnt_10s) begin
state <= IDLE ;
end else begin
state <= WATER_R ;
end
end
WATER_C : begin
if(end_cnt_10s) begin
state <= IDLE ;
end else begin
state <= WATER_C ;
end
end
default : state <= IDLE ;
endcase
end
end
// ctrl
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
ctrl <= 1'b1 ;
end else begin
if(water_led == 4'b1110) begin
ctrl <= 1'b0 ;
end else begin
if(water_led == 4'b0111) begin
ctrl <= 1'b1 ;
end else begin
ctrl <= ctrl ;
end
end
end
end
// water_led
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
water_led <= 4'b0111 ;
end else begin
case (state)
WATER_R : begin
if(end_cnt_1s) begin
water_led <= { led[0], led[03:01] } ;
end else begin
water_led <= water_led ;
end
end
WATER_C : begin
if(end_cnt_1s) begin
if( ctrl && end_cnt_1s ) begin // 这里需要一个使能或者控制信号 需要与上end_cnt_1s 以维持led 1110 状态 1s
water_led <= { led[0], led[03:01] } ;
end else begin
water_led <= { led[02:00], led[3] } ;
end
end else begin
water_led <= water_led ;
end
end
default: water_led <= 4'b0111 ; // 不用的时候 不变 节省功耗。
endcase
end
end
// led
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
led <= 4'b1010 ;
end else begin
case (state)
IDLE : begin
led <= 4'b1111 ;
end
HALF : begin
led <= 4'b1110 ;
end
ONE : begin
led <= 4'b1100 ;
end
ONE_HALF : begin
led <= 4'b1000 ;
end
TWO : begin
led <= 4'b0000 ;
end
WATER_R : begin
led <= water_led ;
end
WATER_C : begin
led <= water_led ;
end
default: led <= 4'b0101 ;
endcase
end
end
// po_cola
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
po_cola <= 0 ;
end else begin
if( (state == ONE_HALF && money == 2'b10) || (state == TWO && money != 2'b00) ) begin
po_cola <= 1'b1 ;
end else begin
po_cola <= 0 ;
end
end
end
// po_money
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
po_money <= 0 ;
end else begin
if(state == TWO && money == 2'b10) begin
po_money <= 1 ;
end else begin
po_money <= 0 ;
end
end
end
endmodule