《数码管动态显示》
理论学习
利用人眼的视觉暂留(说白了就是趁我们人眼没反应过来,我就灭了,就亮下一个了,以此往复循环,我们人眼就以为他们是一直亮的,可恶好卑鄙)和余晖效应(数码管会短暂的停留一下下)来完成动态显示
实战演练
目的:使用六位八段数码管实现数码管的动态显示十进制0~999999,达到最大值之后回到0(第一个0.1s(100ms)显示0,第二个0.1s显示1....)
顶层模块:串行数据ds;使能信号oe;移位寄存器时钟shcp;存储寄存器时钟stcp;

代码部分:
module top_seg_595
(
input wire sys_clk ,
input wire sys_rst_n ,
output wire ds ,
output wire oe ,
output wire shcp ,
output wire stcp
);
wire [19:0] data ;
wire [5:0] point ;
wire sign ;
wire seg_en ;
data_gen
#(
. CNT_MAX (23'd4999_999),
. data_MAX (20'd999_999)
)
data_gen_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (data),
.point (point),
.sign (sign),
.seg_en (seg_en)
);
seg_595_dynamic seg_595_dynamic_inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.data (data) ,
.point (point) ,
.sign (sign) ,
.seg_en (seg_en) ,
.ds (ds) ,
.oe (oe) ,
.shcp (shcp) ,
.stcp (stcp)
);
endmodule
tb部分:
module tb_top_seg_595();
reg sys_clk ;
reg sys_rst_n ;
wire ds ;
wire oe ;
wire shcp ;
wire stcp ;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#30
sys_rst_n <= 1'b1;
end
always #10 sys_clk <= ~sys_clk;
defparam top_seg_595_inst.data_gen_inst.CNT_MAX = 23'd49;
defparam top_seg_595_inst.seg_595_dynamic_inst.seg_dynamic_inst.CNT_MAX = 16'd19;
top_seg_595 top_seg_595_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.ds (ds),
.oe (oe),
.shcp (shcp),
.stcp (stcp)
);
endmodule
1.1数据产生模块:

该开发板使用的时六位八段数码管都有小数点位,位宽为6位宽,每一位表示每个数码位的小数点位,但我们没有用到,所以让他始终保持0,负号位也没有用到因此也为0,使能信号得保持高电平。(这里的cnt_100ms相当于是分频,也就是要想0.1s闪一次,就要过500000个系统时钟周期,计数器才会闪一次,因此才会有cnt_100ms的存在)
波形图:

代码编写:
module data
#(
parameter CNT_MAX = 23'd4999_999,
parameter data_MAX = 20'd999_999
)
( input wire sys_clk ,
input wire sys_rst_n ,
output reg [19:0] data,
output wire [5:0] point,
output wire sign,
output reg seg_en
);
reg [22:0] cnt_100ms;
reg cnt_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
cnt_100ms <= 23'd0;
else if(cnt_100ms == CNT_MAX)
cnt_100ms <= 23'd0;
else
cnt_100ms <= cnt_100ms+1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt_100ms == CNT_MAX-1'b1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
data <= 20'd0;
else if(data == data_MAX)
data <= 20'd0;
else if(cnt_flag == 1'b1)
data <= data + 1'b1;
else
data <= data;
assign point = 6'b000_000;
assign sign = 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 1'b0)
seg_en <= 1'b0;
else
seg_en <= 1'b1;
endmodule
tb文件:
module tb_data_gen();
reg sys_clk;
reg sys_rst_n;
wire data;
wire point;
wire sign;
wire seg_en;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#30
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
data_gen
#(
.CNT_MAX(9),
.DATA_MAX(9)
)
data_gen_inst
(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.data(data),
.point(point),
.sign(sign),
.seg_en(seg_en)
);
endmodule
1.2数码管动态显示的模块:
数据输入端口(data),小数点输入端口(point用于以后温度测量),增加一个负号位(sign用于以后电压测量),使能端口(seg_en更好的控制,使能信号为高电平时可以正常显示,当使能信号为低电平时,不显示)

代码部分:
module seg_595_dynamic(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [19:0] data ,
input wire [5:0] point ,
input wire sign ,
input wire seg_en ,
output wire ds ,
output wire oe ,
output wire shcp ,
output wire stcp
);
wire [5:0] seg;
wire [7:0] sel;
seg_dynamic seg_dynamic_inst
(
. sys_clk (sys_clk) ,
. sys_rst_n (sys_rst_n) ,
. data (data) ,
. point (point) ,
. sign (sign) ,
. seg_en (seg_en) ,
. sel (sel) ,
. seg (seg)
);
hc595_ctrl hc595_ctrl_1
(
. sys_clk (sys_clk),
. sys_rst_n (sys_rst_n),
. sel (sel),
. seg (seg),
. ds (ds),
. shcp (shcp),
. stcp (stcp),
. oe (oe)
);
endmodule
将动态显示模块进行进一步的划分:1.动态显示驱动(生成段选信号和位选信号);2.595控制模块(将位选和段选信号转化为输出端口的ds;oe;shcp;stcp传入到我们的芯片
1.2.1动态显示驱动模块:

因为左端产生的data数据是20位十进制数据,这个传入的数据使用二进制表示,并不能直接转化成6位的位选信号和8位的段选信号。要将此数据转化成6位的位选信号和8位的段选信号,所以要将此数据以BCD码表示的十进制数。因此要了解一下BCD码是什么。
1.2.1.1BCD编码模块
BCD码又称二-十进制码,使用4位二进制数来表示1位十进制数中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。

那为什么会选择BCD码呢,这里我们来举例:比如我们现在想用数码管表示一个十进制数234,234用2进制数来表示是11101010,234用8421码来表示则为0010_0011_0100,这样就需要在第一个显示周期内点亮最低位数码位4,在第二个显示周期点亮下一位数码位3,在第三个显示周期内点亮第三位数码位2,这样就实现了234的显示。这种显示方式要求我们从传入的数据中提取出个位、十位、百位。。而传入的二进制表示的十进制数,在其中并不能提取出个位、十位、百位。而BCD表示的十进制数,则可以提取位的信息(0010为百位,0011为十位,0100为个位)。接下来的问题就是如何把二进制码转化成BCD码
我们还是以234为例,参与的十进制数有多少位就需要多少个BCD码,我们将输入的2进制数1110_1010(234),不断地左移一位,将每一次移位后的三位BCD数据与0100(4)比较,如果大于4,则加0011(3),以此类推直至将所有输入的二进制码左移完成,得到转化后的BCD码,具体过程如下图。(此数码管要显示999_999六位,也就需要6*4=24位BCD码)

因为输入的是999_999需要20位宽,因此需要一个移位计数器,计数到21,即0的时候是第一个状态,21的时候是取输出结果(cnt_shift),才能够实现二进制到BCD码的转换(位宽为[4:0]),在移位的过程中产生的中间数据,也需要一个中间变量储存,而且经过20次的移位操作之后呢,要从中间变量中提取我们的BCD编码,因此需要从中间变量寄存我们的中间数据,他的位宽应该是20二进制码+24BCD码=44位,且判断运算在前移位运算在后,所以需要生成一个标志信号来区分这两个操作,这两个操作有一个共同的点:在一个时钟周期内完成的,而且由图标可知判断运算在移位运算之后完成的,所以说声明的移位标志信号也可以作为移位计数计数的一个条件。计数范围(0-21),这时候可能会提出疑问:只需要移位20次就可以了,为什么要加两个计数状态呢,这是因为计数器为0时,对应的是第一个状态(也就是上图中0000_0000_0000_1110_1010)要进行补0操作,计数器为1-20时才是进行判断的时候,21的时候是最终输出结果(999_999),确定之后计数范围之后,要明确计数条件是什么,之前说过可以使用移位标志信号(shift_flag)作为计数器计数条件,移位信号为低电平时进行计数运算,移位信号高电平时进行移位操作,同时高电平也可以作为条件控制移位计数器计数,即高电平+1,低电平保持不变(时序逻辑,所以要滞后一个时钟),移位数据(data_shift)根据cnt_shift进行变化。具体波形如下图

对应输出波形就按照data_shift向后排即可(例如ten为data_shift的第27-24位)

代码部分:
module bcd_8421(
input wire sys_clk,
input wire sys_rst_n,
input wire [19:0] data,
output reg [3:0] unit,
output reg [3:0] ten,
output reg [3:0] hun,
output reg [3:0] tho,
output reg [3:0] t_tho,
output reg [3:0] h_hun
);
reg [4:0] cnt_shift;
reg [43:0] data_shift;
reg shift_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_shift <= 5'd0;
else if((cnt_shift == 5'd21)&&(shift_flag == 1'b1))
cnt_shift <= 5'd0;
else if(cnt_shift == 1'b1)
cnt_shift <= cnt_shift + 1'b1;
else
cnt_shift <= cnt_shift;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_shift <= 44'b0;
else if(cnt_shift == 5'd0)
data_shift <= {24'b0,data};
else if((cnt_shift <=20)&&(shift_flag == 1'b0))
begin
data_shift[23:20] <= (data_shift[23:20] > 4) ? (data_shift[23:20]+2'd3) :(data_shift[23:20]);
data_shift[27:24] <= (data_shift[27:24] > 4) ? (data_shift[27:24]+2'd3) :(data_shift[27:24]);
data_shift[31:28] <= (data_shift[31:28] > 4 ) ? (data_shift[31:28]+2'd3) :(data_shift[31:28]);
data_shift[35:32] <= (data_shift[35:32] > 4) ? (data_shift[35:32]+2'd3) :(data_shift[35:32]);
data_shift[39:36] <= (data_shift[39:36] > 4) ? (data_shift[39:36]+2'd3) :(data_shift[39:36]);
data_shift[43:40] <= (data_shift[43:40] > 4) ? (data_shift[43:40]+2'd3) :(data_shift[43:40]);
end
else if((cnt_shift <=20)&&(shift_flag == 1'b1))
data_shift <= data_shift << 1;
else
data_shift <= data_shift;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
shift_flag <= 1'b0;
else
shift_flag <= ~shift_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
unit <= 4'b0;
ten <= 4'b0;
hun <= 4'b0;
tho <= 4'b0;
t_tho <= 4'b0;
h_hun <= 4'b0;
end
else if(cnt_shift == 5'd21)
begin
unit <= data_shift[23:20];
ten <= data_shift[27:24];
hun <= data_shift[31:28];
tho <= data_shift[35:32];
t_tho <= data_shift[39:36];
h_hun <= data_shift[43:40];
h_hun <= data_shift[43:40];
end
endmodule
tb代码:
module tb_bcd_8421();
reg sys_clk;
reg sys_rst_n;
reg [19:0] data;
wire [3:0] unit;
wire [3:0] ten;
wire [3:0] hun;
wire [3:0] tho;
wire [3:0] t_tho;
wire [3:0] h_hun;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
data <=20'd0;
#30
sys_rst_n <= 1'b1;
data <= 20'd123_456;
#3000
data <= 20'd654_321;
#3000
data <= 20'd987_654;
#3000
data <= 20'd999_999;
end
always #10 sys_clk = ~sys_clk;
bcd_8421 bcd_8421_inst
(
. sys_clk (sys_clk) ,
. sys_rst_n (sys_rst_n) ,
. data (data) ,
. unit (unit) ,
. ten (ten) ,
. hun (hun) ,
. tho (tho) ,
. t_tho (t_tho) ,
. h_hun (h_hun)
);
endmodule
动态显示驱动模块的波形图:
时钟信号、复位信号、待显示数据(data给一个固定的值20'd98_76)、小数点位(point)这里000_010代表第二位小数点位、符号位(sign)这里高电平代表负数、使能信号(eg_en)、六路显示信号unit、 ten 、 hun 、tho 、t_tho、h_hun、寄存位data_reg(只包含符号位和数据位不包括小数点位显示的是X-9876),这里也就代表要显示-987.6 。因为每个数码管显示时间为1ms,1ms进行计数,就需要一个计数器,因此需要一个计数器变量(cnt_1ms)、1ms=1*10^6ns,每个时钟周期是20ns,因此要完成1ms的计数,要计数5*10^4个周期,从0开始计数,因此需要计数到49999需要16 位,计数到最大值归0,还需要一个1ms标志信号(flag_1ms),相当于分频器,在这里还需要添加一个计数器变量,因为数码管采用的是动态显示,所有的数码管都显示一次之后,才是一个完整的扫描周期,因此需要一个计数器变量(cnt_sel )从0-5六个管,因此需要三位,当flag_1ms为高电平时,相当于一个数码管显示了一次,这时候cnt_sel+1。

接下来要输出位选信号(sel)的波形,这里以6'b000_000,flag_1ms为高电平,cnt_sel为2的时候代表选中第二位,应该在第一位的基础上左移,变为6'b000_010以此类推,但在cnt_sel归零时,应该对输出信号重新赋值,否则在左移的作用下会变成6'b000_000。

继续是对端选信号(data_disp)进行赋值,先给一个初值0,当位选信号为000_0001时,代表第一位被选中,赋值为6,以此类推直到9876显示完,到第五个数码位时,应该添加为符号-,这里选择用10来表示,最高位不显示时用11来表示,一个周期完成后,回到最低为,显示为6.

继续是小数点位,高电平时不显示,低电平时显示,因此应该在第二位显示的时候小数点位显示,其余都不显示小数点位。

最后是端选信号(seg),首先给一个初值,因为是共阳极数码管,所以初值为F代表不点亮,其他时刻要根据待显示数据进行编码,因为这里直接输出的数据 是不能用于显示的,要进行编码,因为是时序逻辑,因此要滞后一个时钟周期,第一个要显示的是数字6,对应的是82,7对应的是f8,但由于7的时候小数点位要点亮,dp为0,因此16位显示的是0111_1000=78而不是f8,8=80,9=90,其他时候只显示g即可,相当于1000_0000=bf,不点亮就是全1,为ff,这样就完成一个周期的扫描,下一个周期继续为82→78→80....因为端选信号和位选信号相差一个时钟周期,因此要进行打拍,把sel变为sel_reg,使用时序逻辑进行打拍生成我们的位选信号,这样位选和段选就同步了。


代码部分
module seg_dynamic(
input wire sys_clk,
input wire sys_rst_n,
input wire [19:0] data,
input wire [4:0] point,
input wire sign,
input wire seg_en,
output reg [5:0] sel,
output reg [7:0] seg
);
parameter cnt_max = 16'd49_999;
wire [3:0] unit;
wire [3:0] ten;
wire [3:0] hun;
wire [3:0] tho;
wire [3:0] t_tho;
wire [3:0] h_hun;
reg [23:0] data_reg;
reg [15:0] cnt_1ms;
reg flag_1ms;
reg [2:0] cnt_sel;
reg [5:0] sel_reg;
reg [3:0] data_disp;
reg dot_disp;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_reg <= 24'b0;
else if((h_hun)|| (point[5]))//第一种情况:十万位不为0,或者最高位的符号位为高电平
data_reg <= {h_hun,t_tho,tho,hun,ten,unit};//数据寄存不进行小数点的寄存,
//我们只使用数码位的第五位,最高位没有使用,第五位同时有两种情况:1.使用了符号,2.没有使用符号,
else if((t_tho)|| (point[4]) && (sign == 1'b1))
data_reg <= {4'd10,t_tho,tho,hun,ten,unit};//代表情况1 1.负号用10来表示
else if((t_tho)|| (point[4]) && (sign == 1'b0))
data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//代表情况2 2.不表示用11来表示
else if((tho) || (point[3]) && (sign == 1'b1))
data_reg <= {4'd11,4'd10,tho,hun,ten,unit};
else if((tho) || (point[3]) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,tho,hun,ten,unit};
else if((hun)|| (point[2]) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit};
else if((hun)|| (point[2]) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit};
else if((ten)|| (point[1]) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit};
else if((ten)|| (point[1]) && (sign == 1'b0))
data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit};
else if((unit)|| (point[0]) && (sign == 1'b1))
data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit};
else
data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit};
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1ms <= 16'b0;
else if(cnt_1ms == cnt_max)
cnt_1ms <= 16'b0;
else
cnt_1ms <= cnt_1ms + 1'b1;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt_sel<=3'd0;
else if((cnt_sel==3'd5) &&(flag_1ms ==1'b1))
cnt_sel<=3'd0;
else if(flag_1ms==1'b1)
cnt_sel<=cnt_sel +1'b1;
else
cnt_sel <=cnt_sel;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
sel_reg <= 6'b000_000;
else if((cnt_sel == 3'd0)&&(flag_1ms == 1'b1))
sel_reg <= 6'b000_001;
else if(flag_1ms == 1'b1)
sel_reg <= sel_reg << 1 ;
else
sel_reg <= sel_reg;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
data_disp <= 4'd0;
else if ((seg_en == 1'b1) && (flag_1ms == 1'b1))
case(cnt_sel)
3'd0: data_disp <= data_reg[3:0];
3'd1: data_disp <= data_reg[7:4];
3'd2: data_disp <= data_reg[11:8];
3'd3: data_disp <= data_reg[15:12];
3'd4: data_disp <= data_reg[19:16];
3'd5: data_disp <= data_reg[23:20];
default : data_disp <= 4'b0;
endcase
else
data_disp <= data_disp ;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
dot_disp <= 1'b1;
else if(flag_1ms == 1'b1)
dot_disp <= ~point[cnt_sel];
else
dot_disp <= dot_disp;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
seg <= 8'b111_111;
else
case(data_disp)
4'd0: seg <= {dot_disp,7'b100_0000};
4'd1: seg <= {dot_disp,7'b111_1001};
4'd2: seg <= {dot_disp,7'b010_0100};
4'd3: seg <= {dot_disp,7'b011_0000};
4'd4: seg <= {dot_disp,7'b001_1001};
4'd5: seg <= {dot_disp,7'b001_0010};
4'd6: seg <= {dot_disp,7'b000_0010};
4'd7: seg <= {dot_disp,7'b111_1000};
4'd8: seg <= {dot_disp,7'b000_1000};
4'd9: seg <= {dot_disp,7'b000_0000};
4'd10: seg <= 8'b1011_1111;
4'd11: seg <= 8'b111_111;
default: seg<= 8'b1100_0000;
endcase
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
sel <= 6'b000_000;
else
sel <= sel_reg;
bcd_8421 bcd_8421_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (data),
.unit (unit),
.ten (ten),
.hun (hun),
.tho (tho),
.t_tho (t_tho),
.h_hun (h_hun)
);
endmodule
tb代码:
module tb_seg_dynamic();
reg sys_clk ;
reg sys_rst_n ;
reg [19:0] data ;
reg [4:0] point ;
reg sign ;
reg seg_en ;
wire [5:0] sel ;
wire [7:0] seg ;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
data <= 20'd0;
point <= 6'b0;
sign <= 1'b0;
seg_en <= 1'b0;
#30
sys_rst_n <= 1'b1;
data <= 20'd9876;
point <= 6'b000_010;
sign <= 1'b1;
seg_en <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
defparam seg_dynamic_inst.cnt_max = 20'd5;
seg_dynamic seg_dynamic_inst
(
.sys_clk (sys_clk) ,
.sys_rst_n(sys_rst_n) ,
.data (data) ,
.point (point) ,
.sign (sign) ,
.seg_en (seg_en) ,
.sel (sel) ,
.seg (seg)
);
endmodule
1.2.2595控制模块:

这里采用的hc595控制模块选自于上一节中《静态数码管显示》 其主要作用是将段选信号和位选信号传输到我们的595芯片当中。这里不再过多阐述。
波形图
代码部分:
module hc595_ctrl(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [5:0] sel ,
input wire [7:0] seg ,
output reg ds ,
output reg shcp ,
output reg stcp ,
output wire oe
);
wire [13:0] data ;
reg [1:0] cnt ;
reg [3:0] cnt_bit ;
assign data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],sel};
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 2'b0;
else if(cnt == 2'd3)
cnt <= 2'd0;
else
cnt <= cnt + 1'b1;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 4'd0;
else if((cnt_bit == 4'd13) && (cnt == 2'd3))
cnt_bit <= 4'd0;
else if(cnt == 2'd3)
cnt_bit <= cnt_bit + 1'b1;
else
cnt_bit <= cnt_bit;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ds <= 1'b0;
else if(cnt == 2'd0)
ds <= data[cnt_bit];
else
ds <= ds;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
shcp <= 1'b0;
else if(cnt_bit == 2'd2)
shcp <= 1'b1;
else if(cnt_bit == 2'd0)
shcp <= 1'b0;
else
shcp <= shcp;
always@ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
stcp <= 1'b0;
else if((cnt_bit ==4'b0)&&(cnt == 2'd0))
stcp <= 1'b1;
else if((cnt_bit == 4'b0)&&(cnt==2'd2))
stcp <= 1'b0;
else
stcp <= stcp;
assign oe = 1'b0;
endmodule
最后仿真结果:

符合理想波形图,实验成功。
这样就完成了动态数码管显示。
ps:由于手中没有实体板子,所以最后没有办法呈现现实的实现效果。