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

这篇具有很好参考价值的文章主要介绍了FPGA基本实验之数码管的动态显示。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        关于数码管的基本知识大家可以参考我上一篇文章数码管的静态显示,

动态数码管的驱动方式

        使用 1ms 的刷新时间让六个数码管轮 流显示:第 1ms 点亮第一个数码管,第 2ms 点亮第二个数码管,以此类推依次点亮六个数 码管,6ms 一个轮回,也就是说每个数码管每 6ms 点亮 1ms,这样就能让人眼感觉到数码 管一直在亮了。点亮相应数码管的时候给其显示相应的值,这样就可以使六个数码管显示 不同的值了,这就是驱动数码管动态显示的方法。

实验目的

        让六位数码管显示从十进制数 0 开始计数,每 0.1s 加 1,一直到加到十进制数 999999。到达 999999 之后回到 0 开始重新计数。

程序设计

整体框架设计

        我们可以先设计出系统的整体框架结构,以便我们明白我们的程序可以分为那几部分进行,明确系统的构成。

六位数码管动态显示,fpga开发,学习

        根据系统框架结构我们可以分析出该系统可以分为六个子模块,现在我们依次来介绍这六个子模块:

模块名称 功能描述
data_gen 数据生成模块
seg_danamic 动态数码管显示驱动模块
bcd_8421 二进制转bcd码模块
hc595_crtl 74HC595控制模块
seg_595_dynamic 数码管动态显示模块
top_seg_595 顶层模块

接下来我们根据各子模块来完成整个实验:

数据生成模块

模块框图:

六位数码管动态显示,fpga开发,学习

数据生成模块输入输出信号描述
信号 位宽 类型 功能描述
sys_clk 1bit Input 系统时钟信号,频率50Mhz
sys_rst_n 1bit Input 复位信号低电平有效
point 6bit Output 输出小数点
data 20bit Output 输出数据
sign 1bit Output 输出符号
seg_en 1bit Output 数码管使能信号

绘制数据波形图

六位数码管动态显示,fpga开发,学习

        cnt_100ms:我们实验的要求是每 0.1s 让显示的数据加 1,所以我们需要用到一个间隔 为 0.1s 的循环计数器。计数器其从 0 开始计数,计到 0.1s(4999999)时归 0 开始下一个 0.1s 计数。因为计数器的时钟为 50MHz,一个时钟为 1/50MHz(s),也就是 20ns,所 以   0.1s=4999_999*20ns。

        cnt_flag:当计数器计到 0.1s 时拉高一个标志信号,让这个标志信号去控制数据的加 1。         data:输出数据。当检测到 0.1s 到来的标志信号为高时让其加 1,当加到 999999 并检 测到 0.1s 到来的标志信号时让其归 0,开始下一轮的数据显示。

        point:输出小数点。在这里我们让其高电平有效(以本次使用的数码管为例,即当小 数点对应段选信号为低,位选信号为高时点亮有效),本次实验不需要显示小数点,让每 一位都为 0 即可。

         seg_en:数码管使能信号。因为我们一直在显示,所以一直给其拉高就行

         sign:负号显示,高电平有效。因本次实验不显示负号,给其一直为低电平即可。

module		data_gen
#(
		parameter		CNT_MAX = 23'd4999_999,
		parameter		DATA_MAX = 20'd999_999
)
(
		input 		wire		sys_clk,
		input		wire		sys_rst_n,
		
		output		reg	[19:0]	data,
		output		wire[5:0]	point,
		output		reg			seg_en,
		output		wire 		sign
);

reg 	[22:0]	cnt_100ms;
reg				cnt_flag;
//不显示小数点和负数
assign		point = 6'b000_000;
assign		sign  = 1'b0;
//cnt_100ms循环计数计数计到100ms
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt_100ms <= 23'd0;
		else if(cnt_100ms == CNT_MAX)
			cnt_100ms <= 23'd0;
		else
			cnt_100ms <= cnt_100ms + 1'b1;
