19. 数码管的静态显示

在许多项目设计中,我们通常需要一些显示设备来显示我们需要的信息,可以选择的显示设备有很多,而数码管是使用最多,最简单的显示设备之一。数码管是一种半导体发光器件,具有响应时间短、体积小、重量轻、寿命长等优点。本章节将为大家介绍数码管的显示原理和驱动方式。

19.1. 理论学习

19.1.1. 数码管简介

数码管是一种半导体发光器件,其基本单元是发光二极管。数码管按段数一般分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管(多一个小数点显示)。当然也还有一些其他类型的数码管如“N”形管、“米”字管以及工业科研领域用的16段管、24段管等,在此就不详细介绍。下面将为大家详细介绍本次实验使 用的八段数码管。

segsta002

图 25‑1 常见数码管

19.1.2. 八段数码管

八段数码管的结构图如图 25‑2所示。

segsta003

图 25‑2 八段数码管结构图

由图 25‑2可以看出,八段数码管是一个八字型数码管,分为八段:a、b、c、d、e、f、g、dp,其中dp为小数点,每一段即为一个发光二极管,这样的八段我们称之为段选信号。数码管常用的有10根管脚,每一段有一根管脚,另外两根管脚为一个数码管的公共端,两根互相连接。

数码管分为共阳极数码管和共阴极数码管。共阳极数码管就是把发光二极管的正极连接在一起作为一个引脚,负极分开。相反的,共阴极数码管就是把发光二极管的阴极连接在一起作为一个引脚,正极分开。这两者的区别在于,公共端是连接到地还是高电平,对于共阳极数码管需要给对应段低电平才会使其点亮,而对于共阴极数码管则需要 给其高电平才会点亮。本次实验使用的是共阳极数码管,也就是说给对应段低电平才会被点亮。给不同的段点亮可显示0~f的值,如表格 25‑1所示。

表格 25‑1 数码管编码译码表

<pre><code> |

</code></pre>








    • a

    • b

    • c

    • d

    • e

    • f

    • g

    • dp

    • 0

    • 0

    • 0

    • 0

    • 0

    • 0

    • 0

    • 1

    • 1

    • 8 ‘hc0

    • 1

    • 1

    • 0

    • 0

    • 1

    • 1

    • 1

    • 1

    • 1

    • 8 ‘hf9

    • 2

    • 0

    • 0

    • 1

    • 0

    • 0

    • 1

    • 0

    • 1

    • 8 ‘ha4

    • 3

    • 0

    • 0

    • 0

    • 0

    • 1

    • 1

    • 0

    • 1

    • 8 ‘hb0

    • 4

    • 1

    • 0

    • 0

    • 1

    • 1

    • 0

    • 0

    • 1

    • 8 ‘h99

    • 5

    • 0

    • 1

    • 0

    • 0

    • 1

    • 0

    • 0

    • 1

    • 8 ‘h92

    • 6

    • 0

    • 1

    • 0

    • 0

    • 0

    • 0

    • 0

    • 1

    • 8 ‘h82

    • 7

    • 0

    • 0

    • 0

    • 1

    • 1

    • 1

    • 1

    • 1

    • 8 ‘hf8

    • 8

    • 0

    • 0

    • 0

    • 0

    • 0

    • 0

    • 0

    • 1

    • 8 ‘h80

    • 9

    • 0

    • 0

    • 0

    • 0

    • 1

    • 0

    • 0

    • 1

    • 8 ‘h90

    • a

    • 0

    • 0

    • 0

    • 1

    • 0

    • 0

    • 0

    • 1

    • 8 ‘h88

    • b

    • 1

    • 1

    • 0

    • 0

    • 0

    • 0

    • 0

    • 1

    • 8 ‘h83

    • c

    • 0

    • 1

    • 1

    • 0

    • 0

    • 0

    • 1

    • 1

    • 8 ‘hc6

    • d

    • 1

    • 0

    • 0

    • 0

    • 0

    • 1

    • 0

    • 1

    • 8 ‘ha1

    • e

    • 0

    • 1

    • 1

    • 0

    • 0

    • 0

    • 0

    • 1

    • 8 ‘h86

    • f

    • 0

    • 1

    • 1

    • 1

    • 0

    • 0

    • 0

    • 1

    • 8 ‘h8e

