明德扬吴老师 发表于 2020-7-15 17:21:26

【每周FPGA案例】状态机实现的LED交通灯2

【上板现象】

状态机实现的LED交通灯2在MP801的上板现象
https://www.bilibili.com/video/BV1Af4y117H4?p=8

状态机实现的LED交通灯2在点拨开发板的上板现象

https://www.bilibili.com/video/BV1Af4y117H4?p=9

状态机实现的LED交通灯2在实验箱的上板现象

https://www.bilibili.com/video/BV1Af4y117H4?p=10

【设计教程】


状态机实现的LED交通灯2
--作者:肖肖肖
本文为明德扬原创及录用文章,转载请注明出处!1.1 总体设计1.1.1 概述发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,可以高效的将电能转化为光能,在现代社会具有广泛的用途,如照明、平板显示、医疗器件等。可通过高低电平的变化来控制LED灯的明灭状态,当输出信号为低电平时,LED灯亮,反之,当输出信号为高电平时,LED灯灭。

1.1.2 设计目标内容:开发板上有红黄绿 LED 灯各四个,分别放在东西南北方向。参考交通灯的情况,即每个方向都是绿灯亮 10 秒,然后黄灯亮 5 秒,然后红灯亮 15 秒。绿灯按照东西和南北的顺序依次亮。具体思路:1.首先分东西方向和南北方向来设计电路。设计两个不同的状态机来指示不同的状态。东西方向的灯状态相同,南北方向的灯状态相同。2.4个方向的灯依次为红绿黄的依次循环时间为 15+10+5=30 秒,所以可以设计一个计数器,计时 30 秒钟,表示一个循环。计数器可以分两个写,一个计时 1秒,一个计时 30秒。3.然后再根据计数器的计数值的不同,决定状态机的不同状态。4.首先设计东西方向的状态机,复位的时候,绿灯亮,计数器计到 10 秒,黄灯亮,计到15 秒,红灯亮,计满 30 秒,又是绿灯亮......依次循环。5.接着设计南北方向的状态机,复位的时候,红灯亮,计数器计到15 秒,绿灯亮,计到20 秒,黄灯亮,计满 30 秒,又是红灯亮......依次循环。

1.1.3信号列表
信号名I/O位宽定义
clkI1系统工作时钟 50M
rst_nI1系统复位信号,低电平有效
led_eastO33 比特信号,表示东面三个 led 灯,最高位为红灯,最低位为绿灯,低电平时灯亮。
led_southO33 比特信号,表示南面三个 led 灯,最高位为红灯,最低位为绿灯,低电平时灯亮。
led_westO33 比特信号,表示西面三个 led 灯,最高位为红灯,最低位为绿灯,低电平时灯亮。
led_northO33 比特信号,表示北面三个 led 灯,最高位为红灯,最低位为绿灯,低电平时灯亮。


1.1.4 设计思路根据题目功能要求,东西南北四个方向LED灯按照“红灯-绿灯-黄灯”的顺序依次循环时间为 15+10+5=30 秒,所以可以设计一个计数器,计时 30 秒钟表示一个循环。该计数器可以分两个写,一个计时 1 秒,一个计时 30秒。因为在数字电路中的延时都是通过计数器实现的,计数器*时钟周期=延时时间。本模块中,由于输入时钟是50MHz,时钟周期为20ns,功能要求每1秒变化一次。我们通过counter来表示延时,当其值为1s/20ns=5000_0000时,表示1秒时间到。两个计数器的架构图:时钟计数器counter:该计数器用于计算1s的时钟个数,加一条件为1,表示一直计数;数到5000_0000下,则表示数到1秒的时间了。秒计数器:该计数器用于计算1个周期内三色LED按顺序各点亮1次的时间,1周期的时间为30秒,加一条件为时钟计数器的结束条件,表示时钟计数器每数完1s,秒计数器计数加一;数到30下,则表示数到30秒的时间了。