//cnt_flag:每100ms产生一个标志信号
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt_flag <= 1'b0;
		else if(cnt_100ms == CNT_MAX - 1'b1)
			cnt_flag <= 1'b1;
		else
			cnt_flag <= 1'b0;
//数码管显示数据0~999——999
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			data <= 20'd0;
		else if((data == DATA_MAX) && (cnt_flag == 1'b1))
			data <= 20'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)
			seg_en <= 1'b0;
		else
			seg_en <= 1'b1;
endmodule



 二进制转BCD码模块

        根据实验目标可知我们要让数码管显示的数是十进制数,而我们数码管是通过段选信 号和位选信号去控制每个数码管进行显示的,一位数码管显示的是一个十进制数,而我们 的十进制数是以二进制进行编码的,如果我们直接使用二进制编码进行显示,那么每个数 码管显示的数就不是十进制数而是十六进制数了。所以我们需要将二进制编码的十进制数 转换为 BCD 编码的十进制数进行显示。

        BCD 码(Binary-Coded Decimal),又称二 - 十进制码,使用 4 位二进制数来表示 1 位 十进制数中的 0~9 这 10 个数码,是一种二进制的数字编码形式,用二进制编码的十进制代 码。

        BCD 码根据权值的有无可分为“有权码”和“无权码”。其中的权字表示的是权值, 有权码的四位二进制数中的每一位都有一个固定的权值,而无权码是没有权值的。常见的 有权码有 8421 码、5421 码和 2421 码,8421 码它的权值从左到右是 8421,5421 码的权值 从左到右是 5421,2421 码的权值是 2421,其中 8421 码是最为常用的 BCD 编码,本实验 中使用的也是这种编码。常用的无权码有余 3 码、余 3 循环码。

六位数码管动态显示,fpga开发,学习

        以本次实验我们使用的 8421BCD 编码 为例,比如说十进制数 5,它的 8421BCD 码为 0101,那么我们怎么通过 0101 得到我们的 数字 5 呢,这里需要用到一个算法,就是将其每位二进制数乘以它的权值然后相加。十进 制 5 的 8421BCD 码为 0101,即:1×1 + 0×2 + 1×4+ 0×8 = 5。其它有权码也是这种计算方 式,

        为什么转换为 BCD 码就能进行十 进制的显示了呢?这里我们举个例子,例如我们需要显示十进制数据 234,根据我们前面讲的动态显示原理,在第一个显示周期我们点亮数码管 1,并让其显示值 4;在第二个显示 周期我们点亮数码管 2,并让其显示值 3;在第三个显示周期我们点亮数码管 3,并让其显 示值 2;第四、五、六个周期我们不点亮其余数码管即可,以此循环就完成了十进制数 234 的显示。那个我们如何才能在点亮相应的值时给其显示相应的值呢?我们先看看 234 的十 进制数的二进制表示为:1110_1010;234 的 8421BCD 码为:0010_0011_0100;可以发现 如果我们使用二进制数赋值我们并不能准确的给到 2、3、4 值,而如果我们用 8421BCD 码 给其赋值的话,每 4 位 8421BCD 码代表一个十进制数,那么就能完美的进行显示了。

绘制本模块的实验框图

        六位数码管动态显示,fpga开发,学习

二进制转BCD码模块输入输出信号描述
信号 位宽 类型 功能描述
sys_clk 1bit Input 系统时钟频率为50MHZ
sys_rst_n 1bit Input 复位信号,低电平有效
data 20bit Input 输入以二进制表示的十进制数
unit 1bit Output 个位BCD码
ten 1bit Output 十位BCD码
hun 1bit Output 百位BCD码
tho 1bit Output 千位BCD码
t_tho 1bit Output 万位BCD码
h_hun 1bit Output 十万位BCD码

绘制波形图:

六位数码管动态显示,fpga开发,学习

        cnt_shift:移位判断计数器,前面我们说到我们输入转换的二进制码有多少位我们就需 要进行多少次判断移位操作,这里我们 data 数据的位宽为 20 位,所以这里我们声明移位 判断计数器对移位 20 次进行判断控制。

        data_shift:移位判断数据寄存器,该寄存器用于存储移位判断操作过程中的数据,这 里我们输入的二进制位宽为 20 位,待转换成的 BCD 码位宽为 24 位,所以这里我们声明该 寄存器的位宽为输入的二进制位宽和待转换完成的 BCD 码位宽之和,即 44 位。根据波形 图可知,这里我们设计当移位计数器等于 0 时寄存器的低 20 位即为待转换数据,而由于还 没开始进行转换,高 24 位的 BCD 码我们补 0 即可。

        shift_flag:移位判断操作标志信号。前面说到我们需要对数据进行移位和判断,判断 在前移位在后,所以这里我们声明一个标志信号,用于控制判断和移位的先后顺序,当 shift_flag 为低时对数据进行判断,当 shift_flag 为高时对数据进行移位。需要注意的是无论 是移位操作和判断操作都是在单个系统时钟下完成的,故我们判断 20 次移位 20 次在 40 个 系统时钟内就能完成。         unit、ten、hum、tho、t_tho、h_hun:六路 BCD 码,前面我们说到我们开发板上有六 位数码管,故可以显示的最大值是六位十进制数,随意这里我们声明了六路 BCD 码,一个 BCD 码代表十进制的一个位数,其中 unit 代表个位、ten 代表十位、hun 代表百位、tho 代 表千位、t_tho 代表万位、h_hun 代表十万位。当我们的移位判断计数器等于 21 是说明 20 位二进制转 BCD 码的移位判断操作已经完成,此时 data_shift 里寄存的数据就是转换的寄 存数据,该数据的高 24 位即转换完成后的 BCD 码。所以当 cnt_shift 等于 20 是就将寄存的 BCD 码值赋值为相对应的各个位数。

编写二进制转BCD码

module	bcd_8421
(
		input		wire			sys_clk		,
		input		wire			sys_rst_n	,
		input		wire	[19:0]	data		,
		
		output		reg		[3:0]	unit		,
		output		reg		[3:0]	ten			,
		output		reg		[3:0]	hun			,
		output		reg		[3:0]	tho			,
		output		reg		[3:0]	t_tho		,
		output		reg		[3:0]	h_hun
);

reg		[4:0]		cnt_shift	;
reg		[43:0]		data_shift	;
reg					shift_flag	;
//cnt_shift:从0~21循环计数
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt_shift <= 5'd0;
		else if((cnt_shift == 5'd21) && (shift_flag == 1'b1))
			cnt_shift <= 5'd0;
		else if(shift_flag == 1'b1)
			cnt_shift <= cnt_shift + 1'b1;
		else
			cnt_shift <= cnt_shift;
//data_shift:计数器为0时赋值,计数器为1~20时进行移位判断操作
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			data_shift <= 44'b0;
		else if(cnt_shift == 5'd0)
			data_shift <= {24'b0,data};
		else if((cnt_shift <= 20) && (shift_flag == 1'b0))
			begin
				data_shift[23:20] <= (data_shift[23:20] > 4) 	? 
 				(data_shift[23:20] + 2'd3) : (data_shift[23:20]);
 				data_shift[27:24] <= (data_shift[27:24] > 4) 	? 
 				(data_shift[27:24] + 2'd3) : (data_shift[27:24]);
				data_shift[31:28] <= (data_shift[31:28] > 4) 	? 
 				(data_shift[31:28] + 2'd3) : (data_shift[31:28]);
 				data_shift[35:32] <= (data_shift[35:32] > 4) 	? 
 				(data_shift[35:32] + 2'd3) : (data_shift[35:32]);
 				data_shift[39:36] <= (data_shift[39:36] > 4) 	? 
 				(data_shift[39:36] + 2'd3) : (data_shift[39:36]);
 				data_shift[43:40] <= (data_shift[43:40] > 4) 	? 
 				(data_shift[43:40] + 2'd3) : (data_shift[43:40]);
			end
		else if((cnt_shift <= 20) && (shift_flag == 1'b1))
				data_shift <= data_shift << 1;
		else
				data_shift <= data_shift;
//shift_flag:移位判断标志信号,用于判断移位控制的先后顺序
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			shift_flag <= 1'b0;
		else
			shift_flag <= ~shift_flag;
//当计数器等于二十时,移位判断操作完成,对各个位数的BCD码进行赋值
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			begin 
				unit	<=	4'b0;
				ten	    <=	4'b0;
                hun	    <=	4'b0;
                tho	    <=	4'b0;
                t_tho   <=	4'b0;
                h_hun   <=	4'b0;
			end
		else if(cnt_shift == 5'd21)
			begin
				unit	<=	data_shift[23:20];
				ten	    <=	data_shift[27:24];
                hun	    <=	data_shift[31:28];
                tho	    <=	data_shift[35:32];
                t_tho   <=	data_shift[39:36];
                h_hun   <=	data_shift[43:40];
			end
endmodule

 数码管动态显示驱动模块

模块框图:

六位数码管动态显示,fpga开发,学习

数码管动态显示驱动模块输入输出信号描述
信号 位宽 类型 功能描述
sys_clk 1bit Input 系统时钟,频率50MHZ
sys_rst_n 1bit Input 复位信号,低电平有效
point 6bit Input 输入小数点
data 20bit Input 输入数据
seg_en 1bit Input 数码管使能信号
sign 1bit Input 输入符号
sel 6bit Output 数码管位选信号
seg 8bit Output 数码管段选信号

 波形图绘制:

六位数码管动态显示,fpga开发,学习

根据波形图对信号进行逐一说明:

        point:输入小数点控制信号,高电平有效,这里我们假设要让第二个数码管显示小数 点,其余数码管不显示小数点,那么此时 point 的输入的值就应该是 6’b000010。

        seg_en:数码管使能信号,这里一直让其拉高即可。

        data:输入的十进制数据,假设这里我们输入的十进制数为 9876。

         sign:符号位控制信号,高电平有效。假设我们需要显示的是负数,那么这里就让符 号位控制信号为高即可。 unit、ten、hun、tho、t_tho、h_hun:这六个信号就是我们例化的 bcd_8421 模块转化的 的 8421BCD 码,也就是说这六个 BCD 码就是输入十进制数 9876 各个位的 BCD 码。所以 这里个位(unit)是 6,十位(ten)是 7,百位(hun)是 8,千位(tho)是 9,万位和十 万位都为 0。

         data_reg:数码管待显示内容寄存器,因为这里我们假设输入要显示的十进制数为 9876,并且显示负号,所以前五个数码管就会显示-9876 的数值,此时最高位数码管什么都 不显示,我们用 X 表示,所以这里六个数码管显示的内容就是:X-9876。

        cnt_1ms:前面讲到要让显示的数码管不会有闪烁感,我们需要使用 1ms 的扫描时间去 扫描各个数码管。所以这里我们需要一个 1ms 的计数器对 1ms 进行循环计数。

        flag_1ms:1ms 计数标志信号,当 1ms 计数器计到 1ms 时拉高该标志信号,我们使用 该标志信号去控制位选数码管计数器的计数。 

        cnt_sel:位选数码管计数器。我们在理论学习中说到动态扫描方式是用 1ms 的刷新时 间让六个数码管轮流显示:第 1ms 点亮第一个数码管,第 2ms 点亮第二个数码管,以此类 推依次点亮六个数码管,6ms 一个轮回,也就是说每个数码管每 6ms 点亮一次。那问题是 我们怎么去选中这个要显示的数码管并且给其要显示的值呢?这个时候我们就引入了一个 cnt_sel 信号,让其从 0~5 循环计数,1 个数代表一个数码管,可以看做是给数码管编号。 这样的话我们只要选择计数器的值就相当于选中了其中对应的数码管。特别要说明的是我 们的 cnt_sel 计数器必须与数码管的刷新状态一致,也就是 1ms 计 1 个数。

        sel_reg:数码管位选信号寄存器,为了让数码管位选信号和段选信号同步,这里我们 先将位选信号进行寄存。刷新到哪个数码管就将 sel 中对应位(6 个位宽,每一位对应一个 数码管)给高点亮即可。选中点亮的数码管后我们需要给其要显示的值,所以我们引入一 个新的信号。

        data_disp:当前点亮数码管显示的值。若我们此时点亮的是第一个数码管,那么我们 就需要给第一个数码管显示值 6,若刷新到第二个数码管,那么我们就需要给第二个数码 管显示值 7,以此类推;当刷新到第五个数码管时,此时显示的是负号,那么我们该如何 表示呢?这里我们让该信号的值为 10 来表示,也就是说当 data_disp 的值为 10 时就让数码 管显示负号,同理这里我们定义 data_disp 的值为 11 时让数码管什么也不显示,即不点亮 数码管。 

        dot_disp:当前数码管显示的小数点,我们输入的 point 信号是点亮第二个数码管的小 数点,而我们的数码管是低电平点亮,所以这里当扫描到第二个数码管时让 dot_disp 信号 为低即可。         seg:数码管段选信号,我们根据数码管编码译码表当扫描到哪个数码管显示需要显示 的值时,我们将对于的段点亮即可。         

        sel:数码管位选信号。将数码管位选信号寄存器打一拍即可,这样就能实现数码管段 选信号和位选信号的同步。

module	seg_dynamic
(
		input 		wire		sys_clk,
		input 		wire		sys_rst_n,
		input		wire [19:0]	data,
		input		wire [5:0]	point,
		input		wire 		seg_en,
		input		wire		sign,
		
		output		reg	[5:0]	sel,
		output		reg [7:0]	seg

);
(

			parameter		CNT_MAX = 16'd49_999
)
wire	[3:0]   unit	;
wire    [3:0]   ten		;
wire    [3:0]   hun		;
wire    [3:0]   tho		;
wire    [3:0]   t_tho	;
wire    [3:0]	h_hun	;

reg		[23:0]	data_reg;
reg		[15:0]	cnt_1ms	;
reg		[2:0]	cnt_sel	;
reg		[5:0]	sel_reg	;
reg		[3:0]	data_disp;
reg				dot_disp;
reg				flag_1ms;
//主程序
//data_reg:控制数码管显示数据
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			data_reg <= 24'd0;
//若显示十进制的十万位为非零数据或需要显示小数点,则六个数码管全部显示
		else if((h_hun) || (point[5]))
			data_reg <= {h_hun,t_tho,tho,hun,ten,unit};
		else if((t_tho) || (point[4]) && (sign == 1'b1))
			data_reg <= {4'd10,t_tho,tho,hun,ten,unit};
		else if(((t_tho) || (point[4])) && (sign == 1'b0))
 			data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//4'd11 我们定义为不显示
		//若显示的十进制数的千位为非零数据或需显示小数点,则值显示 4 个数码管
		else if(((tho) || (point[3])) && (sign == 1'b1))
			data_reg <= {4'd11,4'd10,tho,hun,ten,unit};
		else if(((tho) || (point[3])) && (sign == 1'b0))
			data_reg <= {4'd11,4'd11,tho,hun,ten,unit};
		//若显示的十进制数的百位为非零数据或需显示小数点,则值显示 3 个数码管
		else if(((hun) || (point[2])) && (sign == 1'b1))
			data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit};
		else if(((hun) || (point[2])) && (sign == 1'b0))
			data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit};
		//若显示的十进制数的十位为非零数据或需显示小数点,则值显示 2 个数码管
		else if(((ten) || (point[1])) && (sign == 1'b1))
			data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit};
		else if(((ten) || (point[1])) && (sign == 1'b0))
			data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit};
		//若显示的十进制数的个位且需显示负号
		else if(((unit) || (point[0])) && (sign == 1'b1))
			data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit};
		//若上面都不满足都只显示一位数码管
		else
			data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit};
//cnt_1ms:1ms循环计数
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt_1ms <= 16'd0;
		else if(cnt_1ms == CNT_MAX)
			cnt_1ms <= 16'd0;
		else
			cnt_1ms <= cnt_1ms +1'b1;
			
//flag_1ms:1ms标志信号
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			flag_1ms <= 1'b0;
		else if(cnt_1ms == CNT_MAX -1'b1)
			flag_1ms <= 1'b1;
		else
			flag_1ms <= 1'b0;
//cnt_sel:从0~5循环数,用于选择当前显示的数码管
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			cnt_sel <= 3'd0;
		else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
			cnt_sel <= 3'd0;
		else if(flag_1ms == 1'b1)
			cnt_sel <= cnt_sel + 1'b1;
		else
			cnt_sel <= cnt_sel;
//数码管位选信号寄存器
 always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			sel_reg <= 6'b000_000;
		else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))
			sel_reg <= 6'b000_001;
		else if(flag_1ms == 1'b1)
			sel_reg <= sel_reg << 1;
		else
			sel_reg <= sel_reg;
//控制数码管的位选信号,使六个数码管轮流显示
always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		data_disp <= 4'b0;
	else if((seg_en == 1'b1) && (flag_1ms == 1'b1))
		case(cnt_sel)
			3'd0: data_disp <= data_reg[3:0] ; 
			3'd1: data_disp <= data_reg[7:4] ; 
			3'd2: data_disp <= data_reg[11:8] ;
			3'd3: data_disp <= data_reg[15:12];
			3'd4: data_disp <= data_reg[19:16];
			3'd5: data_disp <= data_reg[23:20];
			default:data_disp <= 4'b0;
		endcase
	else
		data_disp <= data_disp;
//dot_disp:小数点低电平点亮,需对小数点有效信号取反
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			dot_disp <= 1'b1;
		else if(flag_1ms == 1'b1)
			dot_disp <= ~point[cnt_sel];
		else
			dot_disp <= dot_disp;
//控制数码管段选信号,显示数字
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			seg <= 8'b1111_1111;
		else 
			case(data_disp)
				4'd0 : seg <= {dot_disp,7'b100_0000}; //显示数字 0
				4'd1 : seg <= {dot_disp,7'b111_1001}; //显示数字 1
				4'd2 : seg <= {dot_disp,7'b010_0100}; //显示数字 2
				4'd3 : seg <= {dot_disp,7'b011_0000}; //显示数字 3
				4'd4 : seg <= {dot_disp,7'b001_1001}; //显示数字 4
				4'd5 : seg <= {dot_disp,7'b001_0010}; //显示数字 5
				4'd6 : seg <= {dot_disp,7'b000_0010}; //显示数字 6
				4'd7 : seg <= {dot_disp,7'b111_1000}; //显示数字 7
				4'd8 : seg <= {dot_disp,7'b000_0000}; //显示数字 8
				4'd9 : seg <= {dot_disp,7'b001_0000}; //显示数字 9
				4'd10 : seg <= 8'b1011_1111 ; //显示负号
				4'd11 : seg <= 8'b1111_1111 ; //不显示任何字符
				default:seg <= 8'b1100_0000;
			endcase
		
//sel:数码管位选信号赋值
always@(posedge sys_clk or negedge sys_rst_n)
		if(sys_rst_n == 1'b0)
			sel <= 6'b000_000;
		else
			sel <= sel_reg;
bcd_8421 bcd_8421_inst
(
			.sys_clk (sys_clk )		, //系统时钟,频率 50MHz
			.sys_rst_n (sys_rst_n)	, //复位信号,低电平有效
			.data (data )			, //输入需要转换的数据
			
			.unit (unit )			, //个位 BCD 码
			.ten (ten )				, //十位 BCD 码
			.hun (hun )				, //百位 BCD 码
			.tho (tho )				, //千位 BCD 码
			.t_tho (t_tho )			, //万位 BCD 码
			.h_hun (h_hun ) 		//十万位 BCD 码
);

endmodule

数码管动态显示模块

        我们将数码管动态显示驱动模块和 74HC595 控制模块整合到一个模块之中,后面我们 工程要用到数码管动态显示时我们直接例化这一个模块即可,较为方便。其模块框图如图所示:

六位数码管动态显示,fpga开发,学习

        该模块主要是对数码管动态显示驱动模块和 74HC595 控制模块的实例化,以及对应信 号的连接,

六位数码管动态显示,fpga开发,学习

module		seg_595_dynamic

(
		input wire sys_clk 		,
		input wire sys_rst_n 	,
		input wire [19:0] data 	,
		input wire [5:0] point 	,
		input wire seg_en 		,
		input wire sign 		,
                                
		output wire stcp 		,
		output wire shcp 		,
		output wire ds 			,
		output wire oe 			
 
 );
 
 //wire define
 wire [5:0] sel; 
 wire [7:0] seg; 

 
 seg_dynamic seg_dynamic_inst
 (
		.sys_clk (sys_clk )		, 
		.sys_rst_n (sys_rst_n)	, 
		.data (data )			, 
		.point (point )			, 
		.seg_en (seg_en )		, 
		.sign (sign )			, 
 
		.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

595控制模块

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

 顶层模块设计

模块框图

六位数码管动态显示,fpga开发,学习

顶层模块主要是对各个子功能模块的实例化,以及对应信号的连接。

六位数码管动态显示,fpga开发,学习

module		top_seg_595
(
		input	wire	sys_clk,
        input	wire	sys_rst_n,
		
		output	wire	stcp,
		output	wire	shcp,
        output	wire	ds,
        output	wire	oe
);

wire 	[19:0]	data;
wire	[5:0]	point;
wire			seg_en;
wire			sign;

data_gen	
#(
		.CNT_MAX		(23'd4999_999),
		.DATA_MAX		(20'd999_999)
)
data_gen_inst
(
		.sys_clk (sys_clk )		,
		.sys_rst_n (sys_rst_n)	,
		                        
		.data (data )			,
		.point (point )			,
		.seg_en (seg_en )		,
		.sign (sign )			



);
seg_595_dynamic seg_595_dynamic_inst
(
			.sys_clk (sys_clk )			,
			.sys_rst_n (sys_rst_n )		,
			.data (data )				,
			.point (point )				,
			.seg_en (seg_en )			,
			.sign (sign )				,
			                            
			.stcp (stcp )				,
			.shcp (shcp )				,
			.ds (ds )					,
			.oe (oe ) 					

);
endmodule

 仿真代码编写:

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

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;
		#30
		sys_rst_n = 1'b1;
		end
always		#10	sys_clk <= ~sys_clk;

defparam	top_seg_595_inst.seg_595_dynamic_inst.seg_dynamic_inst.CNT_MAX=16'd19;
defparam	top_seg_595_inst.data_gen_inst.CNT_MAX = 23'd49;

top_seg_595		top_seg_595_inst
(
			.sys_clk	(sys_clk	)	,
            .sys_rst_n	(sys_rst_n	)	,
          
            .stcp		(stcp		)	,
            .shcp		(shcp		)	,
            .ds			(ds			)	,
            .oe         (oe         )
);

endmodule

 仿真波形:

六位数码管动态显示,fpga开发,学习

本人亲测结果是正确的,需要自己仿真后慢慢分析波形

绑定FPGA管脚上板验证

六位数码管动态显示,fpga开发,学习

上板验证结果为:

六位数码管动态显示,fpga开发,学习

由于时间关系没有跑到999_999.

创作不易有用请点赞,后续我还会继续更新相关实验。 文章来源地址https://www.toymoban.com/news/detail-715671.html

到了这里,关于FPGA基本实验之数码管的动态显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA(5)数码管静态显示与动态显示

    目录 一、数码管静态显示 二、数码管动态显示 1、变量定义 2、定时(60us) 3、动态显示 代码 FPGA的数码管有4位,8段 。( 位和段都是共阳,即低电平有效 )     位选的4位(二进制):分别为第1、2、3、4位数码管。 段选的8位(二进制):分别为第h、g、f、e、d、c、b、a段

    2023年04月12日
    浏览(46)
  • 实验三 基于FPGA的数码管动态扫描电路设计 quartus/数码管/电路模块设计

    源文件的链接放在最后啦 实验目的: (1) 熟悉7段数码管显示译码电路的设计。 (2) 掌握数码管显示原理及静态、动态扫描电路的设计。 实验任务: (1) 基本任务1:利用FPGA硬件平台上的4位数码管做静态显示,用SW0-3输入BCD码,用SW4-7控制数码管位选; (2) 基本任务

    2024年02月07日
    浏览(59)
  • FPGA项目(5)--FPGA控制数码管动态显示的原理

            数码管是现在电子产品上常用的显示器件,它有驱动简单、显示清晰、价格低廉等优势。数码管的实物图:          数码管的内部结构图如下所示:          从图中可以看出,它由八个段组成,即A B C D E F G DP(小数点),只要将这八个段按规律组合点亮,就

    2024年02月11日
    浏览(56)
  • <微机原理>[汇编语言]-[实验七]数码管动态显示实验

    实验一 软件开发环境和简单程序设计 实验二 I_O输入输出实验 实验三 键盘扫描显示实验 实验四 中断实验 实验五 定时器实验 实验六 串行口实验 实验七 数码管动态显示实验 实验八 矩阵键盘应用实验 实验九 电子时钟 微机原理实验课程,会陆续根据目录更新文章 掌握LED八段

    2024年02月07日
    浏览(39)
  • FPGA 驱动数码管动态显示(Verilog&Vivado)

    应用实例: (1)使用串口发送实现ACX720开发板时钟显示 本章将实现 FPGA 驱动数码管动态显示并提取出实现的电路结构,从电路结构入手编写代码,仿真对设计进行验证。最终板级调试时使用 Virtual Input/Output(VIO,虚拟输入/输出端口工具),输入需要显示的数据,数码管则显

    2023年04月12日
    浏览(57)
  • verilog学习笔记- 15)动态数码管显示实验

    目录 简介: 实验任务: 硬件设计: 程序设计: 下载验证: 由于一般的静态驱动操作虽然方便,但占用的 I/0 口较多,例如要驱动6 位 8 段数码管,以静态驱动方式让数码管各个位显示不同的数值,如“123456”,需要占用6 × 8 = 48个I/O 口,虽然对于 FPGA 这种 I/O 口较多的芯片

    2024年02月07日
    浏览(48)
  • 【数电实验】实验 5 数码管动态扫描显示电路设计

    【2023-11-16:修改Y为4位宽,支持显示学号8和9】 实验要求 一、实验目的 1. 学习动态扫描显示数码管的使用。 2. 学习数据选择器及其信号分配方法。 3. 巩固 Verilog HDL 层次化设计电路的方法。 利用modelsim仿真 模块代码 1.modelsim仿真代码 2.程序源代码 RTL电路图 验收波形图 引脚

    2024年02月03日
    浏览(51)
  • 基于STM32的时钟设计并在六位数码管上显示附proteus仿真

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇

    2024年02月12日
    浏览(50)
  • FPGA_数码管显示

    一位数码管: 数码管等效电路(共阴极 和 共阳极) 数码管显示的值: 假设我们需要b,c亮,我们只需要给b,c接高电平,其他接低电平就可。 seg[7:0]  = 8\\\'b0000_0110 对于数码管显示的值,seg值如下图: 多位数码管-----如下图(以3位为例) 假设现在需要LED1亮,那么就让sel0为1,

    2024年01月23日
    浏览(50)
  • FPGA学习—数码管显示

    数码管动态显示采用了人眼暂存的原理,即时分复用,在数码管实现动态显示。 整个实验设计流程框架如下图所示: 开发板采用共阳极数码管,即低电平点亮。 本实验准备设计一个定时器,6为数码管显示24小时制的时间,所以编写一个计数模块。 改变变量COUNT的数值,可实

    2024年02月09日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包