Verilog HDL3层电梯设计

要求:
(1)输入信号floor1,floor2,floor3分布表示目的楼层为第1,2,3层,该信号通过触发器保存后输出至状态机,状态机根据目的楼层为第1,2,3层,该信号通过触发器保存后输出至状态机,状态机根据目的楼层及当前楼层控制控制楼层变化,目标楼层到达后由状态机输出清除信号清除该楼层对应触发器;(2)输出信号led[2:0]显示当前楼层,高电平有效,例如:3’b100表示当前楼层为第三层;(3)画出状态机的状态图(Moore状态图,复位后为第一层);(4)以Verilog设计图,其中状态机采用3段式过程语句实现。

这里题目要求的楼层很少,因此可以不用考虑顺序的算法问题。只是一些细节需要注意。
思路:

  1. 首先画出状态图:
    状态图1
    2.有了主干之后需要加入一些细节:
    ①首先要求输入不能直接输入,而是保存在触发器之中去,设置3个reg变量tmpx表示输入,因为触发器是时钟触发的,因此一定要放在时序逻辑电路表示的那个always中。
    ②这里做了一个小处理就是因为电梯真实情况下电梯如果在某一楼层而又要去那一楼层是不会被触发的,因此做了一个判断语句,当前楼层是否是目标楼层,不是才输入信号。
    ③***这里还需要注意因为Quartus中不支持一个变量在两个always语句同时被赋值。***因此tmpx只能写在时序逻辑电路表示的always之中。
    ④这里做了一个特殊处理加入了一个表示顺路的信号,就是针对在二楼时,同时有1楼和2楼的输入先去哪里,这里是根据上一次运行的方向判断的,如果是从3楼到的2楼,这次就去1楼,否则如果是从1楼到的2楼,这次就是2楼。
    因此这里需要稍微修改一下状态图。
    状态图2
    3.写成3段式式的代码,第一个always为时序逻辑电路,第二个always为组合逻辑电路,第三个always为时序逻辑电路输出消除毛刺:
module elevator2(CLK, RESET ,floor1, floor2, floor3, led);
  //建成顶层模块
  input CLK;//时钟信号,每过一个时钟周期转化一次楼层
  input RESET;//清零信号
  input floor1;//按下1楼输入信号
  input floor2;//按下2楼输入信号
  input floor3;//按下3楼输入信号
  output reg[2:0]led;//表示当前所对应楼层,[2:0]
  reg tmp1;//floor1的触发器
  reg tmp2;//floor2的触发器
  reg tmp3;//floor3的触发器
  reg dir;//代表方向,1为向上,0为向下,默认为1(上行)
  reg [2:0]next_floor;//下一楼层
  reg [2:0]current_floor;//当前楼层,因为要写成三段式,不直接使用led
  parameter first_floor=3'b001,
            second_floor=3'b010,  
            third_floor=3'b100;
                 
  always @(posedge CLK or negedge RESET)//第一段,时序逻辑状态存储
  begin
    if(~RESET)//清零信号
      begin
        current_floor<=first_floor;
        {tmp3,tmp2,tmp1}<=3'b0;
      end
    else   
      begin
        current_floor=next_floor;
        tmp1=current_floor==first_floor?0:floor1;//真实情况下电梯按下按键之后如果没有到达目的楼层是不能取消的,如果到达当前楼层再继续按下按键是无效的
        tmp2=current_floor==second_floor?0:floor2;
        tmp3=current_floor==third_floor?0:floor3;
      end
  end
  
  always @ (current_floor or tmp1 or tmp2 or tmp3)
  //信号经触发器保存后输出至状态机
  //状态机根据目的楼层及当前楼层控制楼层变化
  //目标楼层到达后由状态机清除该楼层对应触发器
  //如果按键是本楼层,直接清除信号
  //存在一个问题,如果当前在3楼,按下1楼和2楼,下到2楼时,有人按下三楼又往3楼,而此时应该是去2楼才正确
  //因此应该加上一个当前方向优先级最高,表示顺路
  begin
    case(current_floor)
      first_floor:
      begin
        dir=1;//方向设为上行
        if(tmp2||tmp3) next_floor=second_floor;
        else next_floor=first_floor;
        //否则不改变,不用写出
      end
      
      second_floor:
      begin
        if(~tmp3&tmp1)//因为只有方向的楼层此时楼层优先
        begin
          next_floor=first_floor;
          dir=0;//此时为下行,其实设置方向是多余的因为这里楼层太少,到达3或是1都会被重置
        end
        else if(tmp3&~tmp1) 
        begin
          next_floor=third_floor;
          dir=1;//此时为上行
        end
        else if(tmp3&tmp1&dir)//因为两个方向的楼层都被按下因此此时方向优先
          next_floor=third_floor;
        else if(tmp3&tmp1&~dir)
          next_floor=first_floor;
        else
          next_floor=second_floor; 
      end
      
      third_floor:
      begin
        dir=0;//方向设为下行
        if(tmp2||tmp1) next_floor=second_floor;
        else next_floor=third_floor;
      end
    endcase
  end  
  
  
  always @(posedge CLK or negedge RESET)//第三段,经过一个寄存器再送出,消除输出信号的毛刺
  begin
    if(~RESET)  
      begin
        led=3'b001;
      end
    else
      led=current_floor;
  end
  
endmodule

