数字设计实验:自动售货机

自动售货机设计

1.实验目的

本实验旨在设计一个可以出售6种商品的自动售货机,能够实现通过投放不同面值的货币判断是否达到商品价格从而完成出售的功能.

2.实验内容

设计一个自动售货机控制器,可以售出6种货物。货物价格定义如下:

商品编号商品价格
10.5元
21元
31.5元
42元
56.5元
613元

注意: 有的价格需要按两次货币输入按钮开关表示支付两次。例如:按两次2元按钮表示支付4元。

操作流程:

首先通过货物种类开关(选用3个switch开关)选择所需要的商品编号,同时将商品编号显示到7段数码管上。然后用四个按钮输入相应的货币, 当输入货币总和正确以后,点亮购买成功标志的led。此处假定输入的货币总和都是正好的,只要等于所选商品价格即可, 不必找零。

输入:

货物种类: switch开关3个,代表6种货物;

货币输入: 4个按钮, 分别代表5块, 2块, 1块, 5角;

时钟输入: 采用板子上的CLOCK_100,频率为100MHz。

输出:

商品编号:7段数码管,显示商品编号;

输入金额:7段数码管,显示已付金额

交易成功: LED指示灯1个, 表示购买成功。

3.实验设计思路

3.1 自动售货机内在计算逻辑设计

1.选择商品:

always语句中使用一个 case 语句,根据 sw(商品选择开关,值为001~110,即对应1至6商品编号)来选择商品的价格。商品价格用 8'd5(表示 0.5元)到 8'd130(表示 13元)表示。

// 根据开关定义商品价格
always @(*) begin
    case (sw)
        3'b001: product_price = 8'd5;   // 商品1  0.5元
        3'b010: product_price = 8'd10;  // 商品2  1元
        3'b011: product_price = 8'd15;  // 商品3  1.5元
        3'b100: product_price = 8'd20;  // 商品4  2元
        3'b101: product_price = 8'd65;  // 商品5  6.5元
        3'b110: product_price = 8'd130; // 商品6  13元
        default: product_price = 8'd0;  // 默认无商品
    endcase
end

2.输入金额:

四个按钮(5元、2元、1元、5角)用来增加已付金额,输入的金额通过 btn5btn2btn1btn05 信号进行累计,在always语句中,累加赋值使用非阻塞运算符"<="。

always @(*) begin
    if (reset) begin
        paid_amount <= 8'd0;  // 重置已付金额
    end else begin
        if (btn5) paid_amount <= paid_amount + 8'd50;  // 5元按钮
        if (btn2) paid_amount <= paid_amount + 8'd20;  // 2元按钮
        if (btn1) paid_amount <= paid_amount + 8'd10;  // 1元按钮
        if (btn05) paid_amount <= paid_amount + 8'd5;   // 5角按钮
    end
end

3.等值判断:

如果某时刻已输入金额等于当前选择的商品价格,则LED灯亮起表示支付成功,使用连续赋值语句即可:

// 判断是否支付成功
assign led_success = (paid_amount >= product_price);  // 支付金额与商品价格相等时,点亮LED

3.2 结合按钮输入和数码管显示的用户交互设计

实现了自动售货机的基本计算逻辑,接下来应该考虑如何与硬件对接以及直观显示.

首先是按钮,将表示输入金额的四种输入变量与板子中的四个按钮对接即可,按下一次按钮表示对应的输入变量经历过一次高电平;

其次是数码管,本实验的实验过程中需要显示8位数码管中的4位,其中1位需要显示商品编号,另外3位需要显示已付金额(小数位和个位的数码之间要带小数点,此处的DP为低电平).其设计可仿照先前实验的思路,用一个2位中间变量(有4种值)与时钟绑定,作为选择变量,会在00~11之间迅速地做周期性变化,当频率大于人眼分辨频率时,人眼中就会产生四位数码管同时点亮的视觉效果.

实现代码段如下:

