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

目录

  • 1、实验平台
  • 2、实验目的
    • 2.1、实验内容
  • 3、实验流程
    • 3.1、实验原理
    • 3.2、系统架构
    • 3.3、功能模块划分
      • 3.3.1、数据产生模块
          • 模块框图
          • 信号定义
          • 设计文件
      • 3.3.2、数码管驱动模块
          • 模块框图
          • 信号定义
          • 设计文件
      • 3.3.3、顶层文件
    • 3.4、板级验证
  • 4、总结

1、实验平台

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

2、实验目的

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

2.1、实验内容

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

3、实验流程

3.1、实验原理

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

数码管:本质上为一组发光二极管按照一定顺序排列而成,其显示原理与LED无异。
Verilog 实现数码管显视驱动【附源码】_第1张图片

Verilog 实现数码管显视驱动【附源码】_第2张图片

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

3.2、系统架构

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

Verilog 实现数码管显视驱动【附源码】_第3张图片

3.3、功能模块划分

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

3.3.1、数据产生模块

模块框图

Verilog 实现数码管显视驱动【附源码】_第4张图片

信号定义
信号名 端口类型 数据位宽 信号说明
Clk i 1 输入时钟信号,50MHz
Rst_n i 1 输入复位信号,低电平有效
data_o O 32 输出待显示数据,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﹕[email protected]
			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、数码管驱动模块

模块框图

Verilog 实现数码管显视驱动【附源码】_第5张图片

信号定义
信号名 端口类型 数据位宽 信号说明
Clk i 1 输入时钟信号,50MHz
Rst_n i 1 输入复位信号,低电平有效
data_in i 32 输入按键消抖信号,高电平有效
dig_sel O 8 数码管位选信号,由原理图可知,低电平有效
dig_seg O 8 数码管段选信号,由原理图可知,低电平有效
设计文件
/*================================================*\
		  Filename ﹕seg_driver.v
			Author ﹕Adolph
	  Description  ﹕对输入的数据译码,并驱动数码管显示对应数据
		 Called by ﹕seg_top.v
Revision History   ﹕ 2022-5-30 14:27:22
		  			  Revision 1.0
  			  Email﹕[email protected]
			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 

Verilog 实现数码管显视驱动【附源码】_第6张图片

3.3.3、顶层文件

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

RTL视图
Verilog 实现数码管显视驱动【附源码】_第7张图片

3.4、板级验证

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

4、总结

本设计实现了基本的字符显示,动态扫描
大家可以基于自己的理解完成秒表、数字钟之类的设计,相信聪明的你一定可以,加油!

你可能感兴趣的:(FPGA学习,fpga开发,Verilog,数码管)