数码管是一种半导体发光器件,具有响应时间短、体积小、重量轻、寿命长等优点。本章节将介绍数码管的显示原理和驱动方式。
1. 理论学习
数码管分为共阳极数码管和共阴极数码管。共阳极数码管就是把发光二极管的正极连接在一起作为一个引脚,负极分开。相反的,共阴极数码管就是把发光二极管的阴极连接在一起作为一个引脚,正极分开。这两者的区别在于,公共端是连接到地还是高电平,对于共阳极数码管需要给对应段低电平才会使其点亮,而对于共阴极数码管则需要给其高电平才会点亮。本次实验使用的是共阳极数码管,也就是说给对应段低电平才会被点亮。
二进制段码右边为高位左边为低位。我们只要点亮相应的段码,就 能显示我们需要显示的内容。
段式数码管工作方式有两种:静态显示和动态显示。静态显示的特点是每个数码管的段选必须接一个 8 位数据线来显示字形,显示字形可一直保持,直到送入新字形码为止。 那么如果点亮 6 个码管是不是需要 48 位数据线去分别控制每一个码管的段选?当然这种方法也可以,但是其占用的 I/O 口较多,因此硬件电路比较复杂,成本较高,很少使用。
由上图可以看到,我们将六个数码管的段选信号连接在一起,而位选(sel)独立控制,这样六个数码管接在一起就少了 8×5 个 I/O 口。这里对位选信号特别说明一下:由上图可以看到每一个数码管都有一个位选信号,而这个位选信号就控制着数码管的亮灭。这样我们就可以通过位选信号去控制数码管亮,而在同一时刻,位选选通的数码管上显示的字形是一样的,因为我们将 6 个数码管相对应的段选连在了一起,数码管的显示自然就相同了,数码管的这种显示方式即为静态显示。而如果要让每个数码管显示的值不同,我们要用到另外一种显示方式,即动态显示,将在下一章节给大家介绍。本章节先讲述 6 位共阳极数码管的静态显示,为下个章节的动态显示做准备。
2. 实验演练
2.1 实验目标
设计一个这样的 6 位数码管静态显示:控制六位数码管让其以 000000、111111、222222 一直到 FFFFFF 循环显示。每个字符显示 0.5s 后变化。
图1 系统框图
2.2 波形图绘制
cnt_wait:根据实验要求需要等待 0.5s 后显示的字符才发生变化。所以我们需要一个 0.5s 的循环计数器。我们输入的时钟频率是 50MHz,一个时钟周期的时间就是(1/50MHz)s , 也就是 20ns 。 所以我们计数器从0计到24_999_999即为0.5s (25000000*20ns)的时间。计到0.5s 后让其归0 开始下一个 0.5s 的计数。
num:每个数码管显示的字符,初始显示为 0,六个就是 000000。当检测到跳转的标志信号为高时,让各个数码管显示的字符加 1。当加到 4’hF 时让其归 0 重新相加以此循环。
3. 代码编写
module seg_static
#(
parameter CNT_WAIT_MAX = 25'd24_999_999 //计数器最大值(0.5s)
)
(
input wire sys_clk,
input wire sys_rst_n,
output reg [5:0] sel,
output reg [7:0] seg
);
//十六进制数显示编码
parameter SEG_0 = 8'b1100_0000,
SEG_1 = 8'b1111_1001,
SEG_2 = 8'b1010_0100,
SEG_3 = 8'b1011_0000,
SEG_4 = 8'b1001_1001,
SEG_5 = 8'b1001_0010,
SEG_6 = 8'b1000_0010,
SEG_7 = 8'b1111_1000,
SEG_8 = 8'b1000_0000,
SEG_9 = 8'b1001_0000,
SEG_A = 8'b1000_1000,
SEG_B = 8'b1000_0011,
SEG_C = 8'b1100_0110,
SEG_D = 8'b1010_0001,
SEG_E = 8'b1000_0110,
SEG_F = 8'b1000_1110,
IDLE = 8'b1111_1111; //不显示状态
reg [24:0] cnt; //时钟分频计数器
reg cnt_flag; //数码管数值+1 标志信号
reg [3:0] data; //数码管显示的十六进制数
//cnt_wait:0.5 秒计数
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'd0;
else if (cnt == CNT_WAIT_MAX)
cnt <= 25'd0;
else
cnt <= cnt + 1'b1;
//add_flag:0.5s 拉高一个标志信号
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if (cnt == CNT_WAIT_MAX - 1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
//num:从 4'h0 加到 4'hf 循环
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data <= 4'd0;
else if ((cnt_flag == 1'b1)&&(data == 4'd15))
data <= 4'd0;
else if (cnt == CNT_WAIT_MAX)
data <= data + 1'b1;
else
data <= data;
//sel:选中六个数码管
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
sel <= 6'b000_000;
else
sel <= 6'b111_111;
//给要显示的值编码
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
seg <= SEG_0;
else if (cnt_flag == 1'b1)
case(data)
4'd0 :seg <= SEG_0 ;
4'd1 :seg <= SEG_1 ;
4'd2 :seg <= SEG_2 ;
4'd3 :seg <= SEG_3 ;
4'd4 :seg <= SEG_4 ;
4'd5 :seg <= SEG_5 ;
4'd6 :seg <= SEG_6 ;
4'd7 :seg <= SEG_7 ;
4'd8 :seg <= SEG_8 ;
4'd9 :seg <= SEG_9 ;
4'd10:seg <= SEG_A ;
4'd11:seg <= SEG_B ;
4'd12:seg <= SEG_C ;
4'd13:seg <= SEG_D ;
4'd14:seg <= SEG_E ;
4'd15:seg <= SEG_F ;
default:seg <= IDLE;
endcase
endmodule
4. 仿真验证
`timescale 1ns / 1ns
module tb_seg_static();
reg sys_clk;
reg sys_rst_n;
wire [5:0] sel;
wire [7:0] seg;
initial
begin
sys_clk = 1;
sys_rst_n <= 0;
#20
sys_rst_n <= 1;
end
always #10 sys_clk = ~sys_clk;
seg_static
#(
.CNT_WAIT_MAX (25'd24)
)
seg_static_inst
(
. sys_clk (sys_clk ),
. sys_rst_n(sys_rst_n),
. sel (sel ),
. seg (seg )
);
endmodule