// 根据 clkdiv[19:18] 来选择数码管显示
always @(*) begin
    // 先默认所有数码管都不亮
    AN = 8'b11111111;
    DP = 1;
    // 根据 clkdiv[19:18] 切换数码管
    case (clkdiv[19:18])
        2'b00: begin
            AN[4] = 0; // 点亮商品编号数码管
            DP = 1;
            case (product_number)
                4'd1: seg = 7'b1111001; // 显示商品1
                4'd2: seg = 7'b0100100; // 显示商品2
                4'd3: seg = 7'b0110000; // 显示商品3
                4'd4: seg = 7'b0011001; // 显示商品4
                4'd5: seg = 7'b0010010; // 显示商品5
                4'd6: seg = 7'b0000010; // 显示商品6
                default: seg = 7'b1111111; // 无商品时不显示
            endcase
        end
        2'b01: begin
            AN[0] = 0; // 点亮个位数数码管
            DP = 1;
            case (paid_amount % 10)
                0: seg = 7'b1000000;
                1: seg = 7'b1111001;
                2: seg = 7'b0100100;
                3: seg = 7'b0110000;
                4: seg = 7'b0011001;
                5: seg = 7'b0010010;
                6: seg = 7'b0000010;
                7: seg = 7'b1111000;
                8: seg = 7'b0000000;
                9: seg = 7'b0010000;
                default: seg = 7'b1111111;
            endcase
        end
        2'b10: begin
            AN[1] = 0; // 点亮十位数数码管
            DP = 0;
            case ((paid_amount / 10) % 10)
                0: seg = 7'b1000000;
                1: seg = 7'b1111001;
                2: seg = 7'b0100100;
                3: seg = 7'b0110000;
                4: seg = 7'b0011001;
                5: seg = 7'b0010010;
                6: seg = 7'b0000010;
                7: seg = 7'b1111000;
                8: seg = 7'b0000000;
                9: seg = 7'b0010000;
                default: seg = 7'b1111111;
            endcase
        end
        2'b11: begin
            AN[2] = 0; // 点亮百位数数码管
            DP = 1;
            case ((paid_amount / 100) % 10)
                0: seg = 7'b1000000;
                1: seg = 7'b1111001;
                2: seg = 7'b0100100;
                3: seg = 7'b0110000;
                4: seg = 7'b0011001;
                5: seg = 7'b0010010;
                6: seg = 7'b0000010;
                7: seg = 7'b1111000;
                8: seg = 7'b0000000;
                9: seg = 7'b0010000;
                default: seg = 7'b1111111;
            endcase
        end
        default: AN = 8'b11111111; // 默认不显示
    endcase
end

4.实验过程与实验结果

  • 仿真:

根据3.1中设计的自动售货机内部计算逻辑,用verilog编写一个简单的测试平台,模拟仿真自动售货机对购买不同商品的处理:

