基于高云FPGA开发板的十字路口交通灯

一、实验题目:

结合高云MiniStar_Nano EDA开发板,完成设计十字路口交通信号灯控制系统。

1.要求该交通灯为三色灯控制器,红灯亮表示禁止通行,绿灯亮表示允许通行,黄灯亮表示要求压线车辆快速穿越。主干道和次干道灯的亮灭时序按照交通规则设置。

2.可以调整主干道和次干道绿灯和红灯亮的时间(必须是两位数)。

3.能用倒计时显示时间。

4.具有红绿灯手动控制功能。

二、实验原理:

此处使用开发板上的三色灯充当交通灯的显示。由于开发板上接口数量不太充足,因此仅使用两组三色灯以演示主干道和辅道的交通灯的亮灭顺序。

首先是交通灯的显示逻辑。此处共有四种显示状态:主干道红灯辅道绿灯;主干道红灯辅道黄灯;主干道绿灯辅道红灯以及主干道黄灯辅道红灯。交通灯会沿着这些状态依次点亮并在上述四种状态中不断循环。

其次是数码管显示逻辑。此处仅有四位数码管,正好匹配两组三色灯的显示,当交通灯处在某一状态中,需要实时倒计时显示当前状态的剩余时长,故需要匹配计时与数码管显示的内容。

最后是其他的一些模块。如数码管位选模块,倒计时模块和分频模块,由于这些模块在之前的实验中已经使用过,故不在此进行说明,而在下方代码中进行一定说明。

三、实验方案:

下方为模块介绍:

freq_div:分频模块,对开发板的时钟脉冲进行分频,产生所需的时钟信号。此处需要产生1s的时钟脉冲,则分频比应与开发板的时钟频率一致。

`timescale 1ns/1ns

module freq_div#(
    parameter DIV_RATE_2N = 11
)(
    input   clk_in_i,
    input   rst_i,
    output  reg clk_out_o
);

    localparam SIM_DELAY = 1;
    localparam CNT_WIDTH = $clog2(DIV_RATE_2N);

    reg [CNT_WIDTH-1:0] counter;
    always  @(posedge clk_in_i or posedge rst_i) begin
        if(rst_i) begin//高电平置零
            counter     <= #SIM_DELAY 0;
            clk_out_o   <= #SIM_DELAY 0;
        end else begin //低电平计数器加一
            if(counter < DIV_RATE_2N/2-1) begin
                counter     <= #SIM_DELAY counter + 1;
            end else begin //完成一轮计数后置零进行循环计数
                counter     <= #SIM_DELAY 0;
                clk_out_o   <= #SIM_DELAY ~clk_out_o;
            end
        end
    end
endmodule

mux_4x4:位选模块,判断当前显示为哪一位数码管,与seg_ctrl模块和shift_reg模块组合实现数码管显示逻辑

`timescale 1ns/1ns

module mux_4x4(
    input       [3:0]   index_i,
    input       [15:0]   data_i,
    output  reg [3:0]   data_o
);

    always@(*) begin
        case(index_i[3:0])
        4'b0001:data_o = data_i[3:0];
        4'b0010:data_o = data_i[7:4];
        4'b0100:data_o = data_i[11:8];
        4'b1000:data_o = data_i[15:12];
        default:data_o = 4'h0;
        endcase
   end

endmodule

seg_ctrl:数码管控制模块,实例化mux_4x4模块与shift_reg模块,实现数码管显示逻辑

`timescale 1ns/1ns

module seg_ctrl #(
    parameter DIG_WIDTH = 4
)(
    input                   clk,
    input                   rst,
    input [DIG_WIDTH*4-1:0] data_i,
    output[DIG_WIDTH-1:0]   dig_o,
    output[7:0]             seg_o
);

    localparam CLK_IN_FREQ = 27000000;

    wire clk_100Hz;

    freq_div #(
        .DIV_RATE_2N (200000)
    )freq_div_i(
        .clk_in_i   (clk),
        .rst_i      (rst),
        .clk_out_o  (clk_100Hz)
    );

    shift_reg #(
        .WIDTH      (4),
        .INIT_VALUE (4'h1)
    )shift_reg_i(
        .clk        (clk_100Hz),
        .rst        (rst),
        .load_i     (1'b0),
        .load_data_i(4'h0),
        .reg_in_i   (dig_o[3]),
        .reg_out_o  (dig_o[3:0])
    );

    wire [3:0]data_sel;
    mux_4x4 mux_4x4_i(
        .index_i    (dig_o[3:0]),
        .data_i     (data_i[15:0]),
        .data_o     (data_sel[3:0])
    );
    seg_decode seg_decode_i(
        .data_i     (data_sel[3:0]),
        .seg_code_o (seg_o[7:0])
);
endmodule

shift_reg:移位模块,随着mux_4x4模块的移动实现不同数字的显示。与seg_ctrl模块,mux_4x4模块组合实现数码管显示逻辑。

`timescale 1ns/1ns

