一、要求
我们仍以可乐机为背景,一瓶可乐的价格还是 2.5 元。
用按键控制投币(加入按键消 抖功能),可以投 0.5 元硬币 和 1 元硬币,
投入 0.5 元后亮一个灯,投入 1 元后亮 2 个灯, 投入 1.5 元后亮 3 个灯,投入 2 元后亮 4 个灯,
如果投币后 10s 不再继续进行投币操作则可 乐机回到初始状态。
投入 2.5 元后出可乐不找零,此时 led 灯实现单向流水操作,流水 10s 后自动停止;
投入 3 元后出可乐找零,此时 led 灯实现双向流水操作,流水 10s 后自动停 止。
这里也有复位键,其功能是终止本次投币操作,使可乐机立刻回到初始状态
二、分析
1. 模块图
2.状态转移图【5个状态】
3. 分析变量 (波形图省略了)
三、rtl代码
module sys_cola
#(
parameter CNT_MAX = 25'd24_999_999, //0.5s定时器计时
parameter CNT_KEY = 20'd999_999 //20ns按键稳定状态
)
(
input wire key_in_half,//按键1输入 0.5元
input wire key_in_one,//按键2输入 1元
input wire sys_clk,
input wire sys_rst_n,
output reg [3:0] led_out//输出led的三种状态
);
//线
wire pi_money_half;
wire pi_money_one;
//state
reg [4:0] state;
parameter IDLE = 5'b00001;
parameter HALF = 5'b00010;
parameter ONE = 5'b00100;
parameter ONE_HALF = 5'b01000;
parameter TWO = 5'b10000;
//合并投币
wire [1:0] pi_money;//没有延迟--组合逻辑
//led
reg [3:0] led_a;//投币亮灯
reg [3:0] led_b;//单向流水
reg [3:0] led_c;//双向流水
reg led_flag;//流水使能信号
//cnt
reg [24:0] cnt;//计数
reg [4:0] cnt_10s = 5'd21; //10秒倒计时
assign pi_money = {pi_money_one,pi_money_half};
//-------------------------------------------------------------------
//第一段状态机,描述当前状态 state 如何根据输入跳转到下一状态
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
state <= IDLE;
end
else case(state)
IDLE : if (pi_money == 2'b01) begin
state <= HALF;
end
else if (pi_money == 2'b10) begin
state <= ONE;
end
else
state <= IDLE;
HALF : if(cnt_10s == 5'd20) begin
state <= IDLE;
end
else if (pi_money == 2'b10) begin
state <= ONE_HALF;
end
else if (pi_money == 2'b01) begin
state <= ONE;
end
else
state <= HALF;
ONE : if (cnt_10s == 5'd20) begin
state <= IDLE;
end
else if (pi_money == 2'b10) begin
state <= TWO;
end
else if(pi_money == 2'b01)
state <= ONE_HALF;
else
state <= ONE;
ONE_HALF: if (cnt_10s == 5'd20) begin
state <= IDLE;
end
else if (pi_money == 2'b01) begin
state <= TWO;
end
else if (pi_money == 2'b10) begin
state <= IDLE;
end
else
state <= ONE_HALF;
TWO : if (cnt_10s == 5'd20) begin
state <= IDLE;
end
else if (pi_money == 2'b01 || (pi_money == 2'b10)) begin
state <= IDLE;
end
else
state <= TWO;
default: state <= IDLE;
endcase
end
//--------------------------------------------------------------------------------
//第二段状态机,cnt:计数器 计数 0.5s
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'b0;
else if(cnt == CNT_MAX)
cnt <= 25'b0;
else if(cnt_10s != 5'd21) //开始计时
cnt <= cnt + 1'b1;
//第二段状态机,10s计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_10s <= 5'd21;
else if(pi_money != 2'd00) //开始计时
cnt_10s <= 5'd0;
else if(cnt == CNT_MAX) //计数了0.5s
cnt_10s <= cnt_10s + 1'b1;
else if(cnt_10s == 5'd20) //计数了20个0.5s
cnt_10s <= 5'd0 ;//清0
//led_a
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
led_a <= 4'b1111;
end
else if (state == HALF) begin
led_a <= 4'b0111;
end
else if (state == ONE) begin
led_a <= 4'b0011;
end
else if (state == ONE_HALF) begin
led_a <= 4'b0001;
end
else if (state == TWO) begin
led_a <= 4'b0000;
end
else
led_a <= 4'b1111;
end
//led_b
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
led_b <= 0001;//1110取反
end
else if (led_b == 1000 && cnt == CNT_MAX) begin
led_b <= 0001;//1110
end
else if (cnt == CNT_MAX) begin
led_b <= led_b << 1'b1;//左移
end
else
led_b <= led_b;
end
//led_flag控制双向循环
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
led_flag <= 1'b0;
end
else if (led_c == 4'b1000) begin//0111取反
led_flag <= 1'b0;//右
end
else if (led_c == 4'b0001) begin//1110取反
led_flag <= 1'b1;//左移
end
else
led_flag <= led_flag;
end
//led_c
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
led_c <= 4'b0001;//1110
end
else if (led_flag == 1'b0 && cnt == CNT_MAX) begin
led_c <= led_c >> 1'b1;//右移
end
else if (led_flag == 1'b0 && cnt == CNT_MAX) begin
led_c <= led_c >> 1'b1;//左移
end
else
led_c <= led_c;
end
//led_out
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0) begin
led_out <= 4'b1111;
end
else if ((state == TWO) && (pi_money == 2'b01) && (cnt_10s != 5'd20)) begin
led_out <= led_b;
end
else if ((state == TWO) && (pi_money == 2'b10) && (cnt_10s != 5'd20)) begin
led_out <= led_c;
end
else
led_out <= ~led_a;
end
//---------------------------------------------------------------------------------
key_filter
#(
.CNT_MAX(CNT_KEY)
)
key_filter_inst1(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.key_in(key_in_half),
.key_flag(pi_money_half)
);
key_filter
#(
.CNT_MAX(CNT_KEY)
)
key_filter_inst2(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.key_in(key_in_one),
.key_flag(pi_money_one)
);
endmodule
四、 编译后的状态转移图
五、总结
在分析的时候要考虑到变量的问题,怎么才能设计全,同时遵循设计的步骤。
1、首先分析实际问题,然后抽象出我们设计的状态机系统所需要的输入、输出有哪些,以 及每个状态都是什么;
2、根分析绘制状态转移图,状态转移图是可以化简的,我们一般化简到最少状态;
3、根据状态转移图编写代码,代码的编写也是有固定套路的,我们也进行了方法总结; (
4、通过综合器综合的状态转移图以及 ModelSim 仿真验证状态机的设计。