FPGA基本实验之数码管的静态显示

此实验基于FPGA征途pro开发板实现,

数码管的基本知识

数码管简介

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

八段数码管知识

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

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

下图是提供的数码管编码译码表:

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

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

        为节省资源我们采用如图所示的数码管的连接方式:

 由上图可以看到,我们将六个数码管的段选信号连接在一起,而位选(sel)独立控 制,这样六个数码管接在一起就少了 8×5 个 I/O 口。这里对位选信号特别说明一下:由上 图可以看到每一个数码管都有一个位选信号,而这个位选信号就控制着数码管的亮灭。这 样我们就可以通过位选信号去控制数码管亮,而在同一时刻,位选选通的数码管上显示的 字形是一样的,因为我们将 6 个数码管相对应的段选连在了一起,数码管的显示自然就相 同了,数码管的这种显示方式即为静态显示。

为了进一步减少板载I/O口资源我们采用74HC595芯片,

74HC595芯片介绍

        74HC595 是一个 8 位串行输入、并行输出的位移缓存器。其内部具有 8 位移位寄存器 和一个存储器,具有三态输出功能。

通过查找数据手册我们得到74HC595芯片的功能表:

总结一下74HC595芯片的用法

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

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

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

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

 接下来正式开始实验的设计验证和实现:

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

程序设计

根据实验要求绘制系统框架:

根据绘制的系统框架可以将这个程序初步设计为三部分:

第一部分:实现seg_static功能,

第二部分:实现芯片hc595_ctrl控制功能;

第三部分:实现顶层设计模块的综合。

第一部分:seg_static数码管显示模块

 

首先介绍一下程序中的几个变量所代表的含义:

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

cnt_flag:当计数器计到 0.5s 时,我们拉高一个标志信号,让这个标志信号去控制数 码管字符的跳转。 num:每个数码管显示的字符,初始显示为 0,六个就是 000000。当检测到跳转的标 志信号为高时,让各个数码管显示的字符加 1。当加到 4’hF 时让其归 0 重新相加以此循 环。

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

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

module 	seg_static
#(
		parameter  CNT_MAX = 25'd24_999_999
)
(
		input 	wire 	sys_clk,
		input 	wire  	sys_rst_n,
		
		output 	reg		[5:0]	sel	,
		output	reg		[7:0]	seg
);
reg 	[24:0]		cnt;
reg 	[3:0]		data;
reg 				cnt_flag;

always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt  <= 25'd0;
		else if(cnt == CNT_MAX)
			cnt <= 25'd0;
		else
			cnt <= cnt + 1'b1;
			
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt_flag <= 1'b0;
		else if(cnt == CNT_MAX-1)
			cnt_flag <= 1'b1;
		else
			cnt_flag <= 1'b0;

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 <= data + 1'b1;
		else
			data <= data;


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 <= 8'hff;
		else 	case(data)
		4'd0 : seg  <=	8'hc0;
		4'd1 : seg  <=	8'hf9;
		4'd2 : seg  <=	8'ha4;
		4'd3 : seg  <=	8'hb0;
		4'd4 : seg  <=	8'h99;
		4'd5 : seg  <=	8'h92;
		4'd6 : seg  <=	8'h82;
		4'd7 : seg  <=	8'hf8;
		4'd8 : seg  <=	8'h80;
		4'd9 : seg  <=	8'h90;
		4'd10 : seg  <=	8'h88;
		4'd11 : seg  <=	8'h83;
		4'd12 : seg  <=	8'hc6;
		4'd13 : seg  <=	8'ha1;
		4'd14 : seg  <=	8'h86;
		4'd15 : seg	 <=	8'h8e;
		default seg <= 8'hff;
	endcase
	

endmodule

 第二部分74HC595 控制模块

程序中的一些变量所表示的含义: 

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

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

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

ds:串行数据输出(对我们 FPGA 芯片来说其是输出,对 74HC595 来说其是输入, stcp 和 shcp 信号也是如此)。第二片的 Q5 引脚连到 了数码管的 DIG6,也就是最右侧的数码管,而我们最右侧数码管对应的是我们位选信号的 最低位,即 sel[0]。所以我们第一位应传输的数据为 sel[0],当一次数据传完之后再次回到状态 0 开始新一轮 的数码管信号传输。

shcp:移位寄存器时钟,上升沿时将数据写入移位寄存器中。我们在 ds 数据的中间状 态拉高产生上升沿,这样可以使 shcp 采得的 ds 数据更加稳定。

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

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

module	hc595_crtl
(
		input 	wire  		sys_clk		,
		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
);
reg 	[1:0]	cnt_4;
reg		[3:0]	cnt_bit;	
wire	[13:0]	data;

assign 		data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel};

assign 		oe = 1'b0;

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;
			
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;

always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			stcp <= 1'b0;
		else if(cnt_4 == 2'd3 && cnt_bit == 4'd13)
			stcp <= 1'b1;
		else
			stcp <= 1'b0;

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;

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

第三部分顶层模块综合:

module   seg_595_static
(
		input	wire 	sys_clk,
		input	wire	sys_rst_n,
		
		output 	wire	ds,
		output	wire 	shcp,
		output	wire 	stcp,
		output	wire    oe
);
wire  [5:0]	sel;
wire  [7:0] seg;



seg_static   seg_static_inst
(
			.sys_clk	(sys_clk),
			.sys_rst_n	(sys_rst_n),
		          
			.sel        (sel ),
			.seg        (seg )
);
hc595_crtl  hc595_crtl_inst
(
			.sys_clk		(sys_clk),
            .sys_rst_n		(sys_rst_n),
            .sel			(sel),
            .seg			(seg),
                         
            .stcp			(stcp),
            .shcp			(shcp),
            .ds				(ds),
            .oe             (oe )

);

endmodule 

编写仿真代码进行仿真验证

`timescale  1ns/1ns
module	tb_seg_595_static();

reg 	 sys_clk;
reg 	 sys_rst_n;

wire 	stcp;
wire	shcp;
wire	ds;
wire	oe;

initial		begin
	sys_clk = 1'b1;
	sys_rst_n <= 1'b0;
	#20
	sys_rst_n <= 1'b1;
end

always	 #10 sys_clk <= ~sys_clk;

defparam  	seg_595_static_inst.seg_static_inst.CNT_MAX =100;

seg_595_static		seg_595_static_inst
(
			.sys_clk	(sys_clk),
			.sys_rst_n	(sys_rst_n),
		          
			.stcp			(stcp),
			.shcp			(shcp),
			.ds				(ds),
			.oe             (oe )
);		
endmodule

     综合方正结果:

与我们所想的基本一致

上板验证:

给开发板上电,绑定FPGA管脚,下载成功后,开发板数码管六位会依次从111111到FFFFFF循环。

总结:

经过对这个实验的流程,系统层次化设计有一个深刻的感悟 !

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值