下面是两个计数器的代码:parameter   COUNT_TIME      =26'd5000_0000;
parameter   CYCLE_TIME       =   5'd30       ;
parameter   COUNT_WID       =26         ;
parameter   SEC_WID         =5            ;
      
reg    counter            ;
wire                  add_counter         ;
wire                  end_counter         ;
reg        second             ;
wire                  add_second          ;
wire                  end_second          ;
   
always @(posedge clk or negedge rst_n)begin
if (rst_n==0) begin
counter <= 0;
end
else if(add_counter) begin
if(end_counter)
counter <= 0;
else
   counter <= counter+1 ;
end
end
assign add_counter = 1;
assign end_counter = add_counter&& counter == COUNT_TIME-1 ;


always @(posedge clk or negedge rst_n)begin
if (rst_n==0) begin
         second <= 0;
       end
      else if(add_second) begin
            if(end_second)
                second <= 0;
         else
                second <= second+1 ;
      end
    end
   assign add_second = end_counter;
    assign end_second = add_second&& second == CYCLE_TIME-1 ;


按照题目要求:分东西方向和南北方向来设计电路,因此设计两个不同的状态机来指示不同的状态——同一时间内,东西方向的灯状态相同,南北方向的灯状态相同。两个状态机的架构:东西方向的状态机:该状态机用于设定东西方向LED的颜色跳转状态。1)       上电后,就跳转到绿灯亮状态,绿灯亮;2)       10 秒后,黄灯亮,跳转条件为秒计数器计数10下,即add_second &&second==10-1,则表示数到10秒了;3)       5 秒后,红灯亮,跳转条件为秒计数器计数15下,即add_second &&second==15-1,则表示数到15秒了;4)       15 秒后,又是绿灯亮,跳转条件为秒计数器计满30下,即add_second &&second==30-1,则表示数到30秒了......依次循环。南北方向的状态机:该状态机用于设定南北方向LED的颜色跳转状态。1)       上电后,就跳转到红灯亮状态,红灯亮;2)       15 秒后,绿灯亮,跳转条件为秒计数器计数15下,即add_second &&second==15-1,则表示数到15秒了;3)       10 秒后,黄灯亮,跳转条件为秒计数器计数25下,即add_second &&second==25-1,则表示数到25秒了;4)       5 秒后,又是红灯亮,跳转条件为秒计数器计满30下,即add_second &&second==30-1,则表示数到30秒了......依次循环。

下面是东西、南北方向的两个状态机代码:parameter   LED_NUM         =   3            ;
parameter   STA_W         =   2            ;

parameter   STA_G         =   2'd1         ;
parameter   STA_Y         =   2'd2         ;
parameter   STA_R         =   2'd3         ;
parameter   GREEN         =   3'b110       ;
parameter   YELLOW          =   3'b101       ;
parameter   RED             =   3'b011       ;


input                   clk               ;
input                   rst_n               ;
output   led_east            ;
output   led_south         ;
output   led_west            ;
output   led_north         ;

reg      led_east            ;
reg      led_south         ;
reg      led_west            ;
reg      led_north         ;

reg    counter             ;
wire                  add_counter         ;
wire                  end_counter         ;
reg      second            ;
wire                  add_second          ;
wire                  end_second          ;

reg      ew_state_c            ;
reg      ew_state_n            ;
wire                  idle2sta_g_start_ew   ;
wire                  sta_g2sta_y_start_ew;
wire                  sta_y2sta_r_start_ew;
wire                  sta_r2sta_g_start_ew;

reg      sn_state_c            ;
reg      sn_state_n            ;
wire                  idle2sta_r_start_sn   ;
wire                  sta_r2sta_g_start_sn;
wire                  sta_g2sta_y_start_sn;
wire                  sta_y2sta_r_start_sn;


always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      counter <= 0;
    end
    else if(add_counter) begin
      if(end_counter)
            counter <= 0;
      else
            counter <= counter+1 ;
   end
