动态数码管显示

要求:数码管以0.5秒为间隔依次显示0——999999

数码管动态显示利用的是人的视觉暂留效应和数码管的余晖效应

每毫秒只有一个数码管亮,显示数据,数码管依次循环亮,形成同时亮的效果。

程序模块主要要分成:

数码管的显示(具体在什么时候显示什么内容),

数码管显示的内容分解在单个数码管上该是怎样(涉及到BCD码)

74hc595的控制

动态数码管显示_第1张图片

module top(
				 input wire       sys_clk		,
				 input wire       sys_rst		,
				 
				 output wire 		ds		,
				 output wire 		oe		,
				 output wire 		shcp		,
				 output wire 		stcp

    );

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

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

    );
	
shumaguan_show
#(
		.cnt_100ms_max (23'd4999_999 )    ,        //100ms 一次 数字变换
		.	data_max   ( 20'd999_999 )       //显示数字的值
)
shumaguan_show_inst
(
		.sys_clk (sys_clk)   ,
		.sys_rst (sys_rst)   ,
	
		.data    (data   )     ,
		.point   (point  )  ,
		.sign    (sign   )  ,
		.seg_en  (seg_en)

    );


endmodule
module seg_595_dynamic(
						input wire  	 	sys_clk		,
						input wire   		sys_rst		,
						input wire  [19:0] 	data			,
						input wire 	[5:0]	point		,
						input wire 			sign		,
						input wire 			seg_en	,
						
						output wire 		ds		,
						output wire 		oe		,
						output wire 		shcp		,
						output wire 		stcp
						

    );
	
	
wire  [5:0]  sel;
wire [7:0]   seg;


hc595_ctrl	hc595_ctrl_inst(  
							.sys_clk(sys_clk)	 ,
							.sys_rst(sys_rst)    ,
							.seg    (seg)        ,
							.sel    (sel)        ,
												 
							.ds     (ds )        ,
							.shcp   (shcp)       ,
							.stcp   (stcp    )   ,     //每一个上升沿就讲移位寄存器数据送入存储寄存��
							.oe     (oe      )          //低电平时将存储寄存器中数据并行输��
							
    );



seg_dynamic
#(
		.cnt_1ms_max (16'd49)
		//parameter cnt_1ms_max = 16'd49_999
)
seg_dynamic_inst
(
						.sys_clk(sys_clk),
						.sys_rst(sys_rst),
						. data  ( data  ),
						. point ( point ),   //��
						.sign   (sign   ),     //符号
						.seg_en (seg_en ),
				
						.sel    (sel    ), 
						.seg    (seg)
							
    );


endmodule
module hc595_ctrl(  
							input wire sys_clk,
							input wire sys_rst,
							input wire [7:0] seg,
							input wire [5:0] sel,
							
							
							output  reg ds,  //串行输入
							output  reg shcp, //每一个上升沿就往移位寄存器送入一位
							output  reg stcp, //每一个上升沿就讲移位寄存器数据送入存储寄存器
							output  wire oe    //低电平时将存储寄存器中数据并行输出
							
    );
wire [13:0] data;    //利用组合形式给它赋值,那就得是wire型啊

assign 	data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel[5:0]};    //将段选位选数据排列在一起方便串行输入到芯片

	 
reg [1:0] cnt;  //分频计数   74HC595对时钟有要求
reg [3:0] cnt_bit;

	 
always @ (posedge sys_clk or negedge sys_rst)
	if (!sys_rst)
		cnt <= 2'd0;
	else if (cnt == 2'd3)
		cnt <= 2'd0;
	else 
		cnt <= cnt +2'd1;
		
always @ (posedge sys_clk or negedge sys_rst)  //记录串行输入到多少位了
	if (!sys_rst)
		cnt_bit <= 4'd0;
	else if ((cnt_bit == 4'd13)&&(cnt == 2'd3))
		cnt_bit <= 4'd0;
	else if (cnt == 2'd3)
		cnt_bit <= cnt_bit + 1'd1;
	else
		cnt_bit <=cnt_bit;
		
always @ (posedge sys_clk or negedge sys_rst)  //
	if (!sys_rst)
		ds <= 1'b0;
	else if (cnt == 4'd0)
		ds <= data[cnt_bit];
	else 
		ds <= ds;

always @ (posedge sys_clk or negedge sys_rst)  //
	if (!sys_rst)
		shcp <=1'b0;
	else if (cnt == 2'd2)
		shcp <= 1'b1;
	else if (cnt == 2'd0)
		shcp <= 1'b0;
	else 
		shcp <= shcp;

always @ (posedge sys_clk or negedge sys_rst)  //
	if (!sys_rst)
		stcp <= 1'b0;
	else if ((cnt_bit == 4'd0) && (cnt == 2'd0))
		stcp <= 1'b1;
	else if ((cnt_bit == 4'd0) && (cnt == 2'd2))
		stcp <= 1'b0;
	else 
		stcp <= stcp;
		
assign oe = 1'b0;


endmodule
module seg_dynamic
#(
		parameter cnt_1ms_max = 16'd5
		//parameter cnt_1ms_max = 16'd49_999
)
(
						input    wire           sys_clk,
						input    wire	        sys_rst,
						input    wire    [19:0]  data,
						input    wire    [5:0] 	 point,   //点
						input    wire  		sign,     //符号
						input    wire   		seg_en,
				 
						output    reg    [5:0]  sel, 
						output    reg    [7:0]  seg
						  
    );

wire    [3:0]   shi_wan;   //每个位置的具体数据
wire    [3:0]   wan;       //每个位置的具体数据
wire    [3:0]   qian;      //每个位置的具体数据
wire    [3:0]   bai;       //每个位置的具体数据
wire    [3:0]   shi;       //每个位置的具体数据
wire    [3:0]   ge;        //每个位置的具体数据

reg    [23:0]   data_reg;   //数据缓存(BCD码) 
reg    [15:0]   cnt_1ms;     //1ms
reg            flag_1ms;     //1ms标志信号
reg    [2:0]   cnt_sel;      //位选(固定时间节点,哪个位置数码管亮)
reg     [5:0]  sel_reg;      //位选存储
reg    [3:0]   data_disp;    //显示的具体数值表示
reg            dot_disp;     //小数点显示与否

bcd_8421 bcd_8421_inst (
			.sys_clk  (sys_clk   )  ,
			. sys_rst ( sys_rst  )  ,
			. data    ( data     )  ,
			
			. ge      ( ge       )  ,
			. shi     ( shi      )  ,
			. bai     ( bai      )  ,
			. qian    ( qian     )  ,
			. wan     ( wan      )  ,
			. shi_wan ( shi_wan)

    );
	
always @(posedge sys_clk or negedge sys_rst)   //数据寄存,你要考虑到底用了多少个数码管
	if (!sys_rst)
		data_reg <= 24'b0;
	else if ((shi_wan) || (point[5]))   //最前端那个数码管有数值或者有点
		data_reg <= {shi_wan,wan ,qian ,bai,shi,ge};
	else if (((wan) || (point[4]))  && (sign == 1'b1)) // 看第五位有值或有点没,有的话再说第六位(上一位)有符号没
		data_reg <= {4'd10,wan ,qian,bai ,shi ,ge };   //4'd10 表示负号
	else if (((wan) || (point[4]))  && (sign == 1'b0))
		data_reg <= {4'd11,wan ,qian,bai ,shi ,ge };   //4'd11 表示没符号
	else if (((qian) || (point[3]))  && (sign == 1'b1)	)
		data_reg <= {4'd11,4'd10 ,qian,bai ,shi ,ge }; 
	else if (((qian) || (point[3]))  && (sign == 1'b0)	)
    	data_reg <= {4'd11,4'd11,qian,bai ,shi ,ge }; 
	else if (((bai) || (point[2]))  && (sign == 1'b1))
    	data_reg <= {4'd11,4'd11,4'd10,bai ,shi ,ge }; 
    else if (((bai) || (point[2]))  && (sign == 1'b0))
    	data_reg <= {4'd11,4'd11,4'd11,bai ,shi ,ge }; 
	else if (((shi) || (point[1]))  && (sign == 1'b1))
		data_reg <= {4'd11,4'd11,4'd11,4'd10 ,shi ,ge }; 
	else if (((shi) || (point[1]))  && (sign == 1'b0))	
    	data_reg <= {4'd11,4'd11,4'd11,4'd11,shi ,ge }; 
	else if (((ge) || (point[0]))  && (sign == 1'b1))
		data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10 ,ge }; 
	else if (((ge) || (point[0]))  && (sign == 1'b0))	
    	data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,ge }; 
	else 
		data_reg <= 24'b0;
	
always @(posedge sys_clk or negedge sys_rst)   //1ms   49_999
	if (!sys_rst)	
			cnt_1ms <= 16'd0;
	else if (cnt_1ms == cnt_1ms_max)
			cnt_1ms <= 16'd0;
	else 
			cnt_1ms <= cnt_1ms +16'd1;
			
			
always @(posedge sys_clk or  negedge sys_rst)   //1ms标志信号   49_999
	if (!sys_rst)	
		flag_1ms  <= 1'b0;
	else if  (cnt_1ms == cnt_1ms_max-1'b1)
		flag_1ms <= 1'b1 ;
	else 
		flag_1ms <= flag_1ms;
		
always @(posedge sys_clk or  negedge sys_rst)  	//位选,1ms转换一个数码管	
	if (!sys_rst)		
		cnt_sel <= 3'd0;
	else if ((flag_1ms == 1'b1) && (cnt_sel == 3'd5))
		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)  	  //借助flag_1ms对时间分成一份一份,利用cnt_sel知道该谁亮了	,估计不要cnt_sel,用自己也可以判断
	if (!sys_rst)
		sel_reg <= 6'b000_000;
	else if ((flag_1ms == 1'b1) && (cnt_sel == 3'd0))
		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) 		
	if (!sys_rst)	
		data_disp <= 4'b0;
	else if ((seg_en == 1'b1) && (flag_1ms))
		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;
		
always @(posedge sys_clk or  negedge sys_rst) 	 //低电平有效	
	if (!sys_rst)	
		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) 	 //对数码管位选赋值
	if (!sys_rst)	
		sel <= 6'b00_0000;
	else 
		sel <= sel_reg;     // 打拍操作,时序延迟一个时隙,让段选位选在同一个时间点变换
		
		
		
always @(posedge sys_clk or  negedge sys_rst) 	   //数码管共阳
	if (!sys_rst)
		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   //seg要接收到的也需要是表示数字的二进制形式
	        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;          //其他情况显示0
		endcase
	
	
		
		
endmodule
module bcd_8421(
						input  wire sys_clk,
						input  wire  sys_rst,
						input  wire [19:0] data,
						
						output reg [3:0] ge,
						output reg [3:0] shi,
						output reg [3:0] bai,
						output reg [3:0] qian,
						output reg [3:0] wan,
						output reg [3:0] shi_wan

    );
	 
reg [4:0]  cnt_shift;  //移位判断计数器
reg [43:0] data_shift;  //移位判断数据寄存器
reg shift_flag;          //移位判断标志信号

always @(posedge sys_clk or negedge sys_rst) //0到21的循环计数(移位加三的方式实现的话,首位各加一个,999_999十进制换算到二进制有20位)
	if(!sys_rst )
		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;
	
always @(posedge sys_clk or negedge sys_rst) //移位判断标志信号,控制移位判断的先后顺序
	if(!sys_rst )
		shift_flag <= 1'b0;
	else 
		shift_flag <= ~shift_flag;
	
always @(posedge sys_clk or negedge sys_rst) //data_shift有44位,其中高24位是999_999形成的BCD码需要24位,低20位是999_999的二进制形式是20位
	if(!sys_rst )
		data_shift <=44'b0;
	else if (cnt_shift == 5'd0)
		data_shift <= {24'b0, data };	
		else if ((cnt_shift <= 5'd20)&&(shift_flag==1'b0))   //cnt_shift=0时,还是上次的,因为六位数最大变成二进制有20位,所以考虑有20次判断和移位操作,21次稳定,将它输出,
		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 <= 5'd20)&&(shift_flag==1'b1))
		data_shift <= data_shift << 1;
	else 		
		data_shift <= data_shift;
always @(posedge sys_clk or negedge sys_rst) //当计数器等于20时,对各位置的赋值(BCD)
	if(!sys_rst )
		begin 
			ge		  <= 4'b0;
			shi       <= 4'b0;
			bai       <= 4'b0;
			qian      <= 4'b0;
			wan       <= 4'b0;
			shi_wan   <= 4'b0;
		end
	else if (cnt_shift == 5'd21)
		begin
			ge		  <= data_shift[23:20];
		    shi       <= data_shift[27:24];
	        bai       <= data_shift[31:28];
		    qian      <= data_shift[35:32];
	        wan       <= data_shift[39:36];
	        shi_wan   <= data_shift[43:40];
		end 
	else
		begin
			    ge		  <=  ge		     ;
		       shi       <=  shi        ;
			    bai       <=  bai        ;
             qian      <=  qian       ;
             wan       <=  wan        ;
             shi_wan   <=  shi_wan    ;
		end 	
			
endmodule
module shumaguan_show
#(
		parameter cnt_100ms_max=23'd4999_999,        //100ms 一次 数字变换
						data_max = 20'd999_999       //显示数字的值
)
(
									input  wire sys_clk,
									input  wire  sys_rst,
									
									output reg [19:0] data,
									output wire [5:0] point,
									output wire sign,
									output reg  seg_en

    );
	 
reg [22:0] cnt_100ms;
reg cnt_flag;

always @(posedge sys_clk or negedge sys_rst)   //100ms时钟计数
	if (!sys_rst)
		cnt_100ms <= 23'd0;
	else if (cnt_100ms == cnt_100ms_max)
		cnt_100ms <= 23'd0;
	else 
		cnt_100ms <= cnt_100ms + 23'd1;

always @(posedge sys_clk or negedge sys_rst)    //100ms时钟的标志位
	if (!sys_rst)
		cnt_flag <= 1'd0;
	else if (cnt_100ms == cnt_100ms_max-1'd1)
		cnt_flag <= 1'd1;
	else 
		cnt_flag <= 1'd0;
	
always @(posedge sys_clk or negedge sys_rst)    //显示数据
	if (!sys_rst)
		data <= 20'd0;
	else if ( (data == data_max)&&(cnt_flag == 1'd1))
		data <= 20'd0;
	else if (cnt_flag == 1'd1)
		data <= data + 20'd1;
	else 
		data <= data;
assign point = 6'b000000;    //六个小数点(用不上)
assign sign  = 1'b0;      //正负号(用不上)



always @(posedge sys_clk or negedge sys_rst)    //段选使能信号(高电平表示有效)
	if (!sys_rst)
		seg_en <= 1'b0;
	else
		seg_en <= 1'b1;

endmodule

仿真文件

module vtf_top;

	// Inputs
	reg sys_clk;
	reg sys_rst;

	// Outputs
	wire ds;
	wire oe;
	wire shcp;
	wire stcp;

	// Instantiate the Unit Under Test (UUT)
	top uut (
		.sys_clk(sys_clk), 
		.sys_rst(sys_rst), 
		.ds(ds), 
		.oe(oe), 
		.shcp(shcp), 
		.stcp(stcp)
	);

	initial begin
		// Initialize Inputs
		sys_clk = 0;
		sys_rst = 0;

		// Wait 100 ns for global reset to finish
		#100;
		sys_rst <= 1'b1;
		
		
        
		// Add stimulus here

	end
	
	always # 10 sys_clk = ~sys_clk ;
      
endmodule

 

你可能感兴趣的:(fpga开发)