一.项目内容
我参加的实习培训一共有十天我们的学习内容有:按键控制led流水灯,led呼吸灯,按键消抖,数码管显示
1.led流水灯
1.流水灯模块设计
2.实验代码
module led(
input clk,
input rst_n,
output reg [3:0] led
);
//计时1s
parameter MAX1S = 26'd49_999_999 ;
parameter MAX0_2S = 26'd9_999_999;
reg [25:0] cnt_1s;//1s计数寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_1s <= 26'd0;
end
else if (cnt_1s == MAX0_2S) begin
cnt_1s <= 26'd0;
end
else begin
cnt_1s <= cnt_1s + 1'd1;
end
end
//控制led
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= 4'b0001;
end
else if (cnt_1s == MAX0_2S) begin
led <= {led[0],led[3:1]};
end
else begin
led <= led;
end
end
endmodule
3.仿真文件
`timescale 1ns/1ns//单位精度
module led_tb();
reg clk;//52MHz时钟信号,每10ns翻转一次
reg rst_n;
wire [3:0] led;
parameter MAX0_2S = 26'd9 ;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#10 //延迟10ns
rst_n = 1'b1;
#1000;
$stop;//停止仿真
end
always #10//延迟10ns
begin
clk = ~clk;//时钟翻转
end
led #(.MAX0_2S(MAX0_2S))u_led(
.clk (clk),
.rst_n (rst_n),
.led (led)
);
endmodule
2.按键控制led
1.项目要求
2.系统框图
3.项目代码
//led灯控制
module key_led(
input clk ,
input rst_n ,
input wire [3:0] key ,
output reg [3:0] led
);
parameter TIME0_2S = 26'd9_999_999;
reg [25:0] cnt ;
reg [1:0] state;
//0.2s计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 26'd0;
end
else if (cnt == TIME0_2S) begin
cnt <= 26'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//状态
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= 2'b0;
end
else if (cnt == TIME0_2S) begin
state <= state + 1'b1;
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 if (!key[0]) begin
case (state)
2'd0 : led <= 4'b0001;
2'd1 : led <= 4'b0010;
2'd2 : led <= 4'b0100;
2'd3 : led <= 4'b1000;
default: led <= 4'b0000;
endcase
end
else if (!key[1]) begin
case (state)
2'd3 : led <= 4'b0001;
2'd2 : led <= 4'b0010;
2'd1 : led <= 4'b0100;
2'd0 : led <= 4'b1000;
default: led <= 4'b0000;
endcase
end
else if (!key[2]) begin
case (state)
2'd0 : led <= 4'b1111;
2'd1 : led <= 4'b0000;
2'd2 : led <= 4'b1111;
2'd3 : led <= 4'b0000;
default: led <= 4'b0000;
endcase
end
else if (!key[3]) begin
led <= 4'b1111;
end
else begin
led <= led ;
end
end
endmodule
4.测试文件
`timescale 1ns/1ns //单位/精度
module key_led_tb();
reg clk ;
reg rst_n ;
reg [3:0] key;
wire [3:0] led ;
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;//结束复位
key = 4'b1111;
#((MAX_NUM)*CYCLE*4);
key = 4'b1110;
#((MAX_NUM)*CYCLE*4);
key = 4'b1101;
#((MAX_NUM)*CYCLE*4);
key = 4'b1011;
#((MAX_NUM)*CYCLE*4);
key = 4'b0111;
#((MAX_NUM)*CYCLE*4);
$stop;
end
key_led #(.TIME0_2S(MAX_NUM)) u_key_led(
.clk (clk),
.rst_n (rst_n),
.key (key),
.led (led)
);
endmodule
3.按键消抖
1.消抖原理
2.系统框图
3.项目代码
//按键消抖
module key_debounce(
input clk ,
input rst_n ,
input wire [3:0] key_in,
output wire [3:0] key_out
);
parameter MAX20 = 20'd999_999;
wire nedge; //下降沿检测
reg [19:0] cnt_20ms; //20ms计数寄存器
reg start; //倒计时开始标志
reg [3:0] key_r0; //按键消抖寄存器0
reg [3:0] key_r1; //按键消抖寄存器1
reg [3:0] flag; //检测按钮
reg [3:0] key_on; //中间按键寄存器
//20ms计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_20ms <= 20'd0;
end
else if (nedge) begin
cnt_20ms <= MAX20;
end
else if (start) begin
if (cnt_20ms == 1'd0) begin
cnt_20ms <= 20'd0;
end
else begin
cnt_20ms <= cnt_20ms - 1'b1;
end
end
else begin
cnt_20ms <= cnt_20ms ;
end
end
//下降沿检测
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_r0 <= 4'b1111;
key_r1 <= 4'b1111;
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
end
end
assign nedge = (~key_r0[0]&&key_r1[0])||(~key_r0[1]&&key_r1[1])||(~key_r0[2]&&key_r1[2])||(~key_r0[3]&&key_r1[3]);
//约束start
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
start <= 1'b0;
end
else if (nedge) begin
start <= 1'b1;
end
else if (cnt_20ms == 1'd0) begin
start <= 1'b0;
end
else begin
start <= start;
end
end
//记录flag
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
flag <= 4'b0000;
end
else if (cnt_20ms == 1'd0) begin
flag <= ~key_r0;
end
else begin
flag <= 4'b0000;
end
end
//设置key_out的值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_on <= 4'b1111;
end
else if (flag[0]== 1'b1) begin
key_on <= 4'b1110;
end
else if (flag[1]== 1'b1) begin
key_on <= 4'b1101;
end
else if (flag[2]== 1'b1) begin
key_on <= 4'b1011;
end
else if (flag[3]== 1'b1) begin
key_on <= 4'b0111;
end
else begin
key_on <= key_on;
end
end
assign key_out = key_on ;
endmodule
4.数码管显示
1.显示原理
2.系统框图
3.源码显示
module time_count(//计时0.5s,计满后输出高电平
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 ,//计满0.5秒后,标志
output reg [5: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'b111111;
end
else begin
sel <= 6'b000000;
end
end
//flag信号为1时,num+1
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 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'b1111_1111;
endcase
end
endmodule
module top_seg_led_static(
input clk ,
input rst_n ,
output wire [5:0] sel ,
output wire [7:0] seg
);
wire flag ;
parameter MAX_NUM = 26'd24_999_999;
time_count #(.MAX_NUM(MAX_NUM))time_count_inst(//计时0.5s,计满后输出高电平
.clk (clk),
.rst_n (rst_n),
.flag (flag)
);
seg_led_static seg_led_static_inst(
.clk (clk),
.rst_n (rst_n),
.flag (flag),//计满0.5秒后,标志
.sel (sel),//六位位选信号
.seg (seg) //八位段选信号
);
endmodule
4.测试文件
`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;
#(16*(MAX_NUM + 1)*CYCLE);
$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
5.呼吸灯
1.项目源码
module breath_led(
input clk ,
input rst_n ,
output reg [3:0] led
);
parameter CNT_US = 6'd49;//1us
parameter CNT_MS = 10'd999;//1ms
parameter CNT_S = 10'd999;//1s
reg [5:0] cnt_us;
wire add_cnt_us;//us计数器开始标志
wire end_cnt_us;//us计数器结束标志
reg [9:0] cnt_ms;
wire add_cnt_ms;
wire end_cnt_ms;
reg [9:0] cnt_s;
wire add_cnt_s;
wire end_cnt_s;
reg flag ;//亮灭标志
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'b1;
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 == CNT_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'b1;
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 == CNT_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 == CNT_S ;
//约束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
//led控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= 4'b0;
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 <= {cnt_s<cnt_ms ,cnt_s<cnt_ms ,cnt_s<cnt_ms ,cnt_s<cnt_ms };
end
end
endmodule
2.测试文件
`timescale 1ns/1ns
module breath_led_tb();
reg clk ;
reg rst_n ;
wire [3:0] led ;
parameter CYCLE = 20;
parameter CNT_US = 4 ;
parameter CNT_MS = 9 ;
parameter CNT_S = 9 ;
always #(CYCLE / 2) clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;//开始复位
#(CYCLE);
rst_n = 1'b1;//结束复位
#1000;
$stop;
end
breath_led #(.CNT_US(CNT_US),.CNT_MS(CNT_MS),.CNT_S(CNT_S)) u_breath_led(
.clk (clk),
.rst_n (rst_n),
.led (led)
);
endmodule
二.实习总结
在最初的几天,我们学习了按键控制LED流水灯和LED呼吸灯的设计。通过使用Verilog语言编写代码,我们成功实现了按键来控制LED灯的亮灭和流动效果,以及利用PWM信号使LED灯呈现呼吸灯效果。这些实验帮助我熟悉了FPGA开发流程和Verilog语言的使用。
接着,我们学习了按键消抖的原理和方法。按键消抖是一个常见的问题,在实际应用中需要解决。我们通过verilog,设计了一个稳定的按键消抖模块,确保按键输入的准确性和稳定性。
在数码管显示的学习中,我们掌握了数码管的基本原理和控制方法。通过将FPGA与数码管连接,我们成功实现了数码管显示当前时间的功能。这个实验让我更加了解了FPGA如何通过逻辑门和时钟信号来控制外部设备。
通过这次FPGA培训实习,我不仅学到了专业知识和技能,还锻炼了自己的团队合作和问题解决能力。在与同组成员一起完成各项实验和任务的过程中,我们相互交流、互相帮助,共同解决问题。这种合作经验对于我未来的工作和学习都具有重要意义。
在未来的学习和实践中,我将继续努力提升自己在FPGA设计和物联网应用方面的专业能力。我相信通过海云捷讯的培训实习,我将成为一名优秀的物联网工程师,并为推动物联网技术的发展贡献自己的力量。