课程设计-简易电梯系统verilog实现
一.设计要求
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;每一层楼之间的运行时间间隔为4秒。
5、有2个拨码开关
(1)复位开关:向下拨动后,电梯复位回到1楼。
(2)启动开关:向上拨动后,按键有效,电梯正常工作。
6、额外实现要求(选做)
(1)
电梯上行时,LED11至LED7五个指示灯从左到右每隔一秒点亮一个;
电梯下行时,LED7至LED11五个指示灯从右到左每隔一秒点亮一个。
(2)
电梯运行到达新楼层时,蜂鸣器发出一声清晰“嘀”声。
(3)
电梯开始上行或下行时,在最左边两个数码管上倒计时显示运行时间3.9~0.0(秒),精度为0.1秒。到达新楼层时显示0.0(秒)
(4)
电梯上行时,楼层显示数码管前3秒显示1,后1秒显示2;
电梯下行时,楼层显示数码管前3秒显示2,后1秒显示1。
二.实现思路
(读代码最能理解,只需阅读核心代码即可)
1.模块化实现,主要包括时钟配置、按键输入信号处理、状态信号控制数码管、状态信号控制LED、蜂鸣器驱动
2.时钟配置主要需要用于:
【实验板内部固定50Mhz晶振,分频时使用计数器,计数增1一次是1/50MHz】
1KHz、50Hz送给按键模块,前者扫描行列键盘,后者按键消抖;
0.25Hz(4s)用于电梯上行下行所需时间;
数码管动态显示直接使用晶振50MHz即可;
使用了个1MHz送给LED内部使用,可以直接用50MHz;
3.按键输入信号处理:
【行列4*4键盘,需要按键消抖】
拨码开关输入信号;
4.电梯核心控制:
(1)需求分析:
最主要的是电梯状态机的控制,包括state(上行、下行、待机),floor(一楼、二楼),对应的四个KEY(一楼外、二楼外、内部去一楼、内部去二楼)。
因此按键扫描模块需要引入两个时钟,分别做按键扫描和按键消抖(1KHz、50Hz);数码管状态显示根据state变量进行改变,数码管楼层显示根据floor变量进行改变,具体为在1KHz的扫描时钟下,判断变量是否由变化,并通过动态显示将楼层显示在第一位,将状态显示在第二位;蜂鸣器根据lock变量,在每次新楼层产生时发出1KHz信号驱动蜂鸣器;流水灯根据state变量改变,并使用1Hz信号依次点亮。
(2)状态机设计:
(3)特殊之处详解:
(1.如何实现运行状态也能接收按键信号?
答:在运行状态的4s计数时间中加入判断逻辑,如果按键按下且目标楼层和按键不一致,则存储这个按键信号并点亮对应的按键LED。
(2.怎么处理所谓的存储下来的按键信号?
答:在状态为待机状态时需要固定扫描按键信号和存储信号,此时可以处理存储下来的信号,即改变对应的运行状态。
(3.lock信号有什么用处?
Lock信号一方面是源于实际生活,可以说是为了开关门信号做了一个备用的信号,在日常生活中电梯到达之后需要固定的一段时间供人们出入;另一方面,本次实验引入lock信号正好可以用于新楼层到达时驱动蜂鸣器。
三.具体代码
module top_module( input clk, input set, //T9启动信号 input reset, //f3 复位信号 input [3:0]col, output reg buzzlock, output [3:0]row, output [3:0]led, output [4:0]led_o, output [5:0]dig, output [7:0]seg ); wire clk_1k; wire clk_50; //wire clk_4s; clk_div u1(.clk(clk), .clk_1k(clk_1k), .clk_50(clk_50) ); wire buzz; wire k0; wire k1; wire k3; wire k2; keyboard u2( .clk_1k(clk_1k),//键盘扫描 .clk_50(clk_50),//按键消抖 .col(col), .row(row),//行初值 .key0_upButton(k0),//对应一楼按键 .key1_downButton(k1),//对应二楼按键 .key2_upstair(k3), .key3_downstair(k2));//对应电梯内1 2按键 wire [1:0]state; wire [1:0]floor; wire gate; elevator u3( .clk(clk), .set(set), .reset(reset), .lock(buzz), .key0(k0), .key1(k1), .key3(k3), .key2(k2), .led(led),//指示灯 .state(state),//00待机状态 01上行状态 10下机状态 .floor(floor) ); //楼层 01一楼 10二楼 dynamic_led u4( .floor(floor), .state(state), .gate(gate), .clk(clk_1k), .seg(seg), .dig(dig) ); always@(clk) begin if(buzz) buzzlock = clk_1k; else buzzlock = 0; end running u5( .clk_i(clk), .state(state), .reset(reset), //.m(m),//state状态,1时候表示上行,2表示下行,处理一下将state_new输进来(0表示下行 1表示上行) .led_o(led_o) ); endmodule
module clk_div(clk,clk_1k,clk_50); input clk; output reg clk_1k=0; output reg clk_50=0; reg[24:0] clk_div_cnt0=0; reg[24:0] clk_div_cnt1=0; //1khz always @ (posedge clk) begin if (clk_div_cnt0==24999)//分频 begin clk_1k=~clk_1k; clk_div_cnt0=0; end else clk_div_cnt0=clk_div_cnt0+1; end //50hz always @ (posedge clk) begin if (clk_div_cnt1==499999)//分频 begin clk_50=~clk_50; clk_div_cnt1=0; end else clk_div_cnt1=clk_div_cnt1+1; end endmodule
module keyboard( input clk_1k,//键盘扫描 input clk_50,//按键消抖 input [3:0]col, output reg [3:0]row=4'b0001,//行初值 output key0_upButton,//对应一楼按键 output key1_downButton,//对应二楼按键 output key2_upstair, output key3_downstair);//对应电梯内1 2按键 reg [15:0] btn=0;//初始化为0 always @ (posedge clk_1k) begin if (row[3:0]==4'b1000) row[3:0]=4'b0001;//到了1000,下一个是0001 else row[3:0]=row[3:0]<<1; //向左移一位。0001-0010-0100-1000-0001 end always @ (negedge clk_1k) begin case (row[3:0]) 4'b0001: begin btn[3:0]=col; //列输入值存至4位寄存器 end 4'b0010: begin btn[7:4]=col; end 4'b0100: begin btn[11:8]=col; end 4'b1000: begin btn[15:12]=col; end default:btn=0; endcase end ajxd u0( .btn_in(btn[0]), .clk(clk_50), .btn_out(key0_upButton) ); ajxd u1( .btn_in(btn[1]), .clk(clk_50), .btn_out(key1_downButton) ); ajxd u2( .btn_in(btn[3]), .clk(clk_50), .btn_out(key2_upstair) ); ajxd u3( .btn_in(btn[2]), .clk(clk_50), .btn_out(key3_downstair) ); endmodule
module ajxd( input btn_in, input clk, output btn_out ); reg btn0=0;//定义了btn0寄存器 reg btn1=0;//定义了btn1寄存器 reg btn2=0;//定义了btn2寄存器 reg[24:0] clk_div_cnt=0; reg btn_clk=0; always@ (posedge clk) begin btn0<=btn_in; btn1<=btn0; btn2<=btn1; end assign btn_out=(btn2&btn1&btn0)|(~btn2&btn1&btn0); endmodule
module elevator( input clk, input set, input reset,//复位信号 input key0,//对应一楼按键 input key1,//对应二楼按键 input key2,//对应电梯内按键 input key3, output reg lock, output reg [3:0]led=0, output reg [1:0]state=0,//00待机状态 01上行状态 10下机状态 output reg [1:0]floor=1 //楼层 01一楼 10二楼 ); reg [27:0]clk_count=0; //reg [27:0]clk_count3=0; reg [27:0]clk_lock=0; reg [27:0]clk_reset=0; reg [5:0]key_memory=0; reg stop=0; //assign loc = lock; always@(posedge clk) begin if(reset==0)//复位信号有效时 begin if(stop==0) begin // lock = 0;199999999 49999999 if(clk_reset==199999999)//计数到4s begin led = 4'b0000; state=0; lock = 1; floor=1; clk_reset=0; stop = 1; end else begin clk_reset = clk_reset+1; state = (floor==2)? 2:0; stop = 0; if(clk_reset>=190999999) lock = 1; end end else lock=0; end /* if(!reset)//复位信号有效时 begin if(clk_count3==199999999)//计数到2s begin clk_count3=0; led=4'b0000; state=0; floor=1; end else clk_count3=clk_count3+1; end*/ else if(reset && set)//启动信号有效时 begin stop = (reset==1)? 0:1; if(state==0 && lock==1) begin if(clk_lock==24999999)//计数 begin clk_lock=0; lock = 0; end else begin clk_lock=clk_lock+1; if( (key0==1 || key_memory[0]==1) && (floor!=1 || led[1] || led[3]) ) led[0] = 1; else if ((key1==1 || key_memory[1]==1) && (floor!=2 || led[0] || led[2])) led[1] = 1; else if ((key2==1 || key_memory[3]==1) && (floor!=1 || led[1] || led[3]) ) led[2] = 1; else if ((key3==1 || key_memory[4]==1) && (floor!=2 || led[0] || led[2])) led[3] = 1; end end //(***上行下行模式配置)灯一旦是点亮状态,那么意味着进入运行模式,且4s之后楼层到达,灭灯,门开 else if( ( ((led!=0))&&(state!=0)&&(key_memory==0)&&(lock==0) ) || (clk_count!=0) ) begin if(clk_count==199999999)//计数到4s begin if((floor!=1)&&((led[2]==1)|(led[0]==1))) begin clk_count=0; state = 0; floor = 1; lock = 1; led[0] = 0; led[2] = 0; end else if((floor!=2)&&((led[3]==1)|(led[1]==1))) begin clk_count=0; state = 0; lock = 1; led[1] = 0; led[3] = 0; floor = 2; end end else begin clk_count=clk_count+1; if( (key0) && ((~led[2])&(~led[0])) ) begin key_memory[0] = 1; led[0] = 1; end else if( (key2) && ((~led[2])&(~led[0])) ) begin key_memory[3] = 1; led[2] = 1; end else if( (key1) && ((~led[3])&(~led[1])) ) begin key_memory[1] = 1; led[1] = 1; end else if( (key3) && ((~led[3])&(~led[1])) ) begin key_memory[4] = 1; led[3] = 1; end end end //else if((state==0)&((key_floor1 && key_floor2 && key_floor3 && key_lift1 && key_lift2 && key_lift3)==0)) //else if((state==0)&&(gate==1)&&(key_gateoff==0)&&(key_gateon==0)) //&&((key_floor1 && key_floor2 && key_floor3 && key_lift1 && key_lift2 && key_lift3)==0)) //根据外部按键情况改变运行模式 else if((led[0] || led[1] || key0 || key1 || key_memory[0] || key_memory[1] ) && (state==0) && (lock == 0)) //一楼按下key0且电梯待机 begin if( (led[0] || key0==1 || key_memory[0]==1) && floor!=1) begin led[0] = 1; //一楼电梯外灯亮 key_memory[0]=0; state = 2; //下行状态 end else if((led[0] || key0==1 || key_memory[0]==1) && floor==1) begin led[0] = 0; key_memory[0]=0; state = 0; end else if((led[1] || key1==1 || key_memory[1]==1) && floor!=2) begin led[1] = 1; //er楼电梯外灯亮 key_memory[1]=0; state = ((floor>2)? 2:1); //状态 end else if((led[1] || key1==1 || key_memory[1]==1) && floor==2) begin led[1] = 0; key_memory[1]=0; state = 0; end end //根据内部按键情况改变运行模式 else if((led[2] || led[3] || key2 || key3 || key_memory[3] || key_memory[4] ) && (state==0) &&(lock == 0)) begin if((led[2] || key2==1 || key_memory[3]==1) && floor!=1) begin key_memory[3]=0; led[2] = 1; //一楼电梯nei灯亮 state = 2; //下行状态 end else if((led[2] || key3==1 || key_memory[4]==1) && floor!=2) begin key_memory[4]=0; led[3] = 1; //er楼电梯外灯亮 state = ((floor>2)? 2:1); //状态 end end end end endmodule
module dynamic_led( input [1:0]floor,//楼层 input [1:0]state,//状态 input gate,//门 input clk, output reg [7:0] seg, output reg [5:0] dig ); reg [1:0] num=0;//二进制计数器 always @ (posedge clk) begin if (num>=1) num=0; else num=num+1; end //译码器 always @ (num) begin case(num) 0:dig=6'b111110; 1:dig=6'b111101; default: dig=0; endcase end //选择器,确定显示数据 reg [3:0] disp_data; always @ (num) begin case(num) 0:disp_data=floor; 1:disp_data=state+3'b011; default: disp_data=0; endcase end //显示译码器 always@(disp_data) begin case(disp_data) 4'h1: seg=8'h06; 4'h2: seg=8'h5b; 4'h3: seg=8'h40;//待机 4'h4: seg=8'h01;//上行 4'h5: seg=8'h08;//下行 4'h6: seg=8'h06;//关门**'1' 4'h7: seg=8'h5b;//开门**'2' default: seg=0; endcase end endmodule
module running( input clk_i, input [1:0]state, input reset, //input m,//state状态,1时候表示上行,2表示下行,处理一下将state_new输进来(0表示下行 1表示上行) output reg[4:0]led_o ); reg[31:0] clkcount = 0;//对50M时钟1M分频的计数器 reg clk_o = 0;//50Hz信号,周期20ms,初值为0 always@(posedge clk_i) //系统时钟上升沿时 begin if(clkcount==24999999)//499or2499999 begin clk_o=~ clk_o; clkcount = 0; end else begin clkcount = clkcount+1'b1; end end reg[3:0]s_present1 = 0; reg[3:0]s_next1;//up reg[3:0]s_present2 = 0; reg[3:0]s_next2;//down reg m; always@(clk_o) begin if(state==1) m = 1; else if(state==2) m = 0; end always@(m,s_present1,s_present2) if((state == 1)&&(s_present1<=4)) s_next1 = s_present1+1; else if((state == 2)&&(s_present2<=4)) s_next2 = s_present2+1; else begin s_next1 = 0; s_next2 = 0; end always@(negedge clk_o) begin s_present1<=s_next1; s_present2<=s_next2; end always@(m,s_present1,s_present2) if(state==1)//up case(s_present1) 0:led_o = 5'b10000; 1:led_o = 5'b01000; 2:led_o = 5'b00100; 3:led_o = 5'b00010; 4:led_o = 5'b00001; default:led_o = 5'bxxxxx; endcase else if(state==2)//down case(s_present2) 0:led_o = 5'b00001; 1:led_o = 5'b00010; 2:led_o = 5'b00100; 3:led_o = 5'b01000; 4:led_o = 15'b10000; default:led_o = 5'bxxxxx; endcase else if(state==0) led_o = 5'b00000; endmodule
四.RTL分析结果
五.实验结果
1.基础功能全部实现,拓展功能实现了两个(蜂鸣器和上行下行状态的流水灯表示)
2.具体实现情况:
六.附件
https://wwe.lanzouj.com/i6KKIyrbdpa
(失效请及时联系我,尽快更新)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)