本次主要总结在FPGA学习过程中我认为一些比较重要的工程
1、呼吸灯
呼吸灯如果采用PWM,那么实现就非常简单,这里我觉得呼吸灯设计思路比较重要
首先说一下设计思路
将一个大的时钟周期,分为三个时钟周期的乘积。
parameter TIME_US = 6'd49;
parameter TIME_MS = 10'd999;
parameter TIME_S = 10'd999;
然后设置一个判断条件,帮助设置时间间隔里的高低电平的占比,这里用了一个flag信号来控制呼吸灯的转变,先由弱变强再由强变弱。这里是利用判断s与ms的大小来控制占比的大小的渐变。
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= 1'b0;
end
else if(!flag)begin
led <= (cnt_s > cnt_ms)?1'b1:1'b0;
end
else if(flag)begin
led <= (cnt_s > cnt_ms)?1'b0:1'b1;
end
else
led <=led;
end
flag信号控制
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
总代码
module breathe_led(
input clk,
input rst_n,
output reg led
);
parameter TIME_US = 6'd49;
parameter TIME_MS = 10'd999;
parameter TIME_S = 10'd999;
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);
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 <= 1'b0;
end
else if(!flag)begin
led <= (cnt_s > cnt_ms)?1'b1:1'b0;
end
else if(flag)begin
led <= (cnt_s > cnt_ms)?1'b0:1'b1;
end
else
led <=led;
end
endmodule
要观察仿真波形,使用ModelSim软件,编写仿真文件
`timescale 1ns/1ns
module breathe_led_tb();
reg clk;
reg rst_n;
wire led;
parameter CYCLE = 20;
parameter TIME_US = 5;
parameter TIME_MS = 10;
parameter TIME_S = 10;
always #(CYCLE/2) clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(CYCLE);
rst_n = 1'b1;
#((TIME_US+1)*(TIME_MS+1)*(TIME_S+1)*CYCLE);
$stop;
end
breathe_led #(
.TIME_US (TIME_US),
.TIME_MS (TIME_MS),
.TIME_S (TIME_S)
) u_breathe_led(
.clk (clk),
.rst_n (rst_n),
.ld (led)
);
endmodule
二·数码管显示
数码管显示是后面经常要用到的模块,这里编写一个简单的数码管静态显示模块,并且观看仿真波形
先编写一个计时器
module time_count(//计时0.5秒,计满后输出高电平
input clk,
input rst_n,
output reg flag
);
parameter MAX_NUM = 26'd24_999_999;
reg [25:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 26'd0;
flag <= 1'b0;
end
else if (cnt == MAX_NUM ) begin
cnt <= 26'd0;
flag <= 1'b1;
end
else begin
cnt <= cnt + 1'd1;
flag <= 1'b0;
end
end
endmodule
然后编写数码管的开启模块,用于控制段选信号和位选信号还有显示的内容
module seg_led_static(
input clk,
input rst_n,
input flag,
output reg [6:0] sel,//六位的位选信号
output reg [7:0] seg//八位的段选信号
);
reg [3:0] num;//保存当前数码管显示的数字
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sel <= 6'b111_111;
end
else begin
sel <= 6'b000_000;
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'b0;
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
编写顶层文件
module top_seg_led_static(
input clk,
input rst_n,
output [5:0] sel,
output [7:0] seg
);
parameter MAX_NUM = 26'd24_999_999;
wire flag_reg;
time_count #(.MAX_NUM (MAX_NUM)) u_time_count(
.clk (clk),
.rst_n (rst_n),
.flag (flag_reg)
);
seg_led_static u_seg_led_static(
.clk (clk),
.rst_n (rst_n),
.flag (flag_reg),
.sel (sel),
.seg (seg)
);
endmodule
仿真文件编写
`timescale 1ns/1ns
module top_seg_led_static_tb();
reg clk;
reg rst_n;
wire [5:0] sel;//位选信号
wire [7:0] seg;//段选信号
parameter MAX_NUM = 9; //切换一次状态的周期数量
parameter CYCLE = 20 ;//周期
always #(CYCLE/2) clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(CYCLE);
rst_n = 1'b1;
#((MAX_NUM+1)*CYCLE*16);
$stop;
end
top_seg_led_static #(.MAX_NUM (MAX_NUM)) u_top_seg_led_static(
.clk (clk),
.rst_n (rst_n),
.sel (sel),
.seg (seg)
);
endmodule
仿真结果
总结:
通过实践和练习,我逐渐熟悉了在FPGA中实现呼吸灯和数码管显示的技术,也更加了解了状态机的思想,以及对信号转换的理解。并能够理解和调整相应的设计参数,实现所需的效果。这对于深入理解数字系统设计和嵌入式系统开发具有重要意义。
在未来的学习中,我将继续拓展FPGA应用的领域,探索更多有趣和实用的设计,进一步提升我的FPGA设计能力。