end
assign add_counter = 1;
assign end_counter = add_counter&& counter == COUNT_TIME-1 ;


always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      second <= 0;
    end
    else if(add_second) begin
      if(end_second)
            second <= 0;
      else
            second <= second+1 ;
   end
end
assign add_second = end_counter;
assign end_second = add_second&& second == CYCLE_TIME-1 ;


always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      ew_state_c <= STA_G ;
    end
    else begin
      ew_state_c <= ew_state_n;
   end
end

always @(*) begin
    case(ew_state_c)
      STA_G :begin
            if(sta_g2sta_y_start_ew)
                ew_state_n = STA_Y ;
            else
                ew_state_n = ew_state_c ;
      end
      STA_Y :begin
            if(sta_y2sta_r_start_ew)
                ew_state_n = STA_R ;
            else
                ew_state_n = ew_state_c ;
      end
      STA_R :begin
            if(sta_r2sta_g_start_ew)
                ew_state_n = STA_G ;
            else
                ew_state_n = ew_state_c ;
      end
      default : ew_state_n = STA_G ;
    endcase
end

assign sta_g2sta_y_start_ew = ew_state_c==STA_G && add_second&& second == 10-1;
assign sta_y2sta_r_start_ew = ew_state_c==STA_Y && add_second&& second == 15-1;
assign sta_r2sta_g_start_ew = ew_state_c==STA_R && add_second&& second == 30-1;

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_east<=GREEN;
    end
    else if(ew_state_c==STA_G)begin
      led_east<=GREEN;
    end
    else if(ew_state_c==STA_Y)begin
      led_east<=YELLOW;
    end
    else if(ew_state_c==STA_R)begin
      led_east<=RED;
    end
    else begin
      led_east<=GREEN;
    end
end

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_west<=GREEN;
    end
    else if(ew_state_c==STA_G)begin
      led_west<=GREEN;
    end
    else if(ew_state_c==STA_Y)begin
      led_west<=YELLOW;
    end
    else if(ew_state_c==STA_R)begin
      led_west<=RED;
    end
    else begin
      led_west<=GREEN;
    end
end



always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      sn_state_c <= STA_R ;
    end
    else begin
      sn_state_c <= sn_state_n;
   end
end

always @(*) begin
    case(sn_state_c)
      STA_R :begin
            if(sta_r2sta_g_start_sn)
                sn_state_n = STA_G ;
            else
                sn_state_n = sn_state_c ;
      end
      STA_G :begin
            if(sta_g2sta_y_start_sn)
                sn_state_n = STA_Y ;
            else
                sn_state_n = sn_state_c ;
      end
      STA_Y :begin
            if(sta_y2sta_r_start_sn)
                sn_state_n = STA_R ;
            else
                sn_state_n = sn_state_c ;
      end
      default : sn_state_n = STA_R ;
    endcase
end

assign sta_r2sta_g_start_sn = sn_state_c==STA_R && add_second&& second == 15-1;
assign sta_g2sta_y_start_sn = sn_state_c==STA_G && add_second&& second == 25-1;
assign sta_y2sta_r_start_sn = sn_state_c==STA_Y && add_second&& second == 30-1;

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_south<=RED;
    end
    else if(sn_state_c==STA_G)begin
      led_south<=GREEN;
    end
    else if(sn_state_c==STA_Y)begin
      led_south<=YELLOW;
    end
    else if(sn_state_c==STA_R)begin
      led_south<=RED;
    end
    else begin
      led_south<=GREEN;   
    end
end

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_north<=RED;
    end
    else if(sn_state_c==STA_G)begin
      led_north<=GREEN;
    end
    else if(sn_state_c==STA_Y)begin
      led_north<=YELLOW;
    end
    else if(sn_state_c==STA_R)begin
      led_north<=RED;
    end
    else begin
      led_north<=GREEN;   
    end
end


endmodule




