基于FPGA状态机的自动售货机功能实现

用FPGA制作一个简单的自动售货机

这篇博客讲了如何用FPGA模拟实现自动售卖机的功能。

文章目录

  • 用FPGA制作一个简单的自动售货机
  • 1. 程序功能和总体框架详解
  • 2.Divider分频模块
  • 3.Debounce模块,按键去抖
  • 4.FSM状态机
  • 5.Seg显示模块
  • 6.Siren蜂鸣器报警模块
  • 7. Led灯闪烁模块
  • 总结


1. 程序功能和总体框架详解

程序功能:

  • 按键K3为复位信号,led0,led1控制led等,siren控制蜂鸣器,seg_sel和seg_led分别控制数码管的片选和位选信号
  • 有两个商品cnt_40和cnt_50,分别价值40元和50元,K1,K2连接按键,每按一下,代表要购买的cnt_40和cnt_50加1,例如:买2个cnt_40,1个cnt_50,则按2下K1和1下K2,选好商品后,按K4确认;
  • 确认后开始投币,投币时K1,K2分别代表投入10元硬币和20元硬币,按K4确认;
  • 用4个数码管分别显示2个商品购买个数和2种硬币投入个数
  • 若硬币价钱和商品价钱相等,则出货:两个led灯的闪烁次数表示两个商品的出货个数
  • 若不相等,蜂鸣器报警
    基于FPGA状态机的自动售货机功能实现_第1张图片
    整体框架的代码如下所示:
module top(input k1,input k2,input k4,input k3,input clk,output led1,output led2,output [5:0] seg_sel,output [7:0] seg_led,output siren);
wire key1,key2,key4,le,sir,f1hz;
wire [3:0] cnt_40;
wire [3:0] cnt_50;
wire [3:0] coin_10;
wire [3:0] coin_20;
wire [2:0] cnt;
wire [15:0] data;
assign data={cnt_40[3:0],cnt_50[3:0],coin_10[3:0],coin_20[3:0]};
debounce  d1 (.rst(k3),.clock(clk),.noisy(k1),.clean(key1));  //Debounce模块
debounce  d2 (.rst(k3),.clock(clk),.noisy(k2),.clean(key2));
debounce  d3 (.rst(k3),.clock(clk),.noisy(k4),.clean(key4));
fsm f1(.clock(f1hz),.k1(key1),.k2(key2),.k3(k3),.k4(key4),.led(le),.siren(sir),.cnt_40(cnt_40),.cnt_50(cnt_50),.coin_10(coin_10),.coin_20(coin_20));
divider  di1(.clock(clk),.rst(k3),.wave(f1hz)); //对应图中FSM模块
led  l1(.clk(clk),.cnt_40(cnt_40),.cnt_50(cnt_50),.led(le),.rst(k3),.wave(f1hz),.led1(led2),.led0(led1));
siren  si(.clk(clk),.rst(k3),.start(sir),.sir(siren));
display dx(.clk(clk),.rst(k3), .data(data),.seg_sel(seg_sel),.seg_led(seg_led));//对应图中Seg模块
endmodule

clk为时钟信号,下面是各个模块的解析和代码

实物效果图如下:
基于FPGA状态机的自动售货机功能实现_第2张图片

2.Divider分频模块

我使用的FPGA开发板的时钟频率为25Mhz,通过这个程序分频,得到一个1hz的时钟信号,led闪烁时,闪太快我们人眼看不出来,所以需要一个1hz的时钟信号,来让它闪慢一点。

module divider(input clock,input rst,output reg wave);
reg [30:0] cnt;
always@(posedge clock or negedge rst)
if(!rst)
begin
cnt<=0;
wave<=0;
end
else if(cnt==25000000)
begin
cnt<=0;
wave<=!wave;
end
else 
begin
wave<=wave;
cnt<=cnt+1;
end
endmodule

3.Debounce模块,按键去抖

确保按键按一次,只会产生一个下降沿,因此需要一个去抖动模块

module debounce #(parameter DELAY=500000)
	        (input rst, clock, noisy,
	         output reg clean);
   reg [19:0] count;
   reg new;
   always @(posedge clock)
     if (!rst)
       begin
	  count <= 0;
	  new <= noisy;
	  clean <= noisy;
       end
     else if (noisy != new)
       begin
	  new <= noisy;
	  count <= 0;
       end
     else if (count == DELAY)
       clean <= new;
     else
       count <= count+1;  
endmodule

4.FSM状态机

有4种状态,

  • Pay状态读取购物信息
  • BIJIAO状态来比较货物价值和投入硬币价值是否相等
  • SIREN_ON状态控制蜂鸣器报警
  • CHUHUO控制出货(led闪烁)