module shift_reg #(
    parameter DIRECTION = "LEFT",
    parameter WIDTH     = 9,
    parameter INIT_VALUE= 9'h1
)(
    input                  clk,
    input                  rst,
    input                  load_i,
    input       [WIDTH-1:0]load_data_i,
    input                  reg_in_i,
    output  reg [WIDTH-1:0]reg_out_o
);

    localparam SIM_DELAY = 1;

    always @(posedge clk or posedge rst or posedge load_i) begin
        if(rst) begin //时钟信号复位
            reg_out_o <= #SIM_DELAY INIT_VALUE;
        end else if(load_i) begin //对reg_out_o赋初值,但此处并未使用该方法赋值,而是在下方的else语句中赋值
            reg_out_o <= #SIM_DELAY load_data_i;
            end else begin
                reg_out_o <= #SIM_DELAY DIRECTION=="LEFT"?//左移
                        {reg_out_o[WIDTH-2:0],reg_in_i}:              
                        {reg_in_i,reg_out_o[WIDTH-1:1]};   
        end
    end

endmodule

seg_decode:译码模块,将送入的信号进行译码,实现数码管显示对应的数字。

module seg_decode #(
    parameter INVERSE = 1'b1
)(
    input   [3:0] data_i,
    output  [7:0] seg_code_o
);
    reg [7:0] seg_code;
    
    assign seg_code_o[7:0] = INVERSE ?~seg_code[7:0] : seg_code[7:0];
          
        always @(*) begin
            case(data_i[3:0])
            4'h0:   seg_code = 8'h3f;
            4'h1:   seg_code = 8'h06;
            4'h2:   seg_code = 8'h5b;
            4'h3:   seg_code = 8'h4f;
            4'h4:   seg_code = 8'h66;
            4'h5:   seg_code = 8'h6d;
            4'h6:   seg_code = 8'h7d;
            4'h7:   seg_code = 8'h07;
            4'h8:   seg_code = 8'h7f;
            4'h9:   seg_code = 8'h6f;
            default:seg_code = 8'h00;
            endcase
        end
endmodule

sub_counter:自减器模块,基于循环主体产生自减信号,如此处循环分为四个部分,总时长为66s,故产生自66开始自减的计数信号

`timescale 1ns/1ns

module sub_counter#(
    parameter WIDTH    = 8,
    parameter DATA_MAX = 256
)(
    input                clk,
    input                rst,
    input           [1:0]button,
    output reg           cout_o,
    output reg [WIDTH-1:0]count_o
);

    localparam SIM_DELAY = 1;

    always @(posedge clk or posedge rst) begin
        if(rst) begin
            cout_o      <=#SIM_DELAY 0;
            count_o     <=#SIM_DELAY DATA_MAX;      
        end else if(count_o >0) begin    
                cout_o      <=#SIM_DELAY 1'b0;
                count_o     <=#SIM_DELAY count_o - 1;
            end else begin
                cout_o      <=#SIM_DELAY 1'b1;
                count_o     <=#SIM_DELAY DATA_MAX;
        end
    end
endmodule

time_all:控制显示时间的状态机模块,通过总循环的自减信号来判断当前状态的时长,并进行状态跳转。

`timescale 1ns/1ns