1.1.5参考设计代码moduletraf_light2(
    clk         ,
    rst_n       ,
    led_east    ,
    led_south   ,
    led_west    ,
    led_north   
    );



parameter   COUNT_TIME      =   26'd5000_0000;
parameter   CYCLE_TIME      =   5'd30      ;
parameter   COUNT_WID       =   26         ;
parameter   SEC_WID         =   5            ;
parameter   LED_NUM         =   3            ;
parameter   STA_W         =   2            ;

parameter   STA_G         =   2'd1         ;
parameter   STA_Y         =   2'd2         ;
parameter   STA_R         =   2'd3         ;
parameter   GREEN         =   3'b110       ;
parameter   YELLOW          =   3'b101       ;
parameter   RED             =   3'b011       ;


input                   clk               ;
input                   rst_n               ;
output   led_east            ;
output   led_south         ;
output   led_west            ;
output   led_north         ;

reg      led_east            ;
reg      led_south         ;
reg      led_west            ;
reg      led_north         ;

reg    counter             ;
wire                  add_counter         ;
wire                  end_counter         ;
reg      second            ;
wire                  add_second          ;
wire                  end_second          ;

reg      ew_state_c            ;
reg      ew_state_n            ;
wire                  idle2sta_g_start_ew   ;
wire                  sta_g2sta_y_start_ew;
wire                  sta_y2sta_r_start_ew;
wire                  sta_r2sta_g_start_ew;

reg      sn_state_c            ;
reg      sn_state_n            ;
wire                  idle2sta_r_start_sn   ;
wire                  sta_r2sta_g_start_sn;
wire                  sta_g2sta_y_start_sn;
wire                  sta_y2sta_r_start_sn;


always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      counter <= 0;
    end
    else if(add_counter) begin
      if(end_counter)
            counter <= 0;
      else
            counter <= counter+1 ;
   end
end
assign add_counter = 1;
assign end_counter = add_counter&& counter == COUNT_TIME-1 ;


always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      second <= 0;
    end
    else if(add_second) begin
      if(end_second)
            second <= 0;
      else
            second <= second+1 ;
   end
end
assign add_second = end_counter;
assign end_second = add_second&& second == CYCLE_TIME-1 ;


always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      ew_state_c <= STA_G ;
    end
    else begin
      ew_state_c <= ew_state_n;
   end
end

always @(*) begin
    case(ew_state_c)
      STA_G :begin
            if(sta_g2sta_y_start_ew)
                ew_state_n = STA_Y ;
            else
                ew_state_n = ew_state_c ;
      end
      STA_Y :begin
            if(sta_y2sta_r_start_ew)
                ew_state_n = STA_R ;
            else
                ew_state_n = ew_state_c ;
      end
      STA_R :begin
            if(sta_r2sta_g_start_ew)
                ew_state_n = STA_G ;
            else
                ew_state_n = ew_state_c ;
      end
      default : ew_state_n = STA_G ;
    endcase
end

assign sta_g2sta_y_start_ew = ew_state_c==STA_G && add_second&& second == 10-1;
assign sta_y2sta_r_start_ew = ew_state_c==STA_Y && add_second&& second == 15-1;
assign sta_r2sta_g_start_ew = ew_state_c==STA_R && add_second&& second == 30-1;

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_east<=GREEN;
    end
    else if(ew_state_c==STA_G)begin
      led_east<=GREEN;
    end
    else if(ew_state_c==STA_Y)begin
      led_east<=YELLOW;
    end
    else if(ew_state_c==STA_R)begin
      led_east<=RED;
    end
    else begin
      led_east<=GREEN;
    end
end

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_west<=GREEN;
    end
    else if(ew_state_c==STA_G)begin
      led_west<=GREEN;
    end
    else if(ew_state_c==STA_Y)begin
      led_west<=YELLOW;
    end
    else if(ew_state_c==STA_R)begin
      led_west<=RED;
    end
    else begin
      led_west<=GREEN;
    end
end



