基于Verilog的简易电梯控制系统的设计(两层楼)|2024最新版|数电实验课程设计

0.0 作者前言

        本贴作者参考往界学长作品:https://blog.csdn.net/qq_52281875/article/details/122158289?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-122158289-blog-126777610.235^v43^pc_blog_bottom_relevance_base8&spm=1001.2101.3001.4242.2&utm_relevant_index=4icon-default.png?t=N7T8https://blog.csdn.net/qq_52281875/article/details/122158289?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-122158289-blog-126777610.235^v43^pc_blog_bottom_relevance_base8&spm=1001.2101.3001.4242.2&utm_relevant_index=4        兼容并蓄,修改了部分情况bug,并添加了2024版最新要求的功能。

0.1 开发环境

开发环境:Vivado 2017.4

编程语言:Verilog

开发板芯片:xc7a35tftg256-1(作者的约束文件仅仅满足手上实验板要求,学校发的,但芯片是这款)

1.1设计要求及需求分析

1.2验收要求

1.3参考方案

2.1约束文件

再次感谢往年大佬提供的约束文件,未修改,仅仅增加几处注释:

#set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets start_IBUF]
set_property IOSTANDARD LVCMOS33 [get_ports {data_dig[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_dig[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_dig[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_dig[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_dig[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_dig[5]}]
set_property PACKAGE_PIN G12 [get_ports {data_dig[0]}]
set_property PACKAGE_PIN H13 [get_ports {data_dig[1]}]
set_property PACKAGE_PIN M12 [get_ports {data_dig[2]}]
set_property PACKAGE_PIN N13 [get_ports {data_dig[3]}]
set_property PACKAGE_PIN N14 [get_ports {data_dig[4]}]
set_property PACKAGE_PIN N11 [get_ports {data_dig[5]}]
 
set_property PACKAGE_PIN T10 [get_ports {key[3]}]
set_property PACKAGE_PIN R11 [get_ports {key[2]}]
set_property PACKAGE_PIN T12 [get_ports {key[1]}]
set_property PACKAGE_PIN R12 [get_ports {key[0]}]
set_property PACKAGE_PIN T5 [get_ports {led[3]}]
set_property PACKAGE_PIN R7 [get_ports {led[2]}]
set_property PACKAGE_PIN R8 [get_ports {led[1]}]
set_property PACKAGE_PIN P9 [get_ports {led[0]}]
set_property PACKAGE_PIN R10 [get_ports {row[3]}]
set_property PACKAGE_PIN P10 [get_ports {row[2]}]
set_property PACKAGE_PIN M6 [get_ports {row[1]}]
set_property PACKAGE_PIN K3 [get_ports {row[0]}]
set_property PACKAGE_PIN D4 [get_ports clk]
#复位、启动
set_property PACKAGE_PIN F3 [get_ports rst_n]
set_property PACKAGE_PIN T9 [get_ports start]

set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports start]
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]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {row[0]}]
set_property PACKAGE_PIN L13 [get_ports {data_seg[7]}]
set_property PACKAGE_PIN M14 [get_ports {data_seg[6]}]
set_property PACKAGE_PIN P13 [get_ports {data_seg[5]}]
set_property PACKAGE_PIN K12 [get_ports {data_seg[4]}]
set_property PACKAGE_PIN K13 [get_ports {data_seg[3]}]
set_property PACKAGE_PIN L14 [get_ports {data_seg[2]}]
set_property PACKAGE_PIN N12 [get_ports {data_seg[1]}]
set_property PACKAGE_PIN P11 [get_ports {data_seg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {data_seg[0]}]
# 蜂鸣器
set_property PACKAGE_PIN L2 [get_ports buzzer]
set_property IOSTANDARD LVCMOS33 [get_ports buzzer]
# 以下为led计时灯
set_property IOSTANDARD LVCMOS33 [get_ports {state_led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {state_led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {state_led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {state_led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {state_led[4]}]

set_property PACKAGE_PIN T2 [get_ports {state_led[0]}]
set_property PACKAGE_PIN R1 [get_ports {state_led[1]}]
set_property PACKAGE_PIN G5 [get_ports {state_led[2]}]
set_property PACKAGE_PIN H3 [get_ports {state_led[3]}]
set_property PACKAGE_PIN E3 [get_ports {state_led[4]}]

2.2顶层文件

新增了四个数码管,用于复位计时和显示开关状态。

复位模块重构,满足复位计时功能,优化了部分逻辑。

添加了大量注释,方便初学者学习。

美中不足是复位后蜂鸣器有小概率不响。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 请叫我源神
// 
// Create Date: 2024/06/04 00:00:00
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module top(
    input clk,
    input rst_n,
    input start,
    input  [3:0] key,
    output reg [4:0] state_led = 0,
    output reg buzzer = 1,
    output reg [3:0] led =0,
    output  [3:0] row,
    output  [7:0] data_seg,
    output  [5:0] data_dig
    ); 
 //--------------------------------------------------------------------------------------------------   
//按键相关    
assign row = 4'b1110;  //只需要用到最下面一行,不需要按建行扫描
//按键消抖
wire [3:0] key_xd;
ajxd u2 (
.EN_ajxd((start==1&rst_n==1)|(rst_n==0&key_00)),
.clk  (clk),
.btn_in  ( key),
.rst_n  (rst_n),
.btn_out  (key_xd)
);
//定义四个按键信号,代表按下按键并消抖之后的信号
wire key0,key1,key2,key3;
//按键按下时定义为高电平
assign key0 =( ~(key_xd[0]|row[0]))|key_00;
assign key1 = ~(key_xd[1]|row[0]);
assign key2 = ~(key_xd[2]|row[0]);
assign key3 = ~(key_xd[3]|row[0]);
//--------------------------------------------------------------------------------------------------
/*
每一个按键都对应了一个5秒的计时器,
该计时器检测到按键上升沿信号后开始计时,
计时状态位--status:  1 = 计时中  ;  0 = 计时完成
*/
//四个状态位,分别代表了key0下行,key1上行,key2下行,key3上行状态
wire status_dow0;
wire status_up1;
wire status_dow2;
wire status_up3;
/*
四个按键信号延迟位 key_delay_x    x=0,1,2,3
为了完成"在上行状态时按下KEY0或KEY2下行,电梯到达2楼后立刻下行"这一要求
在计时器中添加了一个按键被按下后的延迟按键信号输出
其功能为:
若处于上行状态中,按下KEY0或KEY2,那么KEY0或KEY2对应的计时器会被触发,但由于
还处于上行状态,无法开启下行状态。于是将等待上行状态完成后模拟一次KEY0或KEY2被
按下的信号。相当于在上行状态结束的一瞬间再按下了KEY0或KEY2一次,以此来达到期望的效果。
在下行状态按下上行按键也同理
*/
wire key_d0;
wire key_d1;
wire key_d2;
wire key_d3;
reg key_00=0;//2楼静止时复位用
/*
用来记录四个按键是否被按下的信号,其主要作用是点亮按键对应的led灯
当按键被按下对应的flag就会置位为1,后面会用到该信号来点亮按键对应的
led灯
*/
wire flag0;
wire flag1;
wire flag2;
wire flag3;
//定义两个楼层
reg lou1,lou2;
//定义5个状态---上行(key1)、上行(key3)、下行(key0)、下行(key2)、待机
reg state_up1,state_up3,state_dow0,state_dow2,state_wait;
/*
例化计时器模块
*/
count_5 dow_count0 (
.clk  (clk),
.en  ((key0&(lou2|(~state_wait))&(~led[2]))|key_00),
.en_1(cnt2),
.flag  (flag0),
.key_d (key_d0),
.wait1  (status_up1),
.wait2  (status_up3),
.status  (status_dow0)
);
count_5 up_count1 (
.clk  (clk),
.en  (key1&(lou1|(~state_wait))&(~led[3])),
.en_1(cnt2),
.flag  (flag1),
.key_d (key_d1),
.wait1  (status_dow0),
.wait2  (status_dow2),
.status  (status_up1)
);
count_5 dow_count2 (
.clk  (clk),
.en  (key2&(lou2|(~state_wait))&(~led[0])),
.en_1(cnt2),
.flag  (flag2),
.key_d (key_d2),
.wait1  (status_up1),
.wait2  (status_up3),
.status  (status_dow2)
);
count_5 up_count3 (
.clk  (clk),
.en  (key3&(lou1|(~state_wait))&(~led[1])),
.en_1(cnt2),
.flag  (flag3),
.key_d (key_d3),
.wait1  (status_dow0),
.wait2  (status_dow2),
.status  (status_up3)
);
//--------------------------------------------------------------------------------------------------
/*
这里使用的是共阴极数码管,
调用数码管动态显示模块控制数码管的显示
用两个七段(八段)数码管,一个显示楼层数字,一个显示上行、下行、待机状态。
*/
reg [7:0] data_seg0;  //当前楼层显示
reg [7:0] data_seg1;  //运行状态显示

reg [7:0] data_seg2; 
reg [7:0] data_seg3; //开关状态显示
reg [7:0] data_seg4; 

reg [7:0] data_seg5; //复位记秒
seg_scan  scan (
.clk  (clk),
.data_seg0  (data_seg0),
.data_seg1  (data_seg1),
.data_seg2  (data_seg2),
.data_seg3  (data_seg3),
.data_seg4  (data_seg4),
.data_seg5  (data_seg5),
.data_seg  (data_seg),
.data_dig  (data_dig)
);
 
//--------------------------------------------------------------------------------------------------
/*
楼层及电梯状态判断
*/
//定义5个状态---上行(key1)、上行(key3)、下行(key0)、下行(key2)、待机
//reg state_up1,state_up3,state_dow0,state_dow2,state_wait;
//定义两个楼层
//reg lou1,lou2;
//ps:本来这几个状态位是定义在这的,但是前面用到了这几个状态位,所以将这几个状态位定义在了前面,这里是做提示用
//初始状态:电梯停在一楼,待机状态

/*
使用计时器状态和楼层状态以及按键被按下的信号做逻辑判断
以此来改变楼层状态位和上下行状态位以及待机状态位
*/
initial//初始化楼层状态
begin
        lou1 = 1;
        lou2 = 0;
        state_up1 = 0;
        state_up3 = 0;
        state_dow0 = 0;
        state_dow2 = 0;
        state_wait = 1;
end


//---------------------------------------------------------------------------------
//电梯状态逻辑
always@(posedge clk)
begin
//if (start == 1)   //启动
       // begin                                   
            if(lou1&(~lou2)&(key_d1|key_d3))                  //电梯不处于上行状态且电梯在一楼、KEY1或KEY3被按下,则电梯进入上行状态,跳出待机状态
                begin
                    if(status_up1 == 1)   
                        begin
                            state_up1 <= 1;
                            state_wait <= 0;                           
                        end
                    else if (status_up3 == 1)
                        begin
                            state_up3 <= 1;
                            state_wait <= 0;                           
                        end
                end                                
            else  
                begin
                    if((status_up1 == 0)&(state_up1 == 1))           //上行状态保持5秒后,status_up1/3置位、上行状态结束、进入待机状态、楼层状态翻转
                    begin
                    if(rst_n==0)
                    begin    
                        state_up1 <= 0;
                        state_wait <= 1;
                        lou1 <= 1;
                        lou2 <= 0;
                    end
                    else if(rst_n==1)
                    begin
                         state_up1 <= 0;
                         state_wait <= 1;
                         lou1 <= ~lou1;
                         lou2 <= ~lou2;
                    end
                    end
                    else if((status_up3 == 0)&(state_up3 == 1))
                    begin 
                    if(rst_n==0) 
                    begin  
                        state_up3 <= 0;
                        state_wait <= 1;
                        lou1 <= 1;
                        lou2 <= 0;
                    end
                    else if(rst_n==1)
                    begin
                         state_up3 <= 0;
                         state_wait <= 1;
                         lou1 <= ~lou1;
                         lou2 <= ~lou2;
                    end
                    end
                end
                
            if((lou2&(~lou1)&(key_d0|key_d2))|key_00==1) //电梯不处于下行状态且电梯在二楼、KEY2或KEY0被按下,则电梯进入下行状态,跳出待机状态
                begin
                    if(status_dow0 == 1)   
                        begin
                            state_dow0 <= 1;
                            state_wait <= 0;                           
                        end
                    else if (status_dow2 == 1)
                        begin
                            state_dow2 <= 1;
                            state_wait <= 0;                           
                        end
                    
                end                                
            else  
                begin
                    if((status_dow0 == 0)&(state_dow0 == 1))           //下行状态保持5秒后,status_dow0/2置位、下行状态结束、进入待机状态、楼层状态翻转
                    begin    
                        state_dow0 <= 0;
                        state_wait <= 1;
                        lou1 <= ~lou1;
                        lou2 <= ~lou2;
                    end
                    else if((status_dow2 == 0)&(state_dow2 == 1))
                    begin    
                        state_dow2 <= 0;
                        state_wait <= 1;
                        lou1 <= ~lou1;
                        lou2 <= ~lou2;
                    end
                    
                end                
   
     //   end
end





//----------------------------------------------------------------------------------------------------------------
//开关机显示
always@(posedge clk )
begin
         if(start==0) //关机
                 begin
                      data_seg4 <= 8'b01011100;//off
                      data_seg3 <= 8'b01110001;
                      data_seg2 <= 8'b01110001;
                 end
         else if(start==1)//开机 
                 begin
                    data_seg4 <= 8'b00000000;//on
                    data_seg3 <= 8'b01011100;
                    data_seg2 <= 8'b01010100;
                 end
 end
 //---------------------------------------------------------------------------------------------------------
 //复位计时也可作为测试工程模块,用于显示当前状态
always@(posedge clk )
 begin
               
           if(rst_n==0)
      begin
                case(cnt2)
                6:
                begin
                      data_seg5 <= 8'b01101101;//5
                end
                5:
                begin
                      data_seg5 <= 8'b01100110;//4
                end
                4:
                begin
                      data_seg5 <= 8'b01001111;//3
                end
                3:
                begin
                      data_seg5 <=8'b01011011 ;//2
                end
                2:
                begin
                      data_seg5 <= 8'b00000110;//1
                 end
               default:
                begin
                      data_seg5 <= 8'b00000000;//不显示0
                end

            endcase              
      end     
      else  if(rst_n==1) 
            begin
            data_seg5 <= 8'b00000000;//不显示0
            end            
end
//---------------------------------------------------------------------------------------------------------        
//楼梯运行状态显示    
wire [6:0] status_seg ;
assign status_seg = {lou1,lou2,state_up1,state_up3,state_dow0,state_dow2,state_wait};//定义一个数码管状态变量,方便用数码管显示状态
always@(posedge clk )
begin     
             
               begin
                   led[0] <=  ((status_dow0&(~status_dow2)&(lou2&(~lou1))|((state_up1|state_up3)&flag0))|key_00==1);
                   led[1] <=  (status_up1&(~status_up3)&(lou1&(~lou2)))|((state_dow0|state_dow2)&flag1);
                   led[2] <=  (status_dow2&(~status_dow0)&(lou2&(~lou1)))|((state_up1|state_up3)&flag2);
                   led[3] <=  (status_up3&(~status_up1)&(lou1&(~lou2)))|((state_dow0|state_dow2)&flag3);
               end
               if(rst_n==1)
               begin
                  case(status_seg)    
                      7'b1000001:     //1楼待机
                          begin
                             data_seg1 <= 8'b01000000;
                             data_seg0 <=8'b00000110;
                         end
                       7'b1010000:    //1楼上行--status_up1--KEY1被按下
                          begin
                             data_seg1 <=8'b01000001;
                             data_seg0 <=8'b00000110;
                         end
                       7'b1001000:    //1楼上行--status_up3--KEY3被按下
                          begin
                             data_seg1 <=8'b01000001;
                             data_seg0 <=8'b00000110;
                         end
                       7'b0100001:    //2楼待机
                          begin
                             data_seg1 <= 8'b01000000;
                             data_seg0 <= 8'b01011011;
                         end
                       7'b0100100:    //2楼下行--status_dow0--KEY0被按下
                          begin
                             data_seg1 <= 8'b01001000;
                             data_seg0 <= 8'b01011011;
                         end
                       7'b0100010:    //2楼下行--status_dow2--KEY2被按下
                          begin
                             data_seg1 <= 8'b01001000;
                             data_seg0 <=8'b01011011;
                         end
                     endcase
                  end
                  else if(rst_n==0)
                  begin
                     case(status_seg)    
                         7'b1000001:     //1楼待机
                             begin
                                data_seg1 <= 8'b01000000;
                                data_seg0 <=8'b00000110;
                            end
                          7'b1010000:    //1楼上行--status_up1--KEY1被按下
                             begin
                                data_seg1 <=8'b01001000;
                                data_seg0 <=8'b00000110;
                            end
                          7'b1001000:    //1楼上行--status_up3--KEY3被按下
                             begin
                                data_seg1 <=8'b01001000;
                                data_seg0 <=8'b00000110;
                            end
                          7'b0100001:    //2楼待机
                             begin
                                data_seg1 <= 8'b01000000;
                                data_seg0 <= 8'b01011011;
                            end
                          7'b0100100:    //2楼下行--status_dow0--KEY0被按下
                             begin
                                data_seg1 <= 8'b01001000;
                                data_seg0 <= 8'b01011011;
                            end
                          7'b0100010:    //2楼下行--status_dow2--KEY2被按下
                             begin
                                data_seg1 <= 8'b01001000;
                                data_seg0 <=8'b01011011;
                            end
                        endcase
                     end   
end
 
 

//--------------------------------------------------------------------------------------------------   
//功能:蜂鸣器
//这里采用无源蜂鸣器,需要输入一定频率的方波信号
//输入信号频率决定了蜂鸣器发出的声音频率,这里采用500hz的方波信号

//获得1khz的信号
reg [31:0] temp =0;
reg clk_1khz = 0;
always@(posedge clk)
begin
    if(temp == 49999)
        begin
            temp <= 0;
            clk_1khz <= 1;
        end
    else
        begin
            temp <= temp +1;
            clk_1khz <= 0;
        end
end
 
 
reg lou1_dly;
reg turn = 0;
reg [15:0] cnt = 0;
//这是一个检测信号翻转的模块,采用了非阻塞赋值
//检测楼层状态是否翻转,若翻转则turn = 1并开始计时,计时完成后turn = 0
always@(posedge clk)
begin
    lou1_dly <= lou1;
    if((lou1 != lou1_dly&rst_n==1)|(cnt2==0&rst_n==0))
         turn <= 1;
    else if(cnt == 100)
            turn <= 0;
end  

  parameter cnt_max = 100;        
  always@(posedge clk_1khz)
  begin
      if(turn == 1)
          begin
              if(cnt == cnt_max)
                  cnt <= 0;
              else
                  cnt <= cnt + 1;
          end
      else
          cnt <= 0;
  end 

//当turn = 1时向蜂鸣器输入1khz的信号,蜂鸣器响0.1s,这里蜂鸣器响的时间由上面的cnt计数器决定
//t = cnt_max / 1000     单位s
//作为蜂鸣器的输入信号buzzer的频率其实为500hz
//也可以将clk_1hz信号直接赋给buzzer,令其为1khz,这种办法其实更好,
//可以节约一个寄存器资源,也让buzzer和1khz的时钟频率相同,
//但在这里两个都无所谓,只是频率不同而已,看自己的需要更改即可
always@(posedge clk_1khz)
begin
 begin
    if(turn == 1)
         buzzer <= ~buzzer;
    else if(turn == 0)
         buzzer <= 0;
    end
 end                                                     
   
//--------------------------------------------------------------------------------------------------  
//附加功能:流水灯,上行状态将LED11---LED7从左到右每隔一秒依次点亮;
//                 下行状态将LED11---LED7从右到左每隔一秒依次点亮 
//定义两个状态位控制流水灯的状态
wire state_up;//用来控制上行流水灯
wire state_dow;//用来控制下行流水灯
reg state_up_5=0;//复位上升延时



always@(posedge clk)
begin
    if(rst_n==0&state_up==1)
      begin
        state_up_5<=state_up;
      end
  else if(state_up==0&cnt2==0)
  begin
          state_up_5<=0;
          
           
  end
end


//为了防止预期以外的情况出现,这里采用了楼层和按键对应的led灯来共同控制流水灯的状态
assign state_up = (led[1]|led[3])&(lou1&(~lou2));
assign state_dow = ((led[0]|led[2])&(lou2&(~lou1)))|key_00;
 
/*
获得1.0hz的信号(总共5个灯,每隔一秒亮一个)
*/
reg [31:0] temp1 =0;
reg clk_1hz = 0;
always@(posedge clk )
begin
 
        if(temp1 == 49999999)
            begin
                temp1 <= 0;
                clk_1hz <= 1;
            end
        else
            begin
               temp1 <= temp1 +1;
                clk_1hz <= 0;
            end
        
end
 
 //——————————————————————————————————————————————————————
//计数器
reg [3:0] cnt1 = 0;
reg [3:0] cnt2 = 1;//0为复位,1为剩余0s,2为剩余1s

always@(posedge clk_1hz  /*or negedge state_up or negedge state_dow*/)
begin
 if(rst_n==0&lou2&state_wait)//复位时在二楼等待        
      begin
        key_00<=1;
      end
    else if(~(state_up|state_dow|key_00|state_up_5)&rst_n==0&lou1&state_wait)//复位时在一楼等待      //gaiguo
    begin
        cnt1 <= 0;
        cnt2<=1;
    end
    else if(~(state_up|state_dow)&rst_n==1)//无复位无运行
    begin
     cnt1 <= 0;
     cnt2<=1;
    end
    else if((state_up | state_dow)&rst_n==1)//无复位运行
    begin
      if(cnt1==5)
        begin
        cnt1<=0;
        end
      else
        begin
        cnt2<=cnt1+2;//寄存器且cnt2==0用于复位
        cnt1 <= cnt1 + 1;
        end
    end
         else if((state_up|state_up_5)&rst_n==0)//上升复位(state变化快)
       begin
          if(cnt2==0)
          begin
          cnt2<=0;
          end 
          else
          begin
             cnt2<=cnt2-1;
          end

        end
           else if((state_dow|key_00)&rst_n==0)//下降复位
             begin 
                 if(cnt2==3)
                 begin
                 key_00<=0;
                 end

               if(cnt1==5)
               begin
                 cnt1<=0;
                 cnt2<=1;
               end  
               else
               begin
                cnt2 <= 4-cnt1;
                cnt1 <= cnt1 + 1;
               end
             end
           else
               cnt1 <= 0;               
end



//——————————————————————————————————————————————————
 //附加功能1:轮流点亮led
always@(posedge clk )
begin
    if(state_up&rst_n==1)//正常上升
    begin
         case(cnt1)
            0 :state_led <= 5'b10000;
            1 :state_led <= 5'b11000;
            2 :state_led <= 5'b11100;
            3 :state_led <= 5'b11110;
            4 :state_led <= 5'b11111;
            default:state_led <= 5'b00000;
         endcase
    end
    else if((state_up|state_up_5)&rst_n==0)//上升复位
    begin
         case(5-cnt2)
         0 :state_led <= 5'b00001;
         1 :state_led <= 5'b00011;
         2 :state_led <= 5'b00111;
         3 :state_led <= 5'b01111;
         4 :state_led <= 5'b11111;
            default:state_led <= 5'b00000;
         endcase
     end
    else if(state_dow)//下降
    begin
         case(cnt1)
            0 :state_led <= 5'b00001;
            1 :state_led <= 5'b00011;
            2 :state_led <= 5'b00111;
            3 :state_led <= 5'b01111;
            4 :state_led <= 5'b11111;
            default:state_led <= 5'b00000;
         endcase
    end
     else 
            state_led <= 5'b00000;
end
  
    
endmodule

2.3 状态计时模块

本模块添加部分注释,修改了计时秒数以满足验收要求。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/06/04 00:00:00
// Design Name: 开源之神方为源神
// Module Name: count_5
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module count_5(
    input en,    //使能位
    input wire[3:0] en_1,
    input clk,
    input wait1,         //两个等待位,检测是否处于冲突状态
    input wait2,
    output reg flag =0,   //led灯使能信号
    output reg key_d,    //按键延迟信号
    output reg status = 0 ,  //计数状态输出:为1计数、为0不计数
    output reg [2:0] count = 0//确定计时状态
    );
    
 
//等待位,如果该计时器被用于上行计时,那么如果处于下行状态时,计时器就会等待下行状态结束再开始计时 
//这里的两个等待位分别对应了两个下行计数器的状态输出位---status   
reg flag2 = 1;    
always@(posedge clk)
begin
    if((wait1 | wait2)&en_1!=0)
        flag2 <= 0;
    else 
        flag2 <= 1;              
end        
 
 
//使能位上升沿来临,计数器使能信号置位为1
//作用是记住按键被按下的状态
reg flag1 = 1;
always@(posedge clk)
begin
    if(en == 1)
        flag <= 1;
    else if (flag1 == 0|en_1==0)
        flag <= 0;
end
 
//当使能位为1且不处于冲突状态时计时器开始计时,并将输出状态位置位为1
always@(posedge clk)
begin
    if(flag & flag2)
        status <= 1 ;
    else if (flag1 == 0|en_1==0)
        status <= 0;   
end
 
 
//在开始计时的时候获得一个冲激信号
//这个信号被用来模拟按键被按下的动作
reg [31:0] temp1 =0;
reg impluse = 0;
always@(posedge clk)
begin
    if(status == 0)
        begin
            temp1 <= 0;
            impluse <= 0;
        end
    else if(temp1 < 10)//clk上升下降各一次,5s
        begin
            temp1 <= temp1 +1;
            impluse <= 1;
        end
    else 
        begin
            temp1 <= temp1+1;
            impluse <= 0;
        end  
end
//key_d = key_delay,是处于冲突状态时有按键被按下的延时信号,即冲突状态结束后模拟一次按键被按下的动作
always@(posedge clk)
begin
    if(impluse&en_1!=0)
    key_d = 1;
    else
    key_d = 0;
end
 
 
 
//获得1hz的信号,用来计时
reg [31:0] temp =0;
reg clk_1hz = 0;
always@(posedge clk)
begin
    if(status == 0)
        begin
            temp <= 0;
            clk_1hz <= 0;
        end
    else if(temp == 49999999)
        begin
            temp <= 0;
            clk_1hz <= 1;
        end
    else
        begin
            temp <= temp +1;
            clk_1hz <= 0;
        end
end
 
//开始计时
//计时完成后,计时器状态位复位,使能信号复位,等待下一次按键被按下
//5s复位
reg [2:0] count = 0;
always@(posedge clk_1hz or negedge status)
begin
    if(status == 0)
        begin
            count <=0;
            
            flag1 <= 1;
          
        end
    else if(count == 4)//计时秒数在此修改
        begin
            count <= 0;
            
            flag1 <= 0;
        end
    else
        begin
        count <= count + 1;
        
        flag1 <= 1;
        end
end   
 
endmodule

2.4 按键消抖

本模块新增了一个使能模块EN_ajxd,

连接top里的start,

以便用拨码开关SW0控制按键是否输入。

将前人留下的文件中btn012初始状态修改为4b'1111,

因为按键是低电平有效。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 源神爱开源
// 
// Create Date: 2024/06/04 00:00:00
// Design Name: 
// Module Name: ajxd
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


   module ajxd(
    input wire EN_ajxd,//使能,绑定start
    input clk,
    input  [3:0] btn_in,
    output [3:0] btn_out,
    input rst_n
    );
 
reg clk_20ms = 0;
reg [31:0] temp = 0;//计数器
always@(posedge clk)
begin
    if(rst_n == 0)
    begin
        temp <= 0;
        clk_20ms <= 0;
    end
     else if(rst_n == 1&temp < 999999)//20ms分频计数器
            begin
                clk_20ms <= 0;
                temp <= temp+1;
            end
     else if(rst_n == 1&temp == 999999)
        begin
            temp <= 0;
            clk_20ms <= 1;
        end
       
end    
 
reg [3:0] btn0;
reg [3:0] btn1;
reg [3:0] btn2;
initial
begin
btn0=4'b1111;
btn1=4'b1111;
btn2=4'b1111;
end
 
always@(posedge clk_20ms)
begin
if(EN_ajxd==1)
begin
btn0<=btn_in;
btn1<=btn0;
btn2<=btn1;
end
else if(EN_ajxd==0)
begin
btn0<=4'b1111;
btn1<=4'b1111;
btn2<=4'b1111;
end
end

assign btn_out=((btn0&btn1)&(~btn2) | (btn0&btn1&btn2) | ((~btn0)&btn1&btn2));
 
endmodule

2.5 8段译码动态显示

将前人留下文件中译码管位数增加到六位。该模块最为简单,未加注释。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/06/04 00:00:00
// Design Name: 源神!启动
// Module Name: seg_scan
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module seg_scan(
input  clk,
input [7:0] data_seg0,
input [7:0] data_seg1,
input [7:0] data_seg2,
input [7:0] data_seg3,
input [7:0] data_seg4,
input [7:0] data_seg5,
output reg [7:0] data_seg,
output reg [5:0] data_dig
);
//分频
	reg[24:0] clk_div_cnt=0;
	reg clk_div=0;
	always @ (posedge clk)
	begin
		if (clk_div_cnt==24999)
		begin
			clk_div<=~clk_div;
			clk_div_cnt<=0;
		end
		else 
		    clk_div_cnt<=clk_div_cnt+1;
	end
	//6进制计数器
	reg [2:0] num=0;
	always @ (posedge clk_div)
	begin
		if (num>=5)
			num=0;
		else
			num=num+1;
	end

            
always@(posedge clk)
begin
       
         case(num)
           
            0:
            begin
                data_seg<=data_seg0;
                data_dig<=6'b111110;
            end
            1:
            begin
                data_seg<=data_seg1;
                data_dig<=6'b111101;
            end
          
            2:
            begin
                data_seg<=data_seg2;
                data_dig<=6'b111011;
            end
            3:
            begin
               data_seg<=data_seg3;
               data_dig<=6'b110111;
            end
            4:
            begin
               data_seg<=data_seg4;
               data_dig<=6'b101111;
            end
           5:
           begin
               data_seg<=data_seg5;
               data_dig<=6'b011111;
           end
    
         endcase
end                      
endmodule

3.1 模块概览

3.2 免责声明

请不要完全照抄照搬代码,代码的思路仅供参考,你完全可以有自己的思想。

如该作品涉及侵权请及时联系作者进行删除下架。

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值