2025寒假练 - 基于小脚丫FPGA STEP Baseboard制作交通灯控制系统 - 总结报告
项目介绍
本项目通过WebIDE图形化编程和Lattice Diamond的Verilog语言编程,实现构建了一个完整的交通灯控制系统,基于STEP MXO2 LPC核心板和STEP Baseboard4.0底板,在硬件上实现了:
- 红绿灯的交替显示以及数码管上显示对应的红绿灯倒计时
- 在绿灯即将结束的5秒钟进行蜂鸣器报警提醒行人
- 使用接近传感器检测人员走进,有人员靠近时通过蜂鸣器报警进行提醒
- 通过感应周围环境光,自动点亮LED灯,模拟路灯照亮道路,实现了不同的环境光下有两档LED灯亮度
项目要求
完全使用WebIDE的图形化编程,使用常规的74系列的元器件,构建一个交通灯控制系统:
在数码管上显示计时信息 - 图形化
蜂鸣器报警 - 图形化
接近传感器检测人员走近 -Verilog
环境光感知,自动点亮路灯(小脚丫核心板上的单色LED)- Verilog
在实现项目过程中出现了一点问题,使用Verilog在本地的Lattice Diamond中实现的后两个功能模块后,发现在WebIDE中实现出现传感器无法进行正常通讯的情况。经过和官方的老师商讨,决定采用“前两个要求还是用WebIDE的图形化实现,后两个要求可以选择本地软件Diamond来实现”的方式。但是在本项目中,我在WebIDE的图形化实现了前两个要求,并在本地软件Lattice Diamond上将四个功能都集中实现。
一、硬件介绍
本次项目使用的硬件平台为STEP MXO2 LPC核心板和STEP Baseboard4.0底板,它们提供了丰富的外设接口和扩展功能,包括数码管、蜂鸣器、接近传感器、环境光传感器等。
本次项目则是要使用到两个器械的RGB灯、LED单色灯、数码显示管、接近传感器模块、蜂鸣器模块,实现整个交通灯控制系统。
二、项目设计思路
2.1 方案框图
图1:系统层次结构
图1展示了整个系统的层次结构,其中包含了交通灯模块(traffic_light
)、检测器模块(detector
)以及它们内部的子模块。
交通灯模块内部包含了段式显示模块(segment
)、蜂鸣器模块(beeper
)等子模块,用于实现交通灯的显示和声音提示功能。
检测器模块内部包含了传感器模块(sensor
)、控制器模块(controller
)和蜂鸣器模块(beeper
),用于实现传感器数据的采集、处理和报警功能。
图2:交通信号灯系统
图2展示了检测器模块(detector
)与交通灯模块(traffic_light
)之间的连接关系。
检测器模块接收来自传感器的I2C数据(i2c_sda
和i2c_scl
),并根据传感器数据输出控制信号(led
)和蜂鸣器信号(beeper_singal
)。
交通灯模块接收这些信号,并通过内部的段式显示模块(segment
)和蜂鸣器模块(beeper
)实现交通灯的显示和声音提示功能。
交通灯模块还输出交通灯状态信号(如red_light1
、green_light1
等)和段式显示信号(seg_din
、seg_rck
、seg_sck
),用于控制外部的交通灯和显示设备。
图3:交通灯模块
图3展示了交通灯模块(traffic_light
)与蜂鸣器模块(beeper
)之间的连接关系。
交通灯模块接收时钟信号(clk
)和复位信号(rst_n
),并根据内部逻辑控制交通灯的状态。交通灯模块输出蜂鸣器使能信号(beeper_en
)到蜂鸣器模块,蜂鸣器模块根据该信号输出蜂鸣器控制信号(bee
)。
交通灯模块还输出交通灯状态信号(如red_light1
、green_light1
等)和段式显示信号(seg_din
、seg_rck
、seg_sck
),用于控制外部的交通灯和显示设备。
图4:检测器模块
图4展示了传感器模块(sensor
)与控制器模块(controller
)之间的连接关系。
传感器模块负责从外部环境获取数据,通过I2C总线(i2c_sda
和i2c_scl
)与控制器模块进行通信。传感器模块的输出数据(proximity_data
)和数据有效信号(data_valid
)被传递到控制器模块。
控制器模块根据传感器数据进行处理,并输出控制信号(led[7:0])和蜂鸣器使能信号(beeper_en
)。
这些信号将用于控制后续的交通灯和蜂鸣器模块。
2.2 项目设计思路
本次项目的设计思路是通过模块化的方式实现交通灯控制系统的各项功能。系统主要分为以下几个模块:
- 传感器模块(
sensor
):负责通过I2C总线与外部传感器通信,获取环境数据(如接近传感器数据)。传感器模块将数据处理后输出给控制器模块。 - 控制器模块(
controller
):接收传感器模块输出的数据和数据有效信号,进行数据处理和逻辑判断。控制器模块根据处理结果输出控制信号(Y_out
)和蜂鸣器使能信号(beeper_en
),用于控制交通灯的状态和蜂鸣器的报警。 - 蜂鸣器模块(
beeper
):接收蜂鸣器使能信号(beeper_en
),根据信号的电平状态控制蜂鸣器的开关。蜂鸣器模块输出蜂鸣器控制信号(bee
),用于驱动外部蜂鸣器发出报警声音。 - 交通灯模块(
traffic_light
):接收时钟信号(clk
)和复位信号(rst_n
),根据内部状态机控制交通灯的状态。交通灯模块输出交通灯状态信号(如red_light1
、green_light1
等)和段式显示信号(seg_din
、seg_rck
、seg_sck
),用于控制外部的交通灯和显示设备。
通过以上模块的协同工作,整个交通灯控制系统实现了传感器数据采集、逻辑控制、交通灯状态显示和蜂鸣器报警等功能。
三、关键代码介绍
WebIDE实现项目链接:https://www.stepfpga.com/project/16816/gui?type=top
WebIDE通过三个板块实现了交通灯信号系统
交通灯模块(左侧方框)
代码解析详见下一部分的交通灯控制模块 (traffic_light.v
)
通过一个74系列38译码器对输出的交通灯信号进行译码,从而控制RGB信号灯。
数码管模块(上侧方框)
代码解析详见下一部分的段式显示模块 (segment.v
)
接收交通灯模块传输的倒计时,将时间显示在数码管上。
蜂鸣器模块(右侧方框)
代码解析详见下一部分的蜂鸣器报警模块 (beeper.v
)
关键代码介绍
注:此处只有关键代码,非重要部分已经省略,完整代码请详见附件
交通灯控制模块 (traffic_light.v
)
module traffic_light(
input clk, // 时钟信号
input rst_n, // 复位信号(低电平有效)
output wire red_light1,// 红灯1信号
output wire green_light1,// 绿灯1信号
output wire blue_light1,// 蓝灯1信号
output wire red_light2,// 红灯2信号
output wire green_light2,// 绿灯2信号
output wire blue_light2,// 蓝灯2信号
output wire seg_rck, // 段式显示时钟信号
output wire seg_sck, // 段式显示时钟信号
output wire seg_din, // 段式显示数据信号
output wire beeper // 蜂鸣器信号
);
parameter GREEN_LIGHT = 2'd0; // 绿灯状态
parameter RED_LIGHT = 2'd1; // 红灯状态
reg [1:0] current_state = GREEN_LIGHT; // 当前状态
reg [3:0] countdown_timer; // 倒计时计数器
// 省略非重要内容
// 蜂鸣器使能信号
reg beeper_en = 1'b0;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// 初始化所有信号
current_state <= GREEN_LIGHT;
countdown_timer <= 4'd8;
green_light_internal1 <= 1'b0;
red_light_internal1 <= 1'b0;
beeper_en <= 1'b0;
end else begin
if (sec_pulse) begin
case (current_state)
GREEN_LIGHT: begin
// 绿灯状态逻辑
if (countdown_timer > 0) begin
green_light_internal1 <= 1'b1;
red_light_internal1 <= 1'b0;
countdown_timer <= countdown_timer - 1;
beeper_en <= 1'b0;
end else begin
green_light_internal1 <= 1'b0;
red_light_internal1 <= 1'b1;
current_state <= RED_LIGHT;
countdown_timer <= 4'd12;
beeper_en <= 1'b0;
end
end
RED_LIGHT: begin
// 红灯状态逻辑
if (countdown_timer > 0) begin
green_light_internal1 <= 1'b0;
red_light_internal1 <= 1'b1;
countdown_timer <= countdown_timer - 1;
beeper_en <= (countdown_timer <= 4'd6) ? 1'b1 : 1'b0;
end else begin
green_light_internal1 <= 1'b1;
red_light_internal1 <= 1'b0;
current_state <= GREEN_LIGHT;
countdown_timer <= 4'd8;
beeper_en <= 1'b0;
end
end
endcase
end
end
end
// 省略输出信号赋值
endmodule
代码说明
- 状态机:通过一个有限状态机来控制交通灯的状态切换,包括绿灯和红灯状态。
- 倒计时逻辑:使用
countdown_timer
计数器实现倒计时功能,倒计时时间可以在GREEN_LIGHT
和RED_LIGHT
状态下分别设置。 - 蜂鸣器控制:在绿灯倒计时最后 5 秒时,启用蜂鸣器报警信号
beep_en
。
段式显示模块 (segment.v
)
module segment(
input clk,
input rst_n,
input [3:0] data, // 显示数据
output reg seg_rck, // 数码管 RCK 信号
output reg seg_sck, // 数码管 SCK 信号
output reg seg_din // 数码管 DIN 信号
);
parameter IDLE = 3'd0;
parameter MAIN = 3'd1;
parameter WRITE = 3'd2;
reg [7:0] seg_data;
// 七段数码管数据映射
initial begin
seg_data[0] = 7'h3f;
seg_data[1] = 7'h06;
seg_data[2] = 7'h5b;
// ... 其他数字的映射省略
end
// 时钟分频
reg [9:0] clk_div_cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
clk_div_cnt <= 0;
end else begin
clk_div_cnt <= clk_div_cnt + 1;
end
end
// 数码管显示状态机
reg [2:0] state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
seg_rck <= 1'b0;
seg_sck <= 1'b0;
seg_din <= 1'b0;
end else begin
case (state)
IDLE: begin
state <= MAIN;
end
MAIN: begin
// 数据处理逻辑
seg_data = data;
state <= WRITE;
end
WRITE: begin
// 数据写入逻辑
state <= IDLE;
end
endcase
end
end
endmodule
代码说明
- 七段数码管数据映射:将输入数据转换为七段数码管的显示信号。
- 时钟分频:生成适当的时钟信号,用于控制数码管的显示刷新。
- 数码管显示状态机:通过状态机控制数码管的显示时序,实现倒计时时间的动态显示。
控制器模块 (controller.v
)
module controller(
input rst_n, // 复位信号(低电平有效)
input dat_valid, // 数据有效信号
input [15:0] prox_dat, // 接近传感器数据
output reg [7:0] Y_out, // 输出控制信号
output reg beeper_en // 蜂鸣器使能信号
);
parameter THRESHOLD = 16'h80; // 接近传感器阈值
reg [15:0] prox_dat0, prox_dat1, prox_dat2;
always @(posedge dat_valid) begin
prox_dat0 <= prox_dat;
prox_dat1 <= prox_dat0;
if ((prox_dat1 - prox_dat0 >= 16'h0400) || (prox_dat0 - prox_dat1 >= 16'h0400))
prox_dat2 <= prox_dat2; // 数据变化过大时保持原值
else
prox_dat2 <= prox_dat0; // 数据变化正常时更新
end
always @(*) begin
beeper_en = (prox_dat2 >= THRESHOLD) ? 1'b1 : 1'b0; // 根据阈值判断是否使能蜂鸣器
end
always @(*) begin
case (prox_dat2[11:9]) // 根据接近传感器数据的高位进行判断
3'b000: Y_out = 8'b11111111; // 情况1:输出全1
3'b001: Y_out = 8'b11110000; // 情况2:输出高4位为1
default: Y_out = 8'b00000000; // 默认情况:输出全0
endcase
end
endmodule
代码说明
- 传感器数据滤波:对传感器输入数据进行简单的滤波处理,以消除噪声干扰。
- 阈值判断:根据滤波后的传感器数据值与阈值比较,决定是否启用蜂鸣器报警和点亮相应的 LED 灯。
接近传感器检测模块 (sensor.v
)
module sensor (
input wire clk,
input wire rst_n,
inout wire i2c_sda, // I2C 数据线(双向)
output wire i2c_scl, // I2C 时钟线
output reg data_valid,// 数据有效信号
output reg [15:0] proximity_data // 输出的环境光数据
);
// 省略参数定义与状态机状态
// 省略时钟分频器代码
// 状态机驱动逻辑
always @(posedge phase_clock or negedge rst_n) begin
if (!rst_n) begin
// 复位时初始化所有寄存器和信号
i2c_data_clk <= 1'd1;
i2c_data_bus <= 1'd1;
current_state <= IDLE_STATE;
// ...
end else begin
case (current_state)
IDLE_STATE: begin
// 空闲状态,将信号初始化为高电平
i2c_data_clk <= 1'd1;
i2c_data_bus <= 1'd1;
ack_state <= ACK_LOW;
ack_flag <= 1'b0;
step_counter_1 <= 0;
main_phase <= 4'd0;
mode1_phase <= 4'd0;
mode2_phase <= 4'd0;
start_phase <= 3'd0;
current_state <= MAIN_STATE;
prev_state <= IDLE_STATE;
end
MAIN_STATE: begin
// 主控制状态,用于配置传感器和读取数据
if (main_phase >= 4'd11) begin
main_phase <= 4'd4;
end else begin
main_phase <= main_phase + 1;
end
case (main_phase)
4'd0: begin
device_addr <= 7'h38;
reg_addr <= 8'h40;
reg_data <= 8'h0a;
current_state <= MODE1_STATE;
end
// 其他相位逻辑...
4'd11: begin
data_valid <= 1;
end
default: current_state <= IDLE_STATE;
endcase
end
// 其他状态机(如 MODE1_STATE, MODE2_STATE 等)...
endcase
end
end
// 省略输出信号赋值
endmodule
代码说明
- 传感器数据读取:通过与外部传感器通信,获取原始传感器数据。
- 数据有效信号:在传感器数据更新后,设置数据有效信号
data_valid
,通知控制器模块读取最新数据。
蜂鸣器报警模块 (beeper.v
)
module beeper(
input clk, // 时钟信号
input rst_n, // 复位信号
input beep_en, // 蜂鸣器使能信号
output reg beep // 蜂鸣器输出信号
);
parameter CYCLE = 24000; // 蜂鸣器频率周期
parameter DURATION = 5000000; // 报警持续时间
reg [23:0] counter;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
counter <= 0;
beep <= 0;
end else begin
if (beep_en) begin
if (counter == CYCLE - 1) begin
counter <= 0;
beep <= ~beep; // 切换蜂鸣器状态
end else begin
counter <= counter + 1;
end
end else begin
beep <= 0;
end
end
end
endmodule
代码说明
- 频率控制:通过使用计数器和频率周期参数
CYCLE
,生成可变频率的蜂鸣器信号。 - 使能控制:只有当
beep_en
信号有效时,蜂鸣器才会输出报警信号。
四、功能说明
功能展示详情见视频
交通灯状态切换
红灯、绿灯按照预设的时间间隔(红灯8秒,绿灯12秒)进行相互切换。
数码管显示计时信息
数码管实时显示当前交通灯的计时信息。
蜂鸣器报警
当交通灯即将切换状态时,蜂鸣器发出报警信号。在绿灯还剩5秒时,将进行鸣叫报警。
接近传感器检测
接近传感器检测手指遮挡传感器时,蜂鸣器将进行鸣叫报警。
环境光控制路灯
环境光传感器感知周围光线强度。当用手指遮挡了传感器时,将驱动单色LED灯(共8颗)亮起。设置有两个档位,遮挡的光线亮度的不同,会让LED灯亮起(有4颗亮和8颗亮两个档位)。
五、项目中遇到的难题和解决方法
难题 1:图形化编程与常规的Verilog编程不同
在本次项目中,要求使用 WebIDE 的图形化编程工具来实现交通灯控制系统的部分功能,而传统的 Verilog 编程则是基于文本的硬件描述语言。图形化编程与常规的 Verilog 编程在逻辑表达、模块连接上存在一定差异。这些差异导致在初期对图形化编程工具的使用不够熟练,难以快速上手并实现预期的功能。
解决方法: 通过查阅 WebIDE 的官方文档和教程,了解图形化编程的基本操作和功能模块的使用方法。熟悉了如何在图形化界面中添加、连接和配置各个功能模块。将已有的 Verilog 编程经验与图形化编程相结合,理解图形化模块背后的逻辑原理。通过对比图形化模块和 Verilog 代码的实现方式,逐步掌握了图形化编程的逻辑表达方法。
难题 2:使用Verilog在本地的Lattice Diamond中实现的后两个功能模块后,发现在WebIDE中实现出现传感器无法进行正常通讯的情况。
在项目的开发过程中,我们使用 Verilog 在本地的 Lattice Diamond 开发环境中实现了传感器检测和环境光感知功能模块。然而,当将这些模块移植到 WebIDE 中时,发现传感器无法进行正常的通讯,导致系统无法获取传感器数据,影响了交通灯控制系统的正常运行。
解决方法: 和官方的老师商讨,决定采用“前两个要求还是用WebIDE的图形化实现,后两个要求可以选择本地软件Diamond来实现”的方式来实现系统。
六、对本次活动的心得体会
心得体会
通过参与本次交通灯控制系统项目,我收获颇丰,不仅在技术层面得到了锻炼,也在解决问题的能力和团队协作方面有了显著提升。以下是我对本次活动的一些心得体会:
熟悉图形化编程与Verilog编程的结合
本次活动要求使用WebIDE的图形化编程工具结合Verilog代码来实现交通灯控制系统。起初,我对图形化编程并不熟悉,习惯了传统的Verilog文本编程方式。通过查阅资料和不断实践,我逐渐掌握了图形化编程的技巧,学会了如何将Verilog代码与图形化模块相结合,实现复杂的功能。这种结合不仅提高了开发效率,还使得逻辑更加清晰易懂。
附录
FPGA 资源占用报告
以下是使用WebIDE实现前两个功能时的 FPGA 资源占用报告:
以下是使用Lattice Diamond实现所有功能集合的FPGA资源占用报告: