一.設計要求
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
(失效請及時聯系我,盡快更新)