`timescale 1ps / 1ps

module vending_machine (
    input wire reset,              // 复位输入
    input wire [2:0] sw,          // 商品选择开关 (3个开关,选择1~6商品)
    input wire btn5,               // 5元按钮
    input wire btn2,               // 2元按钮
    input wire btn1,               // 1元按钮
    input wire btn05,              // 5角按钮
    output reg [7:0] product_price, // 商品价格
    output reg [7:0] paid_amount,   // 已支付金额
    output wire led_success         // 购买成功指示灯
);

// 定义商品价格
always @(*) begin
    case (sw)
        3'b001: product_price = 8'd5;   // 商品1  0.5元
        3'b010: product_price = 8'd10;  // 商品2  1元
        3'b011: product_price = 8'd15;  // 商品3  1.5元
        3'b100: product_price = 8'd20;  // 商品4  2元
        3'b101: product_price = 8'd65;  // 商品5  6.5元
        3'b110: product_price = 8'd130; // 商品6  13元
        default: product_price = 8'd0;  // 默认无商品
    endcase
end

// 控制支付金额(根据按钮按下边沿更新支付金额)
always @(posedge btn5 or posedge btn2 or posedge btn1 or posedge btn05 or posedge reset) begin
    if (reset) begin
        paid_amount <= 8'd0;  // 重置已付金额
    end else begin
        if (btn5) paid_amount <= paid_amount + 8'd50;  // 5元按钮
        if (btn2) paid_amount <= paid_amount + 8'd20;  // 2元按钮
        if (btn1) paid_amount <= paid_amount + 8'd10;  // 1元按钮
        if (btn05) paid_amount <= paid_amount + 8'd5;   // 5角按钮
    end
end

// 判断是否支付成功
assign led_success = (paid_amount >= product_price);  // 支付金额与商品价格相等时,点亮LED

endmodule


module tb_vending_machine;

// 1. 声明信号
reg reset;                // 复位信号
reg [2:0] sw;             // 商品选择开关
reg btn5, btn2, btn1, btn05; // 付款按钮
wire [7:0] paid_amount;    // 已支付金额
wire led_success;          // 购买成功指示灯
wire [7:0] product_price;  // 商品价格(需要从自动售货机模块中获取)

// 实例化待测试的自动售货机模块
vending_machine vm (
    .reset(reset),
    .sw(sw),
    .btn5(btn5),
    .btn2(btn2),
    .btn1(btn1),
    .btn05(btn05),
    .paid_amount(paid_amount),
    .led_success(led_success),
    .product_price(product_price)
);

// 3. 测试过程
initial begin
    // 初始化信号
    reset = 0;
    sw = 3'b000;   // 默认无商品
    btn5 = 0;
    btn2 = 0;
    btn1 = 0;
    btn05 = 0;

    // 测试步骤: 每次输入前触发复位
    // 复位操作
    reset = 1;  // 拉高复位信号
    #10 reset = 0; // 解除复位
    #10; // 等待一段时间,确保复位过程完成

    // 测试选择商品1(0.5元)并支付5角
    sw = 3'b001;  // 选择商品1
    #10 btn05 = 1; // 按下5角按钮
    #10 btn05 = 0; // 释放5角按钮

    // 测试选择商品2(1元)并支付1元
    #10 reset = 1;#10 reset = 0;  // 复位清零已支付金额
    sw = 3'b010;  // 选择商品2
    #10 btn1 = 1; // 按下1元按钮
    #10 btn1 = 0; // 释放1元按钮

    // 测试选择商品3(1.5元)并支付1元
    #10 reset = 1;#10 reset = 0;  // 复位清零已支付金额
    sw = 3'b011;  // 选择商品3
    #10 btn1 = 1; // 按下1元按钮
    #10 btn1 = 0; // 释放1元按钮

    // 测试选择商品4(2元)并支付2元
    #10 reset = 1;#10 reset = 0;  // 复位清零已支付金额
    sw = 3'b100;  // 选择商品4
    #10 btn2 = 1; // 按下2元按钮
    #10 btn2 = 0; // 释放2元按钮

    // 测试选择商品5(6.5元)并支付6.5元
    #10 reset = 1;#10 reset = 0;  // 复位清零已支付金额
    sw = 3'b101;  // 选择商品5
    #10 btn5 = 1; // 按下5元按钮
    #10 btn5 = 0; // 释放5元按钮
    #10 btn1 = 1; // 按下1元按钮
    #10 btn1 = 0; // 释放1元按钮
    #10 btn05 = 1; // 按下5角按钮
    #10 btn05 = 0; // 释放5角按钮

    // 测试选择商品6(13元)并支付13元
    #10 reset = 1;#10 reset = 0;  // 复位清零已支付金额
    sw = 3'b110;  // 选择商品6
    #10 btn5 = 1; // 按下5元按钮
    #10 btn5 = 0; // 释放5元按钮
    #10 btn5 = 1; // 再按5元按钮
    #10 btn5 = 0; // 释放5元按钮
    #10 btn2 = 1; // 按下2元按钮
    #10 btn2 = 0; // 释放2元按钮
    #10 btn1 = 1; // 按下1元按钮
    #10 btn1 = 0; // 释放1元按钮

    // 结束测试
    $finish;
end

// 查看测试结果
initial begin
    $monitor("Time: %t, Product Price: %d, Paid Amount: %d, LED Success: %b, sw: %b, btn5: %b, btn2: %b, btn1: %b, btn05: %b",
             $time, product_price, paid_amount, led_success, sw, btn5, btn2, btn1, btn05);
end

endmodule

在vivado上运行测试平台,得到控制台的输入如下,易观察得,商品价格随开关sw的值变化而变化,而已付金额随着按钮被按的情况实时改变:

在这里插入图片描述

仿真波形图能更直观地展现出售过程:

在这里插入图片描述

  • 导入实验板验证:

加上3.2中的设计思路,写入完整的verilog文件如下:

`timescale 1ps / 1ps

module vending_machine (
    input wire clk,                // 时钟输入
    input wire reset,              // 复位输入
    input wire [2:0] sw,          // 商品选择开关 (3个开关,选择1~6商品)
    input wire btn5,               // 5元按钮
    input wire btn2,               // 2元按钮
    input wire btn1,               // 1元按钮
    input wire btn05,              // 5角按钮
    output reg [6:0] seg,         // 七段显示器输出
    output reg [7:0] AN,          // 选择某一位数码管
    output reg DP,
    output wire led_success       // 购买成功指示灯
);

// 定义商品价格
reg [7:0] product_price;
reg [7:0] paid_amount;           // 已付金额
reg [3:0] product_number;        // 商品编号

// 防抖动处理:时钟分频部分
reg [23:0] count;               // 分频计数器
reg clk2;                        // 分频后的低频时钟
reg btn5_in, btn2_in, btn1_in, btn05_in;  // 内部采样信号

// 商品价格定义: 商品编号 1到6的价格
always @(*) begin
    case (sw)
        3'b001: product_price = 8'd5;  // 商品1  0.5元
        3'b010: product_price = 8'd10; // 商品2  1元
        3'b011: product_price = 8'd15; // 商品3  1.5元
        3'b100: product_price = 8'd20; // 商品4  2元
        3'b101: product_price = 8'd65; // 商品5  6.5元
        3'b110: product_price = 8'd130; // 商品6  13元
        default: product_price = 8'd0;  // 默认无商品
    endcase
    // 显示商品编号(1到6)
    if (sw != 3'b000) begin
        product_number = sw;  // 商品编号为 sw 的值
    end else begin
        product_number = 4'd0;  // 无商品
    end
end

// 防抖动:时钟分频部分
always @(posedge clk or posedge reset) begin
    if (reset) begin
        count <= 24'b0;
        clk2 <= 0;  // 清零分频时钟
    end else begin
        count <= count + 1'b1; 
        if (count[23] == 1) begin
            clk2 <= ~clk2;  // 这样clk2的频率约为3Hz
            count <= 24'b0; // 重置计数器
        end
    end
end

// 防抖动:低频时钟下采样按钮信号
always @(posedge clk2) begin
    btn5_in <= btn5;  // 采样按钮信号
    btn2_in <= btn2;
    btn1_in <= btn1;
    btn05_in <= btn05;
end

// 控制支付金额
always @(posedge clk2 or posedge reset) begin
    if (reset) begin
        paid_amount <= 8'd0;  // 重置已付金额
    end else begin
        // 根据按钮输入增加已付金额
        if (btn5_in) paid_amount <= paid_amount + 8'd50;  // 5元按钮
        if (btn2_in) paid_amount <= paid_amount + 8'd20;  // 2元按钮
        if (btn1_in) paid_amount <= paid_amount + 8'd10;  // 1元按钮
        if (btn05_in) paid_amount <= paid_amount + 8'd5;  // 5角按钮
    end
end
    
// 判断是否支付成功
assign led_success = (paid_amount == product_price);  // 支付金额与商品价格相等时,点亮LED

// 7段数码管显示商品编号和已支付金额
reg [19:0] clkdiv;      // 用于分频

// 时钟分频和数码管切换
always @(posedge clk or posedge reset) begin
    if (reset) begin
        clkdiv <= 20'd0;
    end else begin
        clkdiv <= clkdiv + 1;
    end
end

// 根据 clkdiv[19:18] 来选择数码管显示
always @(*) begin
    // 先默认所有数码管都不亮
    AN = 8'b11111111;
    DP = 1;

    // 根据 clkdiv[19:18] 切换数码管
    case (clkdiv[19:18])
        2'b00: begin
            AN[4] = 0; // 点亮商品编号数码管
            DP = 1;
            case (product_number)
                4'd1: seg = 7'b1111001; // 显示商品1
                4'd2: seg = 7'b0100100; // 显示商品2
                4'd3: seg = 7'b0110000; // 显示商品3
                4'd4: seg = 7'b0011001; // 显示商品4
                4'd5: seg = 7'b0010010; // 显示商品5
                4'd6: seg = 7'b0000010; // 显示商品6
                default: seg = 7'b1111111; // 无商品时不显示
            endcase
        end
        2'b01: begin
            AN[0] = 0; // 点亮个位数数码管
            DP = 1;
            case (paid_amount % 10)
                0: seg = 7'b1000000;
                1: seg = 7'b1111001;
                2: seg = 7'b0100100;
                3: seg = 7'b0110000;
                4: seg = 7'b0011001;
                5: seg = 7'b0010010;
                6: seg = 7'b0000010;
                7: seg = 7'b1111000;
                8: seg = 7'b0000000;
                9: seg = 7'b0010000;
                default: seg = 7'b1111111;
            endcase
        end
        2'b10: begin
            AN[1] = 0; // 点亮十位数数码管
            DP = 0;
            case ((paid_amount / 10) % 10)
                0: seg = 7'b1000000;
                1: seg = 7'b1111001;
                2: seg = 7'b0100100;
                3: seg = 7'b0110000;
                4: seg = 7'b0011001;
                5: seg = 7'b0010010;
                6: seg = 7'b0000010;
                7: seg = 7'b1111000;
                8: seg = 7'b0000000;
                9: seg = 7'b0010000;
                default: seg = 7'b1111111;
            endcase
        end
        2'b11: begin
            AN[2] = 0; // 点亮百位数数码管
            DP = 1;
            case ((paid_amount / 100) % 10)
                0: seg = 7'b1000000;
                1: seg = 7'b1111001;
                2: seg = 7'b0100100;
                3: seg = 7'b0110000;
                4: seg = 7'b0011001;
                5: seg = 7'b0010010;
                6: seg = 7'b0000010;
                7: seg = 7'b1111000;
                8: seg = 7'b0000000;
                9: seg = 7'b0010000;
                default: seg = 7'b1111111;
            endcase
        end
        default: AN = 8'b11111111; // 默认不显示
    endcase
end

endmodule

并正确编写约束文件:

# Clock signal
set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {clk}];

