动态扫描数码管显示_总结
上一次写了驱动74HC595的驱动,并测试了他的可行性,但没有把整套电路综合起来测试,现在把三个电路连接起来进行了测试,并做一个总结。
目的:
part1: 输入8个4位的数据(即8个0~f),共32位,使其可以在八个数码管上同时显示出来。
注:前面提到,为了节省引脚,利用人眼的视觉暂留效果,所以设置了一个计数器,让每个数码管显示的刷新频率一定,达成同时显示的效果。为此,此模块的输出为: [7:0] 位选信号(选择8位中的某一位) + [7:0] 段选信号(选择显示哪一段,从而显示 0~f) ,多引脚的设计方案输出的信号为 2 * N ,而本模块输出的信号个数为 8+N 。当N> 8 时 ,用本模块更加节省引脚。
这个模块输出的是16位并行信号([7:0] 位选信号(选择8位中的某一位) + [7:0] 段选信号(选择显示哪一段,从而显示 0~f))。输入是32位信号(8个0~f),时分复用,逐个输出,动态显示。
本模块1是fpga的内部设计,开发板的数码管在外部,用74HC595控制,因此我们需要写一个驱动,驱动74HC595输出。
part2:模块1输出并行数据,模块3接收串行数据,所以这个驱动用于并串转换。
part3:74HC595模块,输入信号设计为3个:串行数据输入信号,时钟信号,锁存信号 。而模块1输出是并行数据,所以需要一个并串转换模块(利用计数器计时,逐个输出)。时钟信号频率需要一致,上一节已经解释了。
注:为什么要并转串,串转并这么麻烦呢?是因为开发板的设计导致的。开发板用的是74HC595来控制数码管,而不是fpga输出信号直接练到数码管,所以我们才需要多写一个驱动,让这几个模块的数据传输匹配,可以实现功能。
电路图:
功能描述用上面三个模块讲完了,但是设计代码时会存在许多细节,需要调试。
①一开始没有设置一开send_en_pulse信号,提示有输入信号到来,这会导致后面的模块不知何时开始接收,接收完一个数据后什么时候再开始接收。
②第一个模块输出每一位的显示时间 t1,与第二个模块接收完这个显示的数据所花的时间 t2 是不一定相等的,t1>= t2 ,当 t1 > t2 时,模块2接收完一次数据之后,还会继续重复接收当前数据并输出,这会导致显示出错,因此需要设置send_dong信号,每输出一位数据,就发送结束标志信号,让模块2接收完这位数据后,输出0,并让内部计数器归零。这样就不会影响下一位的接收。
调试:先把电路图画出来,逻辑关系,引脚信号对应标记清楚,然后从头开始观察信号波形,把无关波形先删除,关键波形加入,分析功能,缺陷,适当引入标志信号,可以让功能实现更准确。
这个系统的代码最终版如下:
module digital_tube(//八个数码管显示,并行输入,并行输出 part1
clk,
reset,
send_en_pulse,
disp_num_all,
dg_tube,
tube_part,
send_done,
send_sig
);
input clk ;
input reset ;
input send_en_pulse ;
input [31:0]disp_num_all ;
output [7:0]dg_tube ;
output [7:0]tube_part ;
output reg send_done ;
output reg send_sig ;
parameter one_dis_t = 25'd1_000_000 ;//每个晶体管显示时间(计数)
reg [16:0]counter1 ;
reg [2:0] counter2 ;
reg send_en ;
reg [31:0]dis_regist;
always @ ( posedge clk or negedge reset )//分频
begin
if (! reset )
send_en <= 1'd0 ;
else if (send_en_pulse)
send_en <= 1'd1 ;
end
always @ ( posedge clk or negedge reset )//分频
begin
if (! reset )
dis_regist <= 32'd0 ;
else if (send_en_pulse)
dis_regist <= disp_num_all ;
end
always @ ( posedge clk or negedge reset )//分频
begin
if (! reset )
counter1 <= 17'd0 ;
else if (send_en)
begin
if ( (one_dis_t-1) <= counter1 )
counter1 <= 17'd0 ;
else
counter1 <= counter1 +1'b1 ;
end
else if ( send_en_pulse == 1 )
counter1 <= 17'd0 ;
end
always @ ( posedge clk or negedge reset )//循环
begin
if (! reset )
counter2 <= 3'd0 ;
else if (( (one_dis_t-1) <= counter1 ) && ( send_en ) )
counter2 <= counter2 +1'b1 ;
else if ( send_en_pulse == 1 )
counter2 <= 3'd0 ;
end
always @ ( posedge clk or negedge reset )//发送结束标志
begin
if (! reset )
send_done <= 1'd0 ;
else if (send_en)
begin
if ( (one_dis_t-1) <= counter1 )
send_done <= 1 ;
else if ( send_done == 1 )
send_done <= 0 ;
end
end
always @ ( posedge clk or negedge reset )//发送结束标志
begin
if (! reset )
send_sig <= 1'd0 ;
else if (send_en_pulse)
send_sig <= 1'd1 ;
else
send_sig <= 1'd0 ;
end
wire [3:0]disp_num_one ;
//3-8译码器 控制哪个数码管显示
decoder_3_8 tube_select(//控制
.a(counter2[2] ),
.b(counter2[1]),
.c(counter2[0]),
.out(dg_tube)
);
//需要一个八选一选通器,对应哪个数码管显示什么内容
mux8 tube_display(//选通
.sel(counter2),
.data(dis_regist),
.out(disp_num_one)
);
//真值表对应显示数字
LUT_truth translator(//控制
.num(disp_num_one),
.out(tube_part)
);
endmodule
module drive_digital_tube(//并行输入,串行输出 part2
clk,
reset,
data,
s_signal,
s_signal1,
DIO,
sclk,
rclk,
s_done
);
input clk;//50MHz
input reset;
input [15:0]data;
input s_signal;
input s_signal1;
output reg DIO;
output reg sclk;
output reg rclk;
output reg s_done;
reg [15:0]dats_regist ;
always@( posedge clk or negedge reset)//使能端
begin
if (!reset)
dats_regist <= 16'd0 ;
else if ((s_signal1) || (s_signal))
dats_regist <= data ;
else if (s_done == 1'd1 )
dats_regist <= 16'd0 ;
end
reg s_en ;
reg [1:0]div_cnt;// 分频计数器,频率为25MHz
always@( posedge clk or negedge reset)
begin
if (!reset)
div_cnt <= 2'd0 ;
else if(div_cnt == 1)
div_cnt <= 2'd0 ;
else
div_cnt <= div_cnt + 1'd1 ;
end
wire pulse25M; //频率为25MHz的取样脉冲
assign pulse25M = ( div_cnt == 2'd1 ) ? 1 : 0 ;
reg [5:0]cnt;//计数33个状态
always@(posedge clk or negedge reset)
begin
if(!reset)
cnt <= 6'd0 ;
else if (s_en)
begin
if (pulse25M)
begin
if(cnt == 6'd32)
cnt <= 6'd0 ;
else
cnt <= cnt + 1 ;
end
end
else
cnt <= 6'd0 ;
end
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
DIO <= 0 ;
sclk <= 0 ;
rclk <= 0 ;
s_done = 0 ;
s_en <= 1'd0 ;
end
else if(s_en)
begin
if (pulse25M)
case(cnt)//sclk为12.5MHz信号,rclk为输出使能(所有数据取完后),在sclk的低电平时取值,可以稳定,在sclk上升沿到来时可稳定输出
0:begin DIO <= dats_regist[15] ;sclk <= 0 ;s_done = 0 ;end
1: begin sclk <= 1 ; end
2:begin DIO <= dats_regist[14];sclk <= 0 ; rclk <= 0 ;end
3:sclk <= 1 ;
4:begin DIO <= dats_regist[13] ;sclk <= 0 ; end
5:sclk <= 1 ;
6:begin DIO <=dats_regist[12] ;sclk <= 0 ; end
7:sclk <= 1 ;
8:begin DIO <=dats_regist[11] ;sclk <= 0 ; end
9:sclk <= 1 ;
10:begin DIO <=dats_regist[10] ;sclk <= 0 ; end
11:sclk <= 1 ;
12:begin DIO <=dats_regist[9] ;sclk <= 0 ; end
13:sclk <= 1 ;
14:begin DIO <=dats_regist[8] ;sclk <= 0 ; end
15:sclk <= 1 ;
16:begin DIO <=dats_regist[7] ;sclk <= 0 ; end
17:sclk <= 1 ;
18:begin DIO <=dats_regist[6] ;sclk <= 0 ; end
19:sclk <= 1 ;
20:begin DIO <=dats_regist[5] ;sclk <= 0 ; end
21:sclk <= 1 ;
22:begin DIO <=dats_regist[4] ;sclk <= 0 ; end
23:sclk <= 1 ;
24:begin DIO <=dats_regist[3] ;sclk <= 0 ; end
25:sclk <= 1 ;
26:begin DIO <=dats_regist[2] ;sclk <= 0 ; end
27:sclk <= 1 ;
28:begin DIO <=dats_regist[1] ;sclk <= 0 ; end
29:sclk <= 1 ;
30:begin DIO <=dats_regist[0] ;sclk <= 0 ; end
31:begin sclk <= 1 ; end
32:begin s_done = 1 ; rclk <= 1 ;s_en = 0; end
default ;
endcase
end
else if((s_signal1) || (s_signal))
s_en <= 1'd1 ;
else
begin
DIO <= 0 ;
sclk <= 0 ;
rclk <= 0 ;
s_done = 0 ;
end
end
endmodule
module my_74HC595(//串转并 part3
clk,
lock_en,
series_data,
reset,
parallel_data
);
input clk ;
input lock_en ;
input series_data ;
input reset ;
output reg [15:0]parallel_data ;
reg [15:0]regist_data ;
always@( posedge clk or negedge reset )
begin
if ( !reset )
regist_data <= 8'd0 ;
else if ( lock_en )
regist_data <= 8'd0 ;
else
regist_data <= { regist_data[14:0], series_data} ;
end
always@(*)
begin
case(lock_en)
0:;
1: parallel_data <= regist_data ;
endcase
end
endmodule
module digital_tube_3ctrl( 顶层连线部分
clk,
reset,
send_en,
data_input,
display_num
);
input clk ;
input reset;
input send_en ;
input [31:0]data_input ;
output [15:0]display_num ;
wire [15:0]byte_part ;
wire en_1 ;
wire en_2 ;
wire sig_free ;
digital_tube
#(.one_dis_t(100))
part_1(//八个数码管显示,并行输入,并行输出
.clk(clk),
.reset(reset),
.send_en_pulse(send_en),
.disp_num_all(data_input),
.dg_tube(byte_part[15:8]),
.tube_part(byte_part[7:0]),
.send_done(en_2),
.send_sig(en_1)
);
wire uart_data1 ;
wire s_clk ;
wire r_clk ;
wire s_done1 ;
drive_digital_tube part_2(//并行输入,串行输出
.clk(clk),
.reset(reset),
.data(byte_part),
.s_signal(en_1),
.s_signal1(en_2),
.DIO(uart_data1),
.sclk(s_clk),
.rclk(r_clk),
.s_done(s_done1)
);
my_74HC595 part_3(//串转并
.clk(s_clk),
.lock_en(r_clk),
.series_data(uart_data1),
.reset(reset),
.parallel_data(display_num)
);
endmodule
`timescale 1ns / 1ns
module digital_tube_3ctrl_tb( 测试激励代码
);
reg clk ;
reg reset ;
reg send_en ;
reg [31:0]data_input ;
wire [15:0]display_num ;
digital_tube_3ctrl digital_tube_3ctrl_sim(
clk,
reset,
send_en,
data_input,
display_num
);
initial clk = 1 ;
always #10 clk = ! clk ;
initial
begin
reset = 0 ;
send_en = 0 ;
data_input = 32'd0 ;
#201 ;
reset = 1 ;
#200 ;
data_input = 32'habb02525 ;
send_en = 1 ;
#20 ;
send_en = 0 ;
#20000;
data_input = 32'h52520bba ;
send_en = 1 ;
#20 ;
send_en = 0 ;
#30000;
$stop;
end
endmodule
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)