module fsm
   (input clock,
    input k1,input k2,input k3,input  k4,
	output reg led,
	output reg siren,
    output reg [3:0] cnt_40,
    output reg [3:0] cnt_50,
    output reg [3:0] coin_10,
    output reg [3:0] coin_20
	 );
	 
  parameter PRICE = 0;
  parameter PAY = 1;//jie shou zhi fu shu liang
  parameter BIJIAO = 2;//pan duan
  parameter SIREN_ON = 3;//bao jing
  parameter CHUHUO = 4;//chu huo
  reg [2:0] state;
  always @ (posedge clock or negedge k3 )
  if (!k3)           //复位
  begin
  			state <= PRICE;
            cnt_40<=0;
            cnt_50<=0;
            coin_20<=0; 
            coin_10<=0;
end
  else			
  begin
    case (state)
    PRICE:      	begin               //K1和K2按键决定cnt_40和cnt_50的购买数量,并输出到数码管显示;
	                 led <= 0;          //k4按键确认时,进入投币付费状态PAY
					 siren <= 0;
						  if(!k1)
						    begin
							   cnt_40 <= cnt_40 + 1;
							 end
						  if(!k2)
						    begin
							   cnt_50 <= cnt_50 + 1;
							 end
						  if(!k4)
						    begin
							   state <= PAY;
                               end
						  else
                              state<=state;

						end
						
						
	 PAY:  	begin                       //投币付费状态,K1,K2按键决定10和20的硬币投入数量,并输出到数码管显示
	                    if(!k1)        //K4按键确认后,进入比较状态BIJIAO 
							   begin
							     coin_10 <= coin_10 + 1;
								end
					    else if(!k2)
							   begin
							     coin_20 <= coin_20 + 1;
								end
                       else  if(!k4)
                            state<=BIJIAO;
                       else
                            state<=state;
						end
						
						
	 BIJIAO:  begin      //比较输入的货物价值和投入硬币价值是否相等,若相等,出货CHUHUO状态,否则报警状态SIREN_ON
						  if((4 * cnt_40+ 5 * cnt_50) == (2*coin_20  + coin_10))
						    begin
							   state <= CHUHUO;
							 end
						  else
						    begin
							   state <= SIREN_ON;
							 end
						
						end
						
	 CHUHUO:     begin   //出货,led=1使能LED模块,LED模块后面介绍,开启两个led闪烁,闪烁次数等于两个货物的数量
						  led <= 1;
						  siren <= 0;
				end
						
						
	 SIREN_ON: 		begin    //报警,siren控制SIREN模块,使蜂鸣器报警
						  led <= 0;
						  siren <= 1;
					end
						
			
			
    default:		state <= PRICE;
    endcase
end

endmodule 

5.Seg显示模块

接受来自状态机FSM的货物数量信号cnt_40,cnt_50,和投入10元20元硬币数量coin_10和coin_20;并使用4个数码管将其显示出来;
数码管显示的程序讲解已经有很多了,这里就不赘述了

module display(
    input                clk,
    input                rst,
    input        [15:0]  data,
    output  reg  [5:0]   seg_sel,
    output  reg  [7:0]   seg_led
);

localparam  CLK_DIVIDE = 4'd10     ;
localparam  MAX_NUM    = 13'd5000  ;

reg    [3:0]              clk_cnt  ; 
reg                       dri_clk  ; 
reg    [12:0]             cnt0     ; 
reg                       flag     ; 
reg    [2:0]              cnt_sel  ;
reg    [3:0]              num_disp ; 


always @(posedge clk or negedge rst) begin
   if(!rst) begin
       clk_cnt <= 4'd0;
       dri_clk <= 1'b1;
   end
   else if(clk_cnt >= CLK_DIVIDE/2 - 1'd1) begin
       clk_cnt <= 4'd0;
       dri_clk <= ~dri_clk;
   end
   else begin
       clk_cnt <= clk_cnt + 1'b1;
       dri_clk <= dri_clk;
   end
end

always @ (posedge dri_clk or negedge rst) begin
    if (rst == 1'b0) begin
        cnt0 <= 13'b0;
        flag <= 1'b0;
     end
    else if (cnt0 < MAX_NUM - 1'b1) begin
        cnt0 <= cnt0 + 1'b1;
        flag <= 1'b0;
     end
    else begin
        cnt0 <= 13'b0;
        flag <= 1'b1;
     end
end

always @ (posedge dri_clk or negedge rst) begin
    if (rst == 1'b0)
        cnt_sel <= 3'b0;
    else if(flag) begin
        if(cnt_sel < 3'd4)
            cnt_sel <= cnt_sel + 1'b1;
        else
            cnt_sel <= 3'b0;
    end
    else
        cnt_sel <= cnt_sel;
end

always @ (posedge dri_clk or negedge rst) begin
    if(!rst) begin
        seg_sel  <= 6'b111111;
        num_disp <= 4'b0;           
    end
    else begin
            case (cnt_sel)
                3'd0 :begin
                    seg_sel  <= 6'b111101;
                    num_disp <= data[3:0] ; 
                end
                3'd1 :begin
                    seg_sel  <= 6'b111011;
                    num_disp <= data[7:4] ;
                end
                3'd2 :begin
                    seg_sel  <= 6'b110111;
                    num_disp <= data[11:8];
                end
                3'd3 :begin
                    seg_sel  <= 6'b101111;
                    num_disp <= data[15:12];
                end
                default :begin
                    seg_sel  <= 6'b111111;
                    num_disp <= 4'b0;
                end
            endcase
    end
end
always @ (posedge clk or negedge rst) begin
    if (!rst)
        seg_led <= 8'b0;
    else begin
        case (num_disp)
            4'h0 :    seg_led <= 8'b1100_0000;
            4'h1 :    seg_led <= 8'b1111_1001;
            4'h2 :    seg_led <= 8'b1010_0100;
            4'h3 :    seg_led <= 8'b1011_0000;
            4'h4 :    seg_led <= 8'b1001_1001;
            4'h5 :    seg_led <= 8'b1001_0010;
            4'h6 :    seg_led <= 8'b1000_0010;
            4'h7 :    seg_led <= 8'b1111_1000;
            4'h8 :    seg_led <= 8'b1000_0000;
            4'h9 :    seg_led <= 8'b1001_0000;
            4'ha :    seg_led <= 8'b1000_1000;
            4'hb :    seg_led <= 8'b1000_0011;
            4'hc :    seg_led <= 8'b1100_0110;
            4'hd :    seg_led <= 8'b1010_0001;
            4'he :    seg_led <= 8'b1000_0110;
            4'hf :    seg_led <= 8'b1000_1110;
            default : seg_led <= 8'b1100_0000;
        endcase
    end
end

endmodule 

6.Siren蜂鸣器报警模块

蜂鸣器报警没什么好讲的,低电平就不响,高电平就响,以一定的频率开关蜂鸣器,能产生不同的声音,该蜂鸣器模块功能:

  • start信号为1时,开启蜂鸣器报警
  • 开启蜂鸣器报警时,蜂鸣器以一定的频率发出声音
module siren(input clk,input rst,input start,output reg sir);
reg [24:0] T;
reg [24:0] cnt;
reg [24:0] cnt1;
reg clk1;
always@(posedge clk)
if(cnt1>1000_0000)
begin
cnt1<=0;
clk1<=!clk1;
end
else
cnt1<=cnt1+1;
 
always@(posedge clk1)
if(!rst)
T<=0;
else if(T<50000 || T>100000)
begin
T<=50000;
end
else begin 
T<=T+10000;
end

always@(posedge clk or negedge rst)
if(!rst)
begin
sir<=0;
cnt<=0;
end
else if(start==1 && cnt<T)
cnt<=cnt+10;
else if(start==1)
begin
cnt<=0;
sir<=!sir;
end
else begin
sir<=0;
cnt<=0; 
end
endmodule

7. Led灯闪烁模块

该模块功能:

  • 控制信号为1时,控制两个led灯闪烁
  • 闪烁的次数等于输入的两个信号代表的数量
module led(input clk,input [3:0] cnt_40,input [3:0] cnt_50,input led,input rst,input wave,output reg led1,output reg led0);
reg  led0_twinkle;
reg	 led1_twinkle;
reg  [3:0]    num1;
reg  [3:0]    num2;            
always@(posedge clk or negedge rst)
  if(!rst)
    begin
    led0 <=1'b0;
	 led1 <=1'b0;
    end
    else if(!led)
	   begin
		    led0 <=1'b0;
	       led1 <=1'b0;
		end
     else
     begin
			    led0 <=led0_twinkle;
				led1 <=led1_twinkle;
	 end

always@(posedge wave)
if(!rst)
begin
led0_twinkle <=0;
num1<=0;
end
else if (led==1 && num1<2*cnt_40)
begin
led0_twinkle <=!led0_twinkle;
num1<=num1+1;
end
else 
begin
led0_twinkle <=0;
num1<=num1;
end


always@(posedge wave)
if(!rst)
begin
led1_twinkle <=0;
num2<=0;
end
else if (led==1 && num2<2*cnt_50)
begin
led1_twinkle <=!led1_twinkle;
num2<=num2+1;
end
else 
begin
led1_twinkle <=0;
num2<=num2;
end

endmodule

总结

这里对文章进行总结,这篇博客用FPGA的状态机的概念,做了一个自动售货机,并详细介绍了各个模块的功能及代码,仅供参考。

你可能感兴趣的:(FPGA,fpga,verilog)