数码管进阶设计验证

前言

        随着数字电路和嵌入式系统的广泛应用,数码管作为一种常见的显示设备,在各种电子产品中扮演着重要角色。数码管以其结构简单、显示清晰和成本低廉的特点,广泛应用于计数器、时钟、测量仪器等领域。然而,传统的数码管设计通常仅支持基本的数字显示功能,难以满足现代应用对更复杂显示需求的要求。

        近年来,随着FPGA技术的发展和应用,数码管的驱动设计也迎来了新的机遇。FPGA(现场可编程门阵列)作为一种强大的数字逻辑设计平台,具有高度的灵活性和并行处理能力,使得数码管驱动的设计可以实现更复杂的功能,例如显示正负号、小数点等。此外,FPGA支持的逻辑设计验证工具和方法也为设计的可靠性和稳定性提供了保障。

        本项目旨在基于FPGA平台,设计一种支持正负号和小数点显示的数码管驱动系统。该系统不仅能够满足基本的数字显示需求,还能够处理更复杂的显示要求,适应现代电子设备对显示功能的多样化需求。通过对FPGA平台的深入研究和应用,我们将探讨如何在数码管显示中实现这些进阶功能,并验证设计的正确性和有效性。

正文

一、数码管进阶设计验证

        1.项目需求

本项目旨在基于FPGA平台,设计一种支持正负号和小数点显示的数码管驱动系统。

        2.技术介绍

驱动部分请参考简易数字钟

本章节重在介绍如何使数码管显示正负号于小数点:

对于在之前做的数码管驱动模块有很多不足:输入数据为BCD码,并且只能输入BCD码,输入数据如果是二进制数据,数码管显示是错误的。


module seg_driver(
 
	input clk,
	input rst_n,
	input [23:0] data_in,//接收数字时钟信号
	
	output reg [2:0] sel,//位选信号
	output reg [7:0] seg//段选信号
);

针对不足处,本章节在模块内调用一二进制码转BCD码模块,使得输入数据易产生与传递,在此基础上,增加一小数点位输入用6位独热编码表示当前小数点位置,增加一标志信号输入,表示现在数值是正数还是负数。

module shu_ma_g_MAX(

	input 				clk		,
	input					rst_n		,
	input	[19:0]		data		,//数据2进制
	input [ 5:0]		point		,//小数点位
	input					signe		,//正负号位
	input					seg_en	,//数码管使能
	
	output reg[2:0]	sel		,
	output reg[7:0]	seg 		

);

        3.顶层架构

        4.端口描述

clk时钟
rst_n复位信号
data[19:0]输入数据(2进制数据)
point[5:0]小数点位
signe正负号位
seg_en数码管使能
sel[2:0]位选信号
seg[7:0]段选信号

二、代码验证

数码管驱动模块

module shu_ma_g_MAX(

	input 				clk		,
	input					rst_n		,
	input	[19:0]		data		,//数据2进制
	input [ 5:0]		point		,//小数点位
	input					signe		,//正负号位
	input					seg_en	,//数码管使能
	
	output reg[2:0]	sel		,
	output reg[7:0]	seg 		

);

parameter cnt_1ms_max = 16'd49999;

wire[3:0] data1				;//数码管数据
wire[3:0] data2				;//数码管数据
wire[3:0] data3				;//数码管数据
wire[3:0] data4				;//数码管数据
wire[3:0] data5				;//数码管数据
wire[3:0] data6				;//数码管数据

reg[23:0]data_reg			;//寄存显示数据

reg[15:0]cnt_1ms			;//1ms计数器
reg		cnt_1ms_flag	;
reg[2:0]	cnt_sel			;//扫描周期计数器
reg[3:0] data_zhan		;//数码管显示暂存数据
reg 		point_flag		;//小数点使能信号
reg[2:0] sel_reg			;//暂存输出

