宋桓公

Verilog打造除法器驱动数码管(下)

0
阅读(2552)


 上次讲到了如何利用自制的除法器,来驱动数码管的基本方法,这次让他支持负数显示,并让其“动起来”做一个“倒计时器”。

  先把模块的层次图贴出来:

               

  “除法器模块”不变,源代码在《Verilog打造除法器驱动数码管(上)》中;

  “数码管显示模块”,主要修改的地方是,让其支持负数显示,并加入Start_Sig信号,供顶层模块重复调用, 具体修改的地方请看注释:

module Display
(
	input CLK,
	input RSTn,
	input Start_Sig,
	//--
	input [15:0]Display_Num,
	//--
	output [7:0]Row_Scan_Sig,
	output [5:0]Column_Scan_Sig
);


/***************************************/
	parameter 
	_0 = 8'b1100_0000, _1 = 8'b1111_1001, _2 = 8'b1010_0100, 
	_3 = 8'b1011_0000, _4 = 8'b1001_1001, _5 = 8'b1001_0010, 
	_6 = 8'b1000_0010, _7 = 8'b1111_1000, _8 = 8'b1000_0000,
	_9 = 8'b1001_0000, _fu = 8'b1011_1111;
	  
	parameter 
	wei_1 = 6'b111110, wei_2 = 6'b111101, wei_3 = 6'b111011,
	wei_4 = 6'b110111, wei_5 = 6'b101111, wei_6 = 6'b011111;
//-------------------------除法器例化------------------------
	reg[15:0] X1;			//被除数   
	reg[15:0] X2;  		//除数     
	wire[15:0] Quotient;	//商	
	wire[15:0] Reminder;	//余数
	reg Div_Start = 1'b0;
	wire Done;
	Divider 		Divi	
	(
		.CLK(CLK),
		.RSTn(RSTn),
		.Start_Sig(Div_Start),
		.X1(X1),  //被除数   
		.X2(X2),  //除数     
		.Quotient(Quotient),
		.Reminder(Reminder),
		.Done(Done)
	);
