Verilog HDL 设计一个电子钟
基于 Verilog HDL 设计电子钟,能够进行正常计时,时间调整,时间复位的模式选择。
文章目录
- Verilog HDL 设计一个电子钟
- 设计思路
- 复位设计
- 计时设计
- 调整设计
- 关键代码
- 源代码
- 设置引脚
设计思路
电子时钟分为主模块(time)和数码管模块(smg):
主模块用来控制时、分、秒的逻辑,数码管模块用来显示时间;计时实际就是进行计数,间隔为1秒;
60秒:分成个位和十位实现,个位:0-9,到9向十位进1;十位:0-6,到6向分钟进1;
60分:分成个位和十位实现,个位:0-9,到9向十位进1;十位:0-6,到6向小时进1;
24时:直接设计0-24,reg[5:0] xy(6位的二进制数),但分成个位、十位进行数码管显示,个位取小时除以10取余,十位取小时除以10取整。
复位设计
用硬件上的开关(SW1)实现时间复位,就是实现电子时钟全部清零,并且不再改变(一直为0),除非关闭时间复位。
计时设计
首先进行时间复位,再关闭时间复位,从0进行正常计时。
调整设计
只调整分钟和小时,用硬件上的开关控制(SW2,SW3),依然是1秒进行计数。
需要再引入两个变量:xn,xn1;当时间调整开关打开时,让xn或xn1进行单独计时,同时再赋值给分钟或小时会显示的变量,调分钟时调的是分的个位。
关键代码
//1S定时时钟产生
reg [31:0] count;
reg clk_1s; //0,1 一秒钟
always @(posedge clk)
if(count==24_000_000-1)
begin
count <= count+1;
clk_1s <= 0;
end
else if(count<48_000_000-1)
count <= count+1;
else
begin
count <= 0;
clk_1s <= 1;
end
源代码
主模块
//
//主模块time
//
module time(
input SW1,//控制复位
input SW2,//控制调分钟
input SW3,//控制调小时
output [7:0] sel,
output [7:0] seg,
input clkin//时钟
);
wire clk;
pll4 pll4_inst0(
.inclk0(clkin),//12Mhz
.c0(clk)//48Mhz
);
//1S定时时钟产生
reg [31:0] count;
reg clk_1s;
always @(posedge clk) //always语句相当于一个死循环
if(count==24_000_000-1)
begin
count <= count+1;
clk_1s <= 0;
end
else if(count<48_000_000-1)
count <= count+1;
else
begin
count <= 0;
clk_1s <= 1;
end
reg [3:0] dout1,dout2,dout3,dout4,dout5,dout6;//时、分、秒的个位和十位
reg co_1;//秒的个位向十位的进位
reg co_2;//秒的十位向分的个位的进位
reg co_3;//分的个位向十位的进位
reg co_4;//分的十位向时的进位
//秒的个位实现
always@(posedge clk_1s,negedge SW1)
begin
if(!SW1) //如果SW1关闭时钟复位
dout1<=0;
else //SW1打开进行计时(0-9)
begin
if(dout1<9)
begin
dout1 <= dout1 +1; //进行加1操作
co_1<=0; //此时进位数为0;
end
else //秒的个位为9时
begin
dout1 <= 0; //重新赋值为0
co_1<=1; //此时进位数为1
end
end
end
//秒的十位的实现
always @(posedge co_1,negedge SW1)
begin
if(!SW1)
dout2<=0;
else
begin
if(dout2<5)
begin
dout2 <= dout2 +1;
co_2<=0;
end
else
begin
dout2 <= 0;
co_2<=1;
end
end
end
//分的个位的实现
always @(posedge co_2,negedge SW1,posedge SW2)
begin
if(!SW1)
dout3<=0;
else //SW1打开才执行以下代码
begin
if(SW2) //SW2打开执行以下代码,即调分钟时
begin
if(xn<=9)
begin
dout3<=xn; //这时直接赋值
co_3<=0; //进位为0
end
else
begin
dout3<=0; //xn为10时,分的个位重新赋值为0
co_3<=1; //进位为1;
end
end
else //SW2关闭执行以下代码,即不调分,正常计时
begin
if(dout3<9)
begin
dout3 <= dout3 +1;
co_3<=0;
end
else
begin
dout3 <= 0;
co_3<=1;
end
end
end
end
//如果要调分钟,引入变量xn(0-9)
reg [3:0] xn;
always @(posedge clk_1s,negedge SW1,negedge SW2)
begin
if(!SW1) //如果SW1关闭时钟复位
xn<=0;
else //SW1打开才执行以下代码
begin
if(!SW2)//如果SW2关闭,即不调分钟时:xn不变
xn<=xn;
else //SW2打开
begin
if(xn<=9)
xn <= xn +1;//此时xn进行计时
else
xn <= 0;//xn为十时再重新赋值为0
end
end
end
//分的十位的实现
always @(posedge co_3,negedge SW1)
begin
if(!SW1)
dout4<=0;
else
begin
if(dout4<5)
begin
dout4 <= dout4 +1;
co_4<=0;
end
else
begin
dout4 <= 0;
co_4<=1;
end
end
end
//时的实现
reg[5:0] xy;
reg x; //是否进行调小时操作的标志
always @(posedge co_4,negedge SW1,posedge SW3)
begin
if(!SW1)
dout5<=0;
else //SW1打开执行以下代码
begin
if(SW3) //SW3打开时执行下面代码,即要调时
begin
dout5<=xn1%10; //将xn1除以10取余赋值给时的个位
dout6<=xn1/10;//将xn1除以10取整赋值给时的十位
x<=1; //此时x为1标志进行了调时
end
else //SW3关闭执行以下代码
begin
if(x==1) //调时后
begin
x<=0; //这时将x赋值为0
if(xn1<23)
xy<=xn1+1;//将这时的xy赋值为调时后的xn1再加1
else
xy<=0;//xn1==23时,xy赋值为0
end
else //调时后x又变为0,正常计时
begin
if(xy<23)
xy<=xy+1;
else
xy<=0;
end
dout5<=xy%10;//将xy除以10取余赋值给时的个位
dout6<=xy/10;//将xy除以10取整赋值给时的十位
end
end
end
//如果要调时,引入变量xn1(0-24)
reg [5:0] xn1;
always @(posedge clk_1s,negedge SW1,negedge SW3)
begin
if(!SW1)
xn1<=0;
else
begin
if(!SW3)
xn1<=xn1;
else
begin
if(xn1<=22)
xn1 <= xn1 +1;
else
xn1 <= 0;
end
end
end
//调用数码管模块
smg smg_inst
(
.clk(clk) , // input clkin
.d1(d1) , // input [4:0] d1
.d2(d2) , // input [4:0] d2
.d3(d3) , // input [4:0] d3
.d4(d4) , // input [4:0] d4
.d5(d5) , // input [4:0] d5
.d6(d6) , // input [4:0] d6
.d7(d7) , // input [4:0] d7
.d8(d8) , // input [4:0] d8
.sel(sel) , // output [7:0] sel
.seg(seg) // output [7:0] seg
);
reg [3:0] d1, d2, d3, d4;//定义数码管显示
reg [3:0] d5, d6, d7, d8;//定义数码管显示
always @(posedge clk)
begin
//显示赋值
d8 <= dout1; //秒的个位
d7 <= dout2; //秒的十位
d5 <= dout3; //分的个位
d4 <= dout4; //分的十位
d2 <= dout5; //时的个位
d1 <= dout6; //时的十位
end
endmodule
数码管模块
//
//8位数码管显示控制模块smg
//
module smg(clk, d1, d2, d3, d4, d5, d6, d7, d8, sel, seg);
input clk;
output reg [7:0] sel;
output reg [7:0] seg; //a~g,dp
input [3:0] d1, d2, d3, d4; //d[7]-dp, d[6:0]-ASCII
input [3:0] d5, d6, d7, d8; //d[7]-dp, d[6:0]-ASCII
//扫描频率:50Hz
parameter update_interval = 48000000 / 400 - 1;
reg [4:0] dat;
reg [2:0] cursel;
integer selcnt;
//扫描计数,选择位
always @(posedge clk)
begin
selcnt <= selcnt + 1;
if (selcnt == update_interval)
begin
selcnt <= 0;
cursel <= cursel + 1;
end
end
//切换扫描位选线和数据
always @(posedge clk)
begin
case (cursel)
3'b000: begin dat = d1; sel = ~8'b00000001; end
3'b001: begin dat = d2; sel = ~8'b00000010;end
3'b010: begin dat = d3; sel = ~8'b00000100; end
3'b011: begin dat = d4; sel = ~8'b00001000; end
3'b100: begin dat = d5; sel = ~8'b00010000; end
3'b101: begin dat = d6; sel = ~8'b00100000;end
3'b110: begin dat = d7; sel = ~8'b01000000; end
3'b111: begin dat = d8; sel = ~8'b10000000; end
endcase
end
//更新段码
always @(posedge clk)
begin
seg[0] <= dat[4]; //dp
case (dat[3:0])
4'h00 : seg[7:1] <= ~7'b0000001; //0
4'h01 : seg[7:1] <= ~7'b1001111; //1
4'h02 : seg[7:1] <= ~7'b0010010; //2
4'h03 : seg[7:1] <= ~7'b0000110; //3
4'h04 : seg[7:1] <= ~7'b1001100; //4
4'h05 : seg[7:1] <= ~7'b0100100; //5
4'h06 : seg[7:1] <= ~7'b0100000; //6
4'h07 : seg[7:1] <= ~7'b0001111; //7
4'h08 : seg[7:1] <= ~7'b0000000; //8
4'h09 : seg[7:1] <= ~7'b0000100; //9
4'h0a : seg[7:1] <= ~7'b0001000; //a
4'h0b : seg[7:1] <= ~7'b1100000; //b
4'h0c : seg[7:1] <= ~7'b1110010; //c
4'h0d : seg[7:1] <= ~7'b1000010; //d
4'h0e : seg[7:1] <= ~7'b0110000; //e
4'h0f : seg[7:1] <= ~7'b0111000; //f
default : seg[7:1] <= ~7'b0110000; //E-rror
endcase
end
endmodule
设置引脚
源代码需要配置引脚才能运行。