上一章己经讲过了数码管的静态显示
传送门: FPGA实战------数码管(1)静态显示
接下来就是讲解一下动态显示。
前言
上一章已经对数码管的基础讲解了一遍,虽然不全且不完美,但基本上知道这些写代码就没问题。这里就不过多讲解了,有什么不会打在评论区或私信。
这里直接上动态显示的代码。
一、代码
/**************************************功能介绍***********************************
Date : 2023年9月30日20:58:00
Author : Yang.
Project : 数码管动态显示
Require : 数码管单显,从1-F六个数码管轮流显示
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module seg_dynamics(
input clk ,
input rst_n ,
output reg [5:0] sel ,
output reg [7:0] dig
);
parameter ZERO = 8'b1100_0000 ,
ONE = 8'b1111_1001 ,
TWO = 8'b1010_0100 ,
THREE = 8'b1011_0000 ,
FOUR = 8'b1001_1001 ,
FIVE = 8'b1001_0010 ,
SIX = 8'b1000_0010 ,
SEVEN = 8'b1111_1000 ,
EIGHT = 8'b1000_0000 ,
NINE = 8'b1001_0000 ,
NUM_A = 8'b1000_1000 ,
NUM_B = 8'b1000_0011 ,
NUM_C = 8'b1100_0110 ,
NUM_D = 8'b1010_0001 ,
NUM_E = 8'b1000_0110 ,
NUM_F = 8'b1000_1110 ;
parameter MAX_1S = 26'd50_000_000 ;
reg [25:0] cnt_1s ;
wire add_cnt_1s ;
wire end_cnt_1s ;
reg [4:0] cnt_num ;//数码管显示的数字
reg [2:0] cnt_seg ;//控制位选信号
//------------------------<1s计时器>---------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1s <= 0 ;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)begin
cnt_1s <= 0 ;
end
else begin
cnt_1s <= cnt_1s + 1 ;
end
end
end
assign add_cnt_1s = 1'b1 ;
assign end_cnt_1s = add_cnt_1s && cnt_1s == MAX_1S - 1 ;
//------------------------<cnt_seg>---------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_seg <= 0 ;
end
else if((end_cnt_1s)&&(cnt_seg== 3'd5))begin
cnt_seg <= 0 ;
end
else if(end_cnt_1s)begin
cnt_seg <= cnt_seg + 1 ;
end
else begin
cnt_seg <= cnt_seg ;
end
end
//------------------------<位选>---------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel <= 6'b111_111 ;
end
else begin
case (cnt_seg)
3'd0 : sel <= 6'b111_110 ;
3'd1 : sel <= 6'b111_101 ;
3'd2 : sel <= 6'b111_011 ;
3'd3 : sel <= 6'b110_111 ;
3'd4 : sel <= 6'b101_111 ;
3'd5 : sel <= 6'b011_111 ;
default: sel <= sel ;
endcase
end
end
//------------------------<cnt_num>---------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_num <= 'd0;
end
else if ((cnt_num == 5'd15)&&(end_cnt_1s)) begin
cnt_num <= 'd0;
end
else if(end_cnt_1s)begin
cnt_num <= cnt_num + 1 ;
end
else begin
cnt_num <= cnt_num;
end
end
//------------------------<段选>---------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dig <= ZERO ;
end
else begin
case (cnt_num)
5'd0 : dig <= ZERO ;
5'd1 : dig <= ONE ;
5'd2 : dig <= TWO ;
5'd3 : dig <= THREE ;
5'd4 : dig <= FOUR ;
5'd5 : dig <= FIVE ;
5'd6 : dig <= SIX ;
5'd7 : dig <= SEVEN ;
5'd8 : dig <= EIGHT ;
5'd9 : dig <= NINE ;
5'd10 : dig <= NUM_A ;
5'd11 : dig <= NUM_B ;
5'd12 : dig <= NUM_C ;
5'd13 : dig <= NUM_D ;
5'd14 : dig <= NUM_E ;
5'd15 : dig <= NUM_F ;
default: dig <= dig ;
endcase
end
end
endmodule
二、细节
为什么直接上代码,因为大致和静态显示差不多,就是多了一个和cnt_num
一样作用的cnt_seg
。
因为它的加入,直接导致六个数码管的“协同合作”终止,变成了“孤军奋战”。六个数码管从右到左按顺序亮,并且改变数值。
case语句的作用,在上一章和led中都说到了,但我就是不厌其烦,为了让看博客的同学们记住(快夸夸我)。最简单的说法: 让sel
知道自己在cnt_seg
是什么值的时候应该让哪个数码管亮。
三、仿真
`timescale 1ns/1ns
module tb_seg_dynamics();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
//输出信号定义
wire [5:0] seg_sel ;
wire [7:0] seg_dig ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//参数重新定义
defparam u_seg_dynamics.MAX_1S = 100;
//模块例化
seg_dynamics u_seg_dynamics(
/*input */.clk (tb_clk ) ,
/*input */.rst_n (tb_rst_n ) ,
/*output reg [5:0] */.sel (seg_sel ) ,
/*output reg [7:0] */.dig (seg_dig )
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
//产生激励
initial begin
tb_rst_n = 1'b1;
#(CLOCK_CYCLE*2);
tb_rst_n = 1'b0;
#(CLOCK_CYCLE*20);
tb_rst_n = 1'b1;
#(CLOCK_CYCLE*10000);
$stop;
end
endmodule
总结
上板效果
FPGA数码管动态显示
仿真结果
总体来说,一切符合代码,合理且正确。
博客上传的工程都在百度网盘,有压缩包也有整个文件,可以自行下载。
链接:https://pan.baidu.com/s/1lQqqWZXfb3i6XHwkKf52zg
提取码:yang
数码管基础的操作就学完了,接下来还有深层的操作,期待吧!!!!