Augus

15.菜鸟初入FPGA之数码管

0
阅读(2730)

        在电子系统中,通常都需要有输出设备来输出或显示一定的信息,以指 示当前系统运行的状态....FPGA 则因为其独特的硬件结构,如果用 RTL 级电路来驱 动彩色液晶屏来显示一定的数据,势必是非常不划算的选择,而且驱动也极 为复杂。数码管作为一种能够直观显示一定数据信息的输出设备,具有驱动 简单、 显示直观的特点,尤其适合作为 FPGA 系统的输出设备。

        数码管所谓的动态扫描,就是利用人眼的视觉暂留特性,在人眼能分辨 的变化速度以外,快速分时的点亮各个数码管及其对应的段。因为分别点亮 所有数码管一次所用时间小于人眼的视觉暂留,因此,在人们眼里看来,这 些数码管都是同时持续点亮的,并不会有闪烁的感觉。

         因为数码管属于低速设备,其正常的扫描频率为 500~10KHz,扫描频率 太快,会导致系统功耗增加,显示效果变暗。扫描频率太慢,会有明显的闪 烁感。本实验通过调试观察,选择以 1KHz 作为扫描频率,实际显示效果非常 好。 因此本实验首先就需要产生一个 1KHz 的扫描时钟,该时钟由系统时钟分 频得到。

设计结构:

2016-04-12_172959.jpg

2016-04-12_173240.jpg

代码如下:

//TOP

module HEX8(
					clk,
					rst_n,
					En,
					disp_data,
					dig_sel,
					dig_seg
				);
				
	input clk,rst_n,En;
	output [7:0]dig_sel;//weixuan
	output [6:0]dig_seg;//duanxuan
	
	input[31:0]disp_data;
	
	divider divider(
						.clk(clk),
						.rst_n(rst_n),
						.En(En),
						.div_clk(div_clk)
					);
	
//	key_detect key_detect(
//									clk,
//									rst_n,
//									key_in
//								);
//	
//	key_ctrl key_ctrl(
//							clk,
//							rst_n,
//							key_flag,
//							key_value,
//							data
//							);
	
	dig_driver dig_driver(
						.div_clk(div_clk),
						.rst_n(rst_n),
						.disp_data(disp_data),
						.dig_sel(dig_sel),
						.dig_seg(dig_seg),
						.En(En)
						);

endmodule 

module divider(
						clk,
						rst_n,
						En,
						div_clk
					);
					
	input clk,rst_n;
	input En;
	
	output reg div_clk;
	
	reg [14:0]div_cnt;//25000-1  
	//	分频计数器计数模块
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		div_cnt <= 15'd0;
	else if(!En)
		div_cnt <= 15'd0;
	else if(div_cnt == 24999)
		div_cnt <= 15'd0;
	else
		div_cnt <= div_cnt + 1'b1;

	//1K扫描时钟生成模块		
	always@(posedge clk or negedge rst_n)
	if(!rst_n)
		div_clk <= 1'b0;
	else if(div_cnt == 24999)
		div_clk <= ~div_clk;
	else
		div_clk <= div_clk;
		
endmodule

	
module dig_driver(
						div_clk,
						rst_n,
						disp_data,
						dig_sel,
						data_tmp,
						dig_seg,
						En
						);
	input div_clk,rst_n,En;
	input [31:0]disp_data;
	
	output [7:0]dig_sel;
	output reg[3:0]data_tmp;//cha zhao biao huan cun
	output reg[6:0]dig_seg;
	
	reg [7:0]sel;
	//8位循环移位寄存器
	always@(posedge div_clk or negedge rst_n)
	if(!rst_n)
		sel <= 8'b0000_0001;
	else if(sel == 8'b1000_0000)
		sel <= 8'b0000_0001;
	else
		sel <=  sel << 1;
	
	always@(*)
		case(dig_sel)
			8'b0000_0001:data_tmp = disp_data[3:0];
			8'b0000_0010:data_tmp = disp_data[7:4];
			8'b0000_0100:data_tmp = disp_data[11:8];
			8'b0000_1000:data_tmp = disp_data[15:12];
			8'b0001_0000:data_tmp = disp_data[19:16];
			8'b0010_0000:data_tmp = disp_data[23:20];
			8'b0100_0000:data_tmp = disp_data[27:24];
			8'b1000_0000:data_tmp = disp_data[31:28];
			default:data_tmp = 4'b0000;
		endcase
		
	always@(*)
		case(data_tmp)
			4'h0: dig_seg = 7'b1000000;
			4'h1: dig_seg = 7'b1111001;
			4'h2: dig_seg = 7'b0100100;
			4'h3: dig_seg = 7'b0110000;
			4'h4: dig_seg = 7'b0011001;
			4'h5: dig_seg = 7'b0010010;
			4'h6: dig_seg = 7'b0000010;
			4'h7: dig_seg = 7'b1111000;
			4'h8: dig_seg = 7'b0000000;
			4'h9: dig_seg = 7'b0010000;
			4'ha: dig_seg = 7'b0001000;
			4'hb: dig_seg = 7'b0000011;
			4'hc: dig_seg = 7'b1000110;
			4'hd: dig_seg = 7'b0100001;
			4'he: dig_seg = 7'b0000110;
			4'hf: dig_seg = 7'b0001110;
		endcase


	assign dig_sel = (En)?sel : 8'b0000_0000;

endmodule
	
`timescale 1ns/1ns

`define clk_period 20

module HEX8_tb;

	reg clk;	
	reg rst_n;
	reg En;	
	
	reg [31:0]disp_data;
	
	wire [7:0] sel;//数码管位选
	wire [6:0] seg;//数码管段选
	
	HEX8 HEX8(
		.clk(clk),
		.rst_n(rst_n),
		.En(En),
		.disp_data(disp_data),
		.dig_sel(sel),
		.dig_seg(seg)
	);
	
	initial clk = 1;
	always#(`clk_period/2) clk = ~clk;
	initial begin
		rst_n = 0;
		En = 1;
		disp_data = 32'h12345678;
		#(`clk_period*20);
		rst_n = 1;
		#(`clk_period*20);
		#20000000;
		$stop;
	end

endmodule	

仿真:

2016-04-12_211803.jpg

   

备注: 本程序整理来源为小梅哥数码管教程

后续设计通过按键控制数码管,(按键包括按键消抖前文都有描述),按键控制数码管不在此上传,有兴趣者可以联系博主一起讨论学习