module time_all(
    input      [7:0]time_all,
    input           clk,
    input           rst,
    output reg[15:0]a
);
always@(posedge rst or posedge clk)begin
    if(rst)begin
        a[3:0]      <=0;//主道个位
        a[7:4]      <=0;//主道十位
        a[11:8]     <=0;//辅道个位
        a[15:12]    <=0;//辅道十位
    end
    else if(time_all>33)begin//主道红灯,辅道绿灯
        a[3:0]      <=(time_all+3-33)%10;
        a[7:4]      <=(time_all+3-33)/10;
        a[11:8]     <=(time_all-33)%10;
        a[15:12]    <=(time_all-33)/10;
    end
    else if((time_all>30)&&(time_all<33))begin//主道红灯,辅道黄灯
        a[3:0]      <=(time_all+3-33)%10;
        a[7:4]      <=(time_all+3-33)/10;
        a[11:8]     <=(time_all-30)%10;
        a[15:12]    <=0;
    end
    else if((time_all>3)&&(time_all<30))begin//主道绿灯,辅道红灯
        a[3:0]      <=(time_all-3)%10;
        a[7:4]      <=(time_all-3)/10;
        a[11:8]     <=(time_all)%10;
        a[15:12]    <=(time_all)/10; 
    end
    else if((time_all>0)&&(time_all<3))begin//主道黄灯,辅道红灯
        a[3:0]      <=(time_all)%10;
        a[7:4]      <=(time_all)/10;
        a[11:8]     <=(time_all)%10;
        a[15:12]    <=0; 
    end
end
endmodule

traffic_led_demo:主函数模块,通过例化模块完成交通灯功能。

`timescale 1ns/1ns

module traffic_led_demo(
    input             clk,
    input           rst_n,
    input     [1:0]button,
    output     [3:0]dig_o,
    output     [7:0]seg_o,
    output     [5:0]led_o
);

    localparam CLK_FREQ_Hz=27000000;
    wire rst=~rst_n;
    wire clk_out_o;
    wire [7:0]time_all;
    wire[15:0]a;
    wire [1:0]c;
    freq_div#(
        .DIV_RATE_2N(27000000)
    )freq_div_i(
        .clk_in_i   (clk),
        .rst_i      (rst),
        .clk_out_o  (clk_out_o)
    );
    sub_counter#(
        .WIDTH      (8),
        .DATA_MAX   (60)
    )sub_counter_i(
         .button     (button[1:0]),
         .clk        (clk_out_o),
         .rst        (rst),
         .cout_o     (),
         .count_o    (time_all[7:0])
    );
    time_all time_all_i(
        .time_all   (time_all[7:0]),
        .clk        (clk_out_o),
        .rst        (rst),
        .a          (a)
);
    state_ctrl state_ctrl_i(
        .clk        (clk),
        .rst        (rst),
        .time_all   (time_all[7:0]),
        .led_o      (led_o[5:0])
);
    seg_ctrl#(
        .DIG_WIDTH  (4)
    )seg_ctrl_i(
        .clk        (clk),
        .rst        (rst),
        .data_i     (a[15:0]),
        .dig_o      (dig_o[3:0]),
        .seg_o      (seg_o[7:0])
);
endmodule

state_ctrl:控制led灯点亮状态的状态机模块,通过总循环的自减信号来判断当前状态的点亮的led灯,注意此处led灯点亮信号为0信号而不是1信号。

`timescale 1ns/1ns

module state_ctrl(
    input            clk,
    input            rst,
    input  [7:0]time_all,
    output reg[5:0]led_o
);
    parameter ST0 =2'h0;
    parameter ST1 =2'h1;
    parameter ST2 =2'h2;
    parameter ST3 =2'h3;

    reg [1:0]curr_state;

    always@(posedge clk or posedge rst)
        if(rst) 
             curr_state<=ST0;
        else  //灯的顺序为红黄绿 
            case(curr_state)  
            ST0:begin
            if(time_all>33)begin//主道红灯,辅道绿灯
                led_o<=6'b011110;
                curr_state<=ST0;
            end else begin
                curr_state<=ST1;
            end
        end
            ST1:begin
            if((time_all>30)&&(time_all<33))begin//主道红灯,辅道黄灯
                led_o<=6'b011101;
                curr_state<=ST1;
            end else begin
                curr_state<=ST2;
            end
        end
            ST2:begin
            if((time_all>3)&&(time_all<30))begin//主道绿灯,辅道红灯
                led_o<=6'b110011;
                curr_state<=ST2;
            end else begin
                curr_state<=ST3;
            end
        end
            ST3:begin
            if((time_all>0)&&(time_all<3))begin//主道黄灯,辅道红灯
                led_o<=6'b101011;
                curr_state<=ST3;
            end else begin
                curr_state<=ST0;
            end
        end
     endcase
