1.1 总体设计
1.1.1 概述
发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,它可以高效的将电能转化为光能,在现代社会具有广泛的用途,如照明、平板显示、医疗器件等。可通过高低电平的变化来控制LED灯的明灭状态,当输出信号为低电平时,LED灯亮,反之,当输出信号为高电平时,LED灯灭。
1.1.2 设计目标
实现开发板上东西南北 4 个方向,每个方向上的 3 个 LED 灯按照“绿灯--黄灯--红灯--绿灯--黄灯......”依次循环变化。变化的速度不同,东面的间隔时间为 1 秒;西面的间隔时间为 2 秒;南面的间隔时间为 3 秒;北面的间隔时间为 4 秒。
具体要求:
1、每个方向的灯分开独立设计。
2、首先设计东向的灯:设计一个计时 1 秒的计数器,用来计算灯的状态变化的时间间隔。
3、如果计时 1 秒到了,把黄灯点亮;再过 1 秒把红灯点亮;再过 1 秒,把绿灯点亮......
依次循环。
4、设计西方向的灯:设计一个计时 2 秒的计数器,其他的类似。
5、其它两个方向的设计类似。
1.1.3 信号列表
1.1.4 设计思路
根据题目功能要求,东西南北四个方向LED灯颜色变换的速度都不同。因为在数字电路中的延时都是通过计数器实现的,计数器*时钟周期=延时时间。本模块中,由于输入时钟是50MHz,时钟周期为20ns,功能要求每1秒变化一次。我们通过counter来表示延时,当其值为1s/20ns=5000_0000时,表示1秒时间到。
本工程架构由四个计数器组成:
东方向计数器e_counter:该计数器用于计算东向1s的时钟个数,加一条件为1,表示一直计数;数到5000_0000下,则表示数到1秒了。
西方向计数器w_counter:该计数器用于计算西向2s的时钟个数,加一条件为1,表示一直计数;数到2*5000_0000下,则表示数到2秒了。
南方向计数器s_counter:该计数器用于计算南向3s的时钟个数,加一条件为1,表示一直计数;数到3*5000_0000下,则表示数到3秒了。
北方向计数器n_counter:该计数器用于计算北向4s的时钟个数,加一条件为1,表示一直计数;数到4*5000_0000下,则表示数到4秒了。
下面是东西南北四个方向的秒计数器的代码。
parameter COUNT_1S = 26'd5000_0000; parameter COUNT_WID = 28;
reg [COUNT_WID-1:0] e_counter; reg [COUNT_WID-1:0] s_counter; reg [COUNT_WID-1:0] w_counter; reg [COUNT_WID-1:0] n_counter;
wire add_e_counter; wire end_e_counter; wire add_w_counter; wire end_w_counter; wire add_s_counter; wire end_s_counter; wire add_n_counter; wire end_n_counter; |
always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin e_counter <= 0; end else if(add_e_counter) begin if(end_e_counter) e_counter <= 0; else e_counter <= e_counter+1 ; end end assign add_e_counter = 1; assign end_e_counter = add_e_counter && e_counter == COUNT_1S-1 ;
always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin w_counter <= 0; end else if(add_w_counter) begin if(end_w_counter) w_counter <= 0; else w_counter <= w_counter+1 ; end end assign add_w_counter = 1; assign end_w_counter = add_w_counter && w_counter == 2*COUNT_1S-1 ;
always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin s_counter <= 0; end else if(add_s_counter) begin if(end_s_counter) s_counter <= 0; else s_counter <= s_counter+1 ; end end assign add_s_counter = 1; assign end_s_counter = add_s_counter && s_counter == 3*COUNT_1S-1 ;
always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin n_counter <= 0; end else if(add_n_counter) begin if(end_n_counter) n_counter <= 0; else n_counter <= n_counter+1 ; end end assign add_n_counter = 1; assign end_n_counter = add_n_counter && n_counter == 4*COUNT_1S-1 ; |
LED灯信号的变化,根据功能要求,东面的间隔时间为 1 秒;西面的间隔时间为 2 秒;南面2的间隔时间为 3 秒;北面的间隔时间为 4 秒。计数时间到时变化;时间没到,则不变化。每一时刻每个方向只有一个灯亮,并且亮灯的颜色顺序按照“绿灯--黄灯--红灯--绿灯--黄灯.....”依次循环变化。
东西南北四个方向的各有三盏不同颜色的LED灯,每个方向的三个LED灯都由3 比特信号控制,最高位为红灯,最低位为绿灯,并且低电平时LED灯亮。led_east表示东面三个LED灯,led_west表示西面三个LED灯,led_south表示南面三个LED灯,led_north表示北面三个LED灯。
三色LED灯的循环变换控制可以通过拼接的方法使数据循环左移来实现。
以led_east的变化为例,上电后,led_east[2:0]=3’b110;然后每隔1秒,依次循环变化:101,011,110。即end_e_counter(每隔1秒)时,led_east[2:0]数值循环左移,其他时候不变。
led_west、led_south、led_north也是同理,即:
上电后,led_west[2:0]=3’b110;然后每隔2秒,依次循环变化:101,011,110。即end_w_counter(每隔2秒)时,led_west[2:0]数值循环左移,其他时候不变。
上电后,led_south[2:0]=3’b110;然后每隔3秒,依次循环变化:101,011,110。即end_s_counter(每隔3秒)时,led_south[2:0]数值循环左移,其他时候不变。
上电后,led_north[2:0]=3’b110;然后每隔4秒,依次循环变化:101,011,110。即end_n_counter(每隔4秒)时,led_north[2:0]数值循环左移,其他时候不变。
下面是个东西南北四个方向的LED灯亮灯控制代码。
parameter LED_LEN = 3;
output [LED_LEN-1:0] led_east; output [LED_LEN-1:0] led_south; output [LED_LEN-1:0] led_west; output [LED_LEN-1:0] led_north;
reg [LED_LEN-1:0] led_east; reg [LED_LEN-1:0] led_south; reg [LED_LEN-1:0] led_west; reg [LED_LEN-1:0] led_north; |
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin led_east<={{(LED_LEN-1){1'b1}},1'b0}; end else if(end_e_counter)begin led_east<={led_east[LED_LEN-2:0],led_east[LED_LEN-1]}; end else begin led_east<=led_east; end end
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin led_west<={{(LED_LEN-1){1'b1}},1'b0}; end else if(end_w_counter)begin led_west<={led_west[LED_LEN-2:0],led_west[LED_LEN-1]}; end else begin led_west<=led_west; end end
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin led_south<={{(LED_LEN-1){1'b1}},1'b0}; end else if(end_s_counter)begin led_south<={led_south[LED_LEN-2:0],led_south[LED_LEN-1]}; end else begin led_south<=led_south; end end
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin led_north<={{(LED_LEN-1){1'b1}},1'b0}; end else if(end_n_counter)begin led_north<={led_north[LED_LEN-2:0],led_north[LED_LEN-1]}; end else begin led_north<=led_north; end end |
1.1.5 参考设计代码
module traf_light1(
clk ,
rst_n ,
led_east ,
led_south ,
led_west ,
led_north
);
parameter LED_LEN = 3;
parameter COUNT_1S = 26'd5000_0000;
parameter COUNT_WID = 28;
input clk ;
input rst_n ;
output [LED_LEN-1:0] led_east ;
output [LED_LEN-1:0] led_south;
output [LED_LEN-1:0] led_west ;
output [LED_LEN-1:0] led_north;
reg [LED_LEN-1:0] led_east ;
reg [LED_LEN-1:0] led_south;
reg [LED_LEN-1:0] led_west ;
reg [LED_LEN-1:0] led_north;
reg [COUNT_WID-1:0] e_counter;
reg [COUNT_WID-1:0] s_counter;
reg [COUNT_WID-1:0] w_counter;
reg [COUNT_WID-1:0] n_counter;
wire add_e_counter;
wire end_e_counter;
wire add_w_counter;
wire end_w_counter;
wire add_s_counter;
wire end_s_counter;
wire add_n_counter;
wire end_n_counter;
always @(posedge clk or negedge rst_n)
begin
if (rst_n==0) begin
e_counter <= 0;
end
else if(add_e_counter) begin
if(end_e_counter)
e_counter <= 0;
else
e_counter <= e_counter+1 ;
end
end
assign add_e_counter = 1;
assign end_e_counter = add_e_counter && e_counter == COUNT_1S-1 ;
always @(posedge clk or negedge rst_n)
begin
if (rst_n==0) begin
w_counter <= 0;
end
else if(add_w_counter) begin
if(end_w_counter)
w_counter <= 0;
else
w_counter <= w_counter+1 ;
end
end
assign add_w_counter = 1;
assign end_w_counter = add_w_counter && w_counter == 2*COUNT_1S-1 ;
always @(posedge clk or negedge rst_n)
begin
if (rst_n==0) begin
s_counter <= 0;
end
else if(add_s_counter) begin
if(end_s_counter)
s_counter <= 0;
else
s_counter <= s_counter+1 ;
end
end
assign add_s_counter = 1;
assign end_s_counter = add_s_counter && s_counter == 3*COUNT_1S-1 ;
always @(posedge clk or negedge rst_n)
begin
if (rst_n==0) begin
n_counter <= 0;
end
else if(add_n_counter) begin
if(end_n_counter)
n_counter <= 0;
else
n_counter <= n_counter+1 ;
end
end
assign add_n_counter = 1;
assign end_n_counter = add_n_counter && n_counter == 4*COUNT_1S-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led_east<={{(LED_LEN-1){1'b1}},1'b0};
end
else if(end_e_counter)begin
led_east<={led_east[LED_LEN-2:0],led_east[LED_LEN-1]};
end
else begin
led_east<=led_east;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led_west<={{(LED_LEN-1){1'b1}},1'b0};
end
else if(end_w_counter)begin
led_west<={led_west[LED_LEN-2:0],led_west[LED_LEN-1]};
end
else begin
led_west<=led_west;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led_south<={{(LED_LEN-1){1'b1}},1'b0};
end
else if(end_s_counter)begin
led_south<={led_south[LED_LEN-2:0],led_south[LED_LEN-1]};
end
else begin
led_south<=led_south;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led_north<={{(LED_LEN-1){1'b1}},1'b0};
end
else if(end_n_counter)begin
led_north<={led_north[LED_LEN-2:0],led_north[LED_LEN-1]};
end
else begin
led_north<=led_north;
end
end
endmodule
1.2 效果和总结
点拨板
1. 复位,东西南北四面都是绿灯
2. 时间经过1秒后,东面变为黄灯,其余三面还是绿灯
3. 时间经过2秒后,东面变为红灯,西面变为黄灯,其余两面不变
4. 时间经过3秒后,东面又变回绿灯,南面变为黄灯,西面还是黄灯,北面还是绿灯不变
5. 时间经过4秒后,东面循环变为黄灯,南面还是黄灯,西面变为红灯,北面变为黄灯
Mp801
1. 复位,东西南北四面都是绿灯
2. 时间经过1秒后,东面变为黄灯,其余三面还是绿灯
3. 时间经过2秒后,东面变为红灯,西面变为黄灯,其余两面不变
4. 时间经过3秒后,东面又变回绿灯,南面变为黄灯,西面还是黄灯,北面还是绿灯不变
5. 时间经过4秒后,东面循环变为黄灯,南面还是黄灯,西面变为红灯,北面变为黄灯
实验箱
1. 复位,东西南北四面都是绿灯
2. 时间经过1秒后,东面变为黄灯,其余三面还是绿灯
3. 时间经过2秒后,东面变为红灯,西面变为黄灯,其余两面不变
4. 时间经过3秒后,东面又变回绿灯,南面变为黄灯,西面还是黄灯,北面还是绿灯不变
5. 时间经过4秒后,东面循环变为黄灯,南面还是黄灯,西面变为红灯,北面变为黄灯
观看上面的现象,可以发现,工程各项功能正常:开发板上东西南北 4 个方向,每个方向上的 3 个 LED 灯按照“绿灯--黄灯--红灯--绿灯--黄灯......”依次循环变化,并且东西南北 4 个方向LED灯变化的速度不同,东面的间隔时间为 1 秒;西面的间隔时间为 2 秒;南面的间隔时间为 3 秒;北面的间隔时间为 4 秒,成功完成设计目标。
设计视频教程、工程源代码请移步明德扬论坛观看下载。
感兴趣的朋友也可以访问明德扬论坛(http://www.FPGAbbs.cn/)进行FPGA相关工程设计学习,也欢迎大家在评论进行讨论!