如表格 25‑1所示,二进制段码右边为高位左边为低位。我们只要点亮相应的段码,就能显示我们需要显示的内容。

段式数码管工作方式有两种:静态显示和动态显示。静态显示的特点是每个数码管的段选必须接一个8位数据线来显示字形,显示字形可一直保持,直到送入新字形码为止。那么如果点亮6个码管是不是需要48位数据线去分别控制每一个码管的段选?当然这种方法也可以,但是其占用的I/O口较多,因此硬件电路比较复杂,成本较高, 很少使用。

那么如何节约资源呢?以我们本实验使用的数码管为例,如图 25‑3所示:

segsta004

图 25‑3 六位数码管等效电路图

由上图可以看到,我们将六个数码管的段选信号连接在一起,而位选(sel)独立控制,这样六个数码管接在一起就少了8\(\times\)5个I/O口。这里对位选信号特别说明一下:由上图可以看到每一个数码管都有一个位选信号,而这个位选信号就控制着数码管的亮灭。这样我们就可以通过位选信号去控制 数码管亮,而在同一时刻,位选选通的数码管上显示的字形是一样的,因为我们将6个数码管相对应的段选连在了一起,数码管的显示自然就相同了,数码管的这种显示方式即为静态显示。而如果要让每个数码管显示的值不同,我们要用到另外一种显示方式,即动态显示,将在下一章节给大家介绍。本章节先讲述6位共阳极数码管的静态显 示,为下个章节的动态显示做准备。

由图 25‑3可以看到,即使这样我们控制数码管仍然需要占用14个I/O口资源(8个段选,6个位选)。如果我想节约FPGA芯片的更多I/O口去做其它设计,那能不能使用更少的I/O口去驱动数码管显示呢?这当然是可以的,我们可通过74HC595芯片(位移缓存器)进行实现。

19.1.3. 74HC595简介

74HC595是一个8位串行输入、并行输出的位移缓存器。其内部具有8位移位寄存器和一个存储器,具有三态输出功能。我们先跟据该芯片的引脚图来为大家讲解其功能,如图 25‑4所示。

segsta005

图 25‑4 74HC595芯片引脚图

其各引脚功能简介如表格 25‑2所示。

表格 25‑2 74HC595引脚功能简介

引脚名

引脚编号

引脚功能

Q0—Q7

15,1—7

并行数据输出

GND

8

电源地

Q7S

9

串行数据输出

10

主复位(低电平有效)

SHCP

11

移位寄存器时钟输入

STCP

12

存储寄存器时钟输入

13

输出使能输入(低电平有效)

DS

14

串行数据输入

VCC

16

电源电压

如表格 25‑2所示,该芯片有个并行的数据输出,同时芯片的输入是串行数据,也就是说我们使用一个串行输入口就可以并行输出八个输入的串行数据。从上一小节数码管的讲解我们知道六位八端数码管需要14个I/O(也就是14bit)去驱动数码管,而使用该芯片后,我们就可以将输出的数码管信号以串行(1bit)的方式 输入该芯片,然后该芯片会将我们输入的数码管信号以并行的方式输出。但是可以看到一片芯片只能并行输出8位数据,而我们的六位八段数码管是需要14位数据驱动的,这该怎么办呢?这里我们可以使用两片74HC595芯片进行输出,事实上这正是74HC595的一大特点,可以进行级联。74HC595芯片有个Q7S引脚, 该引脚的功能为串行数据输出,我们将这个输出接入下一片74HC595芯片的串行数据输入端,这样后面的数据就会在下一片74HC595芯片输出了。应用好这个联级功能,我们最少只需占用三个I/O口就可以控制很多片74HC595。

我们知道了数据的输入输出,那我们该如何控制这些数据输入和输出呢?我们通过74HC595芯片内部结构图来为大家讲解其控制过程,如图 25‑5所示。

segsta006

图 25‑5 74HC595内部结构图

如图 25‑5所示,是主复位,低电平时将移位寄存器的数据清零,通常接到VCC防止数据清零。SHCP为移位寄存器时钟输入,上升沿时将输入的串行数据(DS端输入)移入移位寄存器中。需要注要的是它是一个移位寄存器,也就是说当下一个脉冲(时钟上升沿)到来时,上一个脉冲移入的数据就会往下移动一位。如果我们串行 输入8bit数据,8bit数据输入完之后,那么第一位输入的数据将会移动到最后面。若我们一次输入的数据超过8bit,那么后面的数据就会通过Q7S端口输出,此时我们可以将该接口接到另一片74HC595芯片的串行输入端(级联),这样数据就会随着脉冲依次移位到另一片74HC595芯片上。

当串行数据移入到74HC595芯片的移位寄存器之后,我们如何控制其输出呢?74HC595内部有一个8位存储寄存器,该寄存器由STCP(存储寄存器时钟)控制,STCP上升沿时移位寄存器的数据会进入数据存储寄存器中,通过让(输出使能输入)为低即可让存储寄存器中的数据进行输出。

最后我们总结一下74HC595的使用步骤:

  1. 首先把要传输的数据通过引脚DS输入到74HC595中。

  2. 产生SHCP时钟,将DS上的数据串行移入移位寄存器。

  3. 产生STCP时钟,将移位寄存器里的数据送入存储寄存器。

  4. 将引脚置为低电平,存储寄存器的数据会在Q0—Q7并行输出,同时并行输出的数据会被锁存起来。

输入与输出的关系如图 25‑6所示:

segsta007

图 25‑6 串并输出关系

如上图所示最先输入的数据会被移位到最后位进行输出。通过以上理论知识的讲解相信大家对数码管以及74HC595的使用都有了大致的了解,下面我们通过实战演练去看看具体该如何操作。

19.2. 实战演练

19.2.1. 实验目标

根据上一小节的理论学习,我们可以设计一个这样的6位数码管静态显示:控制六位数码管让其以000000、111111、222222一直到FFFFFF循环显示。每个字符显示0.5s后变化。

19.2.2. 硬件资源

本章节我们要使用开发板上的六位八段数码管进行实验,如图 25‑7所示。

segsta008

图 25‑7 硬件资源

征途Pro开发板上使用的数码管型号为FJ3661BH,以及使用了两片74HC595芯片。原理图如图 25‑8、图 25‑9所示。

segsta009

图 25‑8 数码管原理图

segsta010

图 25‑9 74HC595原理图

图 25‑8的数码管为共阳极数码管,即段选为低电平点亮。对应的数码管点亮原理图为图 25‑3所示。从图中可以看到,其位选为高电平时才能点亮对应数码管,DIG1(sel[5])对应的是开发板上最左侧数码管,依次类推,DIG6(sel[0])对应的就是开发板上最右侧数码管。

如图 25‑9所示开发板上搭载了两片74HC595芯片用于输出数码管驱动信号,其中我们接到了VCC防止数据清零,所以这两片74HC595芯片我们只用四个IO口控制即可。我们将两片74HC595进行级联,一片的Q7S输出端接到另一片的数据输入端DS,这样我们输入的14位串行输入数码管信号的前六位就会在 第二片就行输出。(注意:因为是移位寄存器,如果一次共输入14位数据,那么第一位输入的串行数据会在第二片74HC595芯片的Q5输出)。

19.2.3. 程序设计

硬件资源介绍完毕,我们开始实验工程的程序设计。

19.2.3.1. 整体说明

我们根据实验任务可以先画出我们的系统框图,根据框图我们可以更加明确的看到我们该如何完成这个实验。

segsta011

图 25‑10 数码管静态显示系统框图

根据框图可以看到实验一共分为3个模块,下面分模块为大家介绍。

表格 25‑3 数码管静态显示工程模块简介

模块名称

功能描述

seg_static

静态数码管驱动模块

hc595_ctrl

74HC595控制模块

seg_595_static

数码管静态显示顶层模块

19.2.3.2. 静态数码管驱动模块

模块框图

segsta012

图 25‑11 静态数码管驱动模块框图

表格 25‑4 静态数码管驱动模块输入输出信号描述

信号

