【FPGA入门十一】自动售货机改进

一.项目功能

1.默认只接收0.5元、1元投币。
⒉货物有4种可以选择,价格分别为0.5,1.5,2.4,3元。
3.满足当前选择的商品价格后自动出货,出货动作用4个LED做跑马灯(闪烁2s)表示。
4.超过前选择的商品价格之后,自动出货并找零,动作用4个LED做跑马灯表示(同样闪烁2s)。
5.显示当前投币的总额、当前选择的商品的价格以及找零的数目。
6.复位时播放音乐并显示彩灯。
7.投币不足目标价格时可以取消,动作用灯闪烁表示(2s)。

二.小组分工

机智的橙子:按键选择,数码管显示
WOOZI9600L²:模块仿真,led灯状态
一只特立独行的猪:按键消抖
醉意丶千层梦:模块整合,数码管显示,按键提示音
伊木子曦:蜂鸣器音乐播放,外接彩灯

三.设计思路

按键

原理图:
在这里插入图片描述
在这里插入图片描述
按键按下,KEY输出值为低电平,否则为高电平。

按键实现:

每个按键对应相应的功能:
key0:退货
key1:选择不同的价格(0.5;1.5;2.4,3.0)
key2:投币,按一次个位增加5,即为0.5
key3:投币,按一次十位增加1,即增加1

数码管

原理图:
在这里插入图片描述
在这里插入图片描述

数码管通过位选信号控制指定数码管,给0信号值,对应数码管使能可以亮,给1信号值,对应数码管不能亮,段选信号控制数码管上对应的led灯,数码管的LED灯共阳极,给低电平亮。

数码管显示数字真值表:
在这里插入图片描述
数码管实现:

数码管6位,从左往右,依次是两位的投币输入金额,两位的商品价格,以及两位的找零金额。初始显示1位,两位,1位,位选总共六种显示状态,段选10种状态

蜂鸣器

在这里插入图片描述
不同的音符振动频率不同,周期T=1/频率f
在这里插入图片描述
根据上图可以计算出音符振动的周期,单位微秒。Cyclone IV开发板的晶振是50MHz,振动一次是20纳秒,使用周期时间除以20纳秒得出音符振动的次数。比如高音的DO计算方式如下公式所示。
在这里插入图片描述

按下key0后,蜂鸣器播放5秒的音乐,(音符+每个音符的空白时间),每个音符对应一个外接彩灯闪烁灯
按下key1,key2,key3中任意一个,蜂鸣器会有0.2s的提示音

LED灯

在这里插入图片描述

LDE灯对应8种状态:全灭;全亮;只亮led[0];只亮led[1];只亮led[2];只亮led[3];流水灯;闪烁

四.流程图

在这里插入图片描述

五.代码实现

①按键消抖

key_debounce.v

module key_debounce(
	input 	wire	clk,
	input 	wire 	rst_n,
	input 	wire 	key,
	
	output 	reg 	flag,// 0抖动, 1抖动结束
	output 	reg	key_value//key抖动结束后的值
);

parameter MAX_NUM = 20'd1_000_000;

reg [19:0] delay_cnt;//1_000_000

reg key_reg;//key上一次的值

always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		key_reg <= 1;
		delay_cnt <= 0;
	end
	
	else begin
		key_reg <= key;
		//当key为1 key 为0 表示按下抖动,开始计时
		if(key_reg  != key  ) begin 
		   delay_cnt <= MAX_NUM ;
		end
		else begin
		    if(delay_cnt > 0)
				delay_cnt <= delay_cnt -1;
			else
				delay_cnt <= 0;
		end
	end
end


//当计时完成,获取key的值
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		flag <= 0;
		key_value <= 1;
	end

	else begin
		
		// 计时完成 处于稳定状态,进行赋值
		if(delay_cnt == 1) begin
			flag <= 1;
			key_value <= key;
		end
		else begin
			flag <= 0;
			key_value <= key_value;
		end
	end
end

endmodule

②LED状态选择

led_drive.v

module led_drive (
    input   wire          clk  ,//时钟信号
    input   wire          rst_n,//复位信号
    input   wire    [3:0] value,//LED显示状态

    output  reg     [3:0] led   //4个LED输出
);
/*
    value       效果

    0           全灭
    1           全亮
    2           只亮led[0]
    3           只亮led[1]
    4           只亮led[2]
    5           只亮led[3]
    6           流水灯
    7           闪烁
*/
parameter MAX_TIME_RUNNING = 28'd4_000_000;     //流水灯频率0.08s
parameter MAX_TIME_FLASH =  28'd10_000_000;     //闪烁频率0.2s


reg [27:0] cnt_time_running ;       //流水灯计时器
reg [27:0] cnt_time_flash;          //闪烁灯计时器

reg [7:0] led_running;              //流水灯状态寄存器
reg [3:0] led_flash;                //闪烁灯状态寄存器

//流水灯计数器0.08s
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_time_running <=28'd1;
    else if(value == 4'd6) begin
        if(cnt_time_running == MAX_TIME_RUNNING)
            cnt_time_running <=28'd1;
        else
            cnt_time_running <= cnt_time_running+28'd1;
    end
    else 
        cnt_time_running <= 28'd1;
end

//闪烁灯计数器0.2s
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_time_flash <=28'd1;
    else if (value == 4'd7) begin
        if(cnt_time_flash == MAX_TIME_FLASH )
            cnt_time_flash <=28'd1;
        else
            cnt_time_flash <= cnt_time_flash+28'd1;
    end
    else 
        cnt_time_flash <= 28'd1;
end

//流水灯状态切换 间隔0.08s
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        led_running <= 8'b00001111;
    else if(cnt_time_running == MAX_TIME_RUNNING)begin
        led_running <= {
   led_running[0],led_running[7:1]};
    end
    else 
        led_running <=led_running;
	
end

//闪烁状态切换 间隔0.2s
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        led_flash <= 4'b0000;
    else if(cnt_time_flash == MAX_TIME_FLASH)begin
        led_flash <= ~led_flash;
    end
    else 
        led_flash <=led_flash;
	
end

//根据value值输出对应灯效果
always @(*) begin
    case(value)
        4'd0: begin
            led = 4'b0000;//默认状态LED全灭
        end

        4'd1:begin
            led = 4'b1111;//
        end

        4'd2:begin
            led = 4'b0001;//选择第一种商品
        end

        4'd3:begin
            led = 4'b0010;//选择第二种商品
        end

        4'd4:begin
            led = 4'b0100;//选择第三种商品
        end

        4'd5:begin
            led = 4'b1000;//选择第四种商品
        end

        4'd6: begin
		      led = led_running[3:0];//购买成功找零不找零,流水灯
        end

        4'd7:begin
            led = led_flash;//取消订单,闪烁
        end
        default : led = 4'b0000;
    endcase
end
endmodule 

③蜂鸣器模块

beep_drive.v

module beep_drive (
    input   wire    clk,
    input   wire    rst_n,
    input   wire    flag,       //蜂鸣器开始鸣叫
    input   wire    status,
    output  reg     beep
);

parameter MAX_TIME = 24'd10_000_000;        //鸣叫时间
parameter MAX_TIME_MUSIC = 28'd250_000_000; //音乐播放时间

reg [23:0] cnt_time;        //计时
reg [27:0] cnt_time_music;  //音乐播放计时器
reg flag_beep_time_out;     // 计时是否结束


//音乐播放计时
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_time_music <= 28'd0;
    end

    else if(cnt_time_music < MAX_TIME_MUSIC) begin
        cnt_time_music <= cnt_time_music + 28'd1;
    end

    else 
        cnt_time_music <= cnt_time_music;
end

//蜂鸣器输出
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_time <= 0;
        beep <= 1;
        flag_beep_time_out <= 1;
    end

    else if(!status && cnt_time_music < MAX_TIME_MUSIC) begin
        beep <= 0;
    end

    else if(status && cnt_time_music < MAX_TIME_MUSIC) begin
        beep <= 1;
    end

    else if(flag && flag_beep_time_out) begin //开始鸣叫
        cnt_time <= MAX_TIME;
        flag_beep_time_out <= 0;
    end

    else if(cnt_time >=1 && !flag_beep_time_out) begin
        cnt_time <= cnt_time -24'd1;
        beep <= 0;
    end

    else if(cnt_time == 0) begin//计时结束
		beep <= 1;
		flag_beep_time_out <= 1;
	end
	else begin
		cnt_time <= cnt_time ;
		beep <= beep;
        flag_beep_time_out <= flag_beep_time_out;
	end
end


endmodule

④数码管位选信号选择

总共有6个数码管,分成了3部分,每一部分包含整数和小数部分,左边一位显示带小数点的整数,右边一位显示小数部分。左边两位显示当前投币数,中间两位显示当前商品价格,右边两位显示当前找零数。
运用三段式状态机写法,六个数码管对应六种状态,每20ms状态切换一次,三段式的好处是可以避免冒险和竞争产生的毛刺。每一种状态数码管是否显示和对应的数值大小有关,当数值小于1时,只显示小数部分那个数码管。
sel_drive.v

module sel_drive(
	input    wire             clk			   ,
	input    wire             rst_n			,
	input    wire    [6:0]    price_put		,	//投入的钱
	input    wire    [6:0]    price_need	,	//商品的价格
	input    wire    [6:0]    price_out		,	//找零的钱

	output    reg    [5:0]    sel				//数码管位选
);
//状态
localparam state0 = 3'd0;
localparam state1 = 3'd1;
localparam state2 = 3'd2;
localparam state3 = 3'd3;
localparam state4 = 3'd4;
localparam state5 = 3'd5;

parameter	 MAX_NUM = 1_000;//计数器最大计数值 刷新频率20微秒

reg    [2 
  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值