//-------------------------利用除法器将数字分离出来----------------------------	
	reg [3:0]i = 4'd0;
	reg [3:0]Num [0:5];			//内存的宽度4,内存的深度5;
	reg [2:0]Num_i = 3'd0;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn)begin i <= 4'd0; end
		else if(Start_Sig)
			case(i)
				0://判断数的正负,用于支持负数的显示
				begin
					Div_Start <= 1'b1;														//启动除法器
					X1 <= Display_Num[15] ? (~Display_Num + 1'b1) : Display_Num;//若是负数转化成正数
					X2 <= 16'd10;
					i <= i + 1'b1;
				end
				1://利用除法器,将数字一个个分离出来
				begin
					if(Done) 
					begin
						Div_Start <= 1'b0;
						X1 <= Quotient;
						X2 <= 16'd10;
						Num[Num_i] <= Reminder[3:0];
						i <= i + 1'b1;
					end	
					else begin Div_Start <= 1'b1; i <= i; end
				end
				2://循环
				begin
					if(Num_i < 4)begin Num_i <= Num_i + 1'b1; i <= i - 1'b1;end
					else	i <= i + 1'b1;
				end
				3://完成卡死,等待下一次重启
				begin
					i <= i;
				end
			endcase
		else// 当Start_Sig==0时,该模块重启
		begin
			i <= 4'd0;
			Num_i <= 3'd0;
		end
	
	 
/**************加码模块*************************/
	//每一个SegNum对应一个数码管,SegNum[5],对应的是数符号
	reg [7:0]SegNum[5:0];
	//------显示个位
	always @(posedge CLK)
		case(Num[0])
			0:SegNum[0] <= _0;
			1:SegNum[0] <= _1;
			2:SegNum[0] <= _2;
			3:SegNum[0] <= _3;
			4:SegNum[0] <= _4;
			5:SegNum[0] <= _5;
			6:SegNum[0] <= _6;
			7:SegNum[0] <= _7;
			8:SegNum[0] <= _8;
			9:SegNum[0] <= _9;
		endcase
	//------显示十位	
	always @(posedge CLK)
		case(Num[1])
			0:SegNum[1] <= _0;
			1:SegNum[1] <= _1;
			2:SegNum[1] <= _2;
			3:SegNum[1] <= _3;
			4:SegNum[1] <= _4;
			5:SegNum[1] <= _5;
			6:SegNum[1] <= _6;
			7:SegNum[1] <= _7;
			8:SegNum[1] <= _8;
			9:SegNum[1] <= _9;
		endcase
	//------显示百位
	always @(posedge CLK)
		case(Num[2])
			0:SegNum[2] <= _0;
			1:SegNum[2] <= _1;
			2:SegNum[2] <= _2;
			3:SegNum[2] <= _3;
			4:SegNum[2] <= _4;
			5:SegNum[2] <= _5;
			6:SegNum[2] <= _6;
			7:SegNum[2] <= _7;
			8:SegNum[2] <= _8;
			9:SegNum[2] <= _9;
		endcase
	//------显示千位	
	always @(posedge CLK)
		case(Num[3])
			0:SegNum[3] <= _0;
			1:SegNum[3] <= _1;
			2:SegNum[3] <= _2;
			3:SegNum[3] <= _3;
			4:SegNum[3] <= _4;
			5:SegNum[3] <= _5;
			6:SegNum[3] <= _6;
			7:SegNum[3] <= _7;
			8:SegNum[3] <= _8;
			9:SegNum[3] <= _9;
		endcase
	//------显示万位
	always @(posedge CLK)
		case(Num[4])
			0:SegNum[4] <= _0;
			1:SegNum[4] <= _1;
			2:SegNum[4] <= _2;
			3:SegNum[4] <= _3;
			4:SegNum[4] <= _4;
			5:SegNum[4] <= _5;
			6:SegNum[4] <= _6;
			7:SegNum[4] <= _7;
			8:SegNum[4] <= _8;
			9:SegNum[4] <= _9;
		endcase
	//-----显示符号位
	always @(posedge CLK)
		if(Display_Num[15])SegNum[5] <= _fu;
		else SegNum[5] <= 8'hff;

	
//--------------------数码管循环扫描----------------------------	
	reg [8:0] C0 = 9'd0;
	reg [5:0]Control = 6'b111110;
	always @(posedge CLK)
	if(C0 == 9'd500)//这个数字不能太小,否则出现从影~~
		begin
			C0 <= 9'd0;
			Control <= {Control[4:0],Control[5]};
		end
		else if(i == 3)//说明数字分离完毕,那么开始扫描~~ 	
		begin
			C0 <= C0 + 1'b1;
		end	
		
//------------将每一位数显示在对应的数码管上-----------------------		
	reg [7:0]Seg = 8'hff;
	always @(posedge CLK)
		case(Control)
			wei_1: Seg <= SegNum[0];
			wei_2: Seg <= SegNum[1];	
			wei_3: Seg <= SegNum[2];
			wei_4: Seg <= SegNum[3];
			wei_5: Seg <= SegNum[4];
			wei_6: Seg <= SegNum[5];
		endcase

	assign Column_Scan_Sig = Control;
	assign Row_Scan_Sig = Seg;


endmodule

  紧接着来看“顶层模块”,它主要负责数字的更新,以及调用“数码管显示模块”:具体代码如下:

module Digital_V0
(
	input CLK,
	input RSTn,
	//--
	output [7:0]Row_Scan_Sig,
	output [5:0]Column_Scan_Sig
);

reg [15:0]Display_Num;
reg Start_Sig;
wire Done;
Display	Display
	(
		.CLK(CLK),
		.RSTn(RSTn),
		.Start_Sig(Start_Sig),
		.Display_Num(Display_Num),
		.Row_Scan_Sig(Row_Scan_Sig),
		.Column_Scan_Sig(Column_Scan_Sig)
	);
	
	parameter delay = 10'd100;
	
	reg [15:0]C0;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn)begin C0 <= 16'd0; end
		else if(C0 == 16'd50_000)C0 <= 16'd0;
		else C0 <= C0 + 1'b1;
	
	reg [9:0]C1;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn)begin C1 <= 10'd0; end
		else if(C0 == 16'd50_000)C1 <= C1 + 1'b1;
		else if(C1 == delay)C1 <= 10'd0;
	
	
	reg [3:0]i;
	reg [15:0]TempData = 16'd1000;
	always @(posedge CLK or negedge RSTn)
		if(!RSTn)begin i <= 4'd0; Start_Sig <= 1'b0;end
		else 
			case(i)
				0://准备输入的数据
				begin
					Display_Num <= TempData;
					i <= i + 1'b1;
				end
				1://启动显示模块
				begin
					Start_Sig <= 1'b1;
					i <= i + 1'b1;
				end
				2://每0.1秒修改一次TempData
				begin
				    if(C1 == delay)begin TempData <= TempData - 1'b1; i <= i + 1'b1; end
				    else begin TempData <= TempData; i <= i; end
				end
				3://重启显示模块
				begin
					Start_Sig <= 1'b0;
					i <= 4'd0;
				end
					
			endcase
			

endmodule
  现在,数码管就可以跑起来了,它会从1000开始递减,0.1秒改变一次,减到0后会继续变成负1...等等~~