verilog学习笔记- 14)静态数码管显示实验
使用 FPGA 开发板上的 6 位数码管以静态方式依次显示 000000、111111、222222 至FFFFFF,结束后继续从 000000 开始计数,每 0.5s 变化一次。
目录
简介:
数码管也称半导体数码管,它是将若干发光二极管按一定图形排列并封装在一起的一种数码显示器件。常见的数码管如图这种数码管主要被称为八段数码管或 8 字形数码管,可用来显示小数点、数字 0~9,和英文字母 A~F。
除了常用的八段数码管之外,较常见的还有“±1”数字管、“N”形管、“米”字管以及工业科研领域使用的 14 段管、16 段管、24 段管等。
从该图可以看出,一位数码管的引脚是 10 个,其中 7 个引脚对应连接到组成数码管中间“8”字型的 led,Dp 引脚连接到数码管的小数点显示 led(dp)。最后还有两个公共端,生产商为了封装统一,单个数码管都封装成 10 个引脚,其中 8 和 3 两个公共端引脚(图中为 com)是连接在一起的。公共端又可分为共阳极和共阴极
对共阴极数码管来说,其 8 个发光二极管的阴极在数码管内部全部连接在一起,所以称“共阴”,而阳极独立。对共阳极数码管来说,其 8 个发光二极管的阳极在数码管内部全部连接在一起,所以称“共阳”,而阴极独立。以共阳极数码管为例,当我们想让数码管显示数字“8”,可以给 a、b、c„g 七个引脚送低电平,数码管就显示“8”,显示数字“1”,就给 b、c 引脚低电平,其余引脚(除公共端)给高电平,数码管就显示“1”。
当多位数码管应用于某一系统时,为了减少数码管占用的 I/O 口,将其段选(数码管的 a、b、c 等引脚)连接在一起,而位选(数码管的公共端)独立控制。这样我们可以通过位选信号控制哪几个数码管亮,而且在同一时刻,位选选通的所有数码管上显示的数字始终都是一样的,因为它们的段选是连接在一起的,所以送入所有数码管的段选信号都是相同的,数码管的这种显示方法叫做静态显示。
实验任务:
使用 FPGA 开发板上的 6 位数码管以静态方式依次显示 000000、111111、222222 至FFFFFF,结束后继续从 000000 开始计数,每 0.5s 变化一次。
硬件设计:
为了增加 FPGA 输出信号的驱动能力,我们使用 PNP 型三极管驱动数码管的位选段,所以给三极管基极提供低电平时,位选信号为高电平。
各端口信号的管脚分配如下表所示:
对应的 TCL 约束语句如下:
set_location_assignment PIN_M2 -to sys_clk
set_location_assignment PIN_M1 -to sys_rst_n
set_location_assignment PIN_N16 -to sel[0]
set_location_assignment PIN_N15 -to sel[1]
set_location_assignment PIN_P16 -to sel[2]
set_location_assignment PIN_P15 -to sel[3]
set_location_assignment PIN_R16 -to sel[4]
set_location_assignment PIN_T15 -to sel[5]
set_location_assignment PIN_M11 -to seg_led[0]
set_location_assignment PIN_N12 -to seg_led[1]
set_location_assignment PIN_C9 -to seg_led[2]
set_location_assignment PIN_N13 -to seg_led[3]
set_location_assignment PIN_M10 -to seg_led[4]
set_location_assignment PIN_N11 -to seg_led[5]
set_location_assignment PIN_P11 -to seg_led[6]
set_location_assignment PIN_D9 -to seg_led[7]
程序设计:
系统的控制流程:首先我们需要一个静态数码管显示模块在数码管上显示数据,其次需要一个计时模块每当计时到 0.5s 时改变数码管显示的数值。由此画出系统的功能框图如下所示:
程序中各模块端口及信号连接如图:
FPGA 顶层(seg_led_static_top)例化了以下两个模块:计时模块(time_count)和数码管静态显示模块(seg_led_static),实现各模块之间数据的交互。计时模块将计时到 0.5s 时的标志信号 flag 传递给数码管静态显示模块,数码管静态显示模块接收到此信号时显示的数值增加 1。
计时模块(time_count):计时模块对系统时钟进行计数,当计时到给定值(此处指 0.5s)时输出标志信号。
数码管静态显示模块(seg_led_static):数码管静态显示模块在数码管上以静态方式显示数值。
顶层模块的代码如下:
1 module seg_led_static_top (
2 input sys_clk , // 系统时钟
3 input sys_rst_n, // 系统复位信号(低有效)
4
5 output [5:0] sel , // 数码管位选
6 output [7:0] seg_led // 数码管段选
7
8 );
9
10 //parameter define
11 parameter TIME_SHOW = 25'd25000_000; // 数码管变化的时间间隔 0.5s
12
13 //wire define
14 wire add_flag; // 数码管变化的通知信号
15
16 //*****************************************************
17 //** main code
18 //*****************************************************
19
20 //每隔 0.5s 产生一个时钟周期的脉冲信号
21 time_count #(
22 .MAX_NUM (TIME_SHOW)
23 ) u_time_count(
24 .clk (sys_clk ),
25 .rst_n (sys_rst_n),
26
27 .flag (add_flag )
28 );
29
30 //每当脉冲信号到达时,使数码管显示的数值加 1
31 seg_led_static u_seg_led_static (
32 .clk (sys_clk ),
33 .rst_n (sys_rst_n),
34
35 .add_flag (add_flag ),
36 .sel (sel ),
37 .seg_led (seg_led )
38 );
39
40 endmodule
顶层模块主要完成对其余模块的例化。代码第 11 行的参数 TIME_SHOW 控制数码管每隔多长时间改变显示的数值,该参数值与代码第32行的clk时钟信号频率有关。由于例化时clk为系统时钟sys_clk(50MHz)所以每隔 0.5 秒改变数码管显示的数值时此参数值为0.5 × 50000000 = 25000000,此功能由计时模块(time_count)实现。
计时模块的代码如下所示:
1 module time_count(
2 input clk , // 时钟信号
3 input rst_n , // 复位信号
4
5 output reg flag // 一个时钟周期的脉冲信号
6 );
7
8 //parameter define
9 parameter MAX_NUM = 25000_000; // 计数器最大计数值
10
11 //reg define
12 reg [24:0] cnt; // 时钟分频计数器
13
14 //*****************************************************
15 //** main code
16 //*****************************************************
17
18 //计数器对时钟计数,每计时到 0.5s,输出一个时钟周期的脉冲信号
19 always @ (posedge clk or negedge rst_n) begin
20 if (!rst_n) begin
21 flag <= 1'b0;
22 cnt <= 24'b0;
23 end
24 else if(cnt < MAX_NUM - 1'b1) begin
25 cnt <= cnt +1'b1;
26 flag <= 1'b0;
27 end
28 else begin
29 cnt <= 24'b0;
30 flag <= 1'b1;
31 end
32 end
33
34 endmodule
代码中第 9 行的参数 MAX_NUM 为计数的最大计数值,由于是对时钟计数,相当于计时,第 19 行的always 语句块表示的是当计数器 cnt 计数值小于 MAX_NUM - 1'b1 时,标志(flag)为0,否则标志为1,并且计数器 cnt 清零。通过 SignalTapII 抓到的波形图如下图所示:
数码管静态显示模块的代码如下:
1 module seg_led_static (
2 input clk , // 时钟信号
3 input rst_n , // 复位信号(低有效)
4
5 input add_flag, // 数码管变化的通知信号
6 output reg [5:0] sel , // 数码管位选
7 output reg [7:0] seg_led // 数码管段选
8 );
9
10 //reg define
11 reg [3:0] num; // 数码管显示的十六进制数
12
13 //*****************************************************
14 //** main code
15 //*****************************************************
16
17 //控制数码管位选信号(低电平有效),选中所有的数码管
18 always @ (posedge clk or negedge rst_n) begin
19 if (!rst_n)
20 sel <= 6'b111111;
21 else
22 sel <= 6'b000000;
23 end
24
25 //每次通知信号到达时,数码管显示的十六进制数值加 1
26 always @ (posedge clk or negedge rst_n) begin
27 if (!rst_n)
28 num <= 4'h0;
29 else if(add_flag) begin
30 if (num < 4'hf)
31 num <= num + 1'b1;
32 else
33 num <= 4'h0;
34 end
35 else
36 num <= num;
37 end
38
39 //根据数码管显示的数值,控制段选信号
40 always @ (posedge clk or negedge rst_n) begin
41 if (!rst_n)
42 seg_led <= 8'b0;
43 else begin
44 case (num)
45 4'h0 : seg_led <= 8'b1100_0000;
46 4'h1 : seg_led <= 8'b1111_1001;
47 4'h2 : seg_led <= 8'b1010_0100;
48 4'h3 : seg_led <= 8'b1011_0000;
49 4'h4 : seg_led <= 8'b1001_1001;
50 4'h5 : seg_led <= 8'b1001_0010;
51 4'h6 : seg_led <= 8'b1000_0010;
52 4'h7 : seg_led <= 8'b1111_1000;
53 4'h8 : seg_led <= 8'b1000_0000;
54 4'h9 : seg_led <= 8'b1001_0000;
55 4'ha : seg_led <= 8'b1000_1000;
56 4'hb : seg_led <= 8'b1000_0011;
57 4'hc : seg_led <= 8'b1100_0110;
58 4'hd : seg_led <= 8'b1010_0001;
59 4'he : seg_led <= 8'b1000_0110;
60 4'hf : seg_led <= 8'b1000_1110;
61 default : seg_led <= 8'b1100_0000;
62 endcase
63 end
64 end
65
66 endmodule
代码第 11 行的 num 信号为数码管显示的数值,由代码第 30 行的 if 语句块可知,其能显示的数值为0~f。由于开发板上的数码管为共阳数码管,所以不显示小数点时该位值为―1‖,因而 seg_led 信号的最高位为―1‖。下图为该模块运行时 SignalTapII 抓取到的波形图:
由该波形图可知,当 add_flag 信号拉高时,数值信号 num 加 1,输出对应的段选数据由 F8h 变为 80h
下载验证:
功能正常
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)