Verilog设计实例(二):交通信号灯设计实例

前言

本文为Verilog实例开发的第二弹,缺少Verilog代码练手或者有些生疏的可以在这里参考一些设计实例进行练习。
本系列导航:
Verilog设计实例(一):自动售货机设计实例

交通信号灯设计实例

设计一个交通灯控制电路,红灯30s后转为绿灯。共x,y方向两组交通灯,每组红绿灯各一个,红灯亮30s,绿灯亮30s,设系统时钟频率为50MHz,要求用数码管显示计时结果。

状态转换图

Verilog设计实例(二):交通信号灯设计实例_第1张图片

实现框架

Verilog设计实例(二):交通信号灯设计实例_第2张图片
信号灯结构框架如图所示,需要实现的模块有分频器、计数器、BCD码以及译码器,还包括状态转换和状态输出部分。

Verilog实现

定义模块和IO端口

`timescale 1ns / 1ps

module trafficlight(
    clk_1Hz,
    rst,
    ledR_H,
    ledG_H,
    ledR_V,
    ledG_V,
    seg7_VH, //数码管显示-高位
    seg7_VL, //数码管显示-低位
    led15    //状态指示
    );
    
    parameter S1 = 1;//x方向红灯亮,y方向绿灯亮
    parameter S2 = 0;//x方向绿灯亮,y方向红灯亮
    input clk_50M,rst;
    output reg ledG_H,ledG_V,ledR_H,ledR_V;
    output wire [6:0] seg7_VH;
    output wire [6:0] seg7_VL;
    output wire led15;

实现分频器

reg clk_1Hz;    //1Hz的分频器
reg [31:0] cnt_1Hz;
always@(posedge clk_50M or negedge rst)
begin
    if(!rst)
    begin
        cnt_1Hz <= 1;
        clk_1Hz <= 1;
    end
    else
    begin
        if(cnt_1Hz>=25000000)
            begin
            cnt_1Hz <= 1;
            clk_1Hz <= ~clk_1Hz;
            end
        else
        cnt_1Hz <= cnt_1Hz + 1;
    end
end

实现计数器

reg [7:0] cnt30;    //计数器
always @(posedge clk_1Hz or negedge rst) 
begin
    if(!rst)
    begin
        cnt30 <= 0;
    end
    else
    begin
        if (cnt30>=30) 
        begin
            
            cnt30 <= 0;
        end
        else
            cnt30 <= cnt30 + 1;
    end
end

实现BCD码转换

reg [7:0] cntDis;   //BCD码转换
always @(posedge clk_50M) 
begin
    if(cnt30 > 29)
    begin
        cntDis[7:4] <= 3;
        cntDis[3:0] <= cnt30-30;
    end
    else if (cnt30 > 19) 
    begin
        cntDis[7:4] <= 2;
        cntDis[3:0] <= cnt30-20;
    end
    else if (cnt30 > 9) 
    begin
        cntDis[7:4] <= 1;
        cntDis[3:0] <= cnt30-10;
    end
    else
        cntDis <= cnt30;
    
end

调用数码管译码器

数码管译码器实现(SEG7_LUT.v)
module SEG7_LUT (
  input      [3:0] iDIG,
  output reg [6:0] oSEG
);

always@(iDIG)
begin
  case(iDIG)
    4'h1: oSEG = 7'b1111001;  // ---t----
    4'h2: oSEG = 7'b0100100;  // |      |
    4'h3: oSEG = 7'b0110000;  // lt     rt
    4'h4: oSEG = 7'b0011001;  // |      |
    4'h5: oSEG = 7'b0010010;  // ---m----
    4'h6: oSEG = 7'b0000010;  // |      |
    4'h7: oSEG = 7'b1111000;  // lb     rb
    4'h8: oSEG = 7'b0000000;  // |      |
    4'h9: oSEG = 7'b0011000;  // ---b----
    4'ha: oSEG = 7'b0001000;
    4'hb: oSEG = 7'b0000011;  
    4'hc: oSEG = 7'b1000110;  
    4'hd: oSEG = 7'b0100001;  
    4'he: oSEG = 7'b0000110;  
    4'hf: oSEG = 7'b0001110;  
    default: oSEG = 7'b1000000;  
  endcase                    
end                           
                              
endmodule
调用译码器实例
SEG7_LUT hex1(cntDis[3:0], seg7_VL[6:0]);
SEG7_LUT hex2(cntDis[7:4], seg7_VH[6:0]);

实现状态机

//状态转换
reg state;
always @(posedge clk_1Hz) 
begin
    case(state)
    S1:
    if (cnt30 >= 30)    state <= S2;
    S2:
    if (cnt30 >= 30)    state <= S1;
    default:
        state <= S1;
    endcase
end

always @(posedge clk_50M or negedge rst) 
begin
    if(!rst)
    begin
        ledG_H <= 0;
        ledG_V <= 0;
        ledR_H <= 0;
        ledR_V <= 0;
    end
    else
    begin
        case(state)
        S1: //横向红灯亮,纵向绿灯亮
        begin
            ledG_H <= 0;
            ledG_V <= 1;
            ledR_H <= 1;
            ledR_V <= 0;
        end
        S2: //横向绿灯,纵向红灯
        begin
            ledG_H <= 1;
            ledG_V <= 0;
            ledR_H <= 0;
            ledR_V <= 1;
        end
        default:
        begin
            ledG_H <= 0;
            ledG_V <= 0;
            ledR_H <= 0;
            ledR_V <= 0;
        end
        endcase
    end
end
assign led15 = state;   //状态指示

简单的TestBench编写

这里我没上板子跑,所以没用50MHz的时钟,直接给定了1Hz的时钟输入,有需要的可以自行修改。

module TL_tb();

                    reg clk_1Hz;
                    reg rst;
                    wire ledR_H;
                    wire ledG_H;
                    wire ledR_V;
                    wire ledG_V;
                    wire [6:0] seg7_VH; //数码管显示-高位
                    wire [6:0] seg7_VL; //数码管显示-低位
                    wire led15;    //状态指示



trafficlight tl1(
                    .clk_1Hz(clk_1Hz),
                    .rst(rst),
                    .ledR_H(ledR_H),
                    .ledG_H(ledG_H),
                    .ledR_V(ledR_V),
                    .ledG_V(ledG_V),
                    .seg7_VH(seg7_VH),
                    .seg7_VL(seg7_VL),
                    .led15(led15)
    );
initial begin
    clk_1Hz = 0;
    rst = 0;
    #20;rst = 1;
    //vivado中默认跑1000ns
end

always #5 clk_1Hz = ~clk_1Hz;

endmodule

测试波形

波形中,复位信号失效后,每三十个时钟周期,横向和纵向红绿灯的显示会交替。
Verilog设计实例(二):交通信号灯设计实例_第3张图片

总结

这是一个简单的红绿灯交替的例子,在此基础上,还可以添加黄灯状态,感兴趣的朋友可以自行尝试。本实例在时序逻辑电路的基础上还涉及到了分频器和BCD码、数码管等的实现,是一个比较典型的学习例子。

你可能感兴趣的:(fpga开发)