动态数码管显示

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

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

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

程序模块主要要分成:

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

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

74hc595的控制

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

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这个问题我可以帮你解答。要实现51单片机数码动态显示24秒倒计时,你需要以下步骤: 1. 设置数码管的引脚为输出,将其连接到单片机的IO口上。 2. 在程序中定义一个变量来计时,初值为24秒。 3. 编写数码动态显示函数,该函数可以将数字转换成对应的数码显示,并依次显示。 4. 在主函数中循环调用数码动态显示函数,并将计时变量递减1,直到计时变量为0时停止循环。 下面是一个简单的示例代码: ```c #include <reg52.h> #define uchar unsigned char #define uint unsigned int sbit D1 = P2^0; // 数码管第1位引脚 sbit D2 = P2^1; // 数码管第2位引脚 sbit D3 = P2^2; // 数码管第3位引脚 sbit D4 = P2^3; // 数码管第4位引脚 uchar code table[] = { // 数码显示表 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 }; void delay(uint i) { // 延时函数 while(i--); } void display(uchar num) { // 数码动态显示函数 uchar i; for(i=0; i<50; i++) { // 控制亮度 D1 = 1; D2 = D3 = D4 = 0; P0 = table[num%10]; delay(100); D2 = 1; D1 = D3 = D4 = 0; P0 = table[num/10%10]; delay(100); D3 = 1; D1 = D2 = D4 = 0; P0 = table[num/100%10]; delay(100); D4 = 1; D1 = D2 = D3 = 0; P0 = table[num/1000%10]; delay(100); } } void main() { uchar time = 24; // 倒计时时间 while(time) { // 循环倒计时 display(time); time--; } while(1); // 停止程序 } ``` 希望这可以帮助到你。如果还有其他问题,欢迎继续提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值