always@(posedge clk,negedge rst_n)//判断符号位
begin
	if(rst_n == 0)
		data_reg <= 24'b0;
	else
		if((data6)||(point[5]))
			data_reg <= {data6,data5,data4,data3,data2,data1};
		else
			if(((data5)||(point[4]))&&(signe == 1'b1))//最高位为符号位011-111
				data_reg <= {4'd10,data5,data4,data3,data2,data1};
			else
				if(((data5)||(point[4]))&&(signe == 1'b0))
					data_reg <= {4'd11,data5,data4,data3,data2,data1};
				else
					
			if(((data4)||(point[3]))&&(signe == 1'b1))//次高位为符号位001-111
				data_reg <= {4'd11,4'd10,data4,data3,data2,data1};
			else
				if(((data4)||(point[3]))&&(signe == 1'b0))
					data_reg <= {4'd11,4'd11,data4,data3,data2,data1};
				else
				
			if(((data3)||(point[2]))&&(signe == 1'b1))//次次高位为符号位000-111
				data_reg <= {4'd11,4'd11,4'd10,data3,data2,data1};
			else
				if(((data3)||(point[2]))&&(signe == 1'b0))
					data_reg <= {4'd11,4'd11,4'd11,data3,data2,data1};
				else
				
			if(((data2)||(point[1]))&&(signe == 1'b1))//次次次高位为符号位000-011
				data_reg <= {4'd11,4'd11,4'd11,4'd10,data2,data1};
			else
				if(((data2)||(point[1]))&&(signe == 1'b0))
					data_reg <= {4'd11,4'd11,4'd11,4'd11,data2,data1};
				else
												
			if(((data1)||(point[0]))&&(signe == 1'b1))//次次次次高位为符号位000-001
				data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,data1};
			else
				data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,data1};
						
end

always@(posedge clk,negedge rst_n)	//计数器驱动模块
begin
	if(rst_n == 0)
		cnt_1ms <= 16'd0;
	else
		if(cnt_1ms == cnt_1ms_max)
			cnt_1ms <= 16'd0;
		else
			cnt_1ms <= cnt_1ms + 16'd1;
		
end

always@(posedge clk,negedge rst_n)//1ms标志信号
begin
	if(rst_n == 0)
		cnt_1ms_flag <= 1'b0;
	else
		if(cnt_1ms == cnt_1ms_max)
			cnt_1ms_flag <= 1'b1;
		else
			cnt_1ms_flag <= 1'b0;
end

always@(posedge clk,negedge rst_n)//扫描周期计数器使能
begin
	if(rst_n == 0)
		cnt_sel <= 3'd0;
	else
		if((cnt_sel == 3'd5)&&(cnt_1ms_flag <= 1'b1))
			cnt_sel <= 3'd0;
		else
			if(cnt_1ms_flag <= 1'b1)
				cnt_sel <= cnt_sel + 3'd1;
			else
				cnt_sel <= cnt_sel;
end
		
always@(posedge clk,negedge rst_n)//暂存输出
begin
	if(rst_n == 0)
		sel_reg <= 3'd0;
	else
		if((cnt_sel == 3'd0)&&(cnt_1ms_flag <= 1'b1))
			sel_reg <= 3'd0;
		else
			if(cnt_1ms_flag <= 1'b1)
				sel_reg <= sel_reg + 1'd1;
			else
				sel_reg <= sel_reg;
end

always@(posedge clk,negedge rst_n)//段码数据传递
begin
	if(rst_n == 0)
		data_zhan <= 4'd0;
	else
		if((seg_en == 1'b1)&&(cnt_1ms_flag <= 1'b1))
			case(cnt_sel)
				3'd0 : data_zhan <= data_reg[3:0];
				3'd1 : data_zhan <= data_reg[7:4];
				3'd2 : data_zhan <= data_reg[11:8];
				3'd3 : data_zhan <= data_reg[15:12];
				3'd4 : data_zhan <= data_reg[19:16];
				3'd5 : data_zhan <= data_reg[23:20];
			default : data_zhan <= 4'd0;
			endcase
		else
			data_zhan <= data_zhan;
end

always@(posedge clk,negedge rst_n)//小数点点亮
begin
	if(rst_n == 0)
		point_flag <= 1'b0;
	else
		if(cnt_1ms_flag <= 1'b1)
			point_flag <= ~point[cnt_sel];
		else
			point_flag <= point_flag;
end
							
always@(posedge clk,negedge rst_n)//段码数值判断并输出
begin
	if(rst_n == 0)		
		seg <= 8'b1111_1111;//0
	else
		case(data_zhan)
			4'd0 : seg <= {point_flag,7'd100_0000};//0
			4'd1 : seg <= {point_flag,7'd100_0000};//1
			4'd2 : seg <= {point_flag,7'd100_0000};//2
			4'd3 : seg <= {point_flag,7'd100_0000};//3
			4'd4 : seg <= {point_flag,7'd100_0000};//4
			4'd5 : seg <= {point_flag,7'd100_0000};//5
			4'd6 : seg <= {point_flag,7'd100_0000};//6
			4'd7 : seg <= {point_flag,7'd100_0000};//7
			4'd8 : seg <= {point_flag,7'd100_0000};//8
			4'd9 : seg <= {point_flag,7'd100_0000};//9
			
			4'd10: seg <= 8'b1011_1111;//-
			4'd11: seg <= 8'b1111_1111;//熄灭
/*			
			4'd10: seg <= {point_flag,7'd100_0000};//A
			4'd11: seg <= {point_flag,7'd100_0000};//b
			4'd12 : seg <= {point_flag,7'd100_0000};//C
			4'd13 : seg <= {point_flag,7'd100_0000};//d
			4'd14 : seg <= {point_flag,7'd100_0000};//E
			4'd15 : seg <= {point_flag,7'd100_0000};//F
*/
			default : seg <= 8'd1100_0000;
		endcase
end

always@(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)		
		sel <= 3'd0;//0			
	else
		sel <= sel_reg;
end

er_bcd er_bcd_inst(

	.clk	(clk	),
	.rst_n(rst_n),
	.data	(data	),//数据10进制
	
	.data1(data1),
	.data2(data2),
	.data3(data3),
	.data4(data4), 
	.data5(data5),
	.data6(data6) 		 	

);	
		



endmodule

数据解析模块

module er_bcd(

	input 				clk		,
	input					rst_n		,
	input	[19:0]		data		,//数据10进制
	
	output reg[3:0]	data1		,//bcd码
	output reg[3:0]	data2		,//bcd码
	output reg[3:0]	data3		,//bcd码
	output reg[3:0]	data4		,//bcd码 
	output reg[3:0]	data5		,//bcd码
	output reg[3:0]	data6		 //bcd码	

);

reg [4 :0]cnt_shift	;
reg [43:0]data_shift	;//暂存转换后的BCD码
reg 		 shift_flag	;

always@(posedge clk, negedge rst_n)//记时钟21次后清零,大约20ns*20=400ns进行一次数据转码并输出
begin
	if(rst_n == 0)
		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 + 5'd1;
			else
				cnt_shift <= cnt_shift;
end

always@(posedge clk, negedge rst_n)
begin
	if(rst_n == 0)
		data_shift <= 43'd0;
	else
		if(cnt_shift == 5'd0)
			data_shift <= {24'd0,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;
end

always@(posedge clk, negedge rst_n)//相对于时钟信号,0时数据计算,1时进行数据移位
begin
	if(rst_n == 0)
		shift_flag <= 1'b0;
	else
		shift_flag = ~shift_flag;
end

always@(posedge clk, negedge rst_n)
begin
	if(rst_n == 0)
		begin
			data1   <=  4'b0;
		   data2   <=  4'b0;
		   data3   <=  4'b0;
		   data4   <=  4'b0;
		   data5   <=  4'b0;
		   data6   <=  4'b0;
		end
	else	if(cnt_shift == 5'd21)
		begin
			data1   <=  data_shift[23:20];//分段传递
		   data2   <=  data_shift[27:24];//分段传递
		   data3   <=  data_shift[31:28];//分段传递
		   data4   <=  data_shift[35:32];//分段传递
		   data5   <=  data_shift[39:36];//分段传递
		   data6   <=  data_shift[43:40];//分段传递
		end
end

endmodule

仿真代码

`timescale 1ns/1ps
module shu_ma_g_MAX_tb;
	
	reg 			clk	;
	reg 			rst_n	;
	reg[19:0]	data	;//数据10进制
	reg[ 5:0]	point	;//小数点位
	reg			signe	;//正负号位
	reg			seg_en	;//数码管使能
	
	wire [7:0]	seg	;
	wire [2:0]	sel	;

shu_ma_g_MAX shu_ma_g_MAX(

	.clk		(clk		),
	.rst_n	(rst_n	),
	.data		(data		),//数据10进制
	.point	(point	),//小数点位
	.signe	(signe	),//正负号位
	.seg_en	(seg_en	),//数码管使能
   
	.sel		(sel		),
	.seg 		(seg 		)

);

initial clk =1;
always #10 clk = ~clk;

initial begin
	rst_n = 0;
	#20
	rst_n = 1;
	seg_en = 1'b1;
	#100
	data = 0;
	#490
	data = 20'd98789;
	point = 6'b001000;
	signe = 1'b1;	//-98.789
	#20000
	data = 20'd1111;
	point = 6'b000100;
	#20000
	$stop;
end

endmodule

三、仿真验证

运行仿真,直接调出中间数据进行分析

这里可以看出,在数据未完全解析时,数码管每个位的数据为高阻态,在解析完成后同时赋值给数码管对应段。

下面对其数据显示正确性进行检测:截出一段数据进行分析。

10,9,8,7,8,9对应数据-98789,输入数据应为-98.789。789三位输出相同(懒得改)前面三位分别是-,9,8.,8于8.不同在于最高位的1与0,即01000000表示8.,-为b1011_1111,由于数据的位扫描是按照低位到高位:11000000(9)11000000(8)11000000(7)01000000(8.)11000000(9)1011_1111(-),对应输出显示正确。

参考资料

数码管显示进阶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张明阳.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值