基于FPGA的数字跑表设计
本设计中数字跑表的主要功能有:1、具有显示分、秒以及百分秒的秒表功能,2、具有暂停和复位功能
一、设计准备
输入端口:
1)复位信号CLR,当CLR=1时输出全部置0,当CLR=0时系统正常工作。
2)暂停信号PAUSE,当PAUSE=1时暂停计数,当PAUSE=0时正常计数。
3)系统时钟CLK,CLK=50MHz
输出端口: 数码管驱动----DATA[13:0](位宽14位),其中DATA[7:0](后八位)是数码管显示值,DATA[13:8](前六位)是数码管控制端口
按照自顶向下设计,应该分为以下模块:
分频1----将下载板上50MHz时钟分频为100Hz(周期0.01s)的时钟,提供给百分计数
分频2----将下载板上50MHz时钟分频为10KHz的时钟,提供给动态扫描
计数1----100进制计数器,输入100Hz的时钟,计数满100进位
计数2----60进制计数器,输入百分位,或者秒位的进位,计数满60向高位进位
数码管显示控制----驱动数码管数据,显示控制端口。
二、分频模块
分频模块的输入为50MHz的系统时钟clk50m,输出为分频后的100Hz时钟信号clk100和10KHz时钟信号clk10k,定义了两个计数寄存器cout1[18:0](19位)和cout2[15:0](16位)。系统时钟为50MHz,百分秒计数需要100Hz(周期0.01s)的时钟信号,因此对50MHz进行(50x10^6)/100=500,000次分频,定义计数器cout1对50MHZ方波上升沿进行计数,从0到499,999循环计数,每满499,999后cout1就置0重新计数,并且当cout1=499,999时clk100置1。至于再分频一个10KHz时钟的原因是动态扫描在100Hz时有明显闪烁,而在50MHz时闪烁频率过快导致根本看不清数码管上的数字。
三、计时模块
计时模块的输入为分频后的100Hz时钟信号clk100,输出为分位、秒位、百分位的个位和百分位的十位[3:0]:ML、SL、MSL、MSH和分位、秒位的十位[2:0]:MH、SH,定义了两个进位信号cn1和cn2。每个clk100的上升沿到来代表时间过了0.01s,每个cn1的上升沿到来代表时间过了1s,每个cn2的上升沿到来代表时间过了60s。
四、数码管显示模块
显示模块的输入为分频后的10KHz时钟信号clk10k,输出为带有数码管控制端口和数码管显示值的DATA[12:0],定义了一个位选寄存器dig[2:0]。因为不需要数码管上的小数点亮所以数码管显示值设为7位,而数码管控制端口只用6只所以是6位,数码管分共阴极和共阳极两种,共阴极的1表示亮,共阳极的0表示亮,我是当共阴极来设计的;四位一体数码管用动态扫描控制,每个clk10k的上升沿到来dig在0到5之间循环,dig的值等于几显示控制函数就往那支数码管里送与之对应的数据。
`timescale 1ns / 1ps module timer(clk50m,clr,pause,data); input clk50m; //输入50MHz的时钟信号 input clr; //清零开关 input pause; //暂停开关 output reg[12:0]data; reg [2:0]dig; //数码管位选 reg [3:0] ML,SL,MSL,MSH; //分位,秒位,百分位的个位和百分位的十位 reg [2:0] MH,SH; //分位,秒位的十位 reg [18:0] cout1; //定义计数寄存器 reg [15:0] cout2; //定义计数寄存器 reg clk100; //定义分频得到的100Hz时钟信号 reg clk10k; //频率10KHz的时钟信号 reg cn1; //百分位向秒位的进位 reg cn2; //秒位向分位的进位 //-------------------分频得到100Hz的时钟信号clk100-------------------- always@(posedge clk50m or posedge clr) if(clr) cout1<=0; else if(cout1 == 19'h7a11f) cout1<=0; else cout1<=cout1+1'b1; always@(posedge clk50m or posedge clr) if(clr) clk100<=0; else if(cout1 == 19'h7a11f) clk100<=1'b1; else clk100<=0; //-------------------分频得到10KHz的时钟信号clk10k------------------- always@(posedge clk50m or posedge clr) if(clr) cout2<=0; else if(cout2 == 19'hc34f) cout2<=0; else cout2<=cout2+1'b1; always@(posedge clk50m or posedge clr) if(clr) clk10k<=0; else if(cout2 == 19'hc34f) clk10k<=1'b1; else clk10k<=0; //-----------------------计时模块-------------------- //----------------百分位计时--------------- always@(posedge clk100 or posedge clr) if(clr) begin {MSH,MSL}<=8'h00;cn1<=0;end //异步复位 else if(!pause) //PAUSE为0时正常计数 begin if(MSL == 4'b1001) begin MSL<=0; if(MSH == 4'b1001) begin MSH<=0; cn1<=1; end else begin MSH<=MSH+1'b1;cn1<=0; end end else begin MSL<=MSL+1'b1; cn1<=0; end end //----------------秒位计时----------------- always@(posedge cn1 or posedge clr) if(clr) begin {SH,SL}<=8'h00;cn2<=0; end else if(SL == 4'b1001) begin SL<=0; if(SH == 5) begin SH<=0; cn2<=1; end else begin SH<=SH+1'b1;cn2<=0; end end else begin SL<=SL+1'b1;cn2<=0; end //---------------分位计时------------------ always@(posedge cn2 or posedge clr) if(clr) begin {MH,ML}<=8'h00; end else if(ML == 4'b1001) begin ML<=0; if(MH == 3'b101) begin MH<=0; end else begin MH<=MH+1'b1; end end else begin ML<=ML+1'b1; end //-------------------数码管显示模块-------------------- always@(posedge clk10k or posedge clr) if(clr) data<=13'b1111111111110; else case(dig) 3'b000:data<={6'b000001,Xrom(MSL)}; 3'b001:data<={6'b000010,Xrom(MSH)}; 3'b010:data<={6'b000100,Xrom(SL)}; 3'b011:data<={6'b001000,Xrom(SH)}; 3'b100:data<={6'b010000,Xrom(ML)}; 3'b101:data<={6'b100000,Xrom(MH)}; default:data<=13'h1fff; endcase //-------------------动态扫描------------------- always@(posedge clk10k or posedge clr) if(clr) dig<=0; else begin if(clk10k == 0) begin if(dig==5) dig<=0; else dig<=dig+1'b1; end else dig<=dig; end //--------------数码管显示控制函数--------------- function[6:0] Xrom; input [3:0] sum; case(sum) 4'd0:Xrom=7'h7e; 4'd1:Xrom=7'h30; 4'd2:Xrom=7'h6d; 4'd3:Xrom=7'h79; 4'd4:Xrom=7'h33; 4'd5:Xrom=7'h5b; 4'd6:Xrom=7'h5f; 4'd7:Xrom=7'h70; 4'd8:Xrom=7'h7f; 4'd9:Xrom=7'h7b; default:Xrom=7'h00; endcase endfunction endmodule