jjzjj

FPGA双模式交通灯的设计

绝望ove3r 2024-06-27 原文

一、设计要求

1.模块一:模拟十字路口主干道与辅道灯光变化情况:

a.FPGA板上电后,控制显示的开关拨至高电平,进行五秒的倒计时,然后出现第一次红绿灯转换;

b.接下来会根据所处不同的状态有不同的倒计时,实现一个四状态的红绿灯转换,最大限度模拟了实际路况。

 

具体实现要求为:

(1)主路按照绿灯,黄灯,红灯的顺序循环闪烁;                                 

(2)支路按照红灯,绿灯,黄灯的顺序循环闪烁;

(3)两路的灯光状态转换要分别独立按照次序完成且要有各自的计时显示;

(4)可以任意调整计时时间,在计时结束后自动进入下一循环。                                          

2.模块二:模拟双行道间歇式红绿灯及行人通过的情况:

a.FPGA板上电后,控制显示的开关拨至低电平,输出数码管与LED灯进入此模块,自然情况下一直为绿灯,且数码管不会改变;

b.如果行人按键为高电平时,进入倒计时状态,结束后会更新数码管数字且LED灯变为红灯;

c.等待行人通过后,重新进入自然状态,等待下一次行人按键的触发。

 

具体实现要求为:

(1)没有行人时,两个灯均为绿灯,数码管维持数字9;                                 

(2)有行人按下按键后,此时还是绿灯,数码管进行9秒的倒计时;

(3)倒计时结束后,两个灯均变为红灯,数码管进行9秒的倒计时;

(4)倒计时结束后,恢复触发之前的状态,并检测按键的高低电平判断是否进行下一次状态转换。 

二、设计思路

整体思路:

        首先,要实现不同状态的转换,需要通过状态机来实现,这在两个模块里都是通用的。然后,围绕着状态机的设置,我们可以依据具体的数码管显示和三色灯的颜色变化进行逻辑结构的设计。同时,分频电路和数码管转换部分在将逻辑电路实现在FPGA上是不可缺少的一部分。而对于两个交通灯模块,需要进一步具体分析。

        对于自动计时实现状态转换的十字交叉路口交通灯电路,我们设计的重点是两个方向交通灯的红、绿、黄三种颜色的独立转换,从而更好的模拟实际路况。由于硬件的限制,所以在实现三色灯状态转换的同时,数码管显示状态转换间隔的时间。而对于如何控制每一个灯下一状态是维持原状还是跳转这一问题,我们使用一个计数器模块进行控制,当其满足跳转要求方可进入下一状态。

        对于行人主动触发的主干道双向交通灯电路,我们在状态机的变换上相较于上一程序更加简单,只有红和绿两种模式;但是,添加了一个主动触发的信号,作为自然状态的唯一触发条件,用来控制状态机的跳变。而计时器模块充当了过程中的触发条件,当满足跳转要求进入下一状态,同时灯光也会变化。而经过一个周期后,需要判断行人是否再次触发了信号,如果是,进入新的一轮周期;如果不是,此模块回到并保持自然状态。

        最后,为了代码的整洁性与使用者的方便性,故设计顶层模块,调用这两个子模块,通过一个信号控制两种模式的转换显示,但是两种交通灯模块在同时运行,只是分时输出结果。

 

分频思路:

第一种思路:

(1)复位信号时,计数器清零,输出时钟置低电平。
(2)计数器到 N/2- 1 时,时钟翻转,计数器继续累加
(3)计数器到 N-1 时,时钟翻转,计数器清零
(4)计数器在其他值是输出保持,计数器继续累加
这种思路的思想是,在计数器为 0~N/2-1时,输出时钟保持一个状态,N/2~N-1时保持另一个状态。

第二种思路:
(1)复位信号时,计数器清零,输出时钟置低电平。
(2)计数器到 N/2-1 时,时钟翻转,计数器清零。
(3)计数器为其余值时,继续保持加一。
这种思路的思想是,在计数器为 0~N/2-1 时,输出时钟保持一个状态 ,在计数器为 下一个0~N/2-1时保持另一个状态。

具体思路:

        秒脉冲发生器是该系统中计数器模块、状态转换的标准时钟信号源,计数器模块主要为状态转换模块提供两个转换信号,译码显示模块将计数器的计数状态译码并通过数码管进行显示,状态转换模块是系统的主要部分,由它控制红绿灯的状态转换,译码模块将状态转换模块所处状态进行译码,并驱动红绿灯进行状态显示;

        其中红绿状态正常输出,黄灯状态由红加绿输出显示,数码管具体显示如下:

        S1:主路绿灯点亮,支路红灯点亮,持续5s;

        S2:主路黄灯点亮,支路红灯点亮,持续6s;

        S3:主路红灯点亮,支路绿灯点亮,持续4s;

        S4:主路红灯点亮,支路黄灯点亮,持续7s;

 

三、各模块具体功能分析

1、分频模块

        由于系统自带的时钟信号频率偏高,故需要通过分频的方式得到满足实际需求的频率,输入clk,输出满足要求的clk1。具体程序如下:

 

2、traffic模块

        此模块实现了自动计时的十字交叉路口交通灯电路,作为子模块也需要调用分频器模块作为驱动的输入,包括状态机的转换和数码管电路与三色灯的输出。具体程序如下:

 

 

3、button模块

        此模块实现了行人触发的双直行路口交通灯电路,作为子模块也需要调用分频器模块作为驱动的输入,包括行人触发信号的转换、状态机的转换和数码管电路与三色灯的输出。具体程序如下:

 

 

 

4、top模块

        此模块作为顶层模块,需要调用分频器模块、traffic模块和button模块,通过层次化设计,极大的简化了程序,同时又保证了使用者的良好体验。具体代码如下:

 

 

四、Verilog源程序

完整代码如下所示:

顶层模块

module top(clk,rst_n,M,T,out,seg_led);

    input clk;

    input rst_n;  //清零信号

    input M;  //button模块中的行人触发按键

    input T;  //模式转换按键

    output reg[5:0] out;  //两个三色灯的输出

    output reg[8:0] seg_led;  //数码管的输出

   

    wire [8:0] seg_led1,seg_led2;  //线网类型变量

    wire [5:0] out1,out2;

   

    //时钟信号分频

    divider    #(.N(6000000),.WIDTH(24)) divider1

           (

              .clk(clk),

              .rst(rst_n),

              .clk_out(clk1)

           );

   

    //traffic模块的调用

    traffic traffic1

       (

           .clk(clk),

           .rst_n(rst_n),

           .out(out1),

           .seg_led(seg_led1)

       );

   

    //button模块的调用

    button button1

       (

           .clk(clk),

           .rst_n(rst_n),

           .M(M),

           .out(out2),

           .seg_led(seg_led2)

       );

   

    //外部按键控制输出两种模式的显示

    always @ (*) begin

       if(T) begin

           out=out1;

           seg_led=seg_led1;

           end

       else begin

           out=out2;

           seg_led=seg_led2;

           end

       end

    endmodule

分频器模块

module divider(

inout clk,

input rst,

output reg clk_out

);

parameter N=6000000,WIDTH=24;

reg [WIDTH-1:0] cnt;