位宽

类型

功能描述

sys_clk

1bit

Input

50MHz晶振时钟输入

sys_rst_n

1bit

Input

复位信号,低电平有效

sel

6bit

Output

数码管位选信号

seg

8bit

Output

数码管段选信号

数码管的静态显示驱动模块我们只需要给其输入时钟、复位信号即可,此模块是产生驱动数码管的段选信号和位选信号。下面根据波形图去了解位选信号和段选信号该如何输出才能使数码管达到我们的显示要求。

波形图绘制

六位数码管静态显示参考波形图,具体见图 25‑12。

segsta013

图 25‑12 数码管静态显示波形图

如图 25‑12所示:

cnt_wait:根据实验要求需要等待0.5s后显示的字符才发生变化。所以我们需要一个0.5s的循环计数器。我们输入的时钟频率是50MHz,一个时钟周期的时间就是(1/50MHz)s,也就是20ns。所以我们计数器从0计到24_999_999即为0.5s(25000000*20ns)的时间。计到0. 5s后让其归0开始下一个0.5s的计数。

add_flag:当计数器计到0.5s时,我们拉高一个标志信号,让这个标志信号去控制数码管字符的跳转。

num:每个数码管显示的字符,初始显示为0,六个就是000000。当检测到跳转的标志信号为高时,让各个数码管显示的字符加1。当加到4’hF时让其归0重新相加以此循环。

sel:数码管的位选信号。我们是显示六个数码管,直接给其全点亮即可。根据原理图可知我们需要给其位选信号高电平数码管才会被点亮,所以给对应的位数高电平对应的数码管就会点亮,一位表示一个数码管。这里我们全部点亮即可。

seg:数码管的段选信号,给其相应段码点亮显示num里的值即可。

本设计思路只做参考,并非唯一方法,读者也可利用所学知识,按照自己思路进行设计。

代码编写

根据波形图以及波形的的讲解,详细大家已经对模块所有信号的逻辑关系都已经基本了解,那么代码编写起来就比较简单了。模块参考代码,具体见代码清单 25‑1。