always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
      sn_state_c <= STA_R ;
    end
    else begin
      sn_state_c <= sn_state_n;
   end
end

always @(*) begin
    case(sn_state_c)
      STA_R :begin
            if(sta_r2sta_g_start_sn)
                sn_state_n = STA_G ;
            else
                sn_state_n = sn_state_c ;
      end
      STA_G :begin
            if(sta_g2sta_y_start_sn)
                sn_state_n = STA_Y ;
            else
                sn_state_n = sn_state_c ;
      end
      STA_Y :begin
            if(sta_y2sta_r_start_sn)
                sn_state_n = STA_R ;
            else
                sn_state_n = sn_state_c ;
      end
      default : sn_state_n = STA_R ;
    endcase
end

assign sta_r2sta_g_start_sn = sn_state_c==STA_R && add_second&& second == 15-1;
assign sta_g2sta_y_start_sn = sn_state_c==STA_G && add_second&& second == 25-1;
assign sta_y2sta_r_start_sn = sn_state_c==STA_Y && add_second&& second == 30-1;

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_south<=RED;
    end
    else if(sn_state_c==STA_G)begin
      led_south<=GREEN;
    end
    else if(sn_state_c==STA_Y)begin
      led_south<=YELLOW;
    end
    else if(sn_state_c==STA_R)begin
      led_south<=RED;
    end
    else begin
      led_south<=GREEN;   
    end
end

always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      led_north<=RED;
    end
    else if(sn_state_c==STA_G)begin
      led_north<=GREEN;
    end
    else if(sn_state_c==STA_Y)begin
      led_north<=YELLOW;
    end
    else if(sn_state_c==STA_R)begin
      led_north<=RED;
    end
    else begin
      led_north<=GREEN;   
    end
end


endmodule


1.2 效果和总结

点拨板:


1.       复位,东西绿灯亮,南北红灯亮2.       10秒后,东西黄灯亮,南北还是红灯亮3.       15秒后,东西红灯亮,南北绿灯亮4.       20秒后。东西还是红灯亮,南北黄灯亮5.       30秒后,东西绿灯亮,南北红灯亮Mp801:

1.       复位,东西绿灯亮,南北红灯亮2.       10秒后,东西黄灯亮,南北还是红灯亮3.       15秒后,东西红灯亮,南北绿灯亮4.       20秒后。东西还是红灯亮,南北黄灯亮5.       30秒后,东西绿灯亮,南北红灯亮
实验箱:

1.       复位,东西绿灯亮,南北红灯亮2.       10秒后,东西黄灯亮,南北还是红灯亮3.       15秒后,东西红灯亮,南北绿灯亮4.       20秒后。东西还是红灯亮,南北黄灯亮5.       30秒后,东西绿灯亮,南北红灯亮

观看上面的现象,可以发现,各项功能正常:开发板上有红黄绿三色 LED 灯各四个,在东西南北方向各有一组。参考交通灯的情况,即每个方向都是绿灯亮 10 秒,然后黄灯亮 5 秒,然后红灯亮 15 秒。绿灯按照东西和南北的顺序依次亮。成功完成设计目标。感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也欢迎大家在评论里进行讨论!

也可以看一下我们往期的文章:《基于FPGA的密码锁设计》《波形相位频率可调DDS信号发生器》《基于FPGA的曼彻斯特编码解码设计》《基于FPGA的出租车计费系统》《数电基础与Verilog设计》《基于FPGA的频率、电压测量》《基于FPGA的汉明码编码解码设计》《关于锁存器问题的讨论》《阻塞赋值与非阻塞赋值》《参数例化时自动计算位宽的解决办法》1.3 公司简介明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。



【设计教程下载】



【设计视频教程】

https://www.bilibili.com/video/BV1Af4y117H4?p=7

【工程源码】







请打电话找我138 发表于 2021-8-18 10:23:04

谢谢,拜读一下
页: [1]
查看完整版本: 【每周FPGA案例】状态机实现的LED交通灯2