上章内容介绍了数码管的译码显示,并将数码管固定在右边第一个数码管上。本节内容将接上章内容,如何将多个数码管动态显示不同的内容。在FII-PRA006/010以及FII-PRX100-D的系统中都是采用段共享的6数码管结构。其中FII-PRA006/010的原理图连线如图1,

图1

图2,按键对应的数码管管脚

图3 按键原理图
1.动态扫描原理
动态扫描的基本原理是采用分时复用的方法,在某一时刻只会有一个数码管点亮,其它数码管处在非点亮状态,利用人眼睛的视觉滞留效应,达到看起来所有数码管被同时点亮的效果。在某个数码管点亮的某一时刻,相对应的显示内容为段译码驱动的各个段,使得不同的内容显示在不同的数码管上。图1中,由于ND336P是P沟道小功率MOS管,因此只有栅极为低电平时才会导通,因此为了达到分时复用的目的,每一段时间内只有SEG_3V3_D0, SEG_3V3_D1, SEG_3V3_D2, SEG_3V3_D3, SEG_3V3_D4, SEG_3V3_D5只有对应的一位为低电平,其它的位都为高电平,依次轮流扫描。
2. 变量描述
针对动态扫描的数码管,这里定义3个变量SEVEN_SEG[6:0],DP,SEAT[5:0],对应关系如下:
- SEVEN_SEG[6:0]对应A,B,C,D,E,F,G七段,DP对应数码管上的小数点,见表一。由于PRA006/10采用共阳极数码管,而各段经过限流电阻直接连接到FPGA(I/O)管脚上,因此FPGA对应的管脚在输出低电平时点亮对应的数码管的某一段。
- SEAT[5:0] 对应数码管的位,即在某一时刻到底哪一个数码管应该点亮,SEAT[5:0] 与SEG_3V3_D0, SEG_3V3_D1, SEG_3V3_D2, SEG_3V3_D3, SEG_3V3_D4, SEG_3V3_D5一一对应,见表1。
- 引入时钟信号inclk和按键KEY3(在FII-PRA006上对应PB0),如表1。从图2. 3可以看出,按键在按下后,对应的FPGA管脚为高电平,按键释放后,FPGA对应的管脚为高电平,这一点与PRX100D 的开发板不同,因此在Verilog程序设计中应有所改变。
表1
| 程序信号名 | 网络标号 | FPGA管脚 | 端口说明 |
| clk | C10_50MCLK | 91 | 输入时钟 |
| rst | KEY3 | 11 | 复位按键 |
| SEAT[5] | SEG_3V3_D5 | 124 | 位选信号第5位 |
| SEAT[4] | SEG_3V3_D4 | 127 | 位选信号第4位 |
| SEAT[3] | SEG_3V3_D3 | 129 | 位选信号第3位 |
| SEAT[2] | SEG_3V3_D2 | 141 | 位选信号第2位 |
| SEAT[1] | SEG_3V3_D1 | 142 | 位选信号第1位 |
| SEAT[0] | SEG_3V3_D0 | 136 | 位选信号第0位 |
| SEVEN_SEG[6] | SEG_PG | 135 | 段选信号第7位 |
| SEVEN_SEG[5] | SEG_PF | 138 | 段选信号第6位 |
| SEVEN_SEG[4] | SEG_PE | 126 | 段选信号第5位 |
| SEVEN_SEG[3] | SEG_PD | 125 | 段选信号第4位 |
| SEVEN_SEG[2] | SEG_PC | 133 | 段选信号第3位 |
| SEVEN_SEG[1] | SEG_PB | 137 | 段选信号第2位 |
| SEVEN_SEG[0] | SEG_PA | 132 | 段选信号第1位 |
| DP | SEG_DP | 128 | 段选信号第0位 |
3. 程序设计
设计秒表程序,要求如下:右边两个数码管显示1/100秒的显示,范围0-99。第3,4数码管显示秒计数,计数范围0-59,最左边的数码管显示分计数,范围0-59。当按键按下(rst为高)时,清零所有的计数器,按键释放后,秒表开始计数。代码如下:
/*
mapping :
SEVEN_SEG[0] SEVEN_SEG[1] SEVEN_SEG[2] SEVEN_SEG[3] SEVEN_SEG[4] SEVEN_SEG[5] SEVEN_SEG[6]
*** A***** *****B***** *****C***** *****D**** ******E***** *****F***** *****G******
*/
module scan_8seg
(
input inclk,
input rst,
output reg [6:0] SEVEN_SEG,
output reg DP,
output reg [5:0] SEAT
);
reg [3:0] count_msa, count_msb;
reg [3:0] count_sa, count_sb;
reg [3:0] count_ma, count_mb;
reg [15:0] div_count_ms;
reg f_1ms;
reg [3:0] div_count_10ms;
reg f_10ms;
reg [3:0] count_sel;
//毫秒分频
always@(posedge inclk or posedge rst )
if(rst)
begin
div_count_ms <= 0;
f_1ms <= 0;
end
else
begin
f_1ms <= 0;
if(div_count_ms == 49999)
begin
div_count_ms <= 0;
f_1ms <= 1'b1;
end
else
begin
div_count_ms <= div_count_ms + 1;
end
end
//10毫秒分频
always@(posedge inclk or posedge rst )
if(rst)
begin
div_count_10ms <= 0;
f_10ms <= 0;
end
else
begin
f_10ms <= 0;
if(f_1ms)
begin
if(div_count_10ms == 9)
begin
div_count_10ms <= 0;
f_10ms <= 1'b1;
end
else
begin
div_count_10ms <= div_count_10ms + 1;
end
end
end
//计数,毫秒(精度为10毫秒),秒,分
always@(posedge inclk or posedge rst )
if(rst)
begin
count_msa <= 0;
count_msb <= 0;
count_sa <= 0;
count_sb <= 0;
count_ma <= 0;
count_mb <= 0;
end
else
begin
if(f_10ms)
begin
if(count_msa == 9)
begin
count_msa <= 0;
if(count_msb == 9)
begin
count_msb <= 0;
if(count_sa == 9)
begin
count_sa <= 0;
if(count_sb == 5)
begin
count_sb <= 0;
if(count_ma == 9)
begin
count_ma <= 0;
if(count_mb == 5)
count_mb <= 0;
else
count_mb <= count_mb + 1;
end
else
count_ma <= count_ma + 1;
end
else
count_sb <= count_sb + 1;
end
else
count_sa <= count_sa + 1;
end
else
count_msb <= count_msb + 1;
end
else
count_msa <= count_msa + 1;
end
end
//==================== 数码管扫描
always@(posedge inclk or posedge rst )
if(rst)
begin
SEAT <= 6'b11_1111;
count_sel <= 0;
DP <= 1'b1;
end
else
begin
if(f_1ms)
begin
case (SEAT)
6'b11_1110:
begin
SEAT <= 6'b11_1101;
count_sel <= count_msb;
DP <= 1'b1;
end
6'b11_1101:
begin
SEAT <= 6'b11_1011;
count_sel <= count_sa;
DP <= 1'b0;
end
6'b11_1011:
begin
SEAT <= 6'b11_0111;
count_sel <= count_sb;
DP <= 1'b1;
end
6'b11_0111:
begin
SEAT <= 6'b10_1111;
count_sel <= count_ma;
DP <= 1'b0;
end
6'b10_1111:
begin
SEAT <= 6'b01_1111;
count_sel <= count_mb;
DP <= 1'b1;
end
6'b01_1111:
begin
SEAT <= 6'b11_1110;
count_sel <= count_msa;
DP <= 1'b1;
end
default: SEAT <= 6'b01_1111;
endcase
end
end
//7段显示译码
always@(*)
case(count_sel)
0: SEVEN_SEG = 7'b100_0000;
1: SEVEN_SEG = 7'b111_1001;
2: SEVEN_SEG = 7'b010_0100;
3: SEVEN_SEG = 7'b011_0000;
4: SEVEN_SEG = 7'b001_1001;
5: SEVEN_SEG = 7'b001_0010;
6: SEVEN_SEG = 7'b000_0010;
7: SEVEN_SEG = 7'b111_1000;
8: SEVEN_SEG = 7'b000_0000;
9: SEVEN_SEG = 7'b001_0000;
4'ha: SEVEN_SEG = 7'b000_1000;
4'hb: SEVEN_SEG = 7'b000_0011;
4'hC: SEVEN_SEG = 7'b100_0110;
4'hd: SEVEN_SEG = 7'b010_0001;
4'hE: SEVEN_SEG = 7'b000_0110;
4'hF: SEVEN_SEG = 7'b000_1110;
default: SEVEN_SEG = 7'b111_1111;
endcase
endmodule
- 编译,管脚锁定,重新编译下载,测试。
- 测试中几个方法:
- 由于最低位的数码管显示的1/100秒,变化速度太快,无法看清计数是否正确,可以将f_10ms在分频调慢50倍,用于调式,调试结束后,还原到1/100秒的分频。
- 分计数器,依赖秒计数的进位,因此验证分计数器是否正确计数,等待时间过长,可以将f_10ms再调快100倍用于测试,测设完成后,还原到最初的分屏程序。
思考题:
- DP的显示是如何在正确的位置上的?
- SEVEN_SEG是否可以利用时钟锁存后输出,有什么差别?如果时钟锁存输出,程序如何修改?
作业题:
参照上文,根据PRX100-D的硬件原理(数码管,按键),在Vivado下完成数码管扫描显示设计,并在PRX100-D实验开发板上完成相关实验,最后编写实验设计报告。
老师我这段数码管计数,也用的是非阻塞赋值,每次计数到8时直接清零计数,计数到4时直接清零,帮忙看下什么原因,另外和你们编写的这段程序的区别差异在,帮忙看下,一直没看出来,万分感激,谢谢~~~~~
//数码管计数
else
begin
if (ms_flag)
begin
cnt_msa<=cnt_msa+1;
end
if(cnt_msa==9)
begin
cnt_msa<=4'b0;
cnt_msb<=cnt_msb+1;
end
if(cnt_msb==9)
begin
cnt_msb<=4'b0;
cnt_sa<=cnt_sa+1;
end
if(cnt_sa==9)
begin
cnt_sa<=4'b0;
cnt_sb<=cnt_sb+1;
end
if(cnt_sb==5)
begin
cnt_sb<=3'b0;
cnt_ma<=cnt_ma+1;
end
if(cnt_ma==9)
begin
cnt_ma<=4'b0;
cnt_mb<=cnt_mb+1;
if(cont_mb==5)
begin
cnt_mb<=3'b0;
end
end
end
你有使用仿真test bench来调试,找出错误吗,如果没有的话,建议先自己仿真观察。