由上图可以看出数码管有两种结构:共阴极与共阳极。这两者的区别在于,公共端是连接到地还是高电平,对于共阴数码管需要给对应段以高电平才会使其点亮,而对于共阳极数码管则需要给低电平才会点亮。本次使用的是共阳数码管。同时为了显示数字或字符,必须对数字或字符进行编码译码。先不考虑小数点也就是简化为7段数码管,其编码译码格式如下表所示:
段式数码管工作方式有两种:静态显示方式和动态显示方式。静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法由于每一个数码管均需要独立的数据线因此硬件电路比较复杂,成本较高,很少使用。
为了节约IO以及成本一般采用如下图所示的电路结构,这样3个数码管接在一起就比静态的少了7*2个I/O。
这样就实现了另一种显示模式,动态显示。动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。
现在举例假设将扫描时间定为1s,这三个数码管分成3s,第1秒时sel数据线上为`b100,这时数码管0被选中,这时a=0,数码管0的LED0就可以点亮;第2s时sel数据线上为`b010,这时数码管1被选中,这时b=0,数码管1的LED1就可以点亮;第3s时sel数据线上为`b001,这时数码管2被选中,这时c=0,数码管2的LED2就可以点亮。这时的效果就会是数码管0的LED0亮一秒后数码管1的LED1亮一秒最后是数码管2的LED2亮一秒,这样再次循环。
这样如果使用1ms刷新时间的话由于数码管的余辉效应以及人的视觉暂留这样就会出现数码管0的LED0、数码管1的LED1以及数码管2的LED2 “同时”亮,并不会有闪烁感。
设计代码
module hex8(
Clk,
Reset_n,
Disp_Data,
sel,
seg
);
input Clk;
input Reset_n;
input[31:0]Disp_Data;
output reg [7:0]sel;
output reg[7:0]seg;//输出的显示,根据查表法看每个数字对应哪个管亮
reg clk_1k; //分频器 门控时钟:周期1ms,每500us翻转一次
reg [14:0]div_cnt; //5000000/20=25000 15bits
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(div_cnt == 24999)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
clk_1k <= 0;
else if(div_cnt >= 24999)
clk_1k <= ~clk_1k;
//这种时钟在绝大多数场合不允许使用,后面会进行改进
reg [2:0]num_cnt;
always@(posedge clk_1k or negedge Reset_n)
if(!Reset_n)
num_cnt <= 0;
else
num_cnt <= num_cnt + 1'b1;
//三八译码器
always@(*)
case(num_cnt)
0:sel = 8'b00000001;
1:sel = 8'b00000010;
2:sel = 8'b00000100;
3:sel = 8'b00001000;
4:sel = 8'b00010000;
5:sel = 8'b00100000;
6:sel = 8'b01000000;
7:sel = 8'b10000000;
endcase
//MUX
reg[3:0]disp_tmp;
always@(*)
case(num_cnt)
0:disp_tmp = Disp_Data[31:28];
1:disp_tmp = Disp_Data[27:24];
2:disp_tmp = Disp_Data[23:20];
3:disp_tmp = Disp_Data[19:16];
4:disp_tmp = Disp_Data[15:12];
5:disp_tmp = Disp_Data[11:8];
6:disp_tmp = Disp_Data[7:4];
7:disp_tmp = Disp_Data[3:0];
endcase
// 通过查表进行数码管输出
always@(*)
case(disp_tmp)
4'h0:seg = 8'hc0;
4'h1:seg = 8'hf9;
4'h2:seg = 8'ha4;
4'h3:seg = 8'hb0;
4'h4:seg = 8'h99;
4'h5:seg = 8'h92;
4'h6:seg = 8'h82;
4'h7:seg = 8'hf8;
4'h8:seg = 8'h80;
4'h9:seg = 8'h90;
4'ha:seg = 8'h88;
4'hb:seg = 8'h83;
4'hc:seg = 8'hc6;
4'hd:seg = 8'ha1;
4'he:seg = 8'h86;
4'hf:seg = 8'h8e;
endcase
endmodule
仿真验证代码
`timescale 1ns / 1ps
module hex8_tb();
reg Clk;
reg Reset_n;
reg [31:0]Disp_Data;
wire[7:0]sel;
wire[7:0]seg;
hex8 hex8(
Clk,
Reset_n,
Disp_Data,
sel,
seg
);
initial Clk = 1;
always #10 Clk = ~Clk;
initial begin
Reset_n = 0;
Disp_Data = 32'h00000000;
#201;
Reset_n = 1;
#2000;
Disp_Data = 32'h12345678;
#10000000;
Disp_Data = 32'h9abcdef0;
#10000000;
$stop;
end
endmodule
仿真波形图
改进后的代码(使能时钟)
module hex8_2(
Clk,
Reset_n,
Disp_Data,
sel,
seg
);
input Clk;
input Reset_n;
input[31:0]Disp_Data;
output reg [7:0]sel;
output reg[7:0]seg;
reg clk_1k;
reg [15:0]div_cnt;
//改进:使能时钟,每1ms给一个持续20ns的高电平,其他时候均为低电平
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(div_cnt == 49999)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
clk_1k <= 0;
else if(div_cnt == 49999)
clk_1k <= 1'b1;
else
clk_1k <= 0;
reg [2:0]num_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
num_cnt <= 0;
else if(clk_1k)
num_cnt <= num_cnt + 1'b1;
//尽量都改为时序逻辑
//三八译码器-sel位选
always@(posedge Clk)
case(num_cnt)
0:sel = 8'b00000001;
1:sel = 8'b00000010;
2:sel = 8'b00000100;
3:sel = 8'b00001000;
4:sel = 8'b00010000;
5:sel = 8'b00100000;
6:sel = 8'b01000000;
7:sel = 8'b10000000;
endcase
//提取该位数码管需要输出的内容-查表
reg[3:0]disp_tmp;
always@(posedge Clk)
case(num_cnt)
7:disp_tmp = Disp_Data[31:28];
6:disp_tmp = Disp_Data[27:24];
5:disp_tmp = Disp_Data[23:20];
4:disp_tmp = Disp_Data[19:16];
3:disp_tmp = Disp_Data[15:12];
2:disp_tmp = Disp_Data[11:8];
1:disp_tmp = Disp_Data[7:4];
0:disp_tmp = Disp_Data[3:0];
endcase
//seg段选
always@(posedge Clk)
case(disp_tmp)
4'h0:seg = 8'hc0; //注意,是用十六进制表示,这里的0也是十六进制的0
4'h1:seg = 8'hf9;
4'h2:seg = 8'ha4;
4'h3:seg = 8'hb0;
4'h4:seg = 8'h99;
4'h5:seg = 8'h92;
4'h6:seg = 8'h82;
4'h7:seg = 8'hf8;
4'h8:seg = 8'h80;
4'h9:seg = 8'h90;
4'ha:seg = 8'h88;
4'hb:seg = 8'h83;
4'hc:seg = 8'hc6;
4'hd:seg = 8'ha1;
4'he:seg = 8'h86;
4'hf:seg = 8'h8e;
endcase
endmodule