(一个很简陋的电梯控制器设计,但是应该可以过关了吧?️)
通过实验,巩固有限状态机设计方法,并设计实现一个电梯控制器。
利用 BASYS 开发板资源设计一个 5 层楼的电梯控制器系统,并能在开发板上
模拟电梯运行状态,具体要求如下:
系统输入/输出变量
对于一个系统,首先需要一个时钟输入,设为 clk;按键输入,设为btn;数码管显示输出设为 seg;叫梯楼层状态灯输出,设为 nfloor。
按键设计
parameter N=99_999999;
always@(posedge clk)
begin
clk_200ms<=0;
if(count<N/5)
count<=count+1;
else
begin
count<=0;
clk_200ms<=1;
end
end
reg [4:0]btn_pre_re,btn_off;
always@(posedge clk_200ms)
begin
btn_pre_re=btn_pre_re^btn;
btn_pre_re=btn_pre_re&btn_off;
end
电梯控制器包括两种显示,即数码管显示电梯所在楼层和 LED 灯显示所叫楼层服务。
实现“电梯控制器包括两种显示,即数码管显示电梯所在楼层和 LED 灯显示所叫楼层服务”。
`timescale 1ns / 1ps
module scan_led_hex_disp(clk,reset,btn,an,sseg,current_floor,Floor_call_state_output);
parameter n = 5;
input clk;
input reset;
input [n-1:0] btn; //第一个数码管显示的数字:电梯叫梯楼层
//input [3:0] dp_in, //小数点控制
output reg [3:0] an; //片选,使能端
output reg [7:0] sseg; //段选
output [n-1:0] current_floor;
output [n-1:0] Floor_call_state_output; //第一个数码管显示的数字:电梯所在楼层
wire stop;
localparam N = 18; //使用低16位对50Mhz的时钟进行分频(50MHZ/2^16)
reg [N-1:0] regN; //高两位作为控制信号,低16位为计数器,对时钟进行分频
reg [n-1:0] hex_in; //段选控制信号
always@(posedge clk, posedge reset)
begin
if(reset)
regN <= 0;
else
regN <= regN + 1;
end
reg dp;
debounce_button u2(.clk(clk),.rst(reset),.key(btn),.key_pulse(),.current_floor(current_floor),.Floor_call_state_output(Floor_call_state_output),.stop(stop));
always@ *
begin
case(regN[N-1:N-2])
2'b00:begin
an = 4'b0111; //选中第1个数码管
hex_in = current_floor; //数码管显示的数字由hex_in控制,显示current_floor:电梯所在楼层
dp = ~stop; //控制该数码管的小数点的亮灭
end
2'b01:begin
an = 4'b0111; //选中第二个数码管
hex_in = current_floor;//数码管显示的数字由hex_in控制,显示current_floor;电梯所在楼层
dp = ~stop;
end
2'b10:begin
an = 4'b1110;//选中第三个数码管
hex_in = Floor_call_state_output;//数码管显示的数字由hex_in控制,显示Floor_call_state_output,也就是电梯叫梯的楼层;
dp = 1;
end
default:begin
an = 4'b1110;//选中第四个数码管
hex_in = Floor_call_state_output;//数码管显示的数字由hex_in控制,显示Floor_call_state_output,也就是电梯叫梯的楼层;
dp = 1;
end
endcase
end
always@ *
begin
case(hex_in)
5'b00000: sseg[6:0] = 7'b0000001; //共阳极数码管
5'b00001: sseg[6:0] = 7'b1001111;
5'b00010: sseg[6:0] = 7'b0010010;
5'b00100: sseg[6:0] = 7'b0000110;
5'b01000: sseg[6:0] = 7'b1001100;
5'b10000: sseg[6:0] = 7'b0100100;
default: sseg[6:0] = 7'b1111111;
endcase
sseg[7] = dp; //控制小数点,小数点亮,代表电梯停止移动了;不亮,则是电梯上移动或者下移
end
endmodule
实现“按键消抖”。
本实验使用板上 5 个按键按钮模拟电梯的叫梯按键,1 层按键为 BTNU,2 层按键为 BTNL,3 层按键为 BTNC,4 层按键为 BTNR,5 层按键为 BTND。所以,定义一个 5 位按键寄存器 key_pulse,同时考虑到防抖(按键按下去和松开会产生抖动现象会影响到我们的操作),在对按键寄存器进行赋值的时候要注意时间延时。
最后,实现了“对于电梯按键,当没有叫梯时,按键相应的 LED 指示灯应处于熄灭状态;当有叫梯时,按键相应的 LED 指示灯应处于点亮状态;当在某一层已经叫梯,但是由于某种原因发现所叫梯不是自己想要的梯层时,能够取消此层的叫梯状态”。
`timescale 1ns / 1ps
module debounce_button (clk,rst,key,key_pulse,current_floor,Floor_call_state_output,stop);
parameter N = 5; //要消除的按键的数量
input clk;
input rst;
input [N-1:0] key; //输入的按键
output [N-1:0] key_pulse; //按键动作产生的脉冲
output [N-1:0] current_floor;
output [N-1:0] Floor_call_state_output;
output stop;
reg [N-1:0] key_pulse;
reg [N-1:0] key_rst_pre; //定义一个寄存器型变量存储上一个触发时的按键值
reg [N-1:0] key_rst; //定义一个寄存器变量储存储当前时刻触发的按键值
wire [N-1:0] key_edge; //检测到按键由高到低变化是产生一个高脉冲
//利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
always @(posedge clk or negedge rst)
begin
if (rst) begin
key_rst <= {N{1'b1}}; //初始化时给key_rst赋值全为1,{}中表示N个1
key_rst_pre <= {N{1'b1}};
end
else begin
key_rst <= key; //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre
key_rst_pre <= key_rst; //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值
end
end
assign key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平
reg [17:0] cnt; //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器
//产生20ms延时,当检测到key_edge有效是计数器清零开始计数
always @(posedge clk or negedge rst)
begin
if(rst)
cnt <= 18'h0;
else if(key_edge)
cnt <= 18'h0;
else
cnt <= cnt + 1'h1;
end
reg [N-1:0] key_sec_pre; //延时后检测电平寄存器变量
reg [N-1:0] key_sec;
//延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
always @(posedge clk or negedge rst)
begin
if (rst)
key_sec <= {N{1'b1}};
else if (cnt==18'h3ffff)
key_sec <= key;
end
always @(posedge clk or negedge rst)
begin
if (rst)
key_sec_pre <= {N{1'b1}};
else
key_sec_pre <= key_sec;
end
always @(posedge clk or negedge rst)
begin
if (rst)
key_pulse <= {N{1'b0}};
else
begin
/*按键被按下后,存在一个时钟周期:key_sec_pre[x]=1和key_sec[x]=0。这时,key_pulse[x]<= ~key_pulse[x],反转一下。
实现要求中的“重复进行叫梯按键操作,可以进行叫梯或取消叫梯服务” */
if(~key_sec_pre[4] & key_sec[4])
key_pulse[4] <= ~key_pulse[4];
if(~key_sec_pre[3] & key_sec[3])
key_pulse[3] <= ~key_pulse[3];
if(~key_sec_pre[2] & key_sec[2])
key_pulse[2] <= ~key_pulse[2];
if(~key_sec_pre[1] & key_sec[1])
key_pulse[1] <= ~key_pulse[1];
if(~key_sec_pre[0] & key_sec[0])
key_pulse[0] <= ~key_pulse[0];
end
end
Elevator u1(
.clk(clk), .rst(rst),.Floor_call_state(key_pulse),.current_floor(current_floor),.Floor_call_state_output(Floor_call_state_output),.stop(stop)
);
endmodule
实现“利用时钟分频设计电梯控制器控制电梯每秒运行一层”。
`timescale 1ns / 1ps
module Elevator(
clk, rst,Floor_call_state,current_floor,Floor_call_state_output,stop
);
input clk;
input rst;
input [4:0] Floor_call_state; //输入的按键
output reg [4:0] current_floor;
output reg [4:0] Floor_call_state_output;
output reg stop;
integer k;
reg [30:0] cnt=30'b0;
parameter M=100000000; //1s=1000000000ns
reg clk_out=0;
initial begin
stop=1;
Floor_call_state_output=5'b00001;
current_floor=5'b00001;
end
//时钟分频器:每过1s,clk_out=~clk_out。实现“每一秒钟,电梯移动一层”。
always @(posedge clk)
begin
if(rst)
begin
clk_out=0;
end
//从0开始计时的,所以cnt==M-1,一个循环是 1s
if (cnt==M-1)
begin
clk_out=~clk_out;
cnt=0;
end
else
begin
clk_out=0; //每次重新开始,都使 clk_out=0。好在cnt==M-1时,clk_out=~clk_out,产生clk_out的上升沿,
cnt=cnt+1'd1;
end
end
//当Floor_call_state发证变化,或者rst发生变化时,将Floor_call_state赋给Floor_call_state_output。
always @(*/*Floor_call_state,rst,current_floor,Floor_call_state_output*/)
begin
if(rst)
begin
Floor_call_state_output=5'b00001;
end
else
begin
Floor_call_state_output=Floor_call_state;
end
end
always @(posedge clk_out or posedge rst)
begin
if(rst)
begin
stop=1;
current_floor=5'b00001;
end
else if(Floor_call_state_output!=5'b00000)
begin
stop=0;
if(current_floor<Floor_call_state_output) //电梯所在楼层 低于 叫梯,就上升
current_floor={current_floor[3:0],current_floor[4]};
else if(current_floor>Floor_call_state_output) //电梯所在楼层 高于 叫梯,就下降
current_floor={current_floor[0],current_floor[4:1]};
else
stop=1;
end
else
begin
stop=1;
end
end
endmodule
set_property IOSTANDARD LVCMOS33 [get_ports {an[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Floor_call_state_output[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Floor_call_state_output[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Floor_call_state_output[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Floor_call_state_output[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Floor_call_state_output[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {btn[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {btn[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {btn[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {btn[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {btn[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {current_floor[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {current_floor[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {current_floor[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {current_floor[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {current_floor[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sseg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports reset]
set_property PACKAGE_PIN W4 [get_ports {an[3]}]
set_property PACKAGE_PIN V4 [get_ports {an[2]}]
set_property PACKAGE_PIN U4 [get_ports {an[1]}]
set_property PACKAGE_PIN U2 [get_ports {an[0]}]
set_property package_pin W18 [get_ports {Floor_call_state_output[4]}]
set_property PACKAGE_PIN U17 [get_ports {btn[4]}]
set_property package_pin V19 [get_ports {Floor_call_state_output[3]}]
set_property PACKAGE_PIN T17 [get_ports {btn[3]}]
set_property package_pin U19 [get_ports {Floor_call_state_output[2]}]
set_property PACKAGE_PIN U18 [get_ports {btn[2]}]
set_property package_pin E19 [get_ports {Floor_call_state_output[1]}]
set_property PACKAGE_PIN W19 [get_ports {btn[1]}]
set_property package_pin U16 [get_ports {Floor_call_state_output[0]}]
set_property PACKAGE_PIN T18 [get_ports {btn[0]}]
set_property PACKAGE_PIN U3 [get_ports {current_floor[0]}]
set_property PACKAGE_PIN P3 [get_ports {current_floor[1]}]
set_property PACKAGE_PIN N3 [get_ports {current_floor[2]}]
set_property PACKAGE_PIN P1 [get_ports {current_floor[3]}]
set_property PACKAGE_PIN L1 [get_ports {current_floor[4]}]
set_property PACKAGE_PIN W7 [get_ports {sseg[6]}]
set_property PACKAGE_PIN W6 [get_ports {sseg[5]}]
set_property PACKAGE_PIN U8 [get_ports {sseg[4]}]
set_property PACKAGE_PIN V8 [get_ports {sseg[3]}]
set_property PACKAGE_PIN U5 [get_ports {sseg[2]}]
set_property PACKAGE_PIN V5 [get_ports {sseg[1]}]
set_property PACKAGE_PIN U7 [get_ports {sseg[0]}]
set_property PACKAGE_PIN V7 [get_ports {sseg[7]}]
set_property PACKAGE_PIN W5 [get_ports clk]
set_property PACKAGE_PIN R2 [get_ports reset]
当按下“5楼”叫梯,5s后: