一.任务
功能完整描述:
KEY4:开关机按键,复位时,默认是关机状态,数码管和LED灯均不亮,同时蜂鸣器响,其余按键按下无效。
KEY3:在开机状态时,投币1元
KEY2:在开机状态时,投币为0.5元
KEY1:当投币但少于货物的价格时,取消订单,数码管显示为0.0,同时LED灯实现跑马灯2s然后熄灭
当投币为2.5元时,刚好能够购买货物,4个LED灯同时闪烁2s然后熄灭,同时数码管数字清零
当投币为3元时,购买货物还需找零,4个LED灯实现流水灯2s然后熄灭,同时数码管数字清零
思路:
位选信号:
两种状态 6’b111_110,6’b111_101让这两个状态频繁切换,看起来像是一直亮着一样。
售货机总共有8个状态:
1:用户投币总数为0,也是初始状态,此时数码管显示0.0
①当用户选择投币0.5,跳转到第2个状态
②当用户选择投币1,跳转到第3个状态
2:用户投币总数为0.5,数码管显示0.5
①当用户选择投币0.5,跳转到第3个状态
②当用户选择投币1,跳转到第4个状态
③当用户取消,跳转到第8个状态,同时开始计时
3:用户投币总数为1,数码管显示1.0
①当用户选择投币0.5,跳转到第4个状态
②当用户选择投币1,跳转到第5个状态
③当用户取消,跳转到第8个状态,同时开始计时
4:用户投币总数为1.5,数码管显示1.5
①当用户选择投币0.5,跳转到第5个状态
②当用户选择投币1,跳转到第6个状态,同时开始计时
③当用户取消,跳转到第8个状态,同时开始计时
5:用户投币总数为2.0,数码管显示2.0
①当用户选择投币0.5,跳转到第6个状态,同时开始计时
②当用户选择投币1,跳转到第7个状态,同时开始计时
③当用户取消,跳转到第8个状态,同时开始计时
6:用户投币总数为2.5,数码管显示2.5
①该状态保持2s后结束计时
7:用户投币总数为3,数码管显示3.0
①该状态保持2s后结束计时
8:用户取消订,数码管显示0.0
①该状态保持2s后结束计时
二.工程项目
Verilog HDL编写
①设计按键消抖模块
key_debounce.v
module key_debounce(
input wire clk,
input wire rst_n,
input wire key,
output reg flag, //判断抖动是否消除的标志信号,0为抖动,1为抖动结束
output reg key_value //消抖后稳定的按键值给到蜂鸣器模块
);
//定义20ms延迟计数器,0.2s,1_000_000次
reg [19:0] delay_cnt;
//寄存依次key的值用来判断按键是否消抖成功
reg key_reg;
//20ms延时计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
key_reg <= 1'b1; //复位信号,设置按键无效
delay_cnt <= 1'b0; //计数器设置为0
end
else
begin
key_reg <= key;
if(key_reg == 1 && key == 0) //当这一次key值和上一次key值不一样,证明正在抖动
delay_cnt <= 20'd1_000_000; //延迟时间20ms
else if(delay_cnt > 0)
delay_cnt <= delay_cnt - 1; //没有抖动,开始20ms倒计时
else
delay_cnt <= 1'b0;
end
end
//根据延时计数器获取按键状态以及按键值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
flag <= 1'b0; //复位信号,设置信号标志为抖动
key_value <= 1'b1; //设置抽样值为1
end
else
begin
if(delay_cnt == 20'd1) //倒计时1_000_000到1
begin
flag <= 1'b1;
key_value <= key; //稳定20ms后将key值给到key_value
end
else
begin
flag <= 1'b0;
key_value <= key_value; //20ms内先不取样
end
end
end
endmodule
②设计数码管位选驱动
sel_drive.v
module sel_drive(
input wire clk,
input wire rst_n,
input wire boot_flag,
output wire [5:0] sel
);
parameter MAX_NUM = 10'd999;//20us
reg [5:0] sel_r;
reg [9:0] cnt;
reg sel_flag;
//20us计时器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 10'd0;
end
else if(cnt == MAX_NUM)begin
cnt <= 10'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//状态切换计数器模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
sel_flag <= 1'b0;
else if(cnt == MAX_NUM)
sel_flag <= ~sel_flag;
else
sel_flag <= sel_flag;
end
//位选信号切换功能
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel_r <= 6'b111_111;
end
else begin
case(boot_flag)
1'b0:begin //关机状态
sel_r <= 6'b111_111;
end
1'b1:begin //开机状态
case(sel_flag)
1'b0: sel_r <= 6'b111_110;
1'b1: sel_r <= 6'b111_101;
default:sel_r <= sel_r;
endcase
end
endcase
end
end
assign sel = sel_r;
endmodule
③设计数码管显示模块
seg_scan.v
module seg_scan(
input wire clk,
input wire rst_n,
input wire [3:0] money_flag,//用户投币标识
input wire [5:0] sel,//位选信号
output wire [7:0] seg//段选信号
);
reg [3:0] number;
reg [7:0] seg_r;
reg flag;//标识当前显示的数字是否是小数点后面的值 1:是 0:不是
//根据用户投币标识的不同显示不同的数字
always@(*)begin
case(money_flag)
4'd0:begin//显示0.0
case(sel)
6'b111_110: begin
number = 4'd0;
flag = 1'b0;//第二个零不带小数点
end
6'b111_101: begin
number = 4'd0;
flag = 1'b1;//第一个零带小数点
end
default : begin
number =