2025寒假练 - 基于小脚丫FPGA实现交通灯控制系统
该项目使用了小脚丫FPGA STEP BaseBoard V4.0套件,实现了交通灯控制系统的设计,它的主要功能为:红绿灯切换,环境亮度检测,环境较暗时开灯,接近时亮灯报警,蜂鸣器催促通过。
标签
FPGA
开发板
FuShenxiao
更新2025-03-17
21

本项目基于小脚丫FPGA STEP BaseBoard V4.0套件,包含小脚丫核心板及其扩展板,其结构如下所示。

FheM1anVmLBxLX0YIiIxNkkAIWf6

硬件介绍

该开发套件的核心板上包含Lattice的LCMXO2-4000HC核心两个八段数码管、8个LED灯、4个拨码开关以及4个按键。为了便于程序的下载,核心板上还使用LPC11U35FHI33芯片将核心芯片在PC端显示为一个大容量U盘,于是可以使用拖拽的方式将生成的.jed文件直接拖入该U盘实现程序的烧录。

扩展板的资源也是相当丰富的,包含多种传感器、LCD显示、数码管、蜂鸣器、矩阵键盘、WiFi、HDMI以及AD/DA模块,适合针对FPGA的入门学习。

本次任务包含四个小任务,即:

  • 在数码管上显示计时信息 - 图形化
  • 蜂鸣器报警 - 图形化
  • 接近传感器检测人员走近 -Verilog
  • 环境光感知,自动点亮路灯(小脚丫核心板上的单色LED)- Verilog

为了实现这几个任务,我将任务1和任务2分为一组,任务3和任务4分为一组,之后再将四个任务整合成一个项目。

任务1和任务2:红绿灯倒计时和蜂鸣器报警

对于任务1和任务2,需要用到核心板的数码管、RGB灯,以及扩展板上的蜂鸣器。

红绿灯的实现可以看作是一个状态机,红绿黄分别为一种状态,系统在这三种状态之间相互转换,如下图所示。

image.png

为了完成以上的状态转换,需要驱动数码管、三色LED灯以及蜂鸣器。

在图形化编程中,构建如下自定义模块,实现红绿灯的转换。其中,时钟信号和复位信号作为输入,三色LED灯的状态、高低两位数码管的状态以及蜂鸣器的输出作为模块的输出。

image.png

程序分析

在程序中首先定义三种状态机状态,即红灯绿灯黄灯

// 红绿黄三种状态
parameter RED = 2'b00;
parameter GREEN = 2'b01;
parameter YELLOW = 2'b10;

在实际红绿灯中,往往是一秒钟数字减一,因此首先需要对时钟信号进行分频,代码实现如下

// 时钟分频
wire sec_pulse = (main_cnt == 24'd11_999_999); // 1s脉冲

接着对LED灯进行控制,我们需要根据当前的状态确定RGB灯的输出

// LED控制
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state <= RED;
timer <= 12;
main_cnt <= 0;
led_reg <= 3'b011;
end
else begin
main_cnt <= (main_cnt == 11_999_999) ? 0 : main_cnt + 1;
if(sec_pulse) begin
case(state)
RED: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= GREEN;
timer <= 12;
led_reg <= 3'b110;
end
end
GREEN: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= YELLOW;
timer <= 3;
led_reg <= 3'b010;
end
end
YELLOW: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= RED;
timer <= 12;
led_reg <= 3'b011;
end
end
endcase
end
end
end

// LED输出
assign rgb = led_reg;

与此同时,我们用timer记录剩余时间,并将timer的值显示到数码管上,其中seg_table为0-9对应的数码管电平信号

// 数码管数据生成
always @(posedge clk) begin
if (timer > 9) begin
num_h <= 1;
num_l <= timer % 10;
end else begin
num_h <= 0;
num_l <= timer;
end
end

// 静态数码管输出
assign seg_h = seg_table[num_h];
assign seg_l = seg_table[num_l];

最后,设置蜂鸣器,我们需要蜂鸣器在绿灯时催促行人快速通过,因此需要对当前状态进行判断

// 蜂鸣器控制
always @(posedge clk) begin
buzz_cnt <= (buzz_cnt == 24'd2_999_999) ? 1'd0 : buzz_cnt + 1'd1;
end

// 蜂鸣器输出逻辑
assign buzzer = (state == GREEN) ? (buzz_cnt < 1_500_000) : 0;

完整代码实现如下

// 红绿黄三种状态
parameter RED = 2'b00;
parameter GREEN = 2'b01;
parameter YELLOW = 2'b10;

// 八段数码管编码表
reg [8:0] seg_table [0:9];
initial begin
seg_table[0] = 9'h3F; // 0
seg_table[1] = 9'h06; // 1
seg_table[2] = 9'h5B; // 2
seg_table[3] = 9'h4F; // 3
seg_table[4] = 9'h66; // 4
seg_table[5] = 9'h6D; // 5
seg_table[6] = 9'h7D; // 6
seg_table[7] = 9'h07; // 7
seg_table[8] = 9'h7F; // 8
seg_table[9] = 9'h6F; // 9
end

reg [1:0] state;
reg [4:0] timer;
reg [23:0] main_cnt;
reg [23:0] buzz_cnt;
reg [2:0] led_reg;
reg [3:0] num_h, num_l;

// 时钟分频
wire sec_pulse = (main_cnt == 24'd11_999_999); // 1s脉冲

// LED控制
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state <= RED;
timer <= 12;
main_cnt <= 0;
led_reg <= 3'b011;
end
else begin
main_cnt <= (main_cnt == 11_999_999) ? 0 : main_cnt + 1;
if(sec_pulse) begin
case(state)
RED: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= GREEN;
timer <= 12;
led_reg <= 3'b110;
end
end
GREEN: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= YELLOW;
timer <= 3;
led_reg <= 3'b010;
end
end
YELLOW: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= RED;
timer <= 12;
led_reg <= 3'b011;
end
end
endcase
end
end
end

// 数码管数据生成
always @(posedge clk) begin
if (timer > 9) begin
num_h <= 1;
num_l <= timer % 10;
end else begin
num_h <= 0;
num_l <= timer;
end
end

// 静态数码管输出
assign seg_h = seg_table[num_h];
assign seg_l = seg_table[num_l];

// 蜂鸣器控制
always @(posedge clk) begin
buzz_cnt <= (buzz_cnt == 24'd2_999_999) ? 1'd0 : buzz_cnt + 1'd1;
end

// 蜂鸣器输出逻辑
assign buzzer = (state == GREEN) ? (buzz_cnt < 1_500_000) : 0;

// LED输出
assign rgb = led_reg;

引脚分配

image.png

image.png

image.png

结果展示

经过代码综合和代码烧录,实现了简单的红绿灯

红灯

image.png

绿灯

image.png

黄灯

image.png

FPGA资源占用报告如下

Design Summary:
Number of registers: 62 out of 4635 (1%)
PFU registers: 62 out of 4320 (1%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 56 out of 2160 (3%)
SLICEs as Logic/ROM: 56 out of 2160 (3%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 26 out of 2160 (1%)
Number of LUT4s: 111 out of 4320 (3%)
Number used as logic LUTs: 59
Number used as distributed RAM: 0
Number used as ripple logic: 52
Number used as shift registers: 0
Number of PIO sites used: 24 + 4(JTAG) out of 105 (27%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 0 out of 2 (0%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)
Notes:-
1. Total number of LUT4s = (Number of logic LUT4s) + 2*(Number of distributed RAMs) + 2*(Number of ripple logic)
2. Number of logic LUT4s does not include count of distributed RAM and ripple logic.
Number of clocks: 1
Net clk_c: 35 loads, 35 rising, 0 falling (Driver: PIO clk )
Number of Clock Enables: 3
Net v866434/clk_c_enable_9: 3 loads, 3 LSLICEs
Net v866434/clk_c_enable_5: 1 loads, 1 LSLICEs
Net v866434/clk_c_enable_4: 1 loads, 1 LSLICEs
Number of LSRs: 2
Net v866434/clk_c_enable_4: 13 loads, 13 LSLICEs
Net v866434/n615: 13 loads, 13 LSLICEs
Number of nets driven by tri-state buffers: 0
Top 10 highest fanout non-clock nets:
Net v866434/clk_c_enable_4: 17 loads
Net v866434/n615: 13 loads
Net v866434/timer_0: 10 loads
Net v866434/timer_1: 9 loads
Net v866434/state_0: 8 loads
Net v866434/state_1: 8 loads
Net v866434/timer_2: 8 loads
Net v866434/timer_3: 8 loads
Net v866434/num_l_0: 7 loads
Net v866434/num_l_1: 7 loads


Number of warnings: 0
Number of errors: 0



Total REAL Time: 0 secs
Peak Memory Usage: 180 MB

Dumping design to file PRJ_17303_implement_map.ncd.

ncd2vdb "PRJ_17303_implement_map.ncd" ".vdbs/PRJ_17303_implement_map.vdb"

Loading device for application ncd2vdb from file 'xo2c4000.nph' in environment: ispfpga.

mpartrce -p "PRJ_17303_implement.p2t" -f "PRJ_17303_implement.p3t" -tf "PRJ_17303_implement.pt" "PRJ_17303_implement_map.ncd" "PRJ_17303_implement.ncd"

代码链接

任务1和任务2图形化界面

任务3和任务4:亮度检测和接近检测

对于任务3和任务4基于官方代码lab8_prox_detect修改实现,其实现流程图如下

image.png

代码分析

首先使用IIC驱动获取RPR-0521RS的数据,由于该传感器为环境光和接近传感器,因此其得到的结果为环境光和接近程度二者数值的结合,需要将二者的结果分离,分别得到相应的传感器值,最后根据采集到的数据驱动数码管输出当前亮度、当亮度低于200勒克斯时点亮核心板上8个LED灯,当有人接近时点亮核心板上的RGB灯。

传感器IIC驱动的代码实现如下

module rpr0521rs_driver(
input clk, //系统时钟
input rst_n, //系统复位,低有效

output i2c_scl, //I2C总线SCL
inout i2c_sda, //I2C总线SDA

output reg dat_valid, //数据有效脉冲
output reg [15:0] ch0_dat, //ALS数据
output reg [15:0] ch1_dat, //IR数据
output reg [15:0] prox_dat //Prox数据
);

parameter CNT_NUM = 15;

localparam IDLE = 4'd0;
localparam MAIN = 4'd1;
localparam MODE1 = 4'd2;
localparam MODE2 = 4'd3;
localparam START = 4'd4;
localparam WRITE = 4'd5;
localparam READ = 4'd6;
localparam STOP = 4'd7;
localparam DELAY = 4'd8;

localparam ACK = 1'b0;
localparam NACK = 1'b1;

//使用计数器分频产生400KHz时钟信号clk_400khz
reg clk_400khz;
reg [9:0] cnt_400khz;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_400khz <= 10'd0;
else if(cnt_400khz == CNT_NUM-1)
cnt_400khz <= 10'd0;
else
cnt_400khz <= cnt_400khz + 1'b1;
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
clk_400khz <= 1'b0;
else if(cnt_400khz == CNT_NUM-1)
clk_400khz <= ~clk_400khz;
end

reg scl,sda,ack,ack_flag;
reg [3:0] cnt, cnt_main, cnt_mode1, cnt_mode2, cnt_start, cnt_write, cnt_read, cnt_stop;
reg [7:0] data_wr, dev_addr, reg_addr, reg_data, data_r, dat_l, dat_h;
reg [23:0] cnt_delay, num_delay;
reg [3:0] state, state_back;

always@(posedge clk_400khz or negedge rst_n) begin
if(!rst_n) begin //如果按键复位,将相关数据初始化
scl <= 1'd1; sda <= 1'd1; ack <= ACK; ack_flag <= 1'b0; cnt <= 1'b0;
cnt_main <= 1'b0; cnt_mode1 <= 1'b0; cnt_mode2 <= 1'b0;
cnt_start <= 1'b0; cnt_write <= 1'b0; cnt_read <= 1'b0; cnt_stop <= 1'b0;
cnt_delay <= 1'b0; num_delay <= 24'd4800;
state <= IDLE; state_back <= IDLE;
end else begin
case(state)
IDLE:begin //软件自复位,主要用于程序跑飞后的处理
scl <= 1'd1; sda <= 1'd1; ack <= ACK; ack_flag <= 1'b0; cnt <= 1'b0;
cnt_main <= 1'b0; cnt_mode1 <= 1'b0; cnt_mode2 <= 1'b0;
cnt_start <= 1'b0; cnt_write <= 1'b0; cnt_read <= 1'b0; cnt_stop <= 1'b0;
cnt_delay <= 1'b0; num_delay <= 24'd4800;
state <= MAIN; state_back <= IDLE;
end
MAIN:begin
if(cnt_main >= 4'd11) cnt_main <= 4'd4; //写完控制指令后循环读数据
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
4'd0: begin dev_addr <= 7'h38; reg_addr <= 8'h40; reg_data <= 8'h0a; state <= MODE1; end //写入配置
4'd1: begin dev_addr <= 7'h38; reg_addr <= 8'h41; reg_data <= 8'hc6; state <= MODE1; end //写入配置
4'd2: begin dev_addr <= 7'h38; reg_addr <= 8'h42; reg_data <= 8'h02; state <= MODE1; end //写入配置
4'd3: begin dev_addr <= 7'h38; reg_addr <= 8'h43; reg_data <= 8'h01; state <= MODE1; end //写入配置
4'd4: begin state <= DELAY; dat_valid <= 1'b0; end //12ms延时
4'd5: begin dev_addr <= 7'h38; reg_addr <= 8'h44; state <= MODE2; end //读取配置
4'd6: begin prox_dat<= {dat_h,dat_l}; end //读取数据
4'd7: begin dev_addr <= 7'h38; reg_addr <= 8'h46; state <= MODE2; end //读取配置
4'd8: begin ch0_dat <= {dat_h,dat_l}; end //读取数据
4'd9: begin dev_addr <= 7'h38; reg_addr <= 8'h48; state <= MODE2; end //读取配置
4'd10: begin ch1_dat <= {dat_h,dat_l}; end //读取数据
4'd11: begin dat_valid <= 1'b1; end //读取数据
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
MODE1:begin //单次写操作
if(cnt_mode1 >= 4'd5) cnt_mode1 <= 1'b0; //对START中的子状态执行控制cnt_start
else cnt_mode1 <= cnt_mode1 + 1'b1;
state_back <= MODE1;
case(cnt_mode1)
4'd0: begin state <= START; end //I2C通信时序中的START
4'd1: begin data_wr <= dev_addr<<1; state <= WRITE; end //设备地址
4'd2: begin data_wr <= reg_addr; state <= WRITE; end //寄存器地址
4'd3: begin data_wr <= reg_data; state <= WRITE; end //写入数据
4'd4: begin state <= STOP; end //I2C通信时序中的STOP
4'd5: begin state <= MAIN; end //返回MAIN
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
MODE2:begin //两次读操作
if(cnt_mode2 >= 4'd10) cnt_mode2 <= 1'b0; //对START中的子状态执行控制cnt_start
else cnt_mode2 <= cnt_mode2 + 1'b1;
state_back <= MODE2;
case(cnt_mode2)
4'd0: begin state <= START; end //I2C通信时序中的START
4'd1: begin data_wr <= dev_addr<<1; state <= WRITE; end //设备地址
4'd2: begin data_wr <= reg_addr; state <= WRITE; end //寄存器地址
4'd3: begin state <= START; end //I2C通信时序中的START
4'd4: begin data_wr <= (dev_addr<<1)|8'h01; state <= WRITE; end //设备地址
4'd5: begin ack <= ACK; state <= READ; end //读寄存器数据
4'd6: begin dat_l <= data_r; end
4'd7: begin ack <= NACK; state <= READ; end //读寄存器数据
4'd8: begin dat_h <= data_r; end
4'd9: begin state <= STOP; end //I2C通信时序中的STOP
4'd10: begin state <= MAIN; end //返回MAIN
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
START:begin //I2C通信时序中的起始START
if(cnt_start >= 3'd5) cnt_start <= 1'b0; //对START中的子状态执行控制cnt_start
else cnt_start <= cnt_start + 1'b1;
case(cnt_start)
3'd0: begin sda <= 1'b1; scl <= 1'b1; end //将SCL和SDA拉高,保持4.7us以上
3'd1: begin sda <= 1'b1; scl <= 1'b1; end //clk_400khz每个周期2.5us,需要两个周期
3'd2: begin sda <= 1'b0; end //SDA拉低到SCL拉低,保持4.0us以上
3'd3: begin sda <= 1'b0; end //clk_400khz每个周期2.5us,需要两个周期
3'd4: begin scl <= 1'b0; end //SCL拉低,保持4.7us以上
3'd5: begin scl <= 1'b0; state <= state_back; end //clk_400khz每个周期2.5us,需要两个周期,返回MAIN
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
WRITE:begin //I2C通信时序中的写操作WRITE和相应判断操作ACK
if(cnt <= 3'd6) begin //共需要发送8bit的数据,这里控制循环的次数
if(cnt_write >= 3'd3) begin cnt_write <= 1'b0; cnt <= cnt + 1'b1; end
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end
end else begin
if(cnt_write >= 3'd7) begin cnt_write <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end
end
case(cnt_write)
//按照I2C的时序传输数据
3'd0: begin scl <= 1'b0; sda <= data_wr[7-cnt]; end //SCL拉低,并控制SDA输出对应的位
3'd1: begin scl <= 1'b1; end //SCL拉高,保持4.0us以上
3'd2: begin scl <= 1'b1; end //clk_400khz每个周期2.5us,需要两个周期
3'd3: begin scl <= 1'b0; end //SCL拉低,准备发送下1bit的数据
//获取从设备的响应信号并判断
3'd4: begin sda <= 1'bz; end //释放SDA线,准备接收从设备的响应信号
3'd5: begin scl <= 1'b1; end //SCL拉高,保持4.0us以上
3'd6: begin ack_flag <= i2c_sda; end //获取从设备的响应信号并判断
3'd7: begin scl <= 1'b0; if(ack_flag)state <= state; else state <= state_back; end //SCL拉低,如果不应答循环写
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
READ:begin //I2C通信时序中的读操作READ和返回ACK的操作
if(cnt <= 3'd6) begin //共需要接收8bit的数据,这里控制循环的次数
if(cnt_read >= 3'd3) begin cnt_read <= 1'b0; cnt <= cnt + 1'b1; end
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end
end else begin
if(cnt_read >= 3'd7) begin cnt_read <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end
end
case(cnt_read)
//按照I2C的时序接收数据
3'd0: begin scl <= 1'b0; sda <= 1'bz; end //SCL拉低,释放SDA线,准备接收从设备数据
3'd1: begin scl <= 1'b1; end //SCL拉高,保持4.0us以上
3'd2: begin data_r[7-cnt] <= i2c_sda; end //读取从设备返回的数据
3'd3: begin scl <= 1'b0; end //SCL拉低,准备接收下1bit的数据
//向从设备发送响应信号
3'd4: begin sda <= ack; end //发送响应信号,将前面接收的数据锁存
3'd5: begin scl <= 1'b1; end //SCL拉高,保持4.0us以上
3'd6: begin scl <= 1'b1; end //SCL拉高,保持4.0us以上
3'd7: begin scl <= 1'b0; state <= state_back; end //SCL拉低,返回MAIN状态
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
STOP:begin //I2C通信时序中的结束STOP
if(cnt_stop >= 3'd5) cnt_stop <= 1'b0; //对STOP中的子状态执行控制cnt_stop
else cnt_stop <= cnt_stop + 1'b1;
case(cnt_stop)
3'd0: begin sda <= 1'b0; end //SDA拉低,准备STOP
3'd1: begin sda <= 1'b0; end //SDA拉低,准备STOP
3'd2: begin scl <= 1'b1; end //SCL提前SDA拉高4.0us
3'd3: begin scl <= 1'b1; end //SCL提前SDA拉高4.0us
3'd4: begin sda <= 1'b1; end //SDA拉高
3'd5: begin sda <= 1'b1; state <= state_back; end //完成STOP操作,返回MAIN状态
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
DELAY:begin //12ms延时
if(cnt_delay >= num_delay) begin
cnt_delay <= 1'b0;
state <= MAIN;
end else cnt_delay <= cnt_delay + 1'b1;
end
default:;
endcase
end
end

assign i2c_scl = scl; //对SCL端口赋值
assign i2c_sda = sda; //对SDA端口赋值

endmodule

为了实现扩展板上的数码管显示,首先定义二进制转BCD码的代码

module bin_to_bcd
(
input rst_n, //系统复位,低有效
input [31:0] bin_code, //需要进行BCD转码的二进制数据
output reg [31:0] bcd_code //转码后的BCD码型数据输出
);

/*
此模块为了将ADC采样的数据转换为我们常用的十进制显示而存在,
主要知识涉及数学中不同制式数据的转换,详细原理这里不做介绍,去百度搜索<FPGA 二进制转BCD码>可得
*/

reg [67:0] shift_reg;
always@(bin_code or rst_n)begin
shift_reg = {36'h0,bin_code};
if(!rst_n)
bcd_code = 0;
else begin
repeat(32) begin //循环32次
//BCD码各位数据作满5加3操作,

if (shift_reg[35:32] >= 5)
shift_reg[35:32] = shift_reg[35:32] + 2'b11;
if (shift_reg[39:36] >= 5)
shift_reg[39:36] = shift_reg[39:36] + 2'b11;
if (shift_reg[43:40] >= 5)
shift_reg[43:40] = shift_reg[43:40] + 2'b11;
if (shift_reg[47:44] >= 5)
shift_reg[47:44] = shift_reg[47:44] + 2'b11;
if (shift_reg[51:48] >= 5)
shift_reg[51:48] = shift_reg[51:48] + 2'b11;
if (shift_reg[55:52] >= 5)
shift_reg[55:52] = shift_reg[55:52] + 2'b11;
if (shift_reg[59:56] >= 5)
shift_reg[59:56] = shift_reg[59:56] + 2'b11;
if (shift_reg[63:60] >= 5)
shift_reg[63:60] = shift_reg[63:60] + 2'b11;
if (shift_reg[67:64] >= 5)
shift_reg[67:64] = shift_reg[67:64] + 2'b11;
shift_reg = shift_reg << 1;

end
bcd_code = shift_reg[67:36];
end
end

endmodule

接着定义数码管的驱动代码

module segment_scan(
input clk, //系统时钟 12MHz
input rst_n, //系统复位 低有效
input [3:0] dat_1, //SEG1 显示的数据输入
input [3:0] dat_2, //SEG2 显示的数据输入
input [3:0] dat_3, //SEG3 显示的数据输入
input [3:0] dat_4, //SEG4 显示的数据输入
input [3:0] dat_5, //SEG5 显示的数据输入
input [3:0] dat_6, //SEG6 显示的数据输入
input [3:0] dat_7, //SEG7 显示的数据输入
input [3:0] dat_8, //SEG8 显示的数据输入
input [7:0] dat_en, //数码管数据位显示使能,[MSB~LSB]=[SEG1~SEG8]
input [7:0] dot_en, //数码管小数点位显示使能,[MSB~LSB]=[SEG1~SEG8]
output reg seg_rck, //74HC595的RCK管脚
output reg seg_sck, //74HC595的SCK管脚
output reg seg_din //74HC595的SER管脚
);

localparam CNT_40KHz = 300; //分频系数

localparam IDLE = 3'd0;
localparam MAIN = 3'd1;
localparam WRITE = 3'd2;
localparam LOW = 1'b0;
localparam HIGH = 1'b1;

//创建数码管的字库,字库数据依段码顺序有关
//这里字库数据[MSB~LSB]={G,F,E,D,C,B,A}
reg[6:0] seg [15:0];
always @(negedge rst_n) begin
seg[0] = 7'h3f; // 0
seg[1] = 7'h06; // 1
seg[2] = 7'h5b; // 2
seg[3] = 7'h4f; // 3
seg[4] = 7'h66; // 4
seg[5] = 7'h6d; // 5
seg[6] = 7'h7d; // 6
seg[7] = 7'h07; // 7
seg[8] = 7'h7f; // 8
seg[9] = 7'h6f; // 9
seg[10] = 7'h77; // A
seg[11] = 7'h7c; // b
seg[12] = 7'h39; // C
seg[13] = 7'h5e; // d
seg[14] = 7'h79; // E
seg[15] = 7'h71; // F
end

//计数器对系统时钟信号进行计数
reg [9:0] cnt = 1'b0;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 1'b0;
else if(cnt>=(CNT_40KHz-1)) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
end

//根据计数器计数的周期产生分频的脉冲信号
reg clk_40khz = 1'b0;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) clk_40khz <= 1'b0;
else if(cnt<(CNT_40KHz>>1)) clk_40khz <= 1'b0;
else clk_40khz <= 1'b1;
end

//使用状态机完成数码管的扫描和74HC595时序的实现
reg [15:0] data;
reg [2:0] cnt_main;
reg [5:0] cnt_write;
reg [2:0] state = IDLE;
always@(posedge clk_40khz or negedge rst_n) begin
if(!rst_n) begin //复位状态下,各寄存器置初值
state <= IDLE;
cnt_main <= 3'd0; cnt_write <= 6'd0;
seg_din <= 1'b0; seg_sck <= LOW; seg_rck <= LOW;
end else begin
case(state)
IDLE:begin //IDLE作为第一个状态,相当于软复位
state <= MAIN;
cnt_main <= 3'd0; cnt_write <= 6'd0;
seg_din <= 1'b0; seg_sck <= LOW; seg_rck <= LOW;
end
MAIN:begin
cnt_main <= cnt_main + 1'b1;
state <= WRITE; //在配置完发给74HC595的数据同时跳转至WRITE状态,完成串行时序
case(cnt_main)
//对8位数码管逐位扫描
//data [15:8]为段选, [7:0]为位选
3'd0: data <= {{dot_en[7],seg[dat_1]},dat_en[7]?8'hfe:8'hff};
3'd1: data <= {{dot_en[6],seg[dat_2]},dat_en[6]?8'hfd:8'hff};
3'd2: data <= {{dot_en[5],seg[dat_3]},dat_en[5]?8'hfb:8'hff};
3'd3: data <= {{dot_en[4],seg[dat_4]},dat_en[4]?8'hf7:8'hff};
3'd4: data <= {{dot_en[3],seg[dat_5]},dat_en[3]?8'hef:8'hff};
3'd5: data <= {{dot_en[2],seg[dat_6]},dat_en[2]?8'hdf:8'hff};
3'd6: data <= {{dot_en[1],seg[dat_7]},dat_en[1]?8'hbf:8'hff};
3'd7: data <= {{dot_en[0],seg[dat_8]},dat_en[0]?8'h7f:8'hff};
default: data <= {8'h00,8'hff};
endcase
end
WRITE:begin
if(cnt_write >= 6'd33) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
//74HC595是串行转并行的芯片,3路输入可产生8路输出,而且可以级联使用
//74HC595的时序实现,参考74HC595的芯片手册
6'd0: begin seg_sck <= LOW; seg_din <= data[15]; end //SCK下降沿时SER更新数据
6'd1: begin seg_sck <= HIGH; end //SCK上升沿时SER数据稳定
6'd2: begin seg_sck <= LOW; seg_din <= data[14]; end
6'd3: begin seg_sck <= HIGH; end
6'd4: begin seg_sck <= LOW; seg_din <= data[13]; end
6'd5: begin seg_sck <= HIGH; end
6'd6: begin seg_sck <= LOW; seg_din <= data[12]; end
6'd7: begin seg_sck <= HIGH; end
6'd8: begin seg_sck <= LOW; seg_din <= data[11]; end
6'd9: begin seg_sck <= HIGH; end
6'd10: begin seg_sck <= LOW; seg_din <= data[10]; end
6'd11: begin seg_sck <= HIGH; end
6'd12: begin seg_sck <= LOW; seg_din <= data[9]; end
6'd13: begin seg_sck <= HIGH; end
6'd14: begin seg_sck <= LOW; seg_din <= data[8]; end
6'd15: begin seg_sck <= HIGH; end
6'd16: begin seg_sck <= LOW; seg_din <= data[7]; end
6'd17: begin seg_sck <= HIGH; end
6'd18: begin seg_sck <= LOW; seg_din <= data[6]; end
6'd19: begin seg_sck <= HIGH; end
6'd20: begin seg_sck <= LOW; seg_din <= data[5]; end
6'd21: begin seg_sck <= HIGH; end
6'd22: begin seg_sck <= LOW; seg_din <= data[4]; end
6'd23: begin seg_sck <= HIGH; end
6'd24: begin seg_sck <= LOW; seg_din <= data[3]; end
6'd25: begin seg_sck <= HIGH; end
6'd26: begin seg_sck <= LOW; seg_din <= data[2]; end
6'd27: begin seg_sck <= HIGH; end
6'd28: begin seg_sck <= LOW; seg_din <= data[1]; end
6'd29: begin seg_sck <= HIGH; end
6'd30: begin seg_sck <= LOW; seg_din <= data[0]; end
6'd31: begin seg_sck <= HIGH; end
6'd32: begin seg_rck <= HIGH; end //当16位数据传送完成后RCK拉高,输出生效
6'd33: begin seg_rck <= LOW; state <= MAIN; end
default: ;
endcase
end
default: state <= IDLE;
endcase
end
end

endmodule

由于传感器的输入结果为光亮信号和接近信号的结合,因此首先定义数据处理代码

其输入输出代码如下

module decoder(
input rst_n,
input dat_valid,
input [15:0] prox_dat,
input [15:0] ch0_dat,
input [15:0] ch1_dat,
output [31:0] lux_data,
output reg [2:0] Y_out
);

实现输入信号分离的代码如下

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'h800)||((prox_dat1-prox_dat0) >= 16'h800))
prox_dat2 <= prox_dat2;
else
prox_dat2 <= prox_dat0;
end

特别的,如果接近信号表明当前位置过于靠近,则点亮核心板上的RGB灯,并显示白色

always@(prox_dat2[11:9]) begin
case (prox_dat2[11:9])
3'b000: Y_out = 3'b111;
3'b001: Y_out = 3'b111;
3'b010: Y_out = 3'b111;
3'b011: Y_out = 3'b111;
3'b100: Y_out = 3'b000;
3'b101: Y_out = 3'b000;
3'b110: Y_out = 3'b000;
3'b111: Y_out = 3'b000;
default:Y_out = 3'b111;
endcase
end

最后将光亮信号转化为以勒克斯为单位的信号,并显示在扩展板的数码管上

wire	[31:0] data0,data1;
reg [31:0] lux;

assign data0 = ch0_dat ;
assign data1 = ch1_dat ;

always@(data0 or data1)
if(data1 < data0*595)
lux = data0*1682 - data1*1877;
else if(data1 < data0*1015)
lux = data0*644 - data1* 132;
else if(data1 < data0*1352)
lux = data0*756 - data1*243;
else if((data1 < data0*3053))
lux = data0*766 - data1*25;
else
lux = 0;

wire [31:0] T_data_bcd;
bin_to_bcd u1
(
.rst_n (rst_n ),
.bin_code (lux ),
.bcd_code (T_data_bcd )
);
assign lux_data = T_data_bcd;
endmodule

对于顶层文件,需要将以上这些驱动代码进行整合

首先定义输入输出

module prox_detect(
input clk,
input rst_n,

output i2c_scl,
inout i2c_sda,
output seg_rck,
output seg_sck,
output seg_din,

output reg [7:0] led, //led
output [2:0] led_rgb
);

IIC驱动RPR0521RS传感器

wire dat_valid;
wire [15:0] ch0_dat, ch1_dat, prox_dat;
wire [31:0] lux_data;
rpr0521rs_driver u1(
.clk (clk ),
.rst_n (rst_n ),
.i2c_scl (i2c_scl ),
.i2c_sda (i2c_sda ),

.dat_valid (dat_valid ),
.ch0_dat (ch0_dat ),
.ch1_dat (ch1_dat ),
.prox_dat (prox_dat )
);

根据IIC读取到的数据进行数据处理并根据结果输出对应电平到RGB灯和数码管灯外设上

decoder u2(
.rst_n (rst_n),
.dat_valid (dat_valid ),
.ch0_dat (ch0_dat ),
.ch1_dat (ch1_dat ),
.prox_dat (prox_dat ),
.lux_data (lux_data ),
.Y_out (led_rgb )
);

如果当前环境较暗(亮度低于200勒克斯),则点亮核心板上的8个LED灯

always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
led <= 8'b1111_1111;
else if(lux_data[31:08] < 200)
led <= 8'b0000_0000;
else
led <= 8'b1111_1111;

最后,驱动扩展板上的数码管显示当前环境光亮度

segment_scan u4(
.clk(clk),
.rst_n(rst_n),
.dat_1(lux_data[31:28] ),
.dat_2(lux_data[27:24] ),
.dat_3(lux_data[23:20] ),
.dat_4(lux_data[19:16] ),
.dat_5(lux_data[15:12] ),
.dat_6(lux_data[11:08] ),
.dat_7(lux_data[07:04] ),
.dat_8(lux_data[03:00] ),
.dat_en(8'b1111_1111 ),
.dot_en(8'b0000_0100 ),
.seg_rck(seg_rck ),
.seg_sck(seg_sck ),
.seg_din(seg_din )
);

endmodule

引脚分配

2025-03-09 18-18-52 的屏幕截图.png

结果展示

经过代码综合和烧录即可实现环境光过暗时点亮核心板上8位LED灯,以及在过于接近传感器时核心板上的RGB灯亮起用于警告。

扩展板上的数码管显示当前光亮

image.png

环境光过暗时点亮核心板上8位LED灯

image.png

过于接近传感器时RGB灯亮用于警告

image.png

FPGA资源占用报告如下

Design Summary
Number of registers: 545 out of 4635 (12%)
PFU registers: 540 out of 4320 (13%)
PIO registers: 5 out of 315 (2%)
Number of SLICEs: 1231 out of 2160 (57%)
SLICEs as Logic/ROM: 1231 out of 2160 (57%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 496 out of 2160 (23%)
Number of LUT4s: 2460 out of 4320 (57%)
Number used as logic LUTs: 1468
Number used as distributed RAM: 0
Number used as ripple logic: 992
Number used as shift registers: 0
Number of PIO sites used: 18 + 4(JTAG) out of 105 (21%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 0 out of 2 (0%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)
Notes:-
1. Total number of LUT4s = (Number of logic LUT4s) + 2*(Number of
distributed RAMs) + 2*(Number of ripple logic)
2. Number of logic LUT4s does not include count of distributed RAM and
ripple logic.
Number of clocks: 4
Net clk_c: 14 loads, 14 rising, 0 falling (Driver: PIO clk )
Net u4.clk_40khz: 20 loads, 20 rising, 0 falling (Driver: u4/clk_40khz )

Net u1.clk_400khz: 251 loads, 251 rising, 0 falling (Driver: u1/clk_400khz
)
Net dat_valid: 17 loads, 17 rising, 0 falling (Driver: u1/dat_valid )
Number of Clock Enables: 32
Net un1_state_3_0_a2_0: 1 loads, 0 LSLICEs
Net data_rce[7]: 1 loads, 0 LSLICEs
Net un1_scl_2_sqmuxa_0: 1 loads, 0 LSLICEs
Net u4/data_1_sqmuxa: 10 loads, 10 LSLICEs
Net u4/N_119_i: 4 loads, 4 LSLICEs
Net un1_state_5_0_0: 1 loads, 0 LSLICEs
Net un1_state_2_0_a2_0_a2: 1 loads, 0 LSLICEs
Net u2/un1_prox_dat0_2lto15: 1 loads, 1 LSLICEs
Net u1.ch1_dat_1_sqmuxa: 54 loads, 54 LSLICEs
Net u1.ch0_dat_1_sqmuxa_1: 123 loads, 123 LSLICEs
Net u1/N_128_i: 1 loads, 1 LSLICEs
Net u1/N_33_i: 3 loads, 3 LSLICEs
Net u1/state_RNI7P7KS[1]: 2 loads, 2 LSLICEs
Net u1/data_r_RNO[1]: 1 loads, 1 LSLICEs
Net u1/data_r_RNO[0]: 1 loads, 1 LSLICEs
Net u1/prox_dat_1_sqmuxa: 8 loads, 8 LSLICEs
Net u1/un1_state_14_i: 1 loads, 1 LSLICEs
Net u1/N_131_i: 1 loads, 1 LSLICEs
Net u1/un1_state_15_i: 1 loads, 1 LSLICEs
Net u1/un1_cnt_400khz12_2_i: 2 loads, 2 LSLICEs
Net u1/un1_cnt_400khz12_3_i: 4 loads, 4 LSLICEs
Net u1/data_rce[2]: 1 loads, 1 LSLICEs
Net u1/data_rce[3]: 1 loads, 1 LSLICEs
Net u1/data_rce[4]: 1 loads, 1 LSLICEs
Net u1/data_rce[5]: 1 loads, 1 LSLICEs
Net u1/data_rce[6]: 1 loads, 1 LSLICEs
Net u1/N_140: 1 loads, 1 LSLICEs
Net u1/dat_l_1_sqmuxa: 4 loads, 4 LSLICEs
Net u1/dat_h_1_sqmuxa: 4 loads, 4 LSLICEs
Net u1/N_132_i: 2 loads, 2 LSLICEs
Net u1/N_37_i: 13 loads, 13 LSLICEs
Net u1/un37_i: 1 loads, 1 LSLICEs
Number of LSRs: 0
Number of nets driven by tri-state buffers: 0
Top 10 highest fanout non-clock nets:
Net u1.ch0_dat_1_sqmuxa_1: 123 loads
Net rst_n_c: 106 loads
Net u2/CO2_111_N_2L1_1: 57 loads
Net u1.ch1_dat_1_sqmuxa: 54 loads
Net u1.dat_h[0]: 54 loads
Net u1.dat_l[0]: 51 loads
Net u1.dat_l[4]: 51 loads
Net u1.dat_l[7]: 51 loads
Net u1.dat_h[3]: 50 loads
Net u1.dat_l[2]: 50 loads




Number of warnings: 0
Number of errors: 0

项目整合

最后将任务1和任务2的代码整合进任务3和任务4的代码,得到prox_detect.v代码如下

module prox_detect(
input clk,
input rst_n,

output i2c_scl, //I2CSCL
inout i2c_sda, //I2CSDA
output seg_rck, // 74HC595RCKܽ
output seg_sck, // 74HC595SCKܽ
output seg_din, // 74HC595SERܽ

output reg [7:0] led, //led
output buzzer,

output [8:0] seg_h,
output [8:0] seg_l,
output [2:0] led_rgb1,
output [2:0] led_rgb2
);

// 红绿黄三种状态
parameter RED = 2'b00;
parameter GREEN = 2'b01;
parameter YELLOW = 2'b10;

// 八段数码管编码表
reg [8:0] seg_table [0:9];
initial begin
seg_table[0] = 9'h3F; // 0
seg_table[1] = 9'h06; // 1
seg_table[2] = 9'h5B; // 2
seg_table[3] = 9'h4F; // 3
seg_table[4] = 9'h66; // 4
seg_table[5] = 9'h6D; // 5
seg_table[6] = 9'h7D; // 6
seg_table[7] = 9'h07; // 7
seg_table[8] = 9'h7F; // 8
seg_table[9] = 9'h6F; // 9
end

reg [1:0] state;
reg [4:0] timer;
reg [23:0] main_cnt;
reg [23:0] buzz_cnt;
reg [2:0] led_reg;
reg [3:0] num_h, num_l;

wire dat_valid;
wire [15:0] ch0_dat, ch1_dat, prox_dat;
wire [31:0] lux_data;
rpr0521rs_driver u1(
.clk (clk ),
.rst_n (rst_n ),
.i2c_scl (i2c_scl ), //I2CSCL
.i2c_sda (i2c_sda ), //I2CSDA

.dat_valid (dat_valid ),
.ch0_dat (ch0_dat ), //ALS
.ch1_dat (ch1_dat ), //IR
.prox_dat (prox_dat ) //Prox
);

decoder u2(
.rst_n (rst_n),
.dat_valid (dat_valid ),
.ch0_dat (ch0_dat ),
.ch1_dat (ch1_dat ),
.prox_dat (prox_dat ),
.lux_data (lux_data ),
.Y_out (led_rgb2 )
);

always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
led <= 8'b1111_1111;
else if(lux_data[31:08] < 200)
led <= 8'b0000_0000;
else
led <= 8'b1111_1111;

segment_scan u4(
.clk(clk), //12MHz
.rst_n(rst_n),
.dat_1(lux_data[31:28] ), //SEG1 ʾ
.dat_2(lux_data[27:24] ), //SEG2 ʾ
.dat_3(lux_data[23:20] ), //SEG3 ʾ
.dat_4(lux_data[19:16] ), //SEG4 ʾ
.dat_5(lux_data[15:12] ), //SEG5 ʾ
.dat_6(lux_data[11:08] ), //SEG6 ʾ
.dat_7(lux_data[07:04] ), //SEG7 ʾ
.dat_8(lux_data[03:00] ), //SEG8 ʾ
.dat_en(8'b1111_1111 ), //[MSB~LSB]=[SEG1~SEG8]
.dot_en(8'b0000_0100 ), //[MSB~LSB]=[SEG1~SEG8]
.seg_rck(seg_rck ), //74HC595RCKܽ
.seg_sck(seg_sck ), //74HC595SCKܽ
.seg_din(seg_din ) //74HC595SERܽ
);

// 时钟分频
wire sec_pulse = (main_cnt == 24'd11_999_999); // 1s脉冲

// LED控制
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state <= RED;
timer <= 12;
main_cnt <= 0;
led_reg <= 3'b011;
end
else begin
main_cnt <= (main_cnt == 11_999_999) ? 0 : main_cnt + 1;
if(sec_pulse) begin
case(state)
RED: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= GREEN;
timer <= 12;
led_reg <= 3'b110;
end
end
GREEN: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= YELLOW;
timer <= 3;
led_reg <= 3'b010;
end
end
YELLOW: begin
if(timer > 0) timer <= timer - 1;
else begin
state <= RED;
timer <= 12;
led_reg <= 3'b011;
end
end
endcase
end
end
end

// 数码管数据生成
always @(posedge clk) begin
if (timer > 9) begin
num_h <= 1;
num_l <= timer % 10;
end else begin
num_h <= 0;
num_l <= timer;
end
end

// 静态数码管输出
assign seg_h = seg_table[num_h];
assign seg_l = seg_table[num_l];

// 蜂鸣器控制
always @(posedge clk) begin
buzz_cnt <= (buzz_cnt == 24'd2_999_999) ? 1'd0 : buzz_cnt + 1'd1;
end

// 蜂鸣器输出逻辑
assign buzzer = (state == GREEN) ? (buzz_cnt < 1_500_000) : 0;

// LED输出
assign led_rgb1 = led_reg;

endmodule

引脚分配

2025-03-09 18-25-58 的屏幕截图.png

2025-03-09 18-27-03 的屏幕截图.png

结果展示

经过代码综合和代码烧录,实现结果即为以上任务的总和

实现结果如下图所示

image.png

FPGA资源占用报告如下

Design Summary
Number of registers: 611 out of 4635 (13%)
PFU registers: 604 out of 4320 (14%)
PIO registers: 7 out of 315 (2%)
Number of SLICEs: 1277 out of 2160 (59%)
SLICEs as Logic/ROM: 1277 out of 2160 (59%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 525 out of 2160 (24%)
Number of LUT4s: 2552 out of 4320 (59%)
Number used as logic LUTs: 1502
Number used as distributed RAM: 0
Number used as ripple logic: 1050
Number used as shift registers: 0
Number of PIO sites used: 40 + 4(JTAG) out of 105 (42%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 0 out of 2 (0%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)
Notes:-
1. Total number of LUT4s = (Number of logic LUT4s) + 2*(Number of
distributed RAMs) + 2*(Number of ripple logic)
2. Number of logic LUT4s does not include count of distributed RAM and
ripple logic.
Number of clocks: 4
Net clk_c: 53 loads, 53 rising, 0 falling (Driver: PIO clk )
Net u1.clk_400khz: 253 loads, 253 rising, 0 falling (Driver: u1/clk_400khz

)
Net u4.clk_40khz: 21 loads, 21 rising, 0 falling (Driver: u4/clk_40khz )
Net dat_valid: 17 loads, 17 rising, 0 falling (Driver: u1/dat_valid )
Number of Clock Enables: 33
Net N_60_i: 3 loads, 1 LSLICEs
Net data_rce[7]: 1 loads, 0 LSLICEs
Net u4/data_1_sqmuxa: 11 loads, 11 LSLICEs
Net u4/N_140_i: 4 loads, 4 LSLICEs
Net un1_state_5_0_0: 1 loads, 0 LSLICEs
Net un1_state_3_0_a2_0: 1 loads, 0 LSLICEs
Net un1_state_2_0_a2_0_a2: 1 loads, 0 LSLICEs
Net u2/un1_prox_dat0_2lto15: 1 loads, 1 LSLICEs
Net u1.ch1_dat_1_sqmuxa: 56 loads, 56 LSLICEs
Net u1.ch0_dat_1_sqmuxa_1: 123 loads, 123 LSLICEs
Net u1/N_128_i: 1 loads, 1 LSLICEs
Net u1/state_RNI7P7KS[1]: 2 loads, 2 LSLICEs
Net u1/prox_dat_1_sqmuxa: 8 loads, 8 LSLICEs
Net u1/data_r_RNO[0]: 1 loads, 1 LSLICEs
Net u1/N_131_i: 1 loads, 1 LSLICEs
Net u1/un1_state_15_i: 1 loads, 1 LSLICEs
Net u1/un1_state_14_i: 1 loads, 1 LSLICEs
Net u1/un1_cnt_400khz12_2_i: 2 loads, 2 LSLICEs
Net u1/un1_cnt_400khz12_3_i: 4 loads, 4 LSLICEs
Net u1/data_rce[1]: 1 loads, 1 LSLICEs
Net u1/data_rce[2]: 1 loads, 1 LSLICEs
Net u1/data_rce[3]: 1 loads, 1 LSLICEs
Net u1/data_rce[4]: 1 loads, 1 LSLICEs
Net u1/data_rce[5]: 1 loads, 1 LSLICEs
Net u1/data_rce[6]: 1 loads, 1 LSLICEs
Net u1/un1_ch0_dat_1_sqmuxa_0: 1 loads, 1 LSLICEs
Net u1/dat_l_1_sqmuxa: 4 loads, 4 LSLICEs
Net u1/dat_h_1_sqmuxa: 4 loads, 4 LSLICEs
Net u1/N_198_i: 3 loads, 3 LSLICEs
Net u1/N_272_i: 2 loads, 2 LSLICEs
Net u1/N_257_i: 13 loads, 13 LSLICEs
Net u1/un37_i: 1 loads, 1 LSLICEs
Net un1_scl_2_sqmuxa: 1 loads, 0 LSLICEs
Number of LSRs: 6
Net un2_buzz_cnt: 5 loads, 5 LSLICEs
Net u1/un1_state_7: 2 loads, 2 LSLICEs
Net u1/un1_state_5: 2 loads, 2 LSLICEs
Net u1/un1_state_13: 2 loads, 2 LSLICEs
Net u1/un1_state_4: 2 loads, 2 LSLICEs
Net N_120: 1 loads, 1 LSLICEs
Number of nets driven by tri-state buffers: 0
Top 10 highest fanout non-clock nets:
Net u1.ch0_dat_1_sqmuxa_1: 123 loads
Net rst_n_c: 98 loads
Net u1.ch1_dat_1_sqmuxa: 56 loads
Net u2/un1_lux49_3: 56 loads
Net u1.dat_h[0]: 54 loads
Net u4/cnt_main[2]: 53 loads
Net u1.dat_l[0]: 52 loads
Net u1.dat_l[5]: 52 loads
Net u1.dat_l[4]: 51 loads
Net u1.dat_l[7]: 51 loads





Number of warnings: 0
Number of errors: 0

项目中遇到的难题和解决方法

这是我第一次参加电子森林的寒假一起练活动,一直到大四了才找到这个宝藏网站。当时在选择平台时,由于再本科前几年的学习中常常使用STM32和ESP32,而且已经做了不少项目了,因此本次活动我选择了FPGA平台,小脚丫的这个扩展板的板载资源相当丰富,适合初学者学习。虽然我在大二的数字电子技术基础课程中已经对FPGA有了一定的讲解,但是那也仅仅停留在考试层面,因此这也算是我第一次真正意义上学习了FPGA。相较于以往C语言开发STM32和ESP32中往往使用库函数完成项目,FPGA的使用对数字逻辑和时序逻辑的要求更高,在代码编写过程中对逻辑梳理的要求远超嵌入式芯片。此外,在嵌入式代码中往往是同一时刻只能执行一个任务,利用中断服务解决突发的事件,而由于FPGA优秀的并行计算能力,我需要综合考虑每一时刻芯片需要做哪些事情,这种思维和嵌入式开发是完全不同的。在这个过程中,我参考了不少Verilog编写教程,也复习了大二时学过的数电,尤其是触发器相关的知识,最后也是能慢慢看懂代码,并且照着示例照猫画虎了。

对本次活动的心得体会

这是我第一次学习使用FPGA。就像第一次听正点原子的课学习STM32一样,一开始思路总是无法梳理,但是通过多看别人写的代码,多亲自写代码实现功能,通过这次寒假的学习也算是真正入门了FPGA。最后提一个小小的建议:希望电子森林能够为一些热门的开发板提供一些教学视频,虽然说教学文档已经相当详细了,但是对于新手而言没有手把手的教学总是会碰到这样或者那样的问题,在这个过程中往往会消耗大量的时间。

附件下载
task1_2.rar
任务1和任务2代码
task3_4.rar
任务3和任务4代码
task_total.rar
整合代码
团队介绍
傅沈骁是哈尔滨工业大学(威海)自动化专业本科大四学生
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号