按键模拟控制售货机实验

一、实验目标

  现有自动售货机,自动售货机只能出售可乐,可乐为2.5元,投入的币种只有0.5元和1元的,投入总金额为2.5元时出可乐,投入金额为3元时,出可乐并且找零0.5元。现在使用开发板模拟自动售货机,使用两个按键模拟投币0.5元和1元,开发板上有四个led灯,当投入总金额为0.5元时,亮一个灯;投入总金额为1元时,亮两个灯;投入总金额为1.5元时,亮三个灯;投入总金额为2元时,亮四个灯。当只出可乐时,led灯为单向流水灯效果;当即出可乐又找零时,led灯为双向流水灯效果,并且流水灯的持续时间为10秒。

 

二、实验原理框图

                                                   

 

三、框图分析

  因为有两个按键输入,所以要有按键消抖模块,且要例化两次。将消抖之后的按键信号输入给状态机模块,状态机模块内部进行一系列操作,并且把实时的状态变量输出给led显示模块,以显示总金额所代表的led灯点亮数。状态机也输出两个变量给led流水灯显示模块,以表示是否需要找零或者出可乐,led流水灯模块判断这两个信号,然后选择是单向流水灯显示还是双向流水灯显示。最后led流水灯和led显示的输出给led输出选择模块,因为只有一组四个led灯,所以需要分时使用这四个led灯。

四、实验程序

1.按键消抖模块

//=============================================================
// ---名 称:key_debounce
// ---作 者:橘子哥哥
// ---Q  Q :1073273114
// ---we chat:15870894502
// ---日 期:2021-1-21
// ---描 述:按键消抖模块
//=============================================================
module key_debounce(
    input     wire   Clk,
    input     wire   Rst_n,
    input     wire   key,
    output    reg    key_flag
);

wire  Rst;
assign Rst=~Rst_n;

//定时5ms
parameter T5ms = 249_999;     //定时5ms所需要时钟周期的个数,时钟频率为50mhz
reg  [17:0]T20ns_count;       //计数器,用于计算20ns的个数,其实就是计算时钟周期的个数

always@(posedge Clk or posedge Rst)
   if(Rst)
      T20ns_count<='d0;
   else if(!key)                        //按键为低电平才计时,即有5ms按键为低电平,才表示按键按下了
        T20ns_count<=T20ns_count+1'b1;
   else 
        T20ns_count<='d0;

//一次按键有效标示位
reg key_vaild;
always@(posedge Clk or posedge Rst)
   if(Rst)
        key_vaild<=1'b0;           
   else if(key)         
        key_vaild<=1'b0;     
   else if(T20ns_count==T5ms)         //标志位,防止一次按键时间过长,在一次按键内产生多个keY_flag
        key_vaild<=1'b1;

//按键标志位
always@(posedge Clk or posedge Rst)
   if(Rst)
       key_flag<=1'b0;
   else if((T20ns_count==T5ms)&&(!key_vaild)) //按键稳定区定时超过5ms即表示一次按键有效,因为有key_vaild标志位
       key_flag<=1'b1;                        //此次按键不会再产生一次key_flag高脉冲,既保证一次按键只会输出一个
   else                                       //时钟高脉冲的key_flag信号
       key_flag<=1'b0;

endmodule 

 

 2.状态机模块

2.1状态转移图 

                                             

 

 2.2程序

//=============================================================
// ---名 称:fsm
// ---作 者:橘子哥哥
// ---Q  Q :1073273114
// ---we chat:15870894502
// ---日 期:2021-1-20
// ---描 述:两段式状态机练习
//=============================================================
module fsm(
    input    wire        Clk,
    input    wire        Rst_n,
    input    wire        in_0p5,
    input    wire        in_1,
    output   wire [4:0]  state_out,
    output   reg         out_0p5,
    output   reg         out_cola
);

parameter IDLE      = 5'b00001;
parameter State_0p5 = 5'b00010;
parameter State_1   = 5'b00100;
parameter State_1p5 = 5'b01000;
parameter State_2   = 5'b10000;

wire Rst;
assign Rst=~Rst_n;

reg [4:0]state;

always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      state<=5'd0;
      end
   else begin
        case(state)
        IDLE:if(in_0p5)
             state<=State_0p5;
             else if(in_1)
             state<=State_1;
        State_0p5:if(in_0p5)
             state<=State_1;
             else if(in_1)
             state<=State_1p5;
        State_1:if(in_0p5)
             state<=State_1p5;
             else if(in_1)
             state<=State_2;
        State_1p5:if(in_0p5)
             state<=State_2;
             else if(in_1)
             state<=IDLE;
        State_2:if(in_0p5||in_1)
             state<=IDLE;
        default:state<=IDLE;
        endcase
        end
//出可乐
always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      out_cola<=1'b0; 
      end
   else if(((state==State_1p5)&&(in_1))||((state==State_2)&&((in_0p5)||(in_1))))
        begin
        out_cola<=1'b1;
        end
   else  
        begin
        out_cola<=1'b0; 
        end

//找钱
always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      out_0p5<=1'b0;
      end
   else if((state==State_2)&&(in_1))
        begin
        out_0p5<=1'b1;
        end
   else
        begin
        out_0p5<=1'b0;
        end

assign state_out=state;

endmodule

 

3.led显示模块

//=============================================================
// ---名 称:led_display
// ---作 者:橘子哥哥
// ---Q  Q :1073273114
// ---we chat:15870894502
// ---日 期:2021-1-21
// ---描 述:显示当前售货机投入的总金额
//=============================================================
module led_display(
    input      wire          Clk,
    input      wire          Rst_n,
    input      wire [4:0]    state,
    output     reg  [3:0]    led_out
);

wire Rst;
assign Rst=~Rst_n;

parameter IDLE      = 5'b00001;
parameter State_0p5 = 5'b00010;
parameter State_1   = 5'b00100;
parameter State_1p5 = 5'b01000;
parameter State_2   = 5'b10000;

always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      led_out<=4'd0;
      end
   else begin
        case (state)
        IDLE       : led_out <= 4'd0;
        State_0p5  : led_out <= 4'b0001;
        State_1    : led_out <= 4'b0011; 
        State_1p5  : led_out <= 4'b0111;
        State_2    : led_out <= 4'b1111;  
        default    : led_out <= 4'd0;
        endcase
        end

endmodule

 4.led流水灯模块

//=============================================================
// ---名 称:led_shift
// ---作 者:橘子哥哥
// ---Q  Q :1073273114
// ---we chat:15870894502
// ---日 期:2021-1-21
// ---描 述:流水灯
//=============================================================
module led_shift(
       input    wire          Clk,
       input    wire          Rst_n,
       input    wire          vaild_cola,
       input    wire          vaild_0p5,
       output   wire   [3:0]  led_out,
       output   wire          start_cnt
);

wire Rst;
assign Rst=~Rst_n;

wire flag_out_cola;                 
wire flag_out_cola_money;
assign flag_out_cola=(vaild_cola)&(!vaild_0p5); //只出可乐,不找钱,流水灯单向移动
assign flag_out_cola_money=(vaild_cola)&(vaild_0p5);//即出可乐,也找钱,流水灯双向流动

reg  start_count;

//定时器,计时0.1s
parameter T100ms = 23'd4_999_999;
reg  [22:0] T20ns_count;
always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      T20ns_count<='d0;
      end
   else if(T20ns_count==T100ms)
        begin
        T20ns_count<='d0;
        end
   else if(start_count)    //使能有效才开始计时
        begin
        T20ns_count<=T20ns_count+1'b1;
        end
   else 
        begin
        T20ns_count<='d0;
        end

//定时器,计时10s
parameter T10s = 7'd99;//100*100ms等于10s  
reg [6:0] T100ms_count;//计算100ms的个数
always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      T100ms_count<='d0;
      end
   else if(T100ms_count==T10s)  //定时时间到10s,自动清零
        begin
        T100ms_count<='d0;
        end
   else if(T20ns_count==T100ms)    //定时时间到100ms,计数器加一
        begin
        T100ms_count<=T100ms_count+1'b1;
        end

//计数使能信号start_count
reg [3:0]led_shift;
always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      start_count<=1'b0;
      end
   else if(flag_out_cola||flag_out_cola_money)//计数开启条件
        begin
        start_count<=1'b1;
        end
   else if(T100ms_count==T10s)//计数停止条件
        begin
        start_count<=1'b0;
        end


//流水灯显示控制
reg led_single_shift;//流水灯单向流动标志位
reg led_double_shift;//流水灯双向流动标志位

always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      led_single_shift<=1'b0;
      end
   else if(flag_out_cola) //只出可乐,流水灯单向流动
        begin
        led_single_shift<=1'b1;
        end
   else if(T100ms_count==T10s)//定时10s到,关闭流水灯显示
        begin
        led_single_shift<=1'b0;
        end

always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      led_double_shift<=1'b0;
      end
   else if(flag_out_cola_money)//即出可乐也找零,流水灯双向流动
        begin
        led_double_shift<=1'b1;
        end
   else if(T100ms_count==T10s)//定时10s到,关闭流水灯显示
        begin
        led_double_shift<=1'b0;
        end

reg L_R_flag;//LED灯左右移位标志位
always@(posedge Clk or posedge Rst)
   if(Rst)
      begin
      led_shift<=4'b0001;
      L_R_flag<=1'b0;
      end
   else if(led_single_shift)
            begin
            if(T20ns_count==T100ms)
                begin
                led_shift<={led_shift[2:0],led_shift[3]};
                end
            end
   else if(led_double_shift)
            begin
            if(T20ns_count==T100ms)
                begin
                if(!L_R_flag)           //左移位
                   begin
                   if(led_shift==4'b1000)//左移位到最左边的一位,改变标志位,转为向右移动
                      L_R_flag<=1'b1;
                   else
                      led_shift<={led_shift[2:0],led_shift[3]};
                   end
                else 
                   begin                 //右移位
                   if(led_shift==4'b0001)//右移位到最右边的一位,改变标志位,转为向左移动
                      L_R_flag<=1'b0;
                   else
                      led_shift<={led_shift[0],led_shift[3:1]};
                   end
                end
            end

assign led_out=led_shift;
assign start_cnt=start_count;//输出给led选择模块,当10s时间到,start_cnt为零,即不再输出流水灯效果
endmodule

 5.led选择模块

//=============================================================
// ---名 称:led_select
// ---作 者:橘子哥哥
// ---Q  Q :1073273114
// ---we chat:15870894502
// ---日 期:2021-1-21
// ---描 述:控制led的显示,是显示投入总金额,还是流水灯显示
//=============================================================
module led_select(
    input     wire [3:0]    led_dsp,
    input     wire [3:0]    led_sft,
    input     wire          led_control,
    output    wire [3:0]    led
 );

assign led=(led_control)?led_sft:led_dsp;//led_control即接流水灯的start_cnt,10s时间到,start_cnt就为零,不在需要流水灯显示
endmodule                                //就转为led显示的led为输出

 

posted @ 2021-01-22 23:42  橘子哥哥hym  阅读(183)  评论(0)    收藏  举报