交通信号灯设计

**

设计说明:

**
此信号灯主要应用在主路与次路相交的交叉口处,信号灯共分为4个不同的状态,利用Moore型状态机实现,用六个灯代表主路与次路的六个信号灯(红、绿、黄),高电平时信号灯亮,低电平时灭。同时用4个数码管分别显示主路与次路当前信号状态下的剩余时间,并设置复位信号按键rst。
**

设计原理图:

**
交通信号灯设计_第1张图片
整个电路可以分为4个部分,分别是时钟模块、BCD码与二进制转换模块、状态机模块、数码管显示模块。Zero1为复位按键,cp为输入的100MHZ时钟信号,red1、yellow1、green1、red2、yellow2、green2为输出的信号灯状态,1代表亮,0代表灭。a_to_g则分别对应于数码管的显示,an则是数码管的位选端,下面分别介绍这几个模块的具体功能。

  • 时钟模块:

此模块的输入为EGO1开发板上的100MHZ时钟,对应着P17管脚,可将时钟信号进行分频,数码管显示需要200HZ的时钟信号,状态机需要1HZ的时钟信号,没有时钟信号,整个系统也将无法正常工作,时钟模块的核心代码如下所示:(时钟模块代码不需要这么长,这里粘贴的是100MHZ的大部分分频,方便后续复习)