endmodule

四、实验结果:

        下方为实验结果截图,此处由于三色灯中没有黄灯,故使用蓝灯代替共工实现了功能一和功能三

e5f36d47f21a4ca3a886eb81d59a0543.png

524604b2341945499292ea847645e108.png

8a19ba75699e4738a142e4c03622a305.png

b4c32eebb9c64811a17debac32b2f62a.png

五、调试中遇到的问题:

        首先遇到的问题便是三色LED灯不受控制的点亮,该问题是由于三色LED灯的点亮是0信号而不是1信号,在修改完对应的显示逻辑后,进而实现了可控点亮。

        其次是数码管显示时会持续显示某一个数字而不发生变化,这是由于模块在实例化时所输入的时钟信号不匹配导致部分模块实例化时出现问题,进而出现数码管显示问题。在修改输入时钟信号后交通灯正常运行

        最后是交通灯逻辑问题。每当交通灯完成第四个状态时重新回到第一个状态时,数码管显示的数字并非对应的式子,而是总时长的倒计时显示。这是由于状态机显示模块的第四个模块对应的状态的逻辑判定中有一定问题,在增加了限制条件后得以消除该现象

六、优化思路:

        在time_all模块中增加常量a初值为60,并在逻辑中将判定值修改为a的倍数,同时在顶层模块中的time_all实例化中增加常量初始化,之后就可以通过修改总时长来修改交通灯中每个灯点亮的时长,提高模块的适用性。

        如果要进一步提高适用性,可以重新写一个模块,将交通灯循环的初值赋予放在模块中,并增加按钮输入实现按一下加一秒或减一秒,进而实现功能二

        对于功能四,可以在time_all的判定中增加两个判定条件,当按钮按下时将状态转移到两个状态的起始状态,但实际编写时发现这样会导致数码管显示出现问题,会卡在某个状态而且不跳转。初步判断是因为状态判断时的逻辑不正确,检测按钮状态应该是先按下在抬起的逻辑,即判断对应端口由低电平跳转到高电平在跳转到低电平,只有这样才可以完成红绿灯的功能三。

 

 

 

 

 

 

 

 

设计任务 (一)有一条主干道和一条支干道的汇合点形成十字交叉路口,主干道为东西向,支干道为南北向。为确保车辆安全,迅速地通行,在交叉道口的每个入口处设置了红,绿,黄3色信号灯。 (二)要求: (1)主干道绿灯亮时,支干道红灯亮,反之亦然,两者交替允许通行,主干道每次放行55s,支干道每次放行25s。每次由绿灯变为红灯的过程中,黄灯亮5s作为过渡。 (2)能实现正常的倒计时显示功能。 (3)能实现总体清零功能:计数器由初始状态开始计数,对应状态的指示灯亮。 (4)能实现特殊状态的功能显示:进入特殊状态时,东西、南北路口均显示红灯状态。 Verilog HDL作为一种规范的硬件描述语言,被广泛应用于电路的设计中。利用Verilog的设计描述可被不同的工具(包括验证仿真、时序分析、测试分析以及综合)所支持,可用不同器件来实现。而可编程器件的广泛应用,为数字系统的设计带来了极大的灵活性。由于可编程器件可以通过软件编程对硬件的结构和工作方式进行重构,使得硬件的设计可以如同软件设计那样快捷方便。 本文用Verilog HDL设计了一个交通灯控制系统,主干道交通灯按绿-黄-红变化,支干道交通灯按红-绿-黄变化。设计采用了自顶向下的设计方法,首先根据功能将电路分为div(包括div1和div2)、counter、controller、Fenwei(包括Fenwei1和Fenwei2)、demx模块,然后针对每个模块进行开发。利用QuartusII6.0中的仿真工具对每个模块进行仿真,保证功能正确。在此基础上,将所有模块连接起来,形成完整的设计,并用QuartusII6.0中的仿真工具再次仿真。仿真结果表明功能正确,符合设计要求。最后利用QuartusII6.0将程序下载到Altera FPGA芯片EP1C3T144C8中,实际结果表明电路工作正常,满足了设计要求。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jerry独爱电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值