4.测试结果:
测试结果1
测试结果2
5.Quartus的电路图和状态图:
电路图1
状态图1
改进:
①可以看出这个电路存在最大的问题就是不会停留,从1楼到2楼再到3楼和直接到3楼停留的时间一样长,因此从这里着手改进加入一个变量open作为开门信号,open为1表示在此楼层开门,为0表示直接离开。
②还有一个按键输入在实际生活中的电梯按下按键之后输入寄存器一般是不能取消的,只能是到达该楼层后才能取消,因此这里重新处理了一下。
③这里的时序逻辑电路部分写得比较复杂,主要是有多种情况需要讨论。
④这里还要注意第一个时序逻辑电路的always电路最外面的那一层if-else一定要和敏感变量有关否则会报错。
⑤还要特别注意组合逻辑电路的电平值会产生毛刺,因此在时序逻辑电路中一定要注意使用<=和=赋值区别很大。

module elevator5(CLK, RESET ,floor1, floor2, floor3, led, open);
  //建成顶层模块
  input CLK;//时钟信号,每过一个时钟周期转化一次楼层
  input RESET;//清零信号
  input floor1;//按下1楼输入信号
  input floor2;//按下2楼输入信号
  input floor3;//按下3楼输入信号
  output reg[2:0]led;//表示当前所对应楼层,[2:0]
  output reg open;//开门信号,1到达某楼层开门,0相反
  reg tmp1;//floor1的触发器
  reg tmp2;//floor2的触发器
  reg tmp3;//floor3的触发器
  reg arrive;//作为一个到达信号,因为一个信号不能在两个always语句中被赋值
  reg dir;//代表方向,1为向上,0为向下,默认为1(上行)
  reg [2:0]next_floor;//下一楼层
  reg [2:0]current_floor;//当前楼层,因为要写成三段式,不直接使用led
  integer running_time;//这是到达之后开门时间倒计时
  parameter first_floor=3'b001,
            second_floor=3'b010,  
            third_floor=3'b100,
            //up_time=3'd1,//楼层转化所需的时间
            total_time=3'd3;//这是设置默认开门时间
                 
  always @(posedge CLK or negedge RESET)//第一段,时序逻辑状态存储
  begin
    if(~RESET)//清零信号
      begin
        current_floor<=first_floor;
        {tmp3,tmp2,tmp1}<=3'b000;
        open<=0;
        running_time<=0;//默认开始时有个上升
      end
    else 
	   begin
		   if(next_floor!=current_floor&running_time==0&open==0)//这里应该是刚刚到达时的情况到达时刻
			begin        
			  current_floor=next_floor;
			  if(next_floor==first_floor&tmp1)
				 begin
					tmp1=0;
					running_time=total_time;
					open=1;    
				 end
			  else if(next_floor==second_floor&tmp2)
				 begin
					tmp2=0;
					running_time=total_time;
					open=1;
					//current_floor=next_floor; 
				 end
			  else if(next_floor==third_floor&tmp3)
				 begin
					tmp3=0;
					running_time=total_time;
					open=1;
					//current_floor=next_floor; 
				 end
			end
			
			if(running_time==0)//倒计时完成后才进行状态转化
				begin
				  open=0;//清零开门信号
				  tmp1=(current_floor==first_floor||tmp1==1)?tmp1:floor1;//真实情况下电梯按下按键之后如果没有到达目的楼层是不能取消的,如果到达当前楼层再继续按下按键是无效的
				  tmp2=(current_floor==second_floor||tmp2==1)?tmp2:floor2;
				  tmp3=(current_floor==third_floor||tmp3==1)?tmp3:floor3;
				end
			else
				begin
				  current_floor=current_floor;
				  running_time=running_time-1;
				end
      end
  end
  
  always @ (current_floor or tmp1 or tmp2 or tmp3)
  //信号经触发器保存后输出至状态机
  //状态机根据目的楼层及当前楼层控制楼层变化
  //目标楼层到达后由状态机清除该楼层对应触发器
  //如果按键是本楼层,直接清除信号
  //存在一个问题,如果当前在3楼,按下1楼和2楼,下到2楼时,有人按下三楼又往3楼,而此时应该是去2楼才正确
  //因此应该加上一个当前方向优先级最高,表示顺路
  begin
    case(current_floor)
      first_floor:
      begin
        dir=1;//方向设为上行
        if(tmp2||tmp3) next_floor=second_floor;
        else next_floor=first_floor;
        //否则不改变,不用写出
      end
      
      second_floor:
      begin
        if(~tmp3&tmp1)//因为只有方向的楼层此时楼层优先
        begin
          next_floor=first_floor;
          dir=0;//此时为下行,其实设置方向是多余的因为这里楼层太少,到达3或是1都会被重置
        end
        else if(tmp3&~tmp1) 
        begin
          next_floor=third_floor;
          dir=1;//此时为上行
        end
        else if(tmp3&tmp1&dir)//因为两个方向的楼层都被按下因此此时方向优先
          next_floor=third_floor;
        else if(tmp3&tmp1&~dir)
          next_floor=first_floor;
        else
          next_floor=second_floor; 
      end
      
      third_floor:
      begin
        dir=0;//方向设为下行
        if(tmp2||tmp1) next_floor=second_floor;
        else next_floor=third_floor;
      end
      
    endcase
  end  
  
  always @(posedge CLK or negedge RESET)//第三段,经过一个寄存器再送出,消除输出信号的毛刺
  begin
    if(~RESET)  
      begin
        led=3'b001;
      end
    else
      led=current_floor;
  end
  
endmodule

2.测试图片:
测试图片1
3.Quartus电路图和状态图:
电路图2
状态图2

不足之处:
①不能动态设置转化时间即open=0的那段时间,因为这里是用的open区分的刚刚到达状态切换和倒计时结束状态切换两种状态。
②其实可以更改一下tmpx被赋值的位置,不然成了开门的时候不能按下按键。
③只能处理三层楼的情况,拓展性不足。
④逻辑有一些混乱,不够简洁。

如果还有什么不足,请指正。

  • 24
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值