目录
下面是设计的细节,再这里,笔者的注释都写得很清楚,因此不会啰嗦的一段段解释,只介绍关键的部分,其余的详见代码注释即可(此处建议直接看代码更好,上面的知识一个框架而已):
设计目的与要求
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 //固定这一行
2)按键消抖
检测到按键[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级联
附页(源代码)
TOP
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;
//