module clk_div(
  input CP_100MHz, 
  output clk_100MHz,
  output clk_1kHz,
  output clk_200Hz,
  output clk_100Hz,
  output clk_50Hz,
  output clk_20Hz,
  output clk_10Hz,
  output clk_5Hz,
  output clk_2Hz,
  output clk_1Hz
  );

  reg [16 : 0] cnt_1kHz;
  reg [2 : 0] cnt1_200Hz;
  reg [2: 0] cnt2_200Hz;
  reg [2 : 0] cnt_100Hz;
  reg [3 : 0] cnt_50Hz;
  reg [4 : 0] cnt_20Hz;
  reg [5 : 0] cnt_10Hz;
  reg [6 : 0] cnt_5Hz;
  reg [7 : 0] cnt_2Hz;
  reg [8 : 0] cnt_1Hz;
  reg clk_1kHz_reg;
  //reg clk_200Hz_reg;
  reg clk_200Hz_reg1;
  reg clk_200Hz_reg2;
  reg clk_100Hz_reg;
  reg clk_50Hz_reg;
  reg clk_20Hz_reg;
  reg clk_10Hz_reg;
  reg clk_5Hz_reg;
  reg clk_2Hz_reg;
  reg clk_1Hz_reg;
  initial
    begin
      cnt_1kHz=0;
      cnt_100Hz=0;
      cnt_50Hz=0;
      cnt_20Hz=0;
      cnt_10Hz=0;
      cnt_5Hz=0;
      cnt_2Hz=0;
      cnt_1Hz=0;                                
      clk_1kHz_reg=0;
      clk_200Hz_reg1=0;
      clk_200Hz_reg2=0;
      clk_100Hz_reg=0;
      clk_50Hz_reg=0;
      clk_20Hz_reg=0;
      clk_10Hz_reg=0;
      clk_5Hz_reg=0;
      clk_2Hz_reg=0;
      clk_1Hz_reg=0;
    end  
  always @ (posedge CP_100MHz) 
    begin      
      if (cnt_1kHz < 17'd100000/2-1)//17'h186A0=100000
        begin
          cnt_1kHz <= cnt_1kHz + 1;
          clk_1kHz_reg <= clk_1kHz_reg;
        end
      else
        begin
          cnt_1kHz = 0;
          clk_1kHz_reg <= ~clk_1kHz_reg;        
        end
    end
  //200Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt1_200Hz < 3'd5-1)
        cnt1_200Hz <= cnt1_200Hz + 1;
      else
        cnt1_200Hz <= 0;  
    end     
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt1_200Hz < 3'd5/2)
          clk_200Hz_reg1 <= 1;
      else
          clk_200Hz_reg1 <= 0;
    end 
  always @ (negedge clk_1kHz) 
    begin      
      if (cnt2_200Hz < 3'd5-1)
        cnt2_200Hz <= cnt2_200Hz + 1;
      else
        cnt2_200Hz <= 0;  
    end      
  always @ (negedge clk_1kHz) 
    begin      
      if (cnt2_200Hz < 3'd5/2)
          clk_200Hz_reg2 <= 1;
      else
          clk_200Hz_reg2 = 0;
    end 
    assign clk_200Hz=clk_200Hz_reg1|clk_200Hz_reg2;
    //100Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt_100Hz < 4'd10/2-1)
        begin
          cnt_100Hz <= cnt_100Hz + 1;
          clk_100Hz_reg <= clk_100Hz_reg;
        end
      else
        begin
          cnt_100Hz = 0;
          clk_100Hz_reg <= ~clk_100Hz_reg;        
        end
    end     
    //50Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt_50Hz < 5'd20/2-1)
        begin
          cnt_50Hz <= cnt_50Hz + 1;
          clk_50Hz_reg <= clk_50Hz_reg;
        end
      else
        begin
          cnt_50Hz = 0;
          clk_50Hz_reg <= ~clk_50Hz_reg;        
        end
    end
    //20Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt_20Hz < 6'd50/2-1)
        begin
          cnt_20Hz <= cnt_20Hz + 1;
          clk_20Hz_reg <= clk_20Hz_reg;
        end
      else
        begin
          cnt_20Hz = 0;
          clk_20Hz_reg <= ~clk_20Hz_reg;        
        end
    end
    //10Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt_10Hz < 7'd100/2-1)
        begin
          cnt_10Hz <= cnt_10Hz + 1;
          clk_10Hz_reg <= clk_10Hz_reg;
        end
      else
        begin
          cnt_10Hz = 0;
          clk_10Hz_reg <= ~clk_10Hz_reg;        
        end
    end 
    //5Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt_5Hz < 8'd200/2-1)
        begin
          cnt_5Hz <= cnt_5Hz + 1;
          clk_5Hz_reg <= clk_5Hz_reg;
        end
      else
        begin
          cnt_5Hz = 0;
          clk_5Hz_reg <= ~clk_5Hz_reg;        
        end
    end 
    //2Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt_2Hz < 9'd500/2-1)
        begin
          cnt_2Hz <= cnt_2Hz + 1;
          clk_2Hz_reg <= clk_2Hz_reg;
        end
      else
        begin
          cnt_2Hz = 0;
          clk_2Hz_reg <= ~clk_2Hz_reg;        
        end
    end 
    //1Hz
  always @ (posedge clk_1kHz) 
    begin      
      if (cnt_1Hz < 10'd1000/2-1)
        begin
          cnt_1Hz <= cnt_1Hz + 1;
          clk_1Hz_reg <= clk_1Hz_reg;
        end
      else
        begin
          cnt_1Hz = 0;
          clk_1Hz_reg <= ~clk_1Hz_reg;        
        end
    end  
  assign clk_100MHz = CP_100MHz;  
  assign clk_1kHz = clk_1kHz_reg;
  assign clk_100Hz = clk_100Hz_reg;
  assign clk_50Hz  = clk_50Hz_reg;
  assign clk_20Hz  = clk_20Hz_reg;
  assign clk_10Hz  = clk_10Hz_reg;
  assign clk_5Hz   = clk_5Hz_reg;
  assign clk_2Hz   = clk_2Hz_reg;
  assign clk_1Hz   = clk_1Hz_reg;

endmodule

**

  • 状态机模块:*

**
此模块的输入为复位信号rst以及时钟信号clk,输出为red1,yellow1,green1;red2,
yellow2,green2。高电平时亮,低电平时灭,利用Moore型状态机实现,整个信号灯周期共分为4个状态,状态表如下图所示:
交通信号灯设计_第2张图片

利用Verilog语言实现过程如下:

module state(  input rst,clk,
                output red1,yellow1,green1,red2,yellow2,green2,
                output [7:0] counter_x,counter_y);
    reg red1,yellow1,green1,red2,yellow2,green2;
    reg [3:0] current_state,next_state;
    reg [7:0] counter,counter_x,counter_y,timeover,timeover_x,timeover_y;
    parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10, S3 = 2'b11;
    parameter time1 = 'd24, time2 = 'd4;
    //状态转换
    always @(posedge clk or negedge rst)
        if(!rst)
            begin
                current_state <= S0;
                counter <= time1;
                counter_x <= 'd24;
                counter_y <= 'd29;
               
            end
        else
            begin
                if(counter == 0)
                    begin
                        current_state <= next_state;
                        counter <= timeover;
                        counter_x <= timeover_x;
                        counter_y <= timeover_y;
                    end
                else
                    begin
                        counter <= counter - 1;
                        counter_x <= counter_x - 1;
                        counter_y <= counter_y -1;
                    end
            end
    //状态改变
    always @(current_state)
        begin
            case(current_state)
                S0:begin //横向绿灯,纵向红灯
                    red1 = 0; yellow1 = 0; green1 = 1;
                    red2 = 1; yellow2 = 0; green2 = 0;
                    next_state <= S1; timeover <= time2;
                    timeover_x <= 'd4;
                    timeover_y <= 'd4;
                    end
                S1:begin //横向黄灯,纵向红灯
                    red1 = 0; yellow1 = 1; green1 = 0;
                    red2 = 1; yellow2 = 0; green2 = 0;
                    next_state <= S2; timeover <= time1;
                    timeover_x <= 'd29;
                    timeover_y <= 'd24;
                    end
                S2:begin //横向红灯,纵向绿灯
                    red1 = 1; yellow1 = 0; green1 = 0;
                    red2 = 0; yellow2 = 0; green2 = 1;
                    next_state <= S3; timeover <= time2;
                    timeover_x <= 'd4;
                    timeover_y <= 'd4;
                    end
                S3:begin //横向红灯,纵向黄灯
                    red1 = 1; yellow1 = 0; green1 = 0;
                    red2 = 0; yellow2 = 1; green2 = 0;
                    next_state <= S1; timeover <= time1;
                    timeover_x <= 'd24;
                    timeover_y <= 'd29;
                    end
                default:begin //横向绿灯,纵向红灯
                            red1 = 0; yellow1 = 0; green1 = 1;
                            red2 = 1; yellow2 = 0; green2 = 0;
                            next_state <= S1; timeover <= time2;
                            timeover_x <= 'd4;
                            timeover_y <= 'd4;
                         end
            endcase
        end        
endmodule

  • 二进制与BCD码转换模块:

此模块的输入为状态机模块的counter_x与counter_y输出,分别表示主路与次路当前状态下所剩余的时间,但它们均是由二进制数表示的,为了让数码管可以正常显示,需要借助BCD码来实现,也就是将二进制转换成BCD码,此次设计采用大4加3移位算法,算法原理如下:

  1. 4位二进制数大于15则进位,4位BCD码大于9则进位,若使4位二进制数变为BCD码,则其大于9时加6,得到的便是其BCD码(由真值表规律可知),例如:12的二进制数为1100,它是大于9的,则1100+0110=1_0010,即为BCD码的12。
  2. 对于5位二进制,先判断高四位是否大于9,若大于9,则加6并向左移位;若小于9,则直接向左移位。例如30的二进制数为11110,高4位1111的BCD码为10101,因为其大于9,所以加6,变为1_0101,左移一位之后,得到0010_1010,新低四位的值1010大于9,所以新低4位加6得到0011_0000,即为30的BCD码。所以只要通过左移及大9加6(大于等于10加6),则可转换任意位2进制数为BCD码。
    但是该算法是移位加6,即加0110。而大4加3(0011)算法比此法占用资源少,但在算法上两者是等价的,即判断4位二进制数是否大于等于5,若大于等于5则加3,再移位;否则直接移位。例如10的二进制数0_1010
  3. 按照大9加6算法,5的二进制数为0101,左移一位之后变为1010(10),此时需要加6并左移一位,变为BCD码1_0000。即10+6=16(进1)
  4. 按照大4加3算法,5的二进制数为0101,大于4,此时需要加3并左移一位,变为BCD码1_0000。即(5+3)*2 = 16(进1),左移一位(低位补0)相当于乘以2。
  5. 8位2进制数最大能表示的数字为255,用BCD码表示,需要10位来表示。因此此模块的输入为8位二进制数,输出为10位二进制数。
    a.把二进制数左移一位;
    b.如果共移了8位,那么BCD数就在百位、十位和个位列,转换完成;
    c.如果在BCD列中,任何一个二进制数是5或比5更大,那么就在BCD列的数值加 3;
    d.返回步骤a。
    例如二进制数1111_1111到255的转换过程如下所示:
    交通信号灯设计_第3张图片交通信号灯设计_第4张图片

利用Verilog语言实现的过程如下:

module binbcd8 (
  input wire [7:0] b,
  output reg [9:0] p
);
  // 中间变量 
  reg [17:0] z;//8位输入,10位输出,共18个数
  integer i;
  always @ ( * )   //always模块中的任何一个输入信号或电平发生变化时,该语句下方的模块将被执行
    begin 
      for (i = 0; i <=17; i = i + 1)
        z[i] = 0;
      z[10:3] = b;   // 向左移3位,8421码,左移两位均不会大于等于5
      repeat (5)                           // 重复5次
        begin 
          if (z[11:8] > 4)                     // 如果个位大于4
            z[11:8] = z[11:8] +3;              // 加3
          if (z[15:12] > 4)                     // 如果十位大于4 
            z[15:12] = z[15:12] +3;            // 加3
          z[17:1] = z[16:0];                   // 左移一位
        end
    p = z[17:8];                          // BCD 
    end
endmodule
  • 数码管显示模块:

此模块的输入为二进制转换为BCD码模块的输出,用来使数码管正确的显示十进制数,也就是此次实验设计中的各个信号灯所剩余的时间,利用Verilog语言实现的过程如下:

module hx7seg(
  input wire [11:0] x,//4的倍数,12位
  input wire cclk,//保证有一个足够的频率。一般工作在200HZ
  input wire clr,//清零端
  output reg [6:0] a_to_g,
  output reg [1:0] an//位选端,控制数码管的选择
);
  reg s;
  reg [3:0] digit;

// 2-位计数器
  always @ (posedge cclk or posedge clr)
    begin 
      if (clr ==1)//高电平清零
          s <= 0;
      else
          s <= s + 1;
    end

// Quad 4-to-1 MUX: mux44
  always @ ( * )//always模块中的任何一个输入信号或电平发生变化时,该语句下方的模块将被执行。
    case (s)//本项目中的所有时间均是两位数,因此s有两种状态即可
      0: digit = x[3:0];
      1: digit = x[7:4];
      default: digit = x[11:8];
    endcase
    
// 选择数码管
  always @ ( * )
    begin 
      an = 2'b00;  
      an[s] = 1;
    end

// 7段解码器:hex7seg
    always @ ( * )
      case (digit)
        0: a_to_g = 7'b1111110;
        1: a_to_g = 7'b0110000;
        2: a_to_g = 7'b1101101;
        3: a_to_g = 7'b1111001;
        4: a_to_g = 7'b0110011;
        5: a_to_g = 7'b1011011;
        6: a_to_g = 7'b1011111;
        7: a_to_g = 7'b1110000;
        8: a_to_g = 7'b1111111;
        9: a_to_g = 7'b1111011;
        'hA: a_to_g = 7'b1110111;
        'hB: a_to_g = 7'b0011111;
        'hC: a_to_g = 7'b1001110;
        'hD: a_to_g = 7'b0111101;
        'hE: a_to_g = 7'b1001111;
        'hF: a_to_g = 7'b1000111;
        default: a_to_g = 7'b0000000;  // 空白
      endcase
endmodule
  • 引脚约束:
set_property IOSTANDARD LVCMOS33 [get_ports zero1]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a_to_g1[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an0[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an1[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {an1[0]}]
set_property PACKAGE_PIN P5 [get_ports zero1]
set_property PACKAGE_PIN P4 [get_ports zero2]
set_property IOSTANDARD LVCMOS33 [get_ports cp]
set_property IOSTANDARD LVCMOS33 [get_ports green1]
set_property IOSTANDARD LVCMOS33 [get_ports red1]
set_property IOSTANDARD LVCMOS33 [get_ports green2]
set_property IOSTANDARD LVCMOS33 [get_ports red2]
set_property IOSTANDARD LVCMOS33 [get_ports yellow2]
set_property IOSTANDARD LVCMOS33 [get_ports yellow1]
set_property IOSTANDARD LVCMOS33 [get_ports zero2]
set_property PACKAGE_PIN P17 [get_ports cp]
set_property PACKAGE_PIN J3 [get_ports red1]
set_property PACKAGE_PIN J2 [get_ports yellow1]
set_property PACKAGE_PIN K2 [get_ports green1]
set_property PACKAGE_PIN L1 [get_ports red2]
set_property PACKAGE_PIN M1 [get_ports yellow2]
set_property PACKAGE_PIN K3 [get_ports green2]
set_property PACKAGE_PIN C1 [get_ports {an0[1]}]
set_property PACKAGE_PIN H1 [get_ports {an0[0]}]
set_property PACKAGE_PIN E1 [get_ports {an1[1]}]
set_property PACKAGE_PIN G6 [get_ports {an1[0]}]
set_property PACKAGE_PIN B4 [get_ports {a_to_g0[6]}]
set_property PACKAGE_PIN A4 [get_ports {a_to_g0[5]}]
set_property PACKAGE_PIN A3 [get_ports {a_to_g0[4]}]
set_property PACKAGE_PIN B1 [get_ports {a_to_g0[3]}]
set_property PACKAGE_PIN A1 [get_ports {a_to_g0[2]}]
set_property PACKAGE_PIN B3 [get_ports {a_to_g0[1]}]
set_property PACKAGE_PIN B2 [get_ports {a_to_g0[0]}]
set_property PACKAGE_PIN D4 [get_ports {a_to_g1[6]}]
set_property PACKAGE_PIN E3 [get_ports {a_to_g1[5]}]
set_property PACKAGE_PIN D3 [get_ports {a_to_g1[4]}]
set_property PACKAGE_PIN F4 [get_ports {a_to_g1[3]}]
set_property PACKAGE_PIN F3 [get_ports {a_to_g1[2]}]
set_property PACKAGE_PIN E2 [get_ports {a_to_g1[1]}]
set_property PACKAGE_PIN D2 [get_ports {a_to_g1[0]}]
  • 效果图展示:
    交通信号灯设计_第5张图片
  • 视频展示:

视频链接如下所示:
视频展示

项目在Vivado运行一切正常,实验所用设备为EGO1开发板,源文件链接如下:(bit流文件与记忆文件均已生成,在traffic文件中,下载后可正常测试)

  • 链接

百度网盘链接分享
提取码:0000
–来自百度网盘超级会员V4的分享

首次blog,多多关照!!!

你可能感兴趣的:(Verilog,单片机,fpga开发,stm32)