always @(posedge(clk),negedge(rst)) begin

    if(rst==1'b0)

       cnt<=24'b0;

    else if (cnt==N-1)

       cnt<=24'b0;

     else

       cnt<=cnt+1;

end

always @(posedge(clk),negedge(rst)) begin

    if(rst==1'b0)

       clk_out<=1'b0;

     else if(cnt==N-1)

       clk_out<=~clk_out;

     else

       clk_out<=clk_out;

end

endmodule

十字路口交通灯模块

module traffic(clk,rst_n,out,seg_led);

    input clk,rst_n;

    output reg[5:0] out;

    output [8:0] seg_led;  //高9位为高位数码管,低9位为低位数码管

    reg[1:0] current_state,next_state;

    reg [8:0] seg[9:0];

    reg[2:0] timecout;  //timecout作为时间计数标志,

    //为1代表之前持续的状态结束,进入下一状态,不为1代表之前持续的状态未结束,维持此状态

    parameter s0=2'b00,s1=2'b01,s2=2'b10,s3=2'b11;  //状态机的状态编码

    parameter time_s0=3'b101,time_s1=3'b110,time_s2=3'b100,time_s3=3'b111;  //一个周期的4种时间间隔

    parameter led_s0=6'b101011,  //led2亮绿灯,led1亮红灯

              led_s1=6'b001011,  //led2亮黄灯,led1亮红灯

              led_s2=6'b011101,  //led2亮红灯,led1亮绿灯

              led_s3=6'b011001;  //led2亮红灯,led1亮黄灯

          

    //数码管初始电路设置,使每一个二进制码与数码管显示的数字一一对应

    initial

       begin

           seg[0] = 9'h3f; //7段显示数字 0

           seg[1] = 9'h06; //7段显示数字 1

           seg[2] = 9'h5b; //7段显示数字 2

           seg[3] = 9'h4f; //7段显示数字 3

           seg[4] = 9'h66; //7段显示数字 4

           seg[5] = 9'h6d; //7段显示数字 5

           seg[6] = 9'h7d; //7段显示数字 6

           seg[7] = 9'h07; //7段显示数字 7

           seg[8] = 9'h7f; //7段显示数字 8

           seg[9] = 9'h6f; //7段显示数字 9

       end

      

    //时钟信号分频

    divider    #(.N(6000000),.WIDTH(24)) divider1

           (

              .clk(clk),

              .rst(rst_n),

              .clk_out(clk1)

           );

   

    //次态到现态的转换

    always @ (posedge clk1,negedge rst_n) begin

       if(!rst_n)

           current_state <= s0; //同步清零

       else

           current_state <= next_state; //在clk的上升沿触发器状态改变

    end

   

    //组合逻辑描述状态转移的判断

    always @ (current_state,rst_n,timecout) begin

       if(!rst_n)

           next_state<=s0;

       else begin

           case(current_state)

              s0:begin

                  if(timecout==1)  //timecout作为时间计数标志

                     next_state=s1;  //为1代表之前持续的状态结束,进入下一状态

                  else

                     next_state=s0;  //不为1代表之前持续的状态未结束,维持此状态

                  end

              s1:begin

                  if(timecout==1) 

                     next_state=s2; 

                  else

                     next_state=s1; 

                  end

              s2:begin

                  if(timecout==1) 

                     next_state=s3; 

                  else

                     next_state=s2; 

                  end

              s3:begin

                  if(timecout==1) 

                     next_state=s0; 

                  else

                     next_state=s3; 

                  end

              endcase

           end

       end

      

    //判断时间计数是否结束,及输出led

    always @ (posedge(clk1),negedge(rst_n)) begin

       if(!rst_n) begin

           out<=led_s0;

           timecout<=time_s0;

           end

       else begin

           case(next_state)

              s0:begin

                  out<=led_s0;

                  if(timecout==1)  //即上一个状态结束后timecout为1,需要重新赋值

                     timecout<=time_s0;

                  else

                     timecout<=timecout-1;

                  end

              s1:begin

                  out<=led_s1;

                  if(timecout==1)

                     timecout<=time_s1;

                  else

                     timecout<=timecout-1;

                  end

              s2:begin

                  out<=led_s2;

                  if(timecout==1)

                     timecout<=time_s2;

                  else

                     timecout<=timecout-1;

                  end

              s3:begin

                  out<=led_s3;

                  if(timecout==1)

                     timecout<=time_s3;

                  else

                     timecout<=timecout-1;

                  end

              endcase

           end

       end

    assign seg_led = seg[timecout];

    endmodule

行人触发双直行交通灯模块

module button(clk,rst_n,M,out,seg_led);

    input clk,rst_n;

    input M;  //行人触发按键,按下后进入倒计时,然后方可通过马路

    output reg[5:0] out;

    output [8:0] seg_led;  //高9位为高位数码管,低9位为低位数码管

    reg current_state;

    reg next_state=1'b0;

    reg start;  //按键触发的标志

    reg[3:0] timecout;

    reg [8:0] seg[9:0];

    parameter s0=1'b0,s1=1'b1;  //状态机的状态编码

    parameter time_s0=4'b1001,time_s1=4'b1001;  //一个周期的2种时间间隔

    parameter led_s0=6'b101101,  //led2,led1均亮绿灯,代表双行道车辆畅行

              led_s1=6'b011011;  //led2,led1均亮红灯,代表行人过马路

   

    //数码管初始电路设置,使每一个二进制码与数码管显示的数字一一对应

    initial

       begin

           seg[0] = 9'h3f; //7段显示数字 0

           seg[1] = 9'h06; //7段显示数字 1

           seg[2] = 9'h5b; //7段显示数字 2

           seg[3] = 9'h4f; //7段显示数字 3

           seg[4] = 9'h66; //7段显示数字 4

           seg[5] = 9'h6d; //7段显示数字 5

           seg[6] = 9'h7d; //7段显示数字 6

           seg[7] = 9'h07; //7段显示数字 7

           seg[8] = 9'h7f; //7段显示数字 8

           seg[9] = 9'h6f; //7段显示数字 9

       end

   

    //时钟信号分频

    divider    #(.N(6000000),.WIDTH(24)) divider1

           (

              .clk(clk),

              .rst(rst_n),

              .clk_out(clk1)

           );

   

    //状态转换

    always @ (posedge clk1,negedge rst_n) begin

       if(!rst_n)

           current_state <= s0; //同步清零

       else

           current_state <= next_state; //在clk的上升沿触发器状态改变

        end

   

    //判断触发条件

    always @ (posedge clk1,negedge rst_n,posedge M) begin

       if(!rst_n)

           start<=0;

       else if(M)

           start<=1;

       else

           start<=0;

       end

      

    //组合逻辑描述状态转移的判断

    always @ (current_state,rst_n,timecout) begin

       if(!rst_n) begin

           next_state<=s0;

           end

       else begin

           case(current_state)

              s0:begin

                  if(start==0)

                     next_state<=s0;

                  else begin

                     if(timecout==1)  //timecout作为时间计数标志

                         next_state<=s1;  //为1代表之前持续的状态结束,进入下一状态

                     else

                         next_state<=s0;  //不为1代表之前持续的状态未结束,维持此状态

                     end

                  end

              s1:begin  //因为到达s1后下一个状态的转换只取决于倒计时是否结束,所以不用考虑start的状态

                  if(timecout==1) 

                     next_state<=s0; 

                  else

                     next_state<=s1; 

                  end

              endcase

           end

       end

   

    //判断时间计数是否结束,及输出led和数码管显示

    always @ (posedge(clk1),negedge(rst_n)) begin

       if(!rst_n) begin

           out<=led_s0;

           timecout<=time_s0;

           end

       else begin

           case(next_state)

              s0:begin

                  out<=led_s0;

                  if(start==1) begin

                     if(timecout==1)  //即上一个状态结束后timecout为1,需要重新赋值

                         timecout<=time_s0;

                     else

                         timecout<=timecout-1;

                     end

                  else

                     timecout=9;

                  end

              s1:begin

                  out<=led_s1;  //start省略,原因和上面一样

                  if(start==1) begin

                     if(timecout==1)

                         timecout<=time_s1;

                     else

                         timecout<=timecout-1;

                     end

                  end

              endcase

           end

       end

    assign seg_led = seg[timecout];

    endmodule

   

五、调试过程

1、Analysis&Synthesis成功的截图 :

 

2、管脚分配表的截图 :

 

3、Compile Design成功的截图 :

 

4、programmer成功的截图:

 

六、实验板演示

1.十字交叉路口交通灯模块演示:

 

 

2.行人触发双直行交通灯模块演示:

 

七、故障分析与解决

1、出现问题:在数码管状态转换时出现了灯光闪烁不正常的现象,如按照设置规律应当显示红黄结果显示了红绿的情况。

     解决方法:可以确定是时序逻辑的转换出现了问题,在always语句中更改为准确的触发条件即可解决这一问题。

2、出现问题:顶层设计中在将两个子模块程序分时显示时出现报错,系统认为输出没有指定变量类型

      解决方法:将两个子程序的输出指定为wire类型,即可满足要求。

3、出现问题:LED灯的展示中没有出现问题,但当到了数码管显示中就会发现从第一个倒计时转到第二个倒计时时就会发现无法实现正常循环。

      解决方法:时序逻辑的问题,改正always敏感条件后即可解决。

八、心得体会

1、在本次实验中,我们学到了非常多的东西,懂得了集思广益对于综合实验的重要性,一个人的思考很难得到有建设性的效果,且很容易走进思维的死胡同。在遇到问题后,多查阅资料与请教他人可以得到更多的解决思路。

2、在实验中,思考和动手是非常重要的,像我们这次实验设计之初遇到了很多问题,也常常出错,在经过相关知识的系统学习和实际动过手之后,我们更迅速地掌握了其中的原理和实践方法,并加深了知识的记忆。

3、在实验中我们通过自顶向下的分析方法确定了设计原理和流程,查阅相关资料,进行Verilog HDL语言程序的编写,进行了分频器,状态转换电路,数码管电路等设计,对于层次化设计和分块思想有了更深的理解。

4、在此小组合作中,我们大家明确分工,提高了实验设计和报告总结的效率,经过多次交流沟通,使得这次实验的任务圆满完成。

有关FPGA双模式交通灯的设计的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  7. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  8. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  9. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  10. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

随机推荐