Verilog 实现数码管显视驱动【附源码】

1、实验平台

软件:PC、Quartus Prime 18.1、Modelsim 10.5b
硬件:Altera FPGA开发板(EP4CE6E22F17C8)

2、实验目的

  • 1、掌握数码管的静态显示与动态刷新
  • 2、计数器练习

2.1、实验内容

静态显示 0~F
动态显示-秒表设计

3、实验流程

3.1、实验原理

根据开发板的原理图,可得到以下资料

数码管:本质上为一组发光二极管按照一定顺序排列而成,其显示原理与LED无异。
在这里插入图片描述

在这里插入图片描述

根据硬件原理图所示,发光二极管,所有的阳极都接通3.3V的正电压,也即—高电平,所以如果我们想要
发光二极管导通的话,需要在阴极接通低电平,就可以让LED亮起来。

3.2、系统架构

根据系统要求,可以得到以下框架分布

在这里插入图片描述

3.3、功能模块划分

根据系统构建,可得到以下模块

3.3.1、数据产生模块

模块框图

在这里插入图片描述

信号定义
信号名端口类型数据位宽信号说明
Clki1输入时钟信号,50MHz
Rst_ni1输入复位信号,低电平有效
data_oO32输出待显示数据,8位数码管可显示32位16进制数据
设计文件
/*================================================*\
		  Filename ﹕data_gen.v
			Author ﹕Adolph
	  Description  ﹕产生数码管需要显示的数据. 32'h11111111~32'h33333333;
		 Called by ﹕seg_top.v
Revision History   ﹕ 2022-6-6 15:49:24
		  			  Revision 1.0
  			  Email﹕adolph1354238998@gmail.com
			Company﹕ 
\*================================================*/
module data_gen(
	input	clk	,
	input	rst_n,
	
	output	reg [31:0] data_dis
);

	parameter TIME_DELAY = 25'd2500;
	
	reg		[24:0]	cnt_delay;
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			cnt_delay <= 25'd0;
		end
		else if(cnt_delay >= TIME_DELAY - 25'd1)begin
			cnt_delay <= 25'd0;
		end
		else begin
			cnt_delay <= cnt_delay + 25'd1;
		end
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			data_dis <= 32'h1111_1111;
		end
		else if(data_dis == 32'h3333_3333)begin
			data_dis <= 32'h1111_1111;
		end
		else if(cnt_delay >= TIME_DELAY - 25'd1)begin
			data_dis <= data_dis + 32'h1;
		end
		else begin
			data_dis <= data_dis;
		end
	end

endmodule 

本模块较为简单,此处不做仿真验证,如有兴趣可自行验证

3.3.2、数码管驱动模块

模块框图

在这里插入图片描述

信号定义
信号名端口类型数据位宽信号说明
Clki1输入时钟信号,50MHz
Rst_ni1输入复位信号,低电平有效
data_ini32输入按键消抖信号,高电平有效
dig_selO8数码管位选信号,由原理图可知,低电平有效
dig_segO8数码管段选信号,由原理图可知,低电平有效
设计文件
/*================================================*\
		  Filename ﹕seg_driver.v
			Author ﹕Adolph
	  Description  ﹕对输入的数据译码,并驱动数码管显示对应数据
		 Called by ﹕seg_top.v
Revision History   ﹕ 2022-5-30 14:27:22
		  			  Revision 1.0
  			  Email﹕adolph1354238998@gmail.com
			Company﹕ 
\*================================================*/
module seg_driver(
	input			clk		,
	input			rst_n	,
	
	input	  [31:0]dis_data,//待显示的数据
	output reg[7:0]	dig_sel	,
	output reg[7:0]	dig_seg	 
);
//wire  [31:0]dis_data;


//	assign dig_seg = 8'd0;
//	assign dig_sel = 1'b0;
	localparam
		NUM_0  	= 8'hC0,
		NUM_1  	= 8'hF9,
		NUM_2  	= 8'hA4,
		NUM_3  	= 8'hB0,
		NUM_4  	= 8'h99,
		NUM_5  	= 8'h92,
		NUM_6  	= 8'h82,
		NUM_7  	= 8'hF8,
		NUM_8  	= 8'h80,
		NUM_9  	= 8'h90,
		NUM_A  	= 8'h88,
		NUM_B  	= 8'h83,
		NUM_C  	= 8'hC6,
		NUM_D  	= 8'hA1,
		NUM_E  	= 8'h86,
		NUM_F  	= 8'h8E,
		LIT_ALL	= 8'h00,
		BLC_ALL	= 8'hFF;
	parameter CNT_REF = 25'd1000;
	
	reg	[9:0]	cnt_20us; //20us计数器
	reg	[3:0] 	data_tmp; //用于取出不同位选的显示数据
	
//	assign dis_data = 32'hABCD_4413;
//描述位选信号切换
	//描述刷新计数器
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			cnt_20us <= 25'd0;
		end
		else if(cnt_20us >= CNT_REF - 25'd1)begin
			cnt_20us <= 25'd0;
		end
		else begin
			cnt_20us <= cnt_20us + 25'd1;
		end
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			dig_sel <= 8'hfe;//8'b1111_1110
		end
		else if(cnt_20us >= CNT_REF - 25'd1)begin
			dig_sel <= {dig_sel[6:0],dig_sel[7]};
		end
		else begin
			dig_sel <= dig_sel;
		end
	end
	
//段选信号描述
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			data_tmp <= 4'd0;
		end
		else begin
			case(dig_sel)
				8'b1111_1110:data_tmp <= dis_data[ 3-:4];
				8'b1111_1101:data_tmp <= dis_data[ 7-:4];
				8'b1111_1011:data_tmp <= dis_data[11-:4];
				8'b1111_0111:data_tmp <= dis_data[15-:4];
				8'b1110_1111:data_tmp <= dis_data[19-:4];
				8'b1101_1111:data_tmp <= dis_data[23-:4];
				8'b1011_1111:data_tmp <= dis_data[27-:4];
				8'b0111_1111:data_tmp <= dis_data[31-:4];
				default: data_tmp <= 4'hF;
			endcase
		end
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			dig_seg <= BLC_ALL;
		end
		else begin
			case(data_tmp)
				4'h0 : dig_seg <= NUM_0;
				4'h1 : dig_seg <= NUM_1;
				4'h2 : dig_seg <= NUM_2;
				4'h3 : dig_seg <= NUM_3;
				4'h4 : dig_seg <= NUM_4;
				4'h5 : dig_seg <= NUM_5;
				4'h6 : dig_seg <= NUM_6;
				4'h7 : dig_seg <= NUM_7;
				4'h8 : dig_seg <= NUM_8;
				4'h9 : dig_seg <= NUM_9;
				4'hA : dig_seg <= NUM_A;
				4'hB : dig_seg <= NUM_B;
				4'hC : dig_seg <= NUM_C;
				4'hD : dig_seg <= NUM_D;
				4'hE : dig_seg <= NUM_E;
				4'hF : dig_seg <= NUM_F;
				default: ;
			endcase 
		end
	end

endmodule 

仿真验证

`timescale 1ns/1ns

module tb_dig;
	reg		clk;
	reg		rst_n;
	
	wire	[7:0]	dig_sel;
	wire    [7:0]	dig_seg;
	
	reg		[39:0]	CHARAC;//1个 ASCII 码,需要8bit 二进制表示
	
	defparam seg_driver.CNT_REF = 100;
	
	localparam
		NUM_0 = 8'hC0,
		NUM_1 = 8'hF9,
		NUM_2 = 8'hA4,
		NUM_3 = 8'hB0,
		NUM_4 = 8'h99,
		NUM_5 = 8'h92,
		NUM_6 = 8'h82,
		NUM_7 = 8'hF8,
		NUM_8 = 8'h80,
		NUM_9 = 8'h90,
		NUM_A = 8'h88,
		NUM_B = 8'h83,
		NUM_C = 8'hC6,
		NUM_D = 8'hA1,
		NUM_E = 8'h86,
		NUM_F = 8'h8E;
		
	always@(*)begin
		case(dig_seg)
			NUM_0 : CHARAC = "NUM_0";
			NUM_1 : CHARAC = "NUM_1";
			NUM_2 : CHARAC = "NUM_2";
			NUM_3 : CHARAC = "NUM_3";
			NUM_4 : CHARAC = "NUM_4";
			NUM_5 : CHARAC = "NUM_5";
			NUM_6 : CHARAC = "NUM_6";
			NUM_7 : CHARAC = "NUM_7";
			NUM_8 : CHARAC = "NUM_8";
			NUM_9 : CHARAC = "NUM_9";
			NUM_A : CHARAC = "NUM_A";
			NUM_B : CHARAC = "NUM_B";
			NUM_C : CHARAC = "NUM_C";
			NUM_D : CHARAC = "NUM_D";
			NUM_E : CHARAC = "NUM_E";
			NUM_F : CHARAC = "NUM_F";
			default : CHARAC = "NUM_0";
		endcase
	end
	
	seg_driver		seg_driver(
	/*input				*/.clk		(clk	),
	/*input				*/.rst_n	(rst_n	),

	/*output reg[7:0]	*/.dig_sel	(dig_sel),
	/*output reg[7:0]	*/.dig_seg	(dig_seg) 
);

	initial clk = 1'b0;
	always #10 clk = ~clk;
	
	initial begin
		rst_n = 1'b0;
		#36 rst_n = 1'b1;
		
		wait (seg_driver.lut == 15);
		
		#(20 * seg_driver.CNT_REF * 3);
		
		$stop(2);
	end

endmodule 

在这里插入图片描述

3.3.3、顶层文件

顶层文件在此不作讲解,根据下列RTL视图,相信读者可以很轻易的完成相应代码设计

RTL视图
在这里插入图片描述

3.4、板级验证

可以看到数码管由32‘h11111111每次自增1,在数据生成模块中可以调整变化速率。

4、总结

本设计实现了基本的字符显示,动态扫描
大家可以基于自己的理解完成秒表、数字钟之类的设计,相信聪明的你一定可以,加油!
  • 5
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是用Verilog语言实现数码管显示的基本步骤: 1. 定义数码管显示的模块。 2. 定义输入输出端口。输入端口包括时钟信号、控制信号和数码信号。输出端口为数码管的七段显示信号。 3. 编写数码管的七段显示的真值表,确定每个数字在七段数码管中对应的编码。 4. 定义时序逻辑,通过时钟信号和控制信号来控制数码的显示,如何控制可以根据具体的需求来设计。 5. 编写组合逻辑,将输入的数码信号转换为七段数码管的编码。 6. 测试并验证代码。 下面是一个简单的Verilog代码实现示例: ``` module SegmentDisplay( input clk, // 时钟信号 input rst, // 复位信号 input [3:0] digit, // 数码信号 output reg [6:0] seg // 七段数码管的七个段的信号 ); reg [3:0] count; always @(posedge clk) begin if (rst) begin count <= 0; seg <= 7'b0000001; // 初始化为显示数字0 end else begin count <= count + 1; case (count) 4'd0: seg <= 7'b0111111; // 数字0 4'd1: seg <= 7'b0000110; // 数字1 4'd2: seg <= 7'b1011011; // 数字2 4'd3: seg <= 7'b1001111; // 数字3 4'd4: seg <= 7'b1100110; // 数字4 4'd5: seg <= 7'b1101101; // 数字5 4'd6: seg <= 7'b1111101; // 数字6 4'd7: seg <= 7'b0000111; // 数字7 4'd8: seg <= 7'b1111111; // 数字8 4'd9: seg <= 7'b1101111; // 数字9 default: seg <= 7'b0000001; // 显示数字0 endcase if (count == 4'd9) begin count <= 0; end end end endmodule ``` 这个示例代码实现了一个简单的数码显示器,通过时钟信号和计数器来控制数码的显示,每个数字对应七段数码管的编码在case语句中定义。这只是一个简单的示例,具体的实现方式可以根据具体需求来设计。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值