【FPGA】状态机拓展

一、要求

我们仍以可乐机为背景,一瓶可乐的价格还是 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 仿真验证状态机的设计。

六、链接

本文参考学习FPGA—可乐机拓展训练题(状态机)_咖啡0糖的博客-CSDN博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值