提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
设计要求
用FPGA实现一个数字时钟——用veirlog语言
一、如何分频(即生成想要频率的时钟信号)?
- 源时钟信号 clk 的频率为:f=50MHz
- 假设想要得到时钟信号 clk_1 频率为:f1
- 用一个寄存器变量 count 记录 clk 的 x 个时钟上升沿
- 此时,clk 进行了 x 个周期
- 令 clk_1 反转(即clk_1<=~clk_1),相当于 clk_1 进行了二分之一个周期
- 所以可以得出: x=f/(2*f1)
二、利用视觉暂留
来自百科:物体在快速运动时, 当人眼所看到的影像消失后,人眼仍能继续保留其影像0.1-0.4秒左右的图像,这种现象被称为视觉暂留现象。是人眼具有的一种性质。人眼观看物体时,成像于视网膜上,并由视神经输入人脑,感觉到物体的像。但当物体移去时,视神经对物体的印象不会立即消失,而要延续0.1 -0.4秒的时间,人眼的这种性质被称为"眼睛的视觉暂留"。
也就是说,只要使FPGA上的六个数码管轮流亮起(并且同一个数码管亮起的时间间隔小于视觉暂留时间),就可以实现“动态”。
三、数码管显示数字原理
其中,高位到低位依次是:DP_G F E _D C B A, 共阴极数码管——输入为高电平亮!
下图为,共阴极数码管的编码表
四、设计流程
- 所需端口以及变量定义
input clk; //系统时钟频率50MHz output [5:0]wei; //片选数码管 output [7:0]duan; //段选发光二极管 reg[5:0]wei; reg[7:0]duan; integer count; //计数实现1Hz的时钟clk_1hz reg clk_1hz; //计时时钟 integer count2; //计数实现扫描时钟clk_scan->利用视觉暂留实现动态 reg clk_scan; //扫描时钟 reg [2:0]select; //用于扫描时选择显示位码 reg[3:0]sec_ge; //秒的个位BCD码 0~9 reg[2:0]sec_shi; //秒的十位BCD码 0~5 reg[3:0]min_ge; //分的个位BCD码 0~9 reg[2:0]min_shi; //分的十位BCD码 0~5 //reg[1:0]hour_ge; //时的个位 0~3 reg[3:0] hour_ge; //时的十位 0~9 (也表达0~3) reg[1:0]hour_shi; //时的十位 0~2
- 产生频率为1的累计时钟信号
//生成1Hz的时钟clk_1hz always@(posedge clk) begin if(count==25000000) begin count<=0; clk_1hz<=~clk_1hz; end else count<=count+1'b1; end
- 秒、分、时 各个位数的累加
//秒、分、时 各个位数的累加 always @(posedge clk_1hz)//秒分时各位累加功能进程 begin if(sec_ge==4'b1001) //秒的个位等于9 begin sec_ge<=4'b0000; if(sec_shi==3'b101) //秒的十位等于5 begin sec_shi<=3'b000; if(min_ge==4'b1001) //分的个位等于9 begin min_ge<=4'b0000; if(min_shi==3'b101) //分的十位等于5 begin min_shi<=3'b000; if(hour_shi==2'b00) //时的十位等于0 begin if(hour_ge==4'b1001) //时的个位等于9 begin hour_ge<=0; hour_shi<=hour_shi+1'b1; end else hour_ge<=hour_ge+1'b1; end else if(hour_shi==2'b01) //判断 时的十位等于1 begin if(hour_ge==4'b1001) //判断 时的十位等于9 begin hour_ge<=0; hour_shi<=hour_shi+1'b1; end end else //时的十位不等于0,也不等于1, 即等于2 if(hour_ge==4'b0011) //判断 时的个位等于3 begin hour_ge<=0; hour_shi<=0; end else hour_ge<=hour_ge+1'b1; end else min_shi<=min_shi+1'b1; end else min_ge<=min_ge+1'b1; end else sec_shi<=sec_shi+1'b1; end else sec_ge<=sec_ge+1'b1; end
- 产生数码管扫描时钟信号
//产生数码管扫描时钟 always @(posedge clk) begin if(count2==10000) //if(count2==25000000) //测试视觉暂留 begin count2<=0; clk_scan<=~clk_scan; end else count2<=count2+1; end
- 扫描时实现片选,以便利用视觉暂留
//扫描时实现片选 视觉暂留 always @(posedge clk_scan) begin if(select==3'b110) select<=3'b000; else select<=select+1'b1; end
- 显示数字(注意分隔点DP的点亮与否)
//显示数字 always @(sec_ge or sec_shi or min_ge or min_shi or hour_ge or hour_shi or select)//敏感信号列表 begin if(select==3'b001) begin wei<=6'b111110;//秒个位数显示 不显示点 case(sec_ge) 4'b0000:begin duan<=8'b0_011_1111;end // 显示0 4'b0001:begin duan<=8'b0_000_0110;end // 显示1 4'b0010:begin duan<=8'b0_101_1011;end // 显示2 4'b0011:begin duan<=8'b0_100_1111;end // 显示3 4'b0100:begin duan<=8'b0_110_0110;end // 显示4 4'b0101:begin duan<=8'b0_110_1101;end // 显示5 4'b0110:begin duan<=8'b0_111_1101;end // 显示6 4'b0111:begin duan<=8'b0_000_0111;end // 显示7 4'b1000:begin duan<=8'b0_111_1111;end // 显示8 4'b1001:begin duan<=8'b0_110_1111;end // 显示9 default duan<=8'bx; endcase end else if(select==3'b010) begin wei<=6'b111101;//秒十位数显示 不显示点 case(sec_shi) 3'b000:duan<=8'b0_011_1111; // 显示0 3'b001:duan<=8'b0_000_0110; // 显示1 3'b010:duan<=8'b0_101_1011; // 显示2 3'b011:duan<=8'b0_100_1111; // 显示3 3'b100:duan<=8'b0_110_0110; // 显示4 3'b101:duan<=8'b0_110_1101; // 显示5 //3'b110:duan<=8'b1111_1101; // 显示6 default duan<=8'bx; endcase end else if(select==3'b011) begin wei<=6'b111011;//分钟个位数显示数字 显示点 case(min_ge) 4'b0000:begin duan<=8'b1_011_1111;end // 显示0 4'b0001:begin duan<=8'b1_000_0110;end // 显示1 4'b0010:begin duan<=8'b1_101_1011;end // 显示2 4'b0011:begin duan<=8'b1_100_1111;end // 显示3 4'b0100:begin duan<=8'b1_110_0110;end // 显示4 4'b0101:begin duan<=8'b1_110_1101;end // 显示5 4'b0110:begin duan<=8'b1_111_1101;end // 显示6 4'b0111:begin duan<=8'b1_000_0111;end // 显示7 4'b1000:begin duan<=8'b1_111_1111;end // 显示8 4'b1001:begin duan<=8'b1_110_1111;end // 显示9 default duan<=8'bx; endcase end else if(select==3'b100) begin wei<=6'b110111;//分钟十位数显示 不显示点 case(min_shi) 3'b000:duan<=8'b0_011_1111; // 显示0 3'b001:duan<=8'b0_000_0110; // 显示1 3'b010:duan<=8'b0_101_1011; // 显示2 3'b011:duan<=8'b0_100_1111; // 显示3 3'b100:duan<=8'b0_110_0110; // 显示4 3'b101:duan<=8'b0_110_1101; // 显示5 //3'b110:duan<=8'b1111_1101; // 显示6 default duan<=8'bx; endcase end else if(select==3'b101) begin wei<=6'b101111;//时钟个位数显示 显示点 case(hour_ge) 4'b0000:begin duan<=8'b1_011_1111;end // 显示0 4'b0001:begin duan<=8'b1_000_0110;end // 显示1 4'b0010:begin duan<=8'b1_101_1011;end // 显示2 4'b0011:begin duan<=8'b1_100_1111;end // 显示3 4'b0100:begin duan<=8'b1_110_0110;end // 显示4 4'b0101:begin duan<=8'b1_110_1101;end // 显示5 4'b0110:begin duan<=8'b1_111_1101;end // 显示6 4'b0111:begin duan<=8'b1_000_0111;end // 显示7 4'b1000:begin duan<=8'b1_111_1111;end // 显示8 4'b1001:begin duan<=8'b1_110_1111;end // 显示9 default duan<=8'bx; endcase end else begin wei<=6'b011111;//时钟十位数显示 不显示点 case(hour_shi) 3'b000:duan<=8'b0_011_1111; // 显示0 3'b001:duan<=8'b0_000_0110; // 显示1 3'b010:duan<=8'b0_101_1011; // 显示2 default duan<=8'bx; endcase end end
五、板上实现
补充:初始化数据——测试功能是否完善
//初始化:从 23 :59 :54 开始计时,测试功能
initial
begin
hour_shi<=2;
hour_ge<=3;
min_shi<=5;
min_ge<=9;
sec_shi<=5;
sec_ge<=4;
end
总结