# Switches
set_property -dict { PACKAGE_PIN J15   IOSTANDARD LVCMOS33 } [get_ports { sw[0] }]; #IO_L24N_T3_RS0_15 Sch=sw[0]
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=sw[1]
set_property -dict { PACKAGE_PIN M13   IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L6N_T0_D08_VREF_14 Sch=sw[2]
set_property -dict { PACKAGE_PIN V10   IOSTANDARD LVCMOS33 } [get_ports { reset }]; #IO_L21P_T3_DQS_14 Sch=sw[15]

# Buttons
set_property -dict { PACKAGE_PIN N17   IOSTANDARD LVCMOS33 } [get_ports { btn05 }]; #IO_L9P_T1_DQS_14 Sch=btnc
set_property -dict { PACKAGE_PIN M18   IOSTANDARD LVCMOS33 } [get_ports { btn1 }]; #IO_L4N_T0_D05_14 Sch=btnu
set_property -dict { PACKAGE_PIN P17   IOSTANDARD LVCMOS33 } [get_ports { btn2 }]; #IO_L12P_T1_MRCC_14 Sch=btnl
set_property -dict { PACKAGE_PIN M17   IOSTANDARD LVCMOS33 } [get_ports { btn5 }]; #IO_L10N_T1_D15_14 Sch=btnr

# LEDs
set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { led_success }]; #IO_L18P_T2_A24_15 Sch=led[0]

