目录
设计目的与要求
设计原理及方案
下面是设计的细节,再这里,笔者的注释都写得很清楚,因此不会啰嗦的一段段解释,只介绍关键的部分,其余的详见代码注释即可(此处建议直接看代码更好,上面的知识一个框架而已):
附页(源代码)
TOP
u0: clk_div
u1: ajxd
u2: control
u3: displine
附页(约束文件)
1、实现2层楼的简易电梯控制系统
2、电梯有4个按键
1楼外只有向上按键(KEY0),2楼外只有向下按键(KEY1),电梯内还有2个按键分别为1楼按键(KEY2)和2楼按键(KEY3)。所有楼层外和电梯内的按键产生的信号作为给电梯的运行请求信号
3、电梯有4个指示灯(LED0、 LED1 、 LED2 、 LED3)
LED0: 按下KEY0键,若电梯不在1楼,则LED0亮
LED1: 按下KEY1键,若电梯不在2楼,则LED1亮
LED2: 电梯在2楼,按KEY2键, 则LED2亮,电梯到1楼后LED2灭
LED3: 电梯在1楼,按KEY3键, 则LED3亮,电梯到2楼后LED3灭
4、有2个数码管,分别显示当前运行状态及楼层
(1)1个数码管显示当前运行状态,电梯有三个运行状态:待机、上行、下行
待机:电梯停在1楼或2楼且无请求信号时均为待机状态
上行状态:电梯停在1楼,有KEY1或KEY3被按下,进入上行状态
下行状态:电梯停在2楼,有KEY0或KEY2被按下,进入下行状态
(2)1个数码管显示所在楼层,显示1或2;每一层楼之间的运行时间间隔为秒
5、有2个拨码开关。
(1)复位开关。向下拨动后电梯复位回到1楼
(2)启动开关。向上拨动后,按键有效,电梯正常工作
6、附加功能
(1)电梯上行时,LED11至LED7五个指示灯从左到右每隔一秒点亮一个;
电梯下行时,LED7至LED11五个指示灯从右到左每隔一秒点亮一个
(2)电梯开始上行或下行时,在最左边两个数码管上倒计时显示运行时间4.9~0.0(秒),精度为0.1秒。到达新楼层时显示0.0(秒)
先看模块之间的关系:
Top:顶层模块
U0 (clk_div): 时钟分频模块,产生20ms时钟
U1 (ajxd) :按键消抖模块
U2 (control):控制模块,控制LED[3:0]按键灯和LEDOUT[4:0]流水灯、电梯运行状态、所处楼层以及开启和复位
U3 (displine):动态显示模块,显示电梯所在楼层、运行状态以及4.9~0.0s倒计时计数
RTL分析:
1)按键的检测
input [3:0]col // 定义4个按键KEY0、1、2、3输入
output [3:0]row //定义一行[3:0]row,
assign row[3:0]=4'b0001 //固定这一行
检测到按键[3:0]col,输入消抖模块的btn_in
module ajxd(
input [3:0]btn_in,
input btn_clk,
output [3:0]btn_out
);
reg [3:0]btn0=0;//定义了btn0寄存器
reg [3:0]btn1=0;//定义了btn1寄存器
reg [3:0]btn2=0;//定义了btn2寄存器
//按键btn_in的消抖程序
assign btn_out=(btn2&btn1&btn0)|(~btn2&btn1&btn0);//btn_out信号 每按下一次按键,只产生一个上升沿
always@ (posedge btn_clk)
begin
btn0<=btn_in;
btn1<=btn0;
btn2<=btn1;
end
endmodule
需要注意的是上面的寄存器用的是4位的,这样就能一一对应4个按键,避免后面需要例化4次按键消抖模块
2)控制模块
在控制模块中,主要的思路就是设置一个32位的[31:0]number主计数器(clk时钟为50M),number计数250M次就是5秒、500M次就是10秒,需要控制的东西就可以在这个过程中被执行,这个执行的过程时间是忽略不计的,对整体的5秒计数是不影响的,这里只举例部分的代码作为说明,具体的可以参考附页上的代码注释说明
因为按键消抖产生的信号[3:0]btn_out相对CLK时钟周期很长,所以要想按照这个思路进行下去就得把[3:0]btn_out缩短位一个CLK周期的单稳态信号[3:0]keyout.
always@(posedge clk)
begin
if((SW0==1)&&(i==0)&&(keyin!=4'b0000)) //将长的按键信号缩短位1个CLK时钟周期
begin
keyout<=keyin;
i<=1;
end
else if((i==1)&&(keyin!=4'b0000))
begin
keyout<=4'b0000;
end
else if((i==1)&&(keyout==4'b0000)&&(keyin==4'b0000)) i<=0;
4)按键LED灯、楼层、运行状态变化
主要是靠判断[3:0]keyout是否有输出(按键被按下),同时也要判断是否有第二次按下,并且禁止第三次按下按键
5)流水灯控制
//LED11--7控制
always@(number)
begin
if (state==2'b01)
case(number)
1: LEDOUT<=5'b10000;
50000000: LEDOUT<=5'b11000;
100000000:LEDOUT<=5'b11100;
150000000:LEDOUT<=5'b11110;
200000000:LEDOUT<=5'b11111;
250000000:if(second_press==0)LEDOUT<=5'b00000;
250000002:LEDOUT<=5'b10000; //此处要特别注意必须为250M+1以上
300000000:LEDOUT<=5'b11000;
350000000:LEDOUT<=5'b11100;
400000000:LEDOUT<=5'b11110;
450000000:LEDOUT<=5'b11111;
500000000:LEDOUT<=5'b00000;
default:;
endcase
另外一个state==2’b10是对称的,此处没写出来,具体可参见附页的源代码
6)动态显示模块
在这个模块中输入了楼层[1:0]floor、运行状态[1:0]state
input clk, //50M时钟
input [1:0]floor, //从控制模块中输入楼层[1:0]floor
input [1:0]state, //从控制模块中输入运行状态[1:0]state
input EN, //从控制模块中输入使能计数4.9s~0.0秒
output reg [7:0] seg, //段码输出
output reg [3:0] dig //位码输出
倒计时计数4.9s~0.0秒采用一个10进制和一个5进制计数器级联构成,注意显示的时候小数点得加上,因此seg输出时需要注意
//10进制计数器器
reg[3:0]Q1=0;
wire cy;
always@(posedge clk_10HZ)
begin
if(EN==1)
begin
if(Q1==0)Q1<=9;
else Q1<=Q1-1;
end
else Q1<=0;
end
assign cy=((EN==1)&&(Q1==0))?1'b1:1'b0;
//5进制计数器
reg[3:0]Q2=0;
always@(posedge clk_10HZ)
begin
if(cy==1)
begin
if(Q2==0)Q2<=4;
else Q2<=Q2-1;
end
end //两个计数器已经通过cy级联
module Top(
input clk, //50M时钟
input [3:0]col, // 定义时钟和4个按键输入
input SW0, //定义SW0启动开关
input SW11, //定义SW11复位开关
output [7:0] seg, //段码输出
output [3:0] dig, //位码输出
output [3:0]row, // 定义行
output [3:0]led, //LED0---LED4按键灯
output [4:0]LEDOUT //LED11----LED7流水灯
);
assign row[3:0]=4'b0001; //固定一行row3
wire clk_20ms; // 20ms clk
wire [3:0]keyin;
reg [3:0]keyout; //脉宽变换后的按键信号
wire [1:0]floor; //定义楼层
wire [1:0]state; //定义电梯的状态
wire enable;
//例化 时钟模块
clk_div u0( .clk_in(clk), .clk_out(clk_20ms));
//例化 按键消抖模块
ajxd u1( .btn_clk(clk_20ms), .btn_in(col), .btn_out(keyin));
//例化 控制模块
control u2 (.clk(clk), .keyin(keyin), .SW0(SW0), .SW11(SW11), .led(led),
.LEDOUT(LEDOUT),.enable_link(enable), .floor_link(floor), .state_link(state));
//例化 动态显示模块
displine u3(.clk(clk),.floor(floor),.state(state),.EN(enable), .seg(seg),.dig(dig));
endmodule
注:本代码完全由本人自主完成,所有功能都已经实现,代码中有关键的注释,各个模块之间的连接关系前面的关系框图中已展示,建议结合RTL分析图一起看能够更加轻松。另外,本代码仅供参考,切勿抄袭哦!
module clk_div(clk_in,clk_out);
input clk_in;
output reg clk_out=0;//用reg后面always中需要改变数值
reg[24:0] clk_div_cnt=0;
//分频为50Hz的信号
always @ (posedge clk_in)
begin
if (clk_div_cnt==499999)//10M分频
begin
clk_out=~clk_out;
clk_div_cnt=0;
end
else
clk_div_cnt=clk_div_cnt+1;
end
endmodule
注:本代码完全由本人自主完成,所有功能都已经实现,代码中有关键的注释,各个模块之间的连接关系前面的关系框图中已展示,建议结合RTL分析图一起看能够更加轻松。
module ajxd(
input [3:0]btn_in,
input btn_clk,
output [3:0]btn_out
);
reg [3:0]btn0=0;//定义了btn0寄存器
reg [3:0]btn1=0;//定义了btn1寄存器
reg [3:0]btn2=0;//定义了btn2寄存器
//在下面参考课件程序,自己填写对按键btn_in的消抖程序
assign btn_out=(btn2&btn1&btn0)|(~btn2&btn1&btn0);//btn_out信号 每按下一次按键,只产生一个上升沿
always@ (posedge btn_clk)
begin
btn0<=btn_in;
btn1<=btn0;
btn2<=btn1;
end
endmodule
注:本代码完全由本人自主完成,所有功能都已经实现,代码中有关键的注释,各个模块之间的连接关系前面的关系框图中已展示,建议结合RTL分析图一起看能够更加轻松。另外,本代码仅供参考,切勿抄袭哦!
module control(
input clk, //50M时钟输入
input [3:0]keyin, //消抖后的按键信号输入
input SW0, //SW0拨码按键输入
input SW11, //SW11拨码按键输入
output reg [3:0]led =0, //输出楼层led
output reg [4:0]LEDOUT=0, //流水灯LED灯输出
output [1:0]floor_link, //楼层信号输出
output [1:0]state_link, //电梯运行状态信号输出
output enable_link
);
reg [3:0]keyout =0; //变换后的按键信号
reg [1:0]floor = 1; //定义楼层
reg[1:0]state = 0; //定义电梯的状态,00待机、01上升、10下降
reg enable=0; //定义使能2位5秒计数器
assign floor_link = floor;
assign state_link = state;
assign enable_link = enable; //连接线接出去
reg i=0; //keyout转换标志位
reg [3:0]first_press =0; //第一次按下按键 变量1
reg [3:0]second_press =0; //第一次按下按键 变量2
reg limitied_third_press =0; //禁止第三次按下,第三次按下无效
reg flag=0; //计数器的标志位
reg f1=0; //标志位 1
reg f2=0; //标志位 2
reg [31:0]number=0; //定义一个32位的计数器
//LED控制部分
always@(posedge clk)
begin
if((SW0==1)&&(i==0)&&(keyin!=4'b0000)) //将长的按键信号缩短位1个CLK时钟周期的单稳态信号
begin
keyout<=keyin;
i<=1;
end
else if((i==1)&&(keyin!=4'b0000))
begin
keyout<=4'b0000;
end
else if((i==1)&&(keyout==4'b0000)&&(keyin==4'b0000)) i<=0;
// LED灯、楼层、运行状态变化
if(keyout[0]) //KEY0被按下
begin
if((first_press==0)&&(floor==2)) //如果电梯在2楼且只按下一次按键KEY0
begin
led[0]<=1; //led[0]亮
state<=2'b10; //状态变为下降状态
first_press[0]<=1; //第一次按下标志位变为1
flag <=1; //主计数器标志位置数为1,本次操作结束,开始计数5秒
enable<=1; //使能输出,用于后面的动态显示模块的使能输入
end
else if((first_press!=0)&&(state==1)&&(limitied_third_press==0))
begin //如果这次按下KEY0是第二次按下,并且没有第三次按下
led[0]<=1;
second_press[0]<=1; //第二次按下标志位置数为1
limitied_third_press<=1; //第三次按下标志位置数为1
end
else if(limitied_third_press) led[0]<=0; //第三次按下无效
end
else if(keyout[1])
begin
if((first_press==0)&&(floor==1))
begin
led[1]<=1;
state<=2'b01;
first_press[1]<=1;
flag <=1;
enable<=1;
end
else if((first_press!=0)&&(state==2)&&(limitied_third_press==0))
begin
led[1]<=1;
second_press[1]<=1;
limitied_third_press<=1;
end
else if(limitied_third_press) led[0]<=0;
end
else if(keyout[2])
begin
if((first_press==0)&&(floor==2))
begin
led[2]<=1;
state<=2'b10;
first_press[2]<=1;
flag <=1;
enable<=1;
end
else if((first_press!=0)&&(state==1)&&(limitied_third_press==0))
begin
led[2]<=1;
second_press[2]<=1;
limitied_third_press <=1;
end
else if(limitied_third_press) led[3]<=0;
end
else if(keyout[3])
begin
if((first_press==0)&&(floor==1))
begin
led[3]<=1;
state<=2'b01;
first_press[3]<=1;
flag <=1;
enable<=1;
end
else if((first_press!=0)&&(state==2)&&(limitied_third_press==0))
begin
led[3]<=1;
second_press[3]<=1;
limitied_third_press<=1;
end
else if(limitied_third_press) led[3]<=0;
end
//SW11复位
else if((floor == 2)&&(state==2'b00)&&(SW11==0))
begin
led<=0;
state<=2'b10;
flag<=1;
enable<=1;
end
//标志位f1成立下进行的操作
else if(f1)
begin
if (floor==1) floor <=2;
else floor =1;
if(second_press==0)
begin
led<=0;
state <=2'b00;
flag<=0;
f1<=0;
number<=0;
first_press <=0;
enable<=0;
end
else begin
case(first_press)
4'b0001:led[0]<=0;
4'b0010:led[1]<=0;
4'b0100:led[2]<=0;
4'b1000:led[3]<=0;
default:led<=0;
endcase
number<=number+1;
state<=~state;
f1<=0;
end
if (SW11==0)
begin
state<=2'b00;
flag<=0;
enable<=0;
end
end
//标志位f2成立下进行的操作
else if(f2)
begin
if (floor==1) floor <=2;
else floor =1;
led<=0;
state<=0;
first_press<=0;
second_press<=0;
flag<=0;
f2<=0;
number<=0;
limitied_third_press<=0;
enable<=0;
end
else if (number==250000000)
begin
f1<=1;
number <= number+1;
end
else if (number==500000000)
begin
f2<=1;
number <= number+1;
end
else
begin
if (flag) number<= number+1;
end
end
//LED11--7控制
always@(number)
begin
if (state==2'b01)
case(number)
1: LEDOUT<=5'b10000;
50000000: LEDOUT<=5'b11000;
100000000:LEDOUT<=5'b11100;
150000000:LEDOUT<=5'b11110;
200000000:LEDOUT<=5'b11111;
250000000:if(second_press==0)LEDOUT<=5'b00000;
250000002:LEDOUT<=5'b10000;
300000000:LEDOUT<=5'b11000;
350000000:LEDOUT<=5'b11100;
400000000:LEDOUT<=5'b11110;
450000000:LEDOUT<=5'b11111;
500000000:LEDOUT<=5'b00000;
default:;
endcase
else if(state==2'b10)
case(number)
1: LEDOUT<=5'b00001;
50000000: LEDOUT<=5'b00011;
100000000:LEDOUT<=5'b00111;
150000000:LEDOUT<=5'b01111;
200000000:LEDOUT<=5'b11111;
250000000: if(second_press==0)LEDOUT<=5'b00000;
250000002:LEDOUT<=5'b00001;
300000000:LEDOUT<=5'b00011;
350000000:LEDOUT<=5'b00111;
400000000:LEDOUT<=5'b01111;
450000000:LEDOUT<=5'b11111;
500000000:LEDOUT<=5'b00000;
default:;
endcase
end
endmodule
注:本代码完全由本人自主完成,所有功能都已经实现,代码中有关键的注释,各个模块之间的连接关系前面的关系框图中已展示,建议结合RTL分析图一起看能够更加轻松。另外,本代码仅供参考,切勿抄袭哦!
module displine(
input clk,
input [1:0]floor,
input [1:0]state,
input EN,
output reg [7:0] seg,
output reg [3:0] dig
);
//分频作为4进制计数器的时钟
reg[24:0] clk_div_cnt=0;
reg clk_div=0;
always @ (posedge clk)
begin
if (clk_div_cnt==25000) //分频
begin
clk_div=~clk_div;
clk_div_cnt=0;
end
else
clk_div_cnt=clk_div_cnt+1;
end
//分频作为10进制计数器的时钟(10HZ)
reg[24:0] clk_div_10=0;
reg clk_10HZ=0;
always @ (posedge clk)
begin
if (clk_div_10==2499999)
begin
clk_10HZ=~clk_10HZ;
clk_div_10=0;
end
else
clk_div_10=clk_div_10 +1;
end
//4进制计数器
reg [2:0] num=0;
always @ (posedge clk_div)
begin
if (num>=3)
num=0;
else
num=num+1;
end
//10进制计数器器
reg[3:0]Q1=0;
wire cy;
always@(posedge clk_10HZ)
begin
if(EN==1)
begin
if(Q1==0)Q1<=9;
else Q1<=Q1-1;
end
else Q1<=0;
end
assign cy=((EN==1)&&(Q1==0))?1'b1:1'b0;
//5进制计数器
reg[3:0]Q2=0;
always@(posedge clk_10HZ)
begin
if(cy==1)
begin
if(Q2==0)Q2<=4;
else Q2<=Q2-1;
end
end
//译码器
always @ (num)
begin
case(num)
0:dig=4'b1110;
1:dig=4'b1101;
2:dig=4'b1011;
3:dig=4'b0111;
default: dig=0;
endcase
end
//显示译码器
always@(num)
begin
if(num == 0)
begin
case(floor)
2'b01: seg=8'h06;
2'b10: seg=8'h5b;
default: seg=0;
endcase
end
else if (num == 1)
begin
case(state)
2'b00: seg=8'h40;
2'b01: seg=8'h01;
2'b10: seg=8'h08;
default: seg=0;
endcase
end
else if (num == 2)
begin
case(Q1)
4'h0: seg=8'h3f;// DP,GFEDCBA
4'h1: seg=8'h06;
4'h2: seg=8'h5b;
4'h3: seg=8'h4f;
4'h4: seg=8'h66;
4'h5: seg=8'h6d;
4'h6: seg=8'h7d;
4'h7: seg=8'h07;
4'h8: seg=8'h7f;
4'h9: seg=8'h6f;
default: seg=0;
endcase
end
else if (num == 3)
begin
case(Q2)
4'h0: seg=8'hbf;// DP,GFEDCBA
4'h1: seg=8'h86;
4'h2: seg=8'hdb;
4'h3: seg=8'hcf;
4'h4: seg=8'he6;
4'h5: seg=8'hed;
default: seg=0;
endcase
end
end
endmodule
注:本代码完全由本人自主完成,所有功能都已经实现,代码中有关键的注释,各个模块之间的连接关系前面的关系框图中已展示,建议结合RTL分析图一起看能够更加轻松。另外,本代码仅供参考,切勿抄袭哦!
#时钟约束文件
set_property PACKAGE_PIN D4 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
#按键检测的约束文件
set_property PACKAGE_PIN K3 [get_ports {row[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[0]}]
set_property PACKAGE_PIN M6 [get_ports {row[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[1]}]
set_property PACKAGE_PIN P10 [get_ports {row[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[2]}]
set_property PACKAGE_PIN R10 [get_ports {row[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[3]}]
##指定KEY0、KEY1、KEY2、KEY3作为按键
set_property PACKAGE_PIN R12 [get_ports col[0]]
set_property IOSTANDARD LVCMOS33 [get_ports col[0]]
set_property PACKAGE_PIN T12 [get_ports col[1]]
set_property IOSTANDARD LVCMOS33 [get_ports col[1]]
set_property PACKAGE_PIN R11 [get_ports col[2]]
set_property IOSTANDARD LVCMOS33 [get_ports col[2]]
set_property PACKAGE_PIN T10 [get_ports col[3]]
set_property IOSTANDARD LVCMOS33 [get_ports col[3]]
##下拉列线到低电平
set_property PULLDOWN true [get_ports col[0]]
set_property PULLDOWN true [get_ports col[1]]
set_property PULLDOWN true [get_ports col[2]]
set_property PULLDOWN true [get_ports col[3]]
##显示数码管部分约束文件
set_property PACKAGE_PIN G12 [get_ports {dig[0]}]
set_property PACKAGE_PIN H13 [get_ports {dig[1]}]
set_property PACKAGE_PIN N14 [get_ports {dig[2]}]
set_property PACKAGE_PIN N11 [get_ports {dig[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {dig[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {dig[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {dig[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {dig[3]}]
set_property PACKAGE_PIN L13 [get_ports {seg[7]}]
set_property PACKAGE_PIN M14 [get_ports {seg[6]}]
set_property PACKAGE_PIN P13 [get_ports {seg[5]}]
set_property PACKAGE_PIN K12 [get_ports {seg[4]}]
set_property PACKAGE_PIN K13 [get_ports {seg[3]}]
set_property PACKAGE_PIN L14 [get_ports {seg[2]}]
set_property PACKAGE_PIN N12 [get_ports {seg[1]}]
set_property PACKAGE_PIN P11 [get_ports {seg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}]
#LED部分约束文件
set_property PACKAGE_PIN P9 [get_ports {led[0]}]
set_property PACKAGE_PIN R8 [get_ports {led[1]}]
set_property PACKAGE_PIN R7 [get_ports {led[2]}]
set_property PACKAGE_PIN T5 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
#LED11---LED7部分约束文件
set_property PACKAGE_PIN E3 [get_ports {LEDOUT[4]}]
set_property PACKAGE_PIN H3 [get_ports {LEDOUT[3]}]
set_property PACKAGE_PIN G5 [get_ports {LEDOUT[2]}]
set_property PACKAGE_PIN R1 [get_ports {LEDOUT[1]}]
set_property PACKAGE_PIN T2 [get_ports {LEDOUT[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LEDOUT[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LEDOUT[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LEDOUT[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LEDOUT[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LEDOUT[0]}]
#SW0、SW11的约束文件
set_property PACKAGE_PIN T9 [get_ports {SW0}]
set_property PACKAGE_PIN F3 [get_ports {SW11}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW0}]
set_property IOSTANDARD LVCMOS33 [get_ports {SW11}]
有什么不清楚的地方可以留言,我会定期查看并给恢复的!!!
over ! Thanks!