代码清单 25‑1 数码管静态显示参考代码(seg_static.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
module seg_static
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低电平有效

output reg [5:0] sel , //数码管位选信号
output reg [7:0] seg //数码管段选信号

);

 ////
 //\* Parameter and Internal Signal \//
 ////

 //parameter define
 parameter CNT_WAIT_MAX = 25'd24_999_999; //计数器最大值(0.5s)
 //十六进制数显示编码
 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;
 parameter IDLE = 8'b1111_1111; //不显示状态

 //reg define
 reg add_flag ; //数码管数值+1标志信号
 reg [24:0] cnt_wait ; //时钟分频计数器
 reg [3:0] num ; //数码管显示的十六进制数

 ////
 //\* Main Code \//
 ////

 //cnt_wait:0.5秒计数
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_wait <= 25'd0;
 else if(cnt_wait == CNT_WAIT_MAX)
 cnt_wait <= 25'd0;
 else
 cnt_wait <= cnt_wait + 1'b1;

 //add_flag:0.5s拉高一个标志信号
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 add_flag <= 1'b0;
 else if(cnt_wait == CNT_WAIT_MAX - 1)
 add_flag <= 1'b1;
 else
 add_flag <= 1'b0;

 //num:从 4'h0 加到 4'hf 循环
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 num <= 4'd0;
 else if(add_flag == 1'b1)
 num <= num + 1'b1;
 else
 num <= num;

 //sel:选中六个数码管
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 sel <= 6'b000000;
 else
 sel <= 6'b111111;

 //给要显示的值编码
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 seg <= IDLE;
 else case(num)
 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

以上代码都是根据波形图进行编写的,这里就不再过多讲解了。

19.2.3.3. 74HC595控制模块

模块框图

segsta014

图 25‑13 74HC595控制模块框图

表格 25‑5 74HC595控制模块框图

信号

位宽

类型

功能简介

sys_clk

1bit

Input

系统时钟,频率50MHz

sys_rst_n

1bit

Input

复位信号,低有效

sel

6bit

Input

数码管位选信号

seg

8bit

Input

数码管段选信号

stcp

1bit

Output

存储寄存器时钟

shcp

1bit

Output

移位寄存器时钟

ds

1bit

Output

串行数据

oe

1bit

Output

输出使能,低有效

由图 25‑13、表格 25‑5所示,该模块我们需要产生stcp、shcp、ds、oe四个信号对74HC595进行控制。其中ds(串行数据)就是输入的数码管位选信号和段选信号;shcp(移位寄存器时钟),这个是ds数据进入移位寄存器的时钟,它的频率是有限制的,我们可以从数据手册中看到,如图 25‑14所示:

segsta015

图 25‑14 shcp、stcp时钟频率

由上图可以看到shcp和stcp的最大频率是有限制的,由原理图可以看到我们使用的电压是3.3v,同时温度不同其支持的最大频率也不同,大家可根据自己的实验环境进行频率的选取,本实验我们使用系统时钟(50MHz)四分频得到的shcp时钟(12.5MHz)去进行驱动,而stcp时钟是在我们串行输入14位数 码管之后拉高的,其频率远远小于shcp,所以这里我们只要确定shcp的频率即可,至于oe信号我们一直让其拉低即可。下面根据波形图图为大家进一步讲解。

波形图绘制

segsta016

图 25‑15 74HC595控制模块波形图

如图 25‑15所示时钟和复位由顶层传来,seg(数码管段选)和sel(数码管位选)由数码管驱动模块传来。seg数据和sel数据就是我们需要串并转换的数据,这两个信号的位宽加起来为14位,即我们每传输14位数据后需并行输出。

cnt:分频计数器。这里我们让计数器在0和3之间循环计数,这样一个循环生成一个时钟即为四分频时钟。

cnt_bit:传输位数计数器。我们知道我们需要传输14bit的数据,故我们需要一个数据器对传输的位数进行计数,这样我们对传输完成14位数据就可以用这个计数器进行判别了。如图所示,当cnt等于3时让cnt_bit计数器加1,让其从0到13循环计数,每个数值代表传输一位数据。

data:我们将需要传输的数码管信号寄存在data中,方便赋值。存储顺序是根据我们传输的位数顺序由低到高位进行存储的,至于数码管各信号的传输顺序我们在硬件部分已有所讲解。

ds:串行数据输出(对我们FPGA芯片来说其是输出,对74HC595来说其是输入,stcp和shcp信号也是如此)。这里我们回到原理图 25‑9,可以看到第二片的Q5引脚连到了数码管的DIG6,也就是最右侧的数码管,而我们最右侧数码管对应的是我们位选信号的最低位,即sel[0]。所以我们第一位应传输 的数据为sel[0],依次类推根据原理图传输相应的数据,具体的传输数据如波形图所示。当一次数据传完之后再次回到状态0开始新一轮的数码管信号传输。

shcp:移位寄存器时钟,上升沿时将数据写入移位寄存器中。我们在ds数据的中间状态拉高产生上升沿,这样可以使shcp采得的ds数据更加稳定。如波形图所示我们在cnt=2时拉高,cnt=0时拉低,即可产生该时钟,其频率即为系统时钟四分频(12.5MHz)。

stcp:存储寄存器时钟。当我们14位数码管控制信号传输完之后我们需要拉高一个stcp时钟来将信号存入存储寄存器之中。最后一个数据是在cnt_bit=13且cnt=2时传输的,所以我们就在下一个时钟(cnt_bit=13且cnt=3时)将stcp拉高一个时钟产生上升沿即可。

oe:存储寄存器数据输出使能信号,低电平有效,这里我们将复位信号取反的值赋给该信号即可。

本设计思路只做参考,并非唯一方法,读者也可利用所学知识,按照自己思路进行设计。

代码编写

根据波形图以及波形图的讲解,相信大家已经对模块所有信号的逻辑关系都已经基本了解,那么代码编写起来就比较简单了。模块参考代码,具体见代码清单 25‑2。

代码清单 25‑2 74HC595控制模块参考代码(hc595.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
module hc595_ctrl
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低有效
input wire [5:0] sel , //数码管位选信号
input wire [7:0] seg , //数码管段选信号

output reg stcp , //数据存储器时钟
output reg shcp , //移位寄存器时钟
 output reg ds , //串行数据输入
 output wire oe //使能信号,低有效
 );

 ////
 //\* Parameter and Internal Signal \//
 ////
 //reg define
 reg [1:0] cnt_4 ; //分频计数器
 reg [3:0] cnt_bit ; //传输位数计数器

 //wire define
 wire [13:0] data ; //数码管信号寄存

 ////
 //\* Main Code \//
 ////

 //将数码管信号寄存
 assign data={seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel};

 //将复位取反后赋值给其即可
 assign oe = ~sys_rst_n;

 //分频计数器:0~3循环计数
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_4 <= 2'd0;
 else if(cnt_4 == 2'd3)
 cnt_4 <= 2'd0;
 else
 cnt_4 <= cnt_4 + 1'b1;

 //cnt_bit:每输入一位数据加一
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_bit <= 4'd0;
 else if(cnt_4 == 2'd3 && cnt_bit == 4'd13)
 cnt_bit <= 4'd0;
 else if(cnt_4 == 2'd3)
 cnt_bit <= cnt_bit + 1'b1;
 else
 cnt_bit <= cnt_bit;

 //stcp:14个信号传输完成之后产生一个上升沿
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 stcp <= 1'b0;
 else if(cnt_bit == 4'd13 && cnt_4 == 2'd3)
 stcp <= 1'b1;
 else
 stcp <= 1'b0;

 //shcp:产生四分频移位时钟
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 shcp <= 1'b0;
 else if(cnt_4 >= 4'd2)
 shcp <= 1'b1;
 else
 shcp <= 1'b0;

 //ds:将寄存器里存储的数码管信号输入即
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 ds <= 1'b0;
 else if(cnt_4 == 2'd0)
 ds <= data[cnt_bit];
 else
 ds <= ds;

 endmodule

代码是根据所绘制的波形图进行讲解的,前面已经波形图有了详细的讲解,再次就不再过多的叙述了。

19.2.3.4. 顶层模块

模块框图

segsta017

图 25‑16 顶层模块

顶层模块主要是对各个子功能模块的实例化,以及对应信号的连接,各输入输出如表格 25‑6所示。

表格 25‑6 顶层模块输入输出信号描述

信号

位宽

类型

功能描述

sys_clk

1bit

Input

系统时钟,50MHz

sys_rst_n

1bit

Input

复位信号,低有效

stcp

1bit

Output

存储寄存器时钟

shcp

1bit

Output

移位寄存器时钟

ds

1bit

Output

串行数据

oe

1bit

Output

输出使能,低有效

代码编写

顶层模块代码编写较为容易,无需波形图的绘制。顶层参考代码,具体见代码清单 25‑3。

代码清单 25‑3 数码管动态显示顶层模块参考代码(seg_595_static.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
module seg_595_static
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低有效

output wire stcp , //输出数据存储寄时钟
output wire shcp , //移位寄存器的时钟输入
output wire ds , //串行数据输入
output wire oe //输出使能信号
 );

 ////
 //\* Parameter And Internal Signal \//
 ////
 //wire define
 wire [5:0] sel;
 wire [7:0] seg;

 ////
 //\* Instantiation \//
 ////
 //---------- seg_static_inst ----------
 seg_static seg_static_inst
 (
 .sys_clk (sys_clk ), //系统时钟,频率50MHz
 .sys_rst_n (sys_rst_n ), //复位信号,低电平有效

 .sel (sel ), //数码管位选信号
 .seg (seg ) //数码管段选信号
 );

 //---------- hc595_ctrl_inst ----------
 hc595_ctrl hc595_ctrl_inst
 (
 .sys_clk (sys_clk ), //系统时钟,频率50MHz
 .sys_rst_n (sys_rst_n), //复位信号,低有效
 .sel (sel ), //数码管位选信号
 .seg (seg ), //数码管段选信号

 .stcp (stcp ), //输出数据存储寄时钟
 .shcp (shcp ), //移位寄存器的时钟输入
 .ds (ds ), //串行数据输入
 .oe (oe ) //输出使能信号
 );

 endmodule

19.2.3.5. RTL视图

顶层模块介绍完毕,使用Quartus II软件对实验工程进行编译,工程通过编译后查看实验工程RTL视图。工程RTL视图,具体见由图 25‑17可知,实验工程的RTL视图与实验整体框图相同,各信号线均已正确连接。

segsta018

图 25‑17 RTL视图

19.2.3.6. 仿真验证

仿真代码编写

编写仿真代码,对参考代码进行仿真验证。仿真参考代码,具体见代码清单 25‑4。

代码清单 25‑4 数码管静态显示仿真参考代码(tb_seg7_static.v)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
\`timescale 1ns/1ns
module tb_seg_595_static();

////
//\* Parameter and Internal Signal \//
////

//wire define
wire stcp ; //输出数据存储寄时钟
 wire shcp ; //移位寄存器的时钟输入
 wire ds ; //串行数据输入
 wire oe ; //输出使能信号

 //reg define
 reg sys_clk ;
 reg sys_rst_n ;

 ////
 //\* Main Code \//
 ////

 //对sys_clk,sys_rst_n赋初始值
 initial
 begin
 sys_clk = 1'b1;
 sys_rst_n <= 1'b0;
 #100
 sys_rst_n <= 1'b1;
 end

 //clk:产生时钟
 always #10 sys_clk <= ~sys_clk;

 //重新定义参数值,缩短仿真时间
 defparam seg_595_static_inst.seg_static_inst.CNT_WAIT_MAX = 100;

 ////
 //\* Instantiation \//
 ////

 //-------------seg_595_static_inst-------------
 seg_595_static seg_595_static_inst(

 .sys_clk (sys_clk ), //系统时钟,频率50MHz
 .sys_rst_n (sys_rst_n ), //复位信号,低电平有效

 .stcp (stcp ), //输出数据存储寄时钟
 .shcp (shcp ), //移位寄存器的时钟输入
 .ds (ds ), //串行数据输入
 .oe (oe ) //输出使能信号

 );

 endmodule

仿真波形分析

配置好仿真文件,使用ModelSim对参考代码进行仿真。

segsta019

图 25‑18 数码管静态驱动模块仿真波形图(一)

由图 25‑18可以看到数码显示的值(num)从0开始跳转到了1,再跳转到了2;同时段选信号(seg)的编码与显示的字符也相吻合,仿真图与我们所画波形图的开头是一致的。

segsta020

图 25‑19 数码管静态驱动模块仿真波形图(二)

由图 25‑19可以看到数码管显示的数值从4’hf跳转回0从头开始显示了,这与我们的设计和绘制的波形图是一致的。可以满足我们的实验要求。

segsta021

图 25‑20 74HC595控制模块仿真波形图

如图 25‑20所示,抓取的74HC595控制模块的各信号时序与我们绘制的波形图时序是一致的。

19.3. 上板调试

19.3.1. 引脚约束

仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 25‑7所示。

表格 25‑7 引脚分配表

信号名

信号类型

对应引脚

备注

sys_clk

input

E1

时钟

sys_rst_n

input

M15

复位

stcp

output

K9

存储寄存器时钟

shcp

output

B1

移位寄存器时钟

ds

output

R1

串行数据

oe

output

L11

输出使能

下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 25‑21所示。

segsta022

图 25‑21 管脚分配

19.3.1.1. 结果验证

管脚配置完成之后重新进行编译,编译完之后就可以进行下载验证了,在下载之前首先将电源与下载线与开发板连接好,连接好之后上电,如图 25‑22所示。

segsta023

图 25‑22 下载连线图

打开下载界面后,当检测到下载器(USB-Blaster)已连接之后,即可点击“Add File…”添加sof文件,添加好后点击“start”开始下载,随后界面会显示下载成功,如图 25‑23所示。

segsta024

图 25‑23 下载成功界面

下载成功后可以看到数码管上的数据从000000~FFFFFF之间循环显示,说明验证成功。

19.4. 章末总结

到这里,本章节讲解完毕,通过本章节相信大家对数码管有了初步的了解,那么对下一章节的数码管的动态显示将会有所帮助。