# 7 segment display
set_property -dict { PACKAGE_PIN T10   IOSTANDARD LVCMOS33 } [get_ports { seg[0] }]; #IO_L24N_T3_A00_D16_14 Sch=ca
set_property -dict { PACKAGE_PIN R10   IOSTANDARD LVCMOS33 } [get_ports { seg[1] }]; #IO_25_14 Sch=cb
set_property -dict { PACKAGE_PIN K16   IOSTANDARD LVCMOS33 } [get_ports { seg[2] }]; #IO_25_15 Sch=cc
set_property -dict { PACKAGE_PIN K13   IOSTANDARD LVCMOS33 } [get_ports { seg[3] }]; #IO_L17P_T2_A26_15 Sch=cd
set_property -dict { PACKAGE_PIN P15   IOSTANDARD LVCMOS33 } [get_ports { seg[4] }]; #IO_L13P_T2_MRCC_14 Sch=ce
set_property -dict { PACKAGE_PIN T11   IOSTANDARD LVCMOS33 } [get_ports { seg[5] }]; #IO_L19P_T3_A10_D26_14 Sch=cf
set_property -dict { PACKAGE_PIN L18   IOSTANDARD LVCMOS33 } [get_ports { seg[6] }]; #IO_L4P_T0_D04_14 Sch=cg

set_property -dict { PACKAGE_PIN H15   IOSTANDARD LVCMOS33 } [get_ports { DP }]; #IO_L19N_T3_A21_VREF_15 Sch=dp

set_property -dict { PACKAGE_PIN J17   IOSTANDARD LVCMOS33 } [get_ports { AN[0] }]; #IO_L23P_T3_FOE_B_15 Sch=an[0]
set_property -dict { PACKAGE_PIN J18   IOSTANDARD LVCMOS33 } [get_ports { AN[1] }]; #IO_L23N_T3_FWE_B_15 Sch=an[1]
set_property -dict { PACKAGE_PIN T9    IOSTANDARD LVCMOS33 } [get_ports { AN[2] }]; #IO_L24P_T3_A01_D17_14 Sch=an[2]
set_property -dict { PACKAGE_PIN J14   IOSTANDARD LVCMOS33 } [get_ports { AN[3] }]; #IO_L19P_T3_A22_15 Sch=an[3]
set_property -dict { PACKAGE_PIN P14   IOSTANDARD LVCMOS33 } [get_ports { AN[4] }]; #IO_L8N_T1_D12_14 Sch=an[4]
set_property -dict { PACKAGE_PIN T14   IOSTANDARD LVCMOS33 } [get_ports { AN[5] }]; #IO_L14P_T2_SRCC_14 Sch=an[5]
set_property -dict { PACKAGE_PIN K2    IOSTANDARD LVCMOS33 } [get_ports { AN[6] }]; #IO_L23P_T3_35 Sch=an[6]
set_property -dict { PACKAGE_PIN U13   IOSTANDARD LVCMOS33 } [get_ports { AN[7] }]; #IO_L23N_T3_A02_D18_14 Sch=an[7]

实验板验证示例:

默认状态为:右侧数码管显示00.0,指默认金额为0,右下角的LED灯默认点亮.

在这里插入图片描述

将开关拨至值为1,左侧数码管显示商品编号,右侧数码管显示已付金额,此时LED灯关闭:

在这里插入图片描述

拨动最左下的开关(SW[15]),启用复位功能,能够将已付金额清零,若再次拨回复位键则右侧数码管显示的已付金额刷新为00.0:

在这里插入图片描述

接下来是对购买不同编号商品的测试:

1.开关拨至值为1,按一次表示5角的按钮,右侧数码管显示的已付金额变成00.5,等于商品价格(0.5),右下LED灯点亮,符合预期结果:

在这里插入图片描述

2.开关拨至值为2,按一次表示1元的按钮后,右侧数码管显示的已付金额变成01.0,等于商品价格(1元),右下LED灯点亮,符合预期结果:

在这里插入图片描述

3.开关拨至值为3,按一次表示5角的按钮和表示1元的按钮后(或者按三次表示5角的按钮,只要满足加和为1.5即可),右侧数码管显示的已付金额变成01.5,等于商品价格(1.5元),右下LED灯点亮,符合预期结果:

在这里插入图片描述

4.开关拨至值为4,按一次表示2元的按钮后,右侧数码管显示的已付金额变成02.0,等于商品价格(2元),右下LED灯点亮,符合预期结果:

在这里插入图片描述

5.开关拨至值为5,按一次表示5元的按钮、一次表示1元的按钮和一次表示5角的按钮后,右侧数码管显示的已付金额变成06.5,等于商品价格(6.5元),右下LED灯点亮,符合预期结果:

在这里插入图片描述

6.开关拨至值为6,按两次表示5元的按钮、三次表示1元的按钮后,右侧数码管显示的已付金额变成13.0,等于商品价格(13元),右下LED灯点亮,符合预期结果:

在这里插入图片描述

经过以上简单的举例验证可基本确定基于硬件实现的自动售货机功能的正确性.

5.实验总结

5.1 实验中遇到的问题与解决方法

本实验中遇到最大的问题是输入端,会涉及抖动问题,由于按开发板上的按钮时由于抖动,有可能会发生输入信号短时间内的多次变化,这样的情况会对结果造成影响.因此需要将高频的时钟信号分频为低频信号, 这样在低速采集输入, 就只会接受正确的输入, 而过滤掉相对高频的抖动.

解决方法:

// 时钟分频
always @(posedge clk or posedge reset) begin
    if (reset) begin
        count <= 24'b0;
        clk2 <= 0;  // 清零分频时钟
    end else begin
        count <= count + 1'b1; 
        if (count[23] == 1) begin
            clk2 <= ~clk2;  // 这样clk2的频率约为3Hz
            count <= 24'b0; // 重置计数器
        end
    end
end

// 低频时钟下采样按钮信号
always @(posedge clk2) begin
    btn5_in <= btn5;  // 采样按钮信号
    btn2_in <= btn2;
    btn1_in <= btn1;
    btn05_in <= btn05;
end

// 控制支付金额
always @(posedge clk2 or posedge reset) begin
    if (reset) begin
        paid_amount <= 8'd0;  // 重置已付金额
    end else begin
        // 根据按钮输入增加已付金额
        if (btn5_in) paid_amount <= paid_amount + 8'd50;  // 5元按钮
        if (btn2_in) paid_amount <= paid_amount + 8'd20;  // 2元按钮
        if (btn1_in) paid_amount <= paid_amount + 8'd10;  // 1元按钮
        if (btn05_in) paid_amount <= paid_amount + 8'd5;  // 5角按钮
    end
end

5.2 实验体会

本实验通过对自动售货机功能的逐步分解,依次给出了实现思路和解决方法,最终基于verilog编程以及实验板实验完成了自动售货机的设计,同时解决了按钮输入的防抖动问题,加强了对时钟分频的理解和运用,同时对七段数码管的显示原理也更加熟练.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值