在过去的十天里,我参加了FPGA的实习,学习了一系列关于FPGA开发的内容。在这段时间里,我掌握了数码管、呼吸灯、按键消抖、LED流水灯等基本概念,并学会了编写测试文件和顶层文件。以下是我对这些内容的总结和归纳:
呼吸灯
呼吸灯是一种能够呈现渐变效果的灯光,类似于人类的呼吸。在FPGA开发中,我们可以使用PWM(脉宽调制)技术来实现呼吸灯的效果。PWM是一种通过调整高电平和低电平的占空比来控制信号的技术。通过改变灯光的亮度和周期,可以让灯光呈现渐变效果。在FPGA中,我们可以使用计数器和比较器(控制占空比)来生成PWM信号,并将其作为控制信号来控制LED的亮度。
module pwm_led(
input clk,
input rst_n,
output reg[3:0] led
);
parameter TIME_US = 6'd49;//微秒
parameter TIME_MS = 10'd999;//毫秒(每50*20微秒=一毫秒)
parameter TIME_S = 10'd999;//秒(每1000毫秒=一秒)
reg [5:0]cnt_us;
reg [9:0]cnt_ms;
reg [9:0]cnt_s;
reg flag;
wire add_cnt_us;//us计数器开始信号
wire end_cnt_us;//us计数器结束信号
wire add_cnt_ms;//ms计数器开始信号
wire end_cnt_ms;//ms计数器结束信号
wire add_cnt_s;//s计数器开始信号
wire end_cnt_s;//s计数器结束信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_us <= 6'd0;
end
else if(add_cnt_us) begin
if (end_cnt_us) begin
cnt_us <= 6'd0;
end
else begin
cnt_us <= cnt_us + 1'd1;
end
end
else begin
cnt_us <= cnt_us;
end
end
assign add_cnt_us = 1'b1;//开始计数条件
assign end_cnt_us = (add_cnt_us) && (cnt_us == TIME_US);//结束计数条件(当cnt_us计满50次(50*20ns=1ms)时,归零并重新计数)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_ms <= 10'd0;
end
else if(add_cnt_ms)begin
if(end_cnt_ms)begin
cnt_ms <= 10'd0;
end
else begin
cnt_ms <= cnt_ms + 1'd1;
end
end
else begin
cnt_ms <= cnt_ms;
end
end
assign add_cnt_ms = end_cnt_us;
assign end_cnt_ms = (add_cnt_ms) && (cnt_ms == TIME_MS);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_s <= 10'd0;
end
else if(add_cnt_s) begin
if (end_cnt_s) begin
cnt_s <= 10'd0;
end
else begin
cnt_s <= cnt_s + 1'd1;
end
end
else begin
cnt_s <= cnt_s;
end
end
assign add_cnt_s = end_cnt_ms;
assign end_cnt_s = (add_cnt_s) && (cnt_s == TIME_S);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
flag <= 1'b0;
end
else if(end_cnt_s)begin
flag <= ~flag;
end
else begin
flag <= flag;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
led <= 4'b0000;
end
else if (!flag)begin
led <= {(cnt_s > cnt_ms),(cnt_s > cnt_ms),(cnt_s > cnt_ms),(cnt_s > cnt_ms)};
end
else if (flag)begin
led <= {(cnt_s < cnt_ms),(cnt_s < cnt_ms),(cnt_s < cnt_ms),(cnt_s < cnt_ms)};
end
else begin
led <= led;
end
end
endmodule
数码管
数码管是一种用于显示数字或字符的设备。常见的数码管有共阳极数码管和共阴极数码管。共阳极数码管是指数码管的阳极连接在一起,通过控制各个阴极的亮灭来显示不同的数字或字符。而共阴极数码管则是指数码管的阴极连接在一起,通过控制各个阳极的亮灭来显示不同的数字或字符。在FPGA开发中,我们可以使用逻辑电路来控制数码管的显示内容,利用时序设计来实现数字的静态显示和动态扫描显示。
module seg_led_static(
input clk,
input rst_n,
input flag,
output reg [7:0]seg, //八位的段选信号
output reg [5:0]sel //六位的位选信号
);
reg [3:0]num;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
sel <= 6'b111_111;//6位数码管全关
end
else begin
sel <= 6'b000_000;//6位数码管全开
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
num <= 4'h0;
end
else if(flag) begin
num <= num + 1'h1;
end
else begin
num <= num;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
seg <= 8'b0000_0000;
end
else begin
case (num)
4'h0: seg <= 8'b1100_0000;//匹配到后参考共阳极真值表
4'h1: seg <= 8'b1111_1001;
4'h2: seg <= 8'b1010_0100;
4'h3: seg <= 8'b1011_0000;
4'h4: seg <= 8'b1001_1001;
4'h5: seg <= 8'b1001_0010;
4'h6: seg <= 8'b1000_0010;
4'h7: seg <= 8'b1111_1000;
4'h8: seg <= 8'b1000_0000;
4'h9: seg <= 8'b1001_0000;
4'ha: seg <= 8'b1000_1000;
4'hb: seg <= 8'b1000_0011;
4'hc: seg <= 8'b1100_0110;
4'hd: seg <= 8'b1010_0001;
4'he: seg <= 8'b1000_0110;
4'hf: seg <= 8'b1000_1110;
default : seg <= 8'b1100_0000;
endcase
end
end
endmodule
按键消抖
在实际应用中,由于按键机械结构的影响,按键可能会产生抖动现象,导致系统误触发。为了解决这个问题,我们需要对按键进行消抖处理。以下是关于FPGA按键消抖的一些要点:
抖动原因:按键的机械结构和接触方式会导致在按下或释放时产生短时间内的不稳定信号,造成按键信号抖动。
软件消抖:
通过软件的方法进行按键消抖是最简单的解决方案之一。可以使用计时器或延迟等待一段时间,在此期间检测按键状态的稳定性。只有在经过一段时间后,按键状态保持不变才认为按键有效。
硬件消抖:
在FPGA设计中,也可以使用硬件电路来进行按键消抖。常见的硬件消抖方法是使用触发器或滤波器电路,以去除抖动信号。触发器可以在给定的时间范围内检测按键状态的转变,并输出稍后稳定的信号。
时钟域问题:在FPGA设计中,按键输入和其他逻辑电路可能处于不同的时钟域中。为了保证按键信号的正确采样和消抖,在不同时钟域之间进行合理的时钟同步是必要的。
综上所述,通过软件或硬件的方法进行按键消抖是有效解决FPGA按键抖动问题的常用手段。具体方法可以根据实际需求和设计复杂度选择合适的方案。同时,需要注意时钟域之间的同步问题以确保按键信号的准确性。
LED流水灯
LED流水灯是多个LED按照特定模式循环点亮的一种效果。在FPGA开发中,我们可以通过控制LED的逻辑电平高低来实现流水灯的效果。通过编写适当的代码,可以让LED在不同的时间点按照特定的顺序点亮和熄灭,形成流动的效果。
流水灯可以分为单向流水灯和双向流水灯。单向流水灯是指LED的点亮方向是单向的,即按照一个方向依次点亮和熄灭LED。双向流水灯则是指LED的点亮方向是双向的,即按照一个方向依次点亮LED,然后再按照相反的方向依次熄灭LED,如此循环。
在FPGA开发中,我们可以使用计数器和时钟信号来控制LED的流水速度和方向。通过根据计数器的值来确定LED点亮的位置,可以实现不同的流水灯效果。同时,还可以通过编写适当的逻辑电路,使得LED在达到最后一个位置时自动返回到第一个位置,从而循环播放流水灯效果。
module led (
input clk,//不定义类型时,默认为wire
input rst_n,
output reg [3:0]led
);
parameter MAX_NUM = 26'd9_999_999;//参数类型
reg [25:0]cnt;//保存时钟上升沿个数
reg [1:0]state;//保存四个状态中当前的状态
//0.2s计数器
always @(posedge clk or negedge rst_n) begin //要对寄存器赋值,所以用always
if(!rst_n)begin//初始化cnt为0
cnt<=26'd0;
end
else if (cnt == MAX_NUM) begin//计满了0.2s
cnt<=26'd0;
end
else begin//判断到时钟上升沿时,cnt+1
cnt <= cnt+1'd1;
end
end
//状态的切换
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= 2'd0;
end
else if (cnt == MAX_NUM) begin//每满0.2s,切换一次状态。
state <= state + 1'd1;
end
else begin
state <= state;
end
end
//根据状态给led赋值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led<=4'b0000;
end
else begin
case (state)
2'd0: led<=4'b0001;
2'd1: led<=4'b0010;
2'd2: led<=4'b0100;
2'd3: led<=4'b1000;
default: ;
endcase
end
end
endmodule
编写测试文件
在FPGA开发中,编写测试文件是验证硬件设计正确性的重要步骤。通过编写测试文件,可以模拟各种输入条件,检验设计的正确性和稳定性。一般情况下,测试文件包括输入数据的生成、预期输出结果的定义以及与设计结果的比较等内容。
编写测试文件需要遵循以下步骤:
(1) 确定测试目标:明确测试的目标,即需要验证哪些功能或模块。
(2) 设计测试用例:根据测试目标,设计一系列合适的输入数据,涵盖不同边界条件和特殊情况。
(3) 生成测试数据:根据设计好的测试用例,生成相应的测试数据。可以手动输入数据,也可以通过程序生成。
(4) 编写测试代码:根据测试数据和测试目标,编写测试代码。测试代码主要包括将测试数据输入到被测试的模块中,获取输出结果,并与预期结果进行比较。
(5) 运行测试:
将测试代码加载到FPGA板上,运行测试。通过查看输出结果和比较实际结果与预期结果,判断设计是否通过测试。
(6) 分析测试结果:
分析测试结果,找出设计中的问题和潜在的改进空间。如果测试不通过,需要进行调试和修复。
通过编写测试文件,可以全面地验证硬件设计的功能和性能,提高系统的可靠性和稳定性。
编写顶层文件
顶层文件是FPGA开发中的重要文件,它定义了整个系统的结构和各个模块之间的连接关系。通过编写顶层文件,可以将各个模块进行实例化,并定义它们之间的信号连接和时序关系。顶层文件一般包括对各个模块的引用和实例化,信号的声明和赋值等内容。
编写顶层文件需要注意以下几点:
(1) 引用模块:首先需要引用所使用的模块,包括数码管、LED、按键消抖等。通过引用模块,可以在顶层文件中直接使用这些模块的功能和信号。
(2) 实例化模块:根据所使用的模块,在顶层文件中进行相应的实例化。实例化模块时需要给出实例名称和端口连接方式。
(3) 定义信号:根据系统需求,定义相应的输入和输出信号。在定义信号时,需要注意信号的宽度、类型和方向等参数。
(4) 连接信号:根据不同模块的输入和输出信号,在顶层文件中进行连接。通过将不同模块的输入和输出信号进行适当的连接,实现整个系统的功能。
(5) 设置时序关系:根据所使用的模块和设计需求,设置时钟信号和时序关系。时序关系包括时钟的上升沿或下降沿触发,以及不同模块之间的数据传输和控制信号的时序要求。
编写顶层文件需要综合考虑系统的功能和性能需求,合理设置模块的连接和时序关系。同时,还需要进行适当的优化,以提高系统的性能和可靠性。
通过这次实习,我不仅学到了FPGA开发中常用的基本概念和技术,还锻炼了自己的编码和调试能力。在未来的学习和工作中,我将继续深入学习FPGA相关知识,不断提升自己的技术水平,为实际应用领域的开发做出更大的贡献。同时,我还会积极参与团队合作,与他人分享经验和知识,共同推动FPGA技术的发展和应用。