FPGA实现简易的自动售货机模型

这篇具有很好参考价值的文章主要介绍了FPGA实现简易的自动售货机模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

环境:
1、Quartus18.1
2、vscode
3、板子型号:EP4CE6F17C8N
要求:
自动售货机功能:
1.两个按键用于投币,1个1元,1个5角
2.一个按键用于商品选择,农夫山泉2块,干脆面1块5
3.找零
设计:
我们选择使用四个按键,分别用于投币一元、五角、商品选择、结算四个功能。用六位数码管进行展示,高两位表示投币金额,中间两位表示所选商品类型('10’表示干脆面、'01’表示矿泉水),低两位表示找零数目。交易成功蜂鸣器响起5s。


一、系统设计

1、模块框图

FPGA实现简易的自动售货机模型

2、状态机框图

FPGA实现简易的自动售货机模型

3、RTL视图

FPGA实现简易的自动售货机模型

二、源码

1.蜂鸣器驱动模块

module beep_ctrl(
    input wire clk,
    input wire rst_n,
    input wire pwm,

    output wire beep
);
reg beep_r;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        beep_r<=1'b1;
    end
    else if(pwm)begin
        beep_r<=1'b0;
    end
    else begin
        beep_r<=1'b1;
    end
end
assign beep=beep_r;
endmodule

2.按键消抖模块

module key_debounce (
    input   wire    clk     ,
    input   wire    rst_n   ,
    input   wire    [3:0] key,
      
    output  wire    [3:0] key_out
);

    localparam      MAX20       = 20'd100_0000; 
    reg [19:0]      cnt_20ms;
    reg             start   ;//稳定信号 开始
    reg [3:0]       key_r0  ;//按键信号寄存器0
    reg [3:0]       key_r1  ;//按键信号寄存器1
    wire            nedge   ;//下降沿
    reg [3:0]       key_r   ;//开启流水

//倒计时计数器20ms
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_20ms <= 20'd0;
    end
    else if(nedge)begin
        cnt_20ms <= MAX20;
    end
    else if(start) begin
        if(cnt_20ms == 1'd1)begin
            cnt_20ms <= 20'd0;  
        end
        else begin
            cnt_20ms <= cnt_20ms - 1'd1;
        end
    end
    else begin
        cnt_20ms <= cnt_20ms;
    end
end

//下降沿检测
always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            key_r0 <= 4'b1111;//非阻塞赋值,同时赋值
            key_r1 <= 4'b1111;
        end
        else begin
            key_r0 <= key; //打一拍,同步时钟域
            key_r1 <= key_r0; //打一拍,检测按键下降沿
        end
    end

assign nedge = (~key_r0[0] && key_r1[0]) || (~key_r0[1] && key_r1[1]) || (~key_r0[2] && key_r1[2]) || (~key_r0[3] && key_r1[3]);//检测到下降沿

//约束start信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        start <= 1'b0;
    end
    else if(nedge)begin
        start <= 1'b1;
    end
    else if(cnt_20ms == 1'b1)begin
        start <= 1'b0;
    end
    else begin
        start <= start;
    end
end

//约束flag信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
       key_r <= 4'b0000;
    end
    else if(cnt_20ms == 1'b1)begin
       key_r <= ~key_r0;
    end
    else begin
        key_r <= 4'b0000;//修改
    end
end

assign key_out =key_r;
endmodule

3、PWM模块

module pwm (
    input wire clk,
    input wire rst_n,
    input wire flag_beep,

    output wire pwm
);
parameter  CNT_300MS= 24'd15_000_000;
parameter  NUM_NOTE =6'd34;
parameter  DO = 16'd47749,
           RE = 16'd42549,
           MI = 16'd37899,
           FA = 16'd37549,
           SO = 16'd31849,
           LA = 16'd28399,
           XI = 16'd25399;
reg [23:0]  cnt_300ms;
wire add_cnt_300ms;
wire end_cnt_300ms;

reg [5:0]   cnt_note;  
wire add_cnt_note;
wire end_cnt_note;

reg [15:0] cnt_freq;
wire add_cnt_freq;
wire end_cnt_freq;

reg [15:0] freq_data;
wire [15:0] duty_data;//占空比
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_300ms<=24'd0;
    end
    else if(add_cnt_300ms)begin
        if(end_cnt_300ms)begin
            cnt_300ms<=24'd0;
        end
        else begin
            cnt_300ms<=cnt_300ms+1'd1;
        end
    end
    else begin
        cnt_300ms<=cnt_300ms;
    end
end
assign add_cnt_300ms=1'd1;
assign end_cnt_300ms=add_cnt_300ms&&(cnt_300ms==CNT_300MS-1'd1);
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_note<=6'd0;
    end
    else if(add_cnt_note)begin
        if(end_cnt_note)begin
            cnt_note<=6'd0;
        end
        else begin
            cnt_note<=cnt_note+1'd1;
        end
    end
    else begin
        cnt_note<=cnt_note;
    end
end
assign add_cnt_note=end_cnt_300ms;
assign end_cnt_note=add_cnt_note&&cnt_note==NUM_NOTE-1'd1;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_freq<=16'd0;
    end
    else if(add_cnt_freq)begin
        if(end_cnt_freq)begin
            cnt_freq<=16'd0;
        end
        else begin
            cnt_freq<=cnt_freq+1'd1;
        end
    end
    else begin
        cnt_freq<=cnt_freq;
    end
end
assign add_cnt_freq=1'b1;
assign end_cnt_freq=add_cnt_freq&&cnt_freq==freq_data;

//音符查找表
always @(*)begin
    case (cnt_note)
        6'd0:   freq_data=DO;
        6'd1:   freq_data=RE;
        6'd2:   freq_data=MI;
        6'd3:   freq_data=DO;
        6'd4:   freq_data=DO;
        6'd5:   freq_data=RE;
        6'd6:   freq_data=MI;
        6'd7:   freq_data=DO;
        6'd8:   freq_data=MI;
        6'd9:   freq_data=FA;
        6'd10:  freq_data=SO;
        6'd11:  freq_data=MI;
        6'd12:  freq_data=FA;
        6'd13:  freq_data=SO;
        6'd14:  freq_data=SO;
        6'd15:  freq_data=LA;
        6'd16:  freq_data=SO;
        6'd17:  freq_data=FA;
        6'd18:  freq_data=MI;
        6'd19:  freq_data=DO;
        6'd20:  freq_data=SO;
        6'd21:  freq_data=LA;
        6'd22:  freq_data=SO;
        6'd23:  freq_data=FA;
        6'd24:  freq_data=MI;
        6'd25:  freq_data=DO;
        6'd26:  freq_data=RE;
        6'd27:  freq_data=SO;
        6'd28:   freq_data=DO;
        6'd29:   freq_data=DO;
        6'd30:   freq_data=RE;
        6'd31:   freq_data=SO;
        6'd32:   freq_data=DO;
        6'd33:   freq_data=DO;
        default: freq_data=DO;
    endcase
end
assign duty_data=freq_data>>1;
assign pwm = (cnt_freq>duty_data&&flag_beep==1)?1'b1:1'b0;
endmodule

4、sale_goods模块(状态机部分)

module sale_goods (
    input  wire             clk      ,
    input  wire             rst_n    ,
    input  wire  [3:0]      key_in   ,

    output wire  [11:0]     dout,
    output wire             beep  
);
//状态空间
parameter IDLE       = 3'd0,
          Half       = 3'd1,
          ONE        = 3'd2,
          ONE_Half   = 3'd3,
          TWO        = 3'd4;

parameter IDLE1       = 3'd0,
          shui       =  3'd1, 
          mian       =  3'd2;
parameter MAX1S = 26'd50_000_000;
parameter MAX_5 =3'd5;
reg [25:0] cnt_1s;
reg [2:0] cstate;//现态
reg [2:0] nstate;//次态
reg [2:0] cstate1;//现态
reg [2:0] nstate1;//次态
reg [4:0] pay_r;
reg [3:0] choose_r;
reg [2:0] pay_back;
reg beep_r;
reg beep_r1;
reg [2: 0]  cnt_5s;
//1s计数器
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_1s <= 26'd0;
    end 
    else if(cnt_1s == MAX1S - 1'd1)begin
        cnt_1s <= 26'd0;
    end   
    else begin
        cnt_1s <= cnt_1s + 1'd1;
    end   
end 
//五秒计数器
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_5s <= 3'd0;
    end 
    else if(cnt_5s == MAX_5 - 1'd1 && cnt_1s == MAX1S - 1'd1)begin
        cnt_5s <= 3'd0;
    end
    else if(cnt_1s == MAX1S - 1'd1&&beep_r==1)begin
        cnt_5s <= cnt_5s + 1'd1;
    end 
    else begin
        cnt_5s <= cnt_5s;
    end 
end 

//第一段
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cstate<=IDLE;
    end
    else begin
        cstate<=nstate;
    end
end
//第二段
always @(*)begin
    case(cstate)                                  //key[0]0.5元,key[1]1  ,key[2]选择 ,key[3]下单
        IDLE:   begin
                    if(key_in[0])begin
                        nstate = Half;
                    end
                    else if(key_in[1])begin
                        nstate=ONE;
                    end
                    else begin
                        nstate=cstate;
                    end
                end
        Half:    begin
                    if(key_in[0])begin
                        nstate = ONE;
                    end
                    else if(key_in[1])begin
                        nstate=ONE_Half;
                    end
                    else begin
                        nstate=cstate;
                    end
                end
        ONE:     begin
                    if(key_in[0])begin
                        nstate = ONE_Half;
                    end
                    else if(key_in[1])begin
                        nstate=TWO;
                    end
                    else begin
                        nstate=cstate;
                    end
                end
        ONE_Half:     begin
                    if(key_in[0])begin
                        nstate = TWO;
                    end
                    else if(key_in[3])begin
                        nstate =IDLE;
                    end
                    else begin
                        nstate=cstate;
                    end
                end
        TWO:    begin
                    if(key_in[3])begin
                        nstate = IDLE;
                    end
                    else begin
                        nstate=cstate;
                    end
                end
        default:   nstate = IDLE;
    endcase
end
//第三段
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        pay_r<=5'd0;
    end
    else begin
        case(cstate)
            IDLE:         pay_r <=5'd0;
            Half    :     pay_r <=5'd5;
            ONE     :     pay_r <=5'd10;
            ONE_Half:     pay_r <=5'd15;
            TWO     :     pay_r <=5'd20;
            default:      pay_r <=5'd0;
        endcase
    end
end

/*商品选择状态机*/
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cstate1<=IDLE1;
    end
    else begin
        cstate1<=nstate1;
    end
end

//第二段
always @(*)begin
    case(cstate1)                                  //00未选择,10mian,01shui
        IDLE1:   begin
                    if(key_in[2])begin
                        nstate1 = shui;
                    end
                    else begin
                        nstate1=cstate1;
                    end
                end
        shui:     begin
                    if(key_in[2])begin
                        nstate1 = mian;
                    end
                    else begin
                        nstate1=cstate1;
                    end
                end
        mian:     begin
                    if(key_in[2])begin
                        nstate1 = IDLE1;
                    end
                    else begin
                        nstate1=cstate1;
                    end
                end
        default:   nstate1 = IDLE1;
    endcase
end
//第三段
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        choose_r<=4'd0;
    end
    else begin
        case(cstate1)
           IDLE1:         choose_r <=4'd0;
           shui     :     choose_r <=4'd1;
           mian     :     choose_r <=4'd10;
           default:       choose_r <=4'd0;
        endcase
    end
end

//找零
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        pay_back <=3'd0;
        beep_r1<=0;
    end
    else if(key_in[3]&&cstate1==shui)begin
        if(pay_r>=20)begin
            pay_back<=pay_r-4'd20;
            beep_r1<=1;
        end
        else
            pay_back<=4'd33;
    end
    else if(key_in[3]&&cstate1==mian)begin
        if(pay_r>=15)begin
            pay_back<=pay_r-4'd15;
            beep_r1<=1;
        end
        else
            pay_back<=4'd33;
    end
    else begin
        beep_r1<=0;
    end
end
//五秒关蜂鸣器
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        beep_r<=1'b0;
    end
    else if(cnt_5s == MAX_5 - 1'd1 && cnt_1s == MAX1S - 1'd1)begin
        beep_r<=1'b0;
    end
    else if(beep_r1)begin
        beep_r<=1'b1;
    end
    else begin
        beep_r<=beep_r;
    end
end
assign dout={pay_r,choose_r,pay_back};
assign beep=beep_r;
endmodule

5、数码管驱动模块

module seg_driver(  //数码管驱动
    input               clk     ,
    input               rst_n   ,
    input       [14:0]  din     ,

    output reg  [5:0]   sel     , //片选
    output reg  [7:0]   dig       //段选     
);
parameter TIME_SCAN = 50_000;
    // parameter TIME_SCAN = 50_000 ; // 1MS 让片选一直扫描的移动

    parameter   ZER = 7'b100_0000,  // 0亮 1灭
                ONE = 7'b111_1001,
                TWO = 7'b010_0100,
                THR = 7'b011_0000,
                FOR = 7'b001_1001,
                FIV = 7'b001_0010,
                SIX = 7'b000_0010,
                SEV = 7'b111_1000,
                EIG = 7'b000_0000,
                NIN = 7'b001_0000;

    reg    [15:0]   cnt_scan    ; //扫描计数器
    wire            add_cnt_scan;
    wire            end_cnt_scan;

    reg    [3:0]    data        ; //寄存器 缓存数据
    reg             dot         ; //小数点

    wire   [3:0]    sec_l       ;  //秒低位
    wire   [3:0]    sec_h       ;  //秒 高位
    wire   [3:0]    min_l       ;  // 分地位
    wire   [3:0]    min_h       ;  //分高位
    wire   [3:0]    hou_l       ; 
    wire   [3:0]    hou_h       ;

    assign  sec_l = din[2:0]   % 10 ;  // 59 % 10 = 9 
    assign  sec_h = din[2:0]   / 10 ;  // 59 / 10 = 5 
    assign  min_l = din[6:3]  % 10 ;
    assign  min_h = din[6:3]  / 10 ;
    assign  hou_l = din[11:7] % 10 ;
    assign  hou_h = din[11:7] / 10 ;

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_scan <= 16'b0;
        end
        else if(add_cnt_scan)begin
            if(end_cnt_scan)begin
                cnt_scan <= 16'b0;
            end
            else begin
                cnt_scan <= cnt_scan + 1'b1;
            end
        end
        else begin
            cnt_scan <= cnt_scan ;
        end
    end

    assign add_cnt_scan = 1'b1;
    assign end_cnt_scan = add_cnt_scan && cnt_scan == TIME_SCAN - 1;

    always @(posedge clk or negedge rst_n)begin   // 片选
        if(!rst_n)begin
            sel <= 6'b011_111;
        end
        else if(end_cnt_scan)begin
            sel <= {sel[0],sel[5:1]};//循环向右移动  
        end
        else begin
            sel <= sel;
        end
    end 

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            data <= 3'b0;
            dot  <= 1'b1;
        end
        else begin
            case(sel)
                6'b011_111 : begin data <= sec_l; dot <= 1'b1; end
                6'b101_111 : begin data <= sec_h; dot <= 1'b0; end
                6'b110_111 : begin data <= min_l; dot <= 1'b1; end
                6'b111_011 : begin data <= min_h; dot <= 1'b1; end
                6'b111_101 : begin data <= hou_l; dot <= 1'b1; end
                6'b111_110 : begin data <= hou_h; dot <= 1'b0; end
                default : begin data <= 3'b0; dot <= 1'b1;end
            endcase
        end
    end
    
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            dig <= 8'b0;
        end
        else begin
            case(data)
                0 : dig <= {dot,ZER};
                1 : dig <= {dot,ONE};
                2 : dig <= {dot,TWO};
                3 : dig <= {dot,THR};
                4 : dig <= {dot,FOR};
                5 : dig <= {dot,FIV};
                6 : dig <= {dot,SIX};
                7 : dig <= {dot,SEV};
                8 : dig <= {dot,EIG};
                9 : dig <= {dot,NIN};
                default : dig <= 8'b0;
            endcase
        end
    end
endmodule

6、Sales(顶层模块)

module Sales(
    input clk,
    input rst_n,
    input [3:0] key_in,

    output [5:0] sel,
    output [7:0] seg,
    output wire  beep
);
wire [3:0] key_in1;
wire [11:0] dout;
wire beep1;
wire pwm1;
key_debounce key_debounce_inst(
    .clk       (clk)     ,
    .rst_n     (rst_n)     ,
    .key       (key_in)     ,

    .key_out   (key_in1)
);
sale_goods sale_goods_inst(
    .clk      (clk),
    .rst_n    (rst_n),
    .key_in   (key_in1),

    .dout     (dout),
    .beep     (beep1)
);
seg_driver seg_driver_inst(  //数码管驱动
    .clk   (clk)  ,
    .rst_n (rst_n)  ,
    .din   (dout)  ,

    .sel   (sel)  , //片选
    .dig   (seg)    //段选     
);
pwm pwm_inst(
    .clk         (clk)    ,
    .rst_n       (rst_n)    ,
    .flag_beep   (beep1)    ,
    .pwm         (pwm1) 
);
beep_ctrl beep_ctrl_inst(
    .clk        (clk)    ,
    .rst_n      (rst_n)    ,
    .pwm        (pwm1)    ,

    .beep       (beep)
);
endmodule

三、效果

FPGA模拟自动售货机


四、总结

此次的售货机模型只是一个简单的模拟,将之前所学习的各个小的模块进行一个综合的运用,使得对一些基本操作:数码管驱动、按键消抖、蜂鸣器、状态机等更加熟练,理解上也更加深刻。

五、参考资料

https://blog.csdn.net/weixin_43828944/article/details/124231997?spm=1001.2014.3001.5502文章来源地址https://www.toymoban.com/news/detail-485164.html

到了这里,关于FPGA实现简易的自动售货机模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【附源码】基于fpga的自动售货机(三段式状态机)

    目录 1、VL38 自动贩售机1 题目介绍 思路分析 代码实现 仿真文件 2、VL39 自动贩售机2 题目介绍: 题目分析 代码实现 仿真文件 3、状态机基本知识         设计一个自动贩售机,输入货币有三种,为0.5/1/2元,饮料价格是1.5元,要求进行找零,找零只会支付0.5元。 ps:   

    2024年02月01日
    浏览(28)
  • stm32毕设 自动售货机设计与实现(源码+硬件+论文)

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天

    2024年02月08日
    浏览(30)
  • python自动售货机

    分享python实现自动售货一个小作业 输入2进行购买商品,输入商品id和数量,可以选择不同商品,最后进行结账操作。 可以有两种查询方式,全部查询和按时间条件查询 具体操作如下: 全部查询: 时间条件查询: 就按4直接退出23333 其实就是参考路边的自动售货机贩卖饮料啥的,

    2024年02月11日
    浏览(22)
  • 【Python实训】饮品自动售货机

    这段代码定义了三个函数:show_goods()、total()和main()。show_goods()函数用于展示饮品信息,total()函数用于计算总额,main()函数用于控制整个饮品自动售货机的操作流程。 运行程序后,会首先调用show_goods()函数展示饮品信息,然后用户可以根据展示的信息选择饮品和数量,输入

    2024年02月05日
    浏览(32)
  • 如何用Java设计自动售货机?

    如何用Java设计自动售货机?是大多在高级Java开发人员面试中经常被问到的好问题之一。在典型的编码面试中,你会得到一个问题描述来开发一个售货机,在有限的时间内,通常2到3小时内,你需要在Java中编写设计文档、工作代码和单元测试。这种Java面试的一个关键优势是可以一次

    2024年02月05日
    浏览(86)
  • 自动售货机销售数据分析与应用

    本书不仅适用于零基础的读者自学,还适用于教师教学,为了帮助读者更加高效地掌握本书的内容,本书提供了以下10项附加价值: (1)建模平台:提供一站式大数据挖掘建模平台,免配置,包含大量案例工程,边练边学,告别纸上谈兵 (2)视频讲解:提供不少于600分钟

    2024年02月08日
    浏览(31)
  • 基于单片机的自动售货机系统设计

    欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式   本文设计基于单片机的自动售货机系统,分为硬件电路设计与软件设计。硬件电路设计,本系统由ST(意法半导体)公司设计和生产的STM32F103C8T6单片

    2024年02月03日
    浏览(32)
  • Verilog设计实例(一):自动售货机设计实例

    本系列为FPGA设计实例,基于Verilog HDL,题目一般是我在网上看到的一些FPGA相关的实验题目,基本会是一个实际场景的系统实现,而不是简单单元的设计,这是为了能更全面的练习,这些实例一般是可以基于FPGA进行实现的,因为正好手里有一块zynq板子,所以想把这个东西用起

    2024年02月05日
    浏览(26)
  • VIVADO自动售票机售货机verilog代码ego1开发板验证

    名称:VIVADO自动售票机售货机verilog代码ego1开发板验证 软件:VIVADO 语言:Verilog 代码功能: 自动售票机  1、自动售票机只出售1角、2角、5角和1元4种车票 2、只接收1角、5角和1元硬币,每次只能出售1张车  3、自动售票机具有累加销售额的功能 实验要求  出售车票用SW0~3分别

    2024年02月05日
    浏览(35)
  • 【Python】python自动售货机销售数据分析

    👉博__主👈:米码收割机 👉技__能👈:C++/Python语言 👉公众号👈:测试开发自动化【获取源码+商业合作】 👉荣__誉👈:阿里云博客专家博主、51CTO技术博主 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。 近年来,随着我国经济技术的不断提升,

    2024年02月06日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包