zoukankan      html  css  js  c++  java
  • 09自动售货机综设实验(含按键消抖,led和状态机)

    一设计功能

    1.上次状态机的练习

     

    2这次自动售货机综设

     

    (一)对比两次的售货机

    上次售货机的关键是画出状态转移图。明确输入分几种,输出是啥,有哪些状态。如下图所示

     

    (二)系统或综合设计的经验:

    既然这次的综设,在上次的售货机基础上,加了流水灯,按键等模块。那么根据模块化设计,那我先做核心模块,再做功能模块,然后再连接好各个模块,实现系统功能。

    我的经验:做过录音机综设实验,当时先做串口通信模块,再做LED实现多种功能模块,然后再把录音机模块做好,最后利用控制信号把这三大模块组合起来。利用拆分—设计—验证,模块化设计思路。

    1. 设计方法

    关键是照图施工。

    即一般做一个系统设计,都是先按照功能划分出几个大致的模块,然后画出系统框图。根据框图,找出核心模块,对于一个模块,也是照图施工,即先画出它的模块框图,时序图或状态转移图,然后根据图纸描述模块。

     

    1. 系统框图

    当然咯,这个系统框图是最好完成设计后,我再画出的。实际的动手过程:第一是先看了设计功能,大致晓得有三个模块,即按键模块,LED灯模块,售货机的状态机模块,也感觉到售货机的状态机模块是核心。

    第二:A:在草稿纸画出三个模块的大致框图,并没有连接他们,因为当时也很茫然,一片混乱。B:然后就直接先做售货机的状态机模块,直接在上次的售货机程序上加了两个状态,还有把输出改了一下。C:接着我就做LED模块,从设计功能推出,有三个功能模式,即按下按键亮一个灯或两个;单向流水灯;双向流水灯;我又想起来上次录音机录音与回放实验中,LED灯功能比这个多得多,所以就看了上次录音机的LED模块的设计:由一个输入控制信号的不同值,来给led灯不同功能的标志信号,再由标志信号驱动LED灯实现不同功能。即一个控制信号的不同值对应LED灯的不同功能。

     

    最后就是对按键模块的设计,我刚开始很模糊怎么调用按键消抖,实现按键1和按键2按下;还加了三个按键的程序,即按键消抖,按键的组合模块,按键的控制模块,哈哈哈,太有趣了。

    实际上,按键的调用:一是有一个按键消抖模块(消抖),一个按键控制模块(实现按键功能),再把消抖模块的输出标志(如Pin_out)作为按键控制模块的输入,而按键控制模块的输出可以作为其他功能模块的输入,如售货机的状态机模块,最后就是在顶层模块例化按键消抖和按键控制即可。如这次的两个按键,只需要把按键消抖例化两次,把按键消抖例化1和例化2的输出标志分别接上,按键控制的按键1和按键2输入端口,再把按键消抖例化1和例化2的输入KEY1 和KEY2和顶层的输入按键连起来就行。

    二设计输入

    我个人觉得这次LED灯模块,很值得品味一下。它有三种不同的显示状态,而LED却都是那四个,怎么避免对同一变量多次赋值和竞争冒险,还有就是在同一个模块实现多种功能。

    (一)LED灯部分

    module Water_led(

    input wire clk,

    input  Rst,

     

    input wire [2:0]led_state,

    output reg [3:0]led

        );

     

    reg [3:0]rled; //复位时的LED灯寄存器

      reg [3:0]rled0; //按键按下亮一个或两个的LED灯寄存器

     reg [3:0]rled1;   //单向流水灯的LED灯寄存器

     reg [3:0]rled2;   //双向流水灯的LED灯寄存器

     

    reg en_led1;  //单向流水灯的标志信号

     reg en_led2;//双向流水灯的标志信号

     //200ms timer

     parameter T200MS = 24'd9_999_999;

     reg [23:0]div_cnt;

     always@(posedge clk or negedge Rst)

    if(!Rst)begin

    div_cnt<=24'd0;

    end

    else if(div_cnt==T200MS)begin

    div_cnt<=24'd0;

     end

     else begin

    div_cnt<=div_cnt+1'b1;

     end

     

      //用输入控制信号的不同值拉高不同的LED功能的标志信号

      //最终实现不同的LED功能

     always@(posedge clk or negedge Rst)

    if(!Rst)begin

    rled<=4'b0000;

    end

    else begin

        case(led_state)

            0:begin

            rled0<=4'b0000;

            end

           

            1:begin

            rled0<=4'b0001;

            end

           

            2:begin

            rled0<=4'b0011;

            end

            3:begin

            rled0<=4'b0111;

            end

            4:begin

            rled0<=4'b1111;

            end

            5:begin

            en_led1<=1'b1;

            end

            6:begin

            en_led2<=1'b1;

            end

        endcase

    end

     

     //单向流水灯

     always@(posedge clk or negedge Rst)

    if(!Rst)begin

    rled1<=4'b0001;

    end

    else if(en_led1)begin

        if(div_cnt==T200MS)begin

        rled1<={rled1[2:0],rled1[3]};

        end

        else begin

            rled1<=rled1;

        end

     end

    else begin

        rled1<=rled1;

    end

     //双向流水灯

    reg double_led;

    always@(posedge clk or negedge Rst)

    if(!Rst)begin

    rled2<=4'b0001;

    double_led<=0;

    end

    else if(en_led2)begin

        case(double_led)

        0:if(div_cnt==T200MS)begin

        rled2<={rled2[2:0],rled2[3]};

        end

        else if(rled2==4'b1000)begin

        double_led<=1'b1;

        rled2<=rled2;

        end

       

        1:if(div_cnt==T200MS-1)begin

        rled2<={rled2[0],rled2[3:1]};

        end

        else if(rled2==4'b0001)begin

        double_led<=1'b0;

        rled2<=rled2;

        end

        endcase

     end

     

    //the mean to avoid the multiple values by selector

        always@(*)begin

          if(Rst==0) led = rled;

                else if(led_state<3'd5) led = rled0;

                else if(led_state==3'd5) led = rled1;

                else if(led_state==3'd6) led = rled2;

                else  led = rled;

    end

    endmodule

    通过上面的设计代码,我们清晰得知道了上面提出的那两个问题的答案:通过对不同的LED功能定义一个寄存器且在输出时利用选择器使得在同一时刻,只有一个赋值输出。而实现不同的功能,通过输入控制信号的不同拉高不同LED灯的标志信号,再利用标志信号驱动LED实现不同的功能。

    (二)按键控制模块

    按键控制模块的功能:按键1按下表示投入5毛,按键2按下表示投入1快,再把这两个的输出传递给售货机的状态机模块。

    讲哈,我刚开看到这个按键功能的想法,给按键1和按键2分别给一个输出标志,来表示按键1和按键2按下,但我后面看到售货机的状态机模块输入只有一个,若是再加一个感觉有点麻烦,想想还是算了。那么怎么用一个标志信号来表示两个输入的值。

    哈哈哈哈,我想到了设置一个2bit的输出标志,第零位表示按键1的值,第一位表示按键2的值。

    第二个问题,怎么解决按键1按下和按键2按下给不同的值到输出标志?

    方法是,用组合逻辑的assign语句实现。

    assign po_money =(key1_in==1)?2'd1:(key2_in==1)?2'd2:2'd0;

    没错哈按键控制模块就是只有一句这么简单。

    (三)顶层模块

    `timescale 1ns/1ns

    `define clk_period 20

     

    module top(

    input wire sclk,

    input  Rst_n,  

    input wire key_sw1,

    input wire key_sw2,

    output wire [3:0]LED

        );

     

    wire key1_money;

    wire key2_money;

     

    wire [1:0]kmoney;

    wire [2:0]led_flag;

     

     

    //例化售货机

    fsm  inst_fsm

     (

                .clk      (sclk),

                .rst_n    (Rst_n),

                .pi_money (kmoney),

                .po_led   (led_flag)

            );

     

    //例化LED

        Water_led  inst_Water_led

        (

            .clk(sclk),

            .Rst(Rst_n),

             .led_state(led_flag),

              .led(LED)

        );

     

    //按键按下的例化

    key_ctrl inst_key_ctrl

     (

        .key1_in(key1_money),

        .key2_in(key2_money),

        .po_money(kmoney)

         );

     

    //按键消抖的例化

    key_filter  inst_key1

        (

            .clk(sclk),

             .Rst(Rst_n),

             .Key1(key_sw1),

             .Pin_out(key1_money)

             );

       

     

    //例化按键2

    key_filter  inst_key2

        (

            .clk(sclk),

             .Rst(Rst_n),

             .Key1(key_sw2),

             .Pin_out(key2_money)

             );

    endmodule

    由于按键消抖和状态机设计模块,在前面两次实验中,我已经做了详细分析,这次就不再赘述。

    三仿真波形

    在仿真模块,我是按照拆分--设计—验证的步骤。即每做一个设计模块,先把语法错误等解决了,然后根据设计功能进行仿真,若仿真没问题,再做下一个模块,直到所有模块的仿真没问题,我再把所有模块组合连接起来在顶层,最后我就是对顶层进行仿真,若仿真没问题,再建立工程,关键约束,下载到开发板验证实验现象。

    `timescale 1ns/1ns

    `define clk_period 20

    module top_tb();

    reg  sclk;

    reg  Rst_n;

    reg key1_in;

    reg key2_in;

    wire [3:0]po_led;  

     

    initial sclk = 1;

    always#(`clk_period/2) sclk =~sclk;

     

    initial begin

    Rst_n = 0;

    key1_in=1'b0;key2_in=1'b0;

    #(`clk_period*20)key1_in=1'b1;

    Rst_n = 1;key2_in=1'b1;

    #(`clk_period*15)key2_in=1'b1;

    #(`clk_period*15)key1_in=1'b0;

    #(`clk_period*14)key1_in=1'b1;

    key2_in=1'b1;

    #(`clk_period*15)key2_in=1'b0;

    key1_in=1'b1;

    #(`clk_period*15)key2_in=1'b1;

    key1_in=1'b1;

    #(`clk_period*15)key2_in=1'b1;

    key1_in=1'b0;

    #(`clk_period*15);

    key1_in=1'b1;

    key2_in=1'b1;

    #(`clk_period*15)key2_in=1'b0;

    #(`clk_period*15)key2_in=1'b1;

    #(`clk_period*802)key2_in=1'b1;

    key1_in=1'b1;

    #(`clk_period*100)key2_in=1'b0;

    #(`clk_period*100);

    Rst_n = 0;#(`clk_period*20);

    //第二次仿真单向流水灯

    key1_in=1'b1;

    Rst_n = 1;key2_in=1'b1;

    #(`clk_period*15)key2_in=1'b1;

    #(`clk_period*15)key1_in=1'b0;

    #(`clk_period*14)key1_in=1'b1;

    key2_in=1'b1;

    #(`clk_period*15)key2_in=1'b0;

    key1_in=1'b1;

    #(`clk_period*15)key2_in=1'b1;

    key1_in=1'b1;

    #(`clk_period*15)key2_in=1'b1;

    key1_in=1'b0;

    #(`clk_period*15);

    key1_in=1'b1;

    key2_in=1'b1;

    #(`clk_period*15)key1_in=1'b0;

    #(`clk_period*15)key1_in=1'b1;

    #(`clk_period*802)key2_in=1'b1;

    key1_in=1'b1;

    $stop;

    end

     

    top inst_top

        (

            .sclk(sclk),

            .Rst_n(Rst_n),

            .key_sw1(key1_in),

             .key_sw2(key2_in),

             .LED(po_led)

              );

    endmodule

    从图中得知,LED在按键控制下可以实现,亮一个或两个,单向流水灯,双向流水灯。

    我这次的经验是:

    第一点:每个模块都要进行仿真,所有模块仿真没问题,再按照图纸连接各个模块,最后建立顶层和对顶层进行仿真。第二点:仿真的设计,主要是贴近设计功能,如按键1按下亮一个,那就模拟按键1按下;若是流水灯每亮一个需要200ms那么仿真时间也需要大于这个时间。第三点是全面,如按键1和按键2按下,还有单向和双向流水灯等都要仿真。

  • 相关阅读:
    .net常用框架总结
    微信小程序 语音转换
    nginx+redis实现session共享 .NET分布式架构
    Redis 安装及注册服务
    WebApi跨域
    Uri各个属性取值测试
    一些常用的FFMPEG命令集合
    动态规划重学习笔记
    给自己的电脑时间进行精准校时
    [NOI题库][POJ2536][匈牙利算法][二分图最大匹配]Gopher II
  • 原文地址:https://www.cnblogs.com/Xwangzi66/p/12877386.html
Copyright © 2011-2022 走看看