前言
前一章节我们讲述了基于FPGA的数字钟设计,整个工程只有外部时钟输入和复位按键,虽然实现了数字钟的逻辑,但是该项目是自动化的,不能像我们生活中调表一样去控制数字钟,本章节将介绍如何通过按键去调数字钟。
正文
一、 可调数字时钟
1.项目需求
在现有的设计基础上添加可以调时功能,使设计完成的数字钟能够进行调节小时,调节分钟,调节秒,实现可以对时功能。
2.技术介绍
利用三个独立按键实现调时功能,第一个独立按键实现控制调节模式选择功能,其对应的调节模式包含:调节小时十位,调节小时个位,调节分钟十位,调节分钟个位,调节秒十位,调节秒个位;利用另外两个按键实现加减功能。
利用有限状态机进行设计描述,根据调节模式的设置,将对时功能实现设置为7个状态,分别为:
1: 正常显示状态,对加减按键进行操作时产生的标志信号无效。
2: 调节小时十位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_hour1_add和flag_hour1_sub;产生一个小时十位数据闪烁使能信号bilnk_hour1_en,
3: 调节小时个位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时个位数据加标志信号flag_hour0_add和flag_hour0_sub; 产生一个小时个位数据闪烁使能信号bilnk_hour0_en,
4: 调节分钟十位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_min1_add和flag_min1_sub; 产生一个分钟十位数据闪烁使能信号bilnk_min1_en,
5: 调节分钟个位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_min0_add和flag_min0_sub; 产生一个分钟个位数据闪烁使能信号bilnk_min0_en,
6: 调节秒十位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_sec1_add和flag_sec1_sub; 产生一个秒十位数据闪烁使能信号bilnk_sec1_en,
7: 调节秒个位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_sec0_add和flag_sec0_sub; 产生一个秒个位数据闪烁使能信号bilnk_sec0_en。
需要在234567状态里产生1秒清零使能信号one_sec_en,该使能信号在正常显示状态时为低电平,当进入到调节时间状态时秒清零使能信号one_sec_clren拉高。
3.顶层架构
该项目是以自动数字钟系统为基础,在其上添加按键消抖和显示数据更改模块,整体框架:
按键消抖控制模块:
数字钟控制模块:接收到对应的信号后变化对应位的数据
4.端口描述
clk | 时钟信号(50Mhz) |
rst_n | 复位按键(低电平有效) |
key_adjust | 模式切换按键 |
key_add | 加操作按键 |
key_sub | 减操作按键 |
[2:0] sel | 六位八段数码管位选信号 |
[7:0] seg | 六位八段数码管段选信号 |
二、代码验证
blink_ctrl模块:当切换到调时间模式时,为区别当前选中位,使当前选中位数码管闪烁
module blink_ctrl(
input clk,
input rst_n,
input blink_en,//模块使能信号(1有效)
input [3:0] data,//当前位现在的数据(4位二进制数)
output [3:0] show_data//闪烁输出数据
);
reg [24:0] cnt;
parameter MAX = 25'd25_000_000;//闪烁延时间隔计数器
reg selc_en;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 25'd0;
else
if(blink_en == 1)
if(cnt < MAX - 1)//标准计数器,产生延时闪烁效果
cnt <= cnt + 25'd1;
else
cnt <= 25'd0;
else
cnt <= 25'd0;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
selc_en <= 1'b0;
else
if(blink_en == 1)
if(cnt == MAX - 1)
selc_en <= ~selc_en;
//每经过闪烁延时间隔计数器selc_en取反一次
//相当于产生一个新的时钟
else
selc_en <= selc_en;
else
selc_en <= 1'b0;
end
assign show_data = (selc_en == 0)?data:4'hf;
//在新时钟的低电平段传出数据,高电平段输出全部拉高,即在低电平显示数据,高电平段熄灭
endmodule
hour0_ctrl模块:小时个位控制模块,产生小时个位数据,并在调节小时个位模式下对加减按键操作进行对应的处理,产生新的小时个位数据并输出。
module hour0_ctrl(
input clk,
input rst_n,
input [3:0]hour1,//当前小时十位数据
input flag,//分钟十位产生的进位信号
input flag_add,//加按键产生的有效信号
input flag_sub,//减按键产生的有效信号
output reg [3:0] hour0,//产生的新的小时个位数据
output reg flag_10hour//小时个位进位信号
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
hour0 <= 4'd0;
else
if(hour1 < 2)//小时十位数据小于2的时候,个位最大是9
begin
if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
if(hour0 < 9)//最大到9,在加后跳转到0
hour0 <= hour0 + 4'd1;//有进位或加按键操作时,数据加1
else
hour0 <= 4'd0;
else
if(flag_sub == 1)
if(hour0 == 0)//最小到0,再减后跳转到9
hour0 <= 4'd9;
else
hour0 <= hour0 - 4'd1;//有减按键操作时,数据减1
else
hour0 <= hour0;
end
else//等于2时,小时个位数据最大是3
begin
if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
if(hour0 < 3)//最大到3,在加后跳转到0
hour0 <= hour0 + 4'd1;//有进位或加按键操作时,数据加1
else
hour0 <= 4'd0;
else
if(flag_sub == 1)
if(hour0 == 0)//最小到0,再减后跳转到3
hour0 <= 4'd3;
else
hour0 <= hour0 - 4'd1;//有减按键操作时,数据减1
else
hour0 <= hour0;
end
end
always @(*) begin//组合逻辑,产生进位信号
if(rst_n == 0)
flag_10hour <= 1'b0;
else
if(hour1 < 2)//小时十位数据小于2的时候,个位最大是9
if((hour0 == 9)&&(flag == 1))
flag_10hour <= 1'b1;
else
flag_10hour <= 1'b0;
else//等于2时,小时个位数据最大是3
if((hour0 == 3)&&(flag == 1))
flag_10hour <= 1'b1;
else
flag_10hour <= 1'b0;
end
endmodule
hour1_ctrl模块:小时十位数据控制模块,产生小时十位数据,并在调节小时十位模式下对加减按键操作进行对应的处理,产生新的小时十位数据并输出。
module hour1_ctrl(
input clk,
input rst_n,
input flag,//小时个位产生的进位信号
input flag_add,//加按键产生的有效信号
input flag_sub,//减按键产生的有效信号
output reg [3:0] hour1//小时十位进位信号
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
hour1 <= 4'd0;
else
if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
if(hour1 < 2)//小时十位数据小于2的时,有进位或加按键操作时,数据加1
hour1 <= hour1 + 4'd1;
else
hour1 <= 4'd0;//超过2直接到0
else
if(flag_sub == 1)
if(hour1 == 0)
hour1 <= 4'd2;//减操作到0后,再减到2
else
hour1 <= hour1 - 4'd1;
else
hour1 <= hour1;
end
endmodule
min0_ctrl模块:分钟个位数据控制模块,产生分钟个位数据,并在调节分钟个位模式下对加减按键操作进行对应的处理,产生新的分钟个位数据并输出。
module min0_ctrl(
input clk,
input rst_n,
input flag,//秒十位进位信号
input flag_add,//加按键产生的有效信号
input flag_sub,//减按键产生的有效信号
output reg [3:0] min0,//产生的新的分钟个位数据
output flag_10min//分钟个位进位信号
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
min0 <= 4'd0;
else
if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
if(min0 < 9)//最大到9,在加后跳转到0
min0 <= min0 + 4'd1;//有进位或加按键操作时,数据加1
else
min0 <= 4'd0;
else
if(flag_sub == 1)
if(min0 == 0)//最小到0,再减后跳转到9
min0 <= 4'd9;
else
min0 <= min0 - 4'd1;//有减按键操作时,数据减1
else
min0 <= min0;
end
assign flag_10min = ((min0 == 9)&&(flag == 1))?1'b1:1'b0;
//进位信号是秒十位进位信号到来并且当前分钟个位为9时产生一次进位
endmodule
min1_ctrl模块:分钟十位数据控制模块,产生分钟十位数据,并在调节分钟十位模式下对加减按键操作进行对应的处理,产生新的分钟十位数据并输出。
module min1_ctrl(
input clk,
input rst_n,
input flag,//分钟个位产生的进位信号
input flag_add,//加按键产生的有效信号
input flag_sub,//减按键产生的有效信号
output reg [3:0] min1,//产生的新的分钟十位数据
output flag_hour//分钟十位进位信号
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
min1 <= 4'd0;
else
if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
if(min1 < 5)//最大到5,在加后跳转到0
min1 <= min1 + 4'd1;//有进位或加按键操作时,数据加1
else
min1 <= 4'd0;
else
if(flag_sub == 1)
if(min1 == 0)//最小到0,再减后跳转到5
min1 <= 4'd5;
else
min1 <= min1 - 4'd1;//有减按键操作时,数据减1
else
min1 <= min1;
end
assign flag_hour = ((min1 == 5)&&(flag == 1))?1'b1:1'b0;
//进位信号是分钟个位位进位信号到来并且当前分钟十位为9时产生一次进位
endmodule
one_sec模块: 一秒信号产生模块,产生1S的时钟信号,精确度纳秒级
module one_sec(
input clk,
input rst_n,
input clr_en,//1s信号使能信号
//(按键产生的有效信号,1时1s信号停止产生,0时产生1s信号)
output flag//1s信号
);
reg [25:0] cnt;
parameter MAX = 26'd50_000_000;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 26'd0;
else
if(clr_en == 1)
cnt <= 26'd0;
else
if(cnt < MAX - 1)
cnt <= cnt + 26'd1;
else
cnt <= 26'd0;
end
assign flag = ((cnt == MAX - 1)&&(clr_en == 0))?1'b1:1'b0;
//计数器记到最大值,下一个时钟信号到来时产生1s信号
endmodule
sec0_ctrl模块:秒个位数据控制模块,产生秒个位数据,并在调节秒个位模式下对加减按键操作进行对应的处理,产生新的秒个位数据并输出。
module sec0_ctrl(
input clk,
input rst_n,
input flag,//1s信号
input flag_add,//加按键产生的有效信号
input flag_sub,//减按键产生的有效信号
output reg [3:0] sec0,//产生的新的秒个位数据
output flag_10sec//秒个位进位信号
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
sec0 <= 4'd0;
else
if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
if(sec0 < 9)//最大到9,在加后跳转到0
sec0 <= sec0 + 4'd1;//有进位或加按键操作时,数据加1
else
sec0 <= 4'd0;
else
if(flag_sub == 1)
if(sec0 == 0)//最小到0,再减后跳转到9
sec0 <= 4'd9;
else
sec0 <= sec0 - 4'd1;//有减按键操作时,数据减1
else
sec0 <= sec0;
end
assign flag_10sec = ((sec0 == 9)&&(flag == 1))?1'b1:1'b0;
//进位信号是1s信号到来并且当前秒个位为9时产生一次进位
endmodule
sec1_ctrl模块:秒十位数据控制模块,产生秒十位数据,并在调节秒十位模式下对加减按键操作进行对应的处理,产生新的秒十位数据并输出。
module sec1_ctrl(
input clk,
input rst_n,
input flag,//秒十位产生的进位信号
input flag_add,//加按键产生的有效信号
input flag_sub,//减按键产生的有效信号
output reg [3:0] sec1,//产生的新的秒十位数据
output flag_min//秒十位进位信号
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
sec1 <= 4'd0;
else
if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
if(sec1 < 5)//最大到5,在加后跳转到0
sec1 <= sec1 + 4'd1;//有进位或加按键操作时,数据加1
else
sec1 <= 4'd0;
else
if(flag_sub == 1)
if(sec1 == 0)//最小到0,再减后跳转到5
sec1 <= 4'd5;
else
sec1 <= sec1 - 4'd1;//有减按键操作时,数据减1
else
sec1 <= sec1;
end
assign flag_min = ((sec1 == 5)&&(flag == 1))?1'b1:1'b0;
//进位信号是秒个位进位信号到来并且当前秒十位为5时产生一次进位
endmodule
time_ctrl模块:数字钟数据连接顶层,连线作用
module time_ctrl(
input clk,
input rst_n,
input one_sec_clren,
input flag_sec0_add,
input flag_sec0_sub,
input blink_sec0_en,
input flag_sec1_add,
input flag_sec1_sub,
input blink_sec1_en,
input flag_min0_add,
input flag_min0_sub,
input blink_min0_en,
input flag_min1_add,
input flag_min1_sub,
input blink_min1_en,
input flag_hour0_add,
input flag_hour0_sub,
input blink_hour0_en,
input flag_hour1_add,
input flag_hour1_sub,
input blink_hour1_en,
output [23:0] data_out
);
wire flag;
wire [3:0] sec0;
wire flag_10sec;
wire [3:0] sec1;
wire flag_min;
wire [3:0] min0;
wire flag_10min;
wire [3:0] min1;
wire flag_hour;
wire [3:0] hour1;
wire [3:0] hour0;
wire flag_10hour;
wire [3:0] show_sec0;
wire [3:0] show_sec1;
wire [3:0] show_min0;
wire [3:0] show_min1;
wire [3:0] show_hour0;
wire [3:0] show_hour1;
one_sec one_sec_inst(
.clk(clk),
.rst_n(rst_n),
.clr_en(one_sec_clren),
.flag(flag)
);
sec0_ctrl sec0_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.flag(flag),
.flag_add(flag_sec0_add),
.flag_sub(flag_sec0_sub),
.sec0(sec0),
.flag_10sec(flag_10sec)
);
blink_ctrl blink_ctrl_inst1(//数码管秒个位数据闪烁输出
.clk(clk),
.rst_n(rst_n),
.data(sec0),
.blink_en(blink_sec0_en),
.show_data(show_sec0)
);
sec1_ctrl sec1_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.flag(flag_10sec),
.flag_add(flag_sec1_add),
.flag_sub(flag_sec1_sub),
.sec1(sec1),
.flag_min(flag_min)
);
blink_ctrl blink_ctrl_inst2(//数码管秒十位数据闪烁输出
.clk(clk),
.rst_n(rst_n),
.data(sec1),
.blink_en(blink_sec1_en),
.show_data(show_sec1)
);
min0_ctrl min0_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.flag(flag_min),
.flag_add(flag_min0_add),
.flag_sub(flag_min0_sub),
.min0(min0),
.flag_10min(flag_10min)
);
blink_ctrl blink_ctrl_inst3(//数码管分钟个位数据闪烁输出
.clk(clk),
.rst_n(rst_n),
.data(min0),
.blink_en(blink_min0_en),
.show_data(show_min0)
);
min1_ctrl min1_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.flag(flag_10min),
.flag_add(flag_min1_add),
.flag_sub(flag_min1_sub),
.min1(min1),
.flag_hour(flag_hour)
);
blink_ctrl blink_ctrl_inst4(//数码管分钟十位数据闪烁输出
.clk(clk),
.rst_n(rst_n),
.data(min1),
.blink_en(blink_min1_en),
.show_data(show_min1)
);
hour0_ctrl hour0_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.hour1(hour1),
.flag(flag_hour),
.flag_add(flag_hour0_add),
.flag_sub(flag_hour0_sub),
.hour0(hour0),
.flag_10hour(flag_10hour)
);
blink_ctrl blink_ctrl_inst5(//数码管小时个位数据闪烁输出
.clk(clk),
.rst_n(rst_n),
.data(hour0),
.blink_en(blink_hour0_en),
.show_data(show_hour0)
);
hour1_ctrl hour1_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.flag(flag_10hour),
.flag_add(flag_hour1_add),
.flag_sub(flag_hour1_sub),
.hour1(hour1)
);
blink_ctrl blink_ctrl_inst6(//数码管小时十位数据闪烁输出
.clk(clk),
.rst_n(rst_n),
.data(hour1),
.blink_en(blink_hour1_en),
.show_data(show_hour1)
);
assign data_out = {show_hour1,show_hour0,show_min1,show_min0,show_sec1,show_sec0};
//时间数据融合
endmodule
seg_driver模块:数码管驱动模块,接收时间数据并按固定方式输出到数码管上。
module seg_driver(
input clk,
input rst_n,
input [23:0] data_in,//接收数字时钟信号
output reg [2:0] sel,//位选信号
output reg [7:0] seg//段选信号
);
reg [18:0] cnt;//1MS
parameter MAX = 19'd50_000;
//20ns记50000次
//产生1ms的延时
//位选信号1ms变换一次
reg [3:0] temp;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 19'd0;
else
if(cnt < MAX - 1)//产生1ms的延时
cnt <= cnt + 19'd1;
else
cnt <= 19'd0;
end
//数码管位选信号控制逻辑
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
sel <= 3'd0;
else
if(cnt == MAX - 1)//位选信号6位,1ms的延时到变化一次
if(sel < 5)
sel <= sel + 3'd1;
else
sel <= 3'd0;
else
sel <= sel;
end
always @(*) begin
if(rst_n == 0)
temp <= 4'd0;
else
case(sel)//位选信号到将数据输出到对应输出位上
3'd0 : temp <= data_in[23:20];
3'd1 : temp <= data_in[19:16];
3'd2 : temp <= data_in[15:12];
3'd3 : temp <= data_in[11:8];
3'd4 : temp <= data_in[7:4];
3'd5 : temp <= data_in[3:0];
default : temp <= 4'd0;
endcase
end
always @(*) begin
if(rst_n == 0)
seg <= 8'hff;
else
case(temp)//段选信号,对应的数码管信号
4'd0 : seg <= 8'b1100_0000;//0
4'd1 : seg <= 8'b1111_1001;//1
4'd2 : seg <= 8'b1010_0100;//2
4'd3 : seg <= 8'b1011_0000;//3
4'd4 : seg <= 8'b1001_1001;//4
4'd5 : seg <= 8'b1001_0010;//5
4'd6 : seg <= 8'b1000_0010;//6
4'd7 : seg <= 8'b1111_1000;//7
4'd8 : seg <= 8'b1000_0000;//8
4'd9 : seg <= 8'b1001_0000;//9
4'd10 : seg <= 8'b1000_1000;//A
4'd11 : seg <= 8'b1000_0011;//b
4'd12 : seg <= 8'b1100_0110;//C
4'd13 : seg <= 8'b1010_0001;//d
4'd14 : seg <= 8'b1000_0110;//E
4'd15 : seg <= 8'b1000_1110;//F
default : seg <= 8'hff;
endcase
end
endmodule
jitter_ctrl模块:按键消抖模块,产生按键有效信号。具体参考独立按键消抖
module jitter_ctrl(
input clk,
input rst_n,
input key,
output reg flag
);
reg [3:0] n_state;
reg [3:0] c_state;
parameter s0 = 4'b0001;//空闲状态
parameter s1 = 4'b0010;//下抖动状态
parameter s2 = 4'b0100;//稳定状态
parameter s3 = 4'b1000;//上抖动状态
reg [18:0] cnt;
parameter MAX = 19'd500_000;
//FSM1
always @(posedge clk,negedge rst_n)//状态传递
begin
if(rst_n == 0)
c_state <= s0;
else
c_state <= n_state;
end
//FSM2
always @(*)//描述状态转移
begin
if(rst_n == 0)
n_state <= s0;
else
case(c_state)
s0 : begin
if(key == 0)
n_state <= s1;
else
n_state <= s0;
end
s1 : begin
if(cnt == MAX - 1)//下抖动延时
n_state <= s2;
else
n_state <= s1;
end
s2 : begin
if(key == 1)
n_state <= s3;
else
n_state <= s2;
end
s3 : begin
if(cnt == MAX - 1)//上抖动延时
n_state <= s0;
else
n_state <= s3;
end
default : n_state <= s0;
endcase
end
//FSM3
always @(posedge clk,negedge rst_n)//描述状态输出
begin
if(rst_n == 0)
begin
cnt <= 19'd0;
flag <= 1'b0;
//key_en <= 1'b0;
end
else
case(c_state)
s0 : begin
cnt <= 19'd0;
flag <= 1'b0;
//key_en <= 1'b0;
end
s1 : begin
if(cnt < MAX - 1)
cnt <= cnt + 19'd1;
else
cnt <= 19'd0;
if(cnt == MAX - 1)
begin
flag <= 1'b1;
//key_en <= 1'b1;
end
else
begin
//key_en <= 1'b0;
flag <= 1'b0;
end
end
s2 : begin
flag <= 1'b0;
//key_en <= 1'b1;
cnt <= 19'd0;
end
s3 : begin
flag <= 1'b0;
//key_en <= 1'b0;
if(cnt < MAX - 1)
cnt <= cnt + 19'd1;
else
cnt <= 19'd0;
end
default : begin
cnt <= 19'd0;
flag <= 1'b0;
//key_en <= 1'b0;
end
endcase
end
endmodule
key_top模块:按键顶层连线模块,读取按键状态并传递给按键消抖模块,产生按键有效信号。
module key_top(
input clk,
input rst_n,
input key_adjust,
input key_add,
input key_sub,
output flag_adjust,
output flag_add,
output flag_sub
);
jitter_ctrl jitter_ctrl_inst1(//模式切换按键
.clk(clk),
.rst_n(rst_n),
.key(key_adjust),
.flag(flag_adjust)
);
jitter_ctrl jitter_ctrl_inst2(//数据加按键
.clk(clk),
.rst_n(rst_n),
.key(key_add),
.flag(flag_add)
);
jitter_ctrl jitter_ctrl_inst3(//数据减按键
.clk(clk),
.rst_n(rst_n),
.key(key_sub),
.flag(flag_sub)
);
endmodule
adjust_ctrl模块:模式切换模块,有限状态机,对目标位进行使能并将按键操作有效信号传递给对应模块。
module adjust_ctrl(
input clk,
input rst_n,
input flag_adjust,
input flag_add,
input flag_sub,
output reg one_sec_clren,
output reg flag_sec0_add,
output reg flag_sec0_sub,
output reg blink_sec0_en,
output reg flag_sec1_add,
output reg flag_sec1_sub,
output reg blink_sec1_en,
output reg flag_min0_add,
output reg flag_min0_sub,
output reg blink_min0_en,
output reg flag_min1_add,
output reg flag_min1_sub,
output reg blink_min1_en,
output reg flag_hour0_add,
output reg flag_hour0_sub,
output reg blink_hour0_en,
output reg flag_hour1_add,
output reg flag_hour1_sub,
output reg blink_hour1_en
);
reg [2:0] c_state;
reg [2:0] n_state;
parameter s0 = 3'd0;//正常显示状态
parameter s1 = 3'd1;//调节小时十位数据状态
parameter s2 = 3'd2;//调节小时个位数据状态
parameter s3 = 3'd3;//调节分钟十位数据状态
parameter s4 = 3'd4;//调节分钟个位数据状态
parameter s5 = 3'd5;//调节秒十位数据状态
parameter s6 = 3'd6;//调节秒个位数据状态
always @(posedge clk,negedge rst_n)//状态传递
begin
if(rst_n == 0)
c_state <= s0;
else
c_state <= n_state;
end
always @(*) begin//描述状态转移
if(rst_n == 0)
n_state <= s0;
else
case(c_state)
s0 : begin
if(flag_adjust == 1)//模式切换按键操作
n_state <= s1;
else
n_state <= s0;
end
s1 : begin
if(flag_adjust == 1)//模式切换按键操作
n_state <= s2;
else
n_state <= s1;
end
s2 : begin
if(flag_adjust == 1)//模式切换按键操作
n_state <= s3;
else
n_state <= s2;
end
s3 : begin
if(flag_adjust == 1)//模式切换按键操作
n_state <= s4;
else
n_state <= s3;
end
s4 : begin
if(flag_adjust == 1)//模式切换按键操作
n_state <= s5;
else
n_state <= s4;
end
s5 : begin
if(flag_adjust == 1)//模式切换按键操作
n_state <= s6;
else
n_state <= s5;
end
s6 : begin
if(flag_adjust == 1)//模式切换按键操作
n_state <= s0;
else
n_state <= s6;
end
default : n_state <= s0;
endcase
end
always @(posedge clk,negedge rst_n)//描述状态输出
begin
if(rst_n == 0)
begin
one_sec_clren <= 1'b0;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
blink_hour1_en <= 1'b0;
end
else
case(c_state)
s0 : begin
one_sec_clren <= 1'b0;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
blink_hour1_en <= 1'b0;
end
s1 : begin
one_sec_clren <= 1'b1;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
blink_hour1_en <= 1'b1;
flag_hour1_add <= flag_add;
flag_hour1_sub <= flag_sub;
end
s2 : begin
one_sec_clren <= 1'b1;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
blink_hour1_en <= 1'b0;
blink_hour0_en <= 1'b1;
flag_hour0_add <= flag_add;
flag_hour0_sub <= flag_sub;
end
s3 : begin
one_sec_clren <= 1'b1;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= flag_add;
flag_min1_sub <= flag_sub;
blink_min1_en <= 1'b1;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
blink_hour1_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
end
s4 : begin
one_sec_clren <= 1'b1;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_min0_add <= flag_add;
flag_min0_sub <= flag_sub;
blink_min0_en <= 1'b1;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
blink_hour1_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
end
s5 : begin
one_sec_clren <= 1'b1;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= flag_add;
flag_sec1_sub <= flag_sub;
blink_sec1_en <= 1'b1;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
blink_hour1_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
end
s6 : begin
one_sec_clren <= 1'b1;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_sec0_add <= flag_add;
flag_sec0_sub <= flag_sub;
blink_sec0_en <= 1'b1;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
blink_hour1_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
end
default : begin
one_sec_clren <= 1'b0;
flag_sec0_add <= 1'b0;
flag_sec0_sub <= 1'b0;
blink_sec0_en <= 1'b0;
flag_sec1_add <= 1'b0;
flag_sec1_sub <= 1'b0;
blink_sec1_en <= 1'b0;
flag_min0_add <= 1'b0;
flag_min0_sub <= 1'b0;
blink_min0_en <= 1'b0;
flag_min1_add <= 1'b0;
flag_min1_sub <= 1'b0;
blink_min1_en <= 1'b0;
flag_hour0_add <= 1'b0;
flag_hour0_sub <= 1'b0;
blink_hour0_en <= 1'b0;
flag_hour1_add <= 1'b0;
flag_hour1_sub <= 1'b0;
blink_hour1_en <= 1'b0;
end
endcase
end
endmodule
digital_clock模块:顶层连线
module digital_clock(
input clk,
input rst_n,
input key_adjust,
input key_add,
input key_sub,
output [2:0] sel,
output [7:0] seg
);
wire flag_adjust;//连线信号
wire flag_add;
wire flag_sub;
wire one_sec_clren;
wire flag_sec0_add;
wire flag_sec0_sub;
wire blink_sec0_en;
wire flag_sec1_add;
wire flag_sec1_sub;
wire blink_sec1_en;
wire flag_min0_add;
wire flag_min0_sub;
wire blink_min0_en;
wire flag_min1_add;
wire flag_min1_sub;
wire blink_min1_en;
wire flag_hour0_add;
wire flag_hour0_sub;
wire blink_hour0_en;
wire flag_hour1_add;
wire flag_hour1_sub;
wire blink_hour1_en;
wire [23:0] data_out;
key_top key_top_inst(
.clk(clk),
.rst_n(rst_n),
.key_adjust(key_adjust),
.key_add(key_add),
.key_sub(key_sub),
.flag_adjust(flag_adjust),
.flag_add(flag_add),
.flag_sub(flag_sub)
);
adjust_ctrl adjust_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.flag_adjust(flag_adjust),
.flag_add(flag_add),
.flag_sub(flag_sub),
.one_sec_clren(one_sec_clren),
.flag_sec0_add(flag_sec0_add),
.flag_sec0_sub(flag_sec0_sub),
.blink_sec0_en(blink_sec0_en),
.flag_sec1_add(flag_sec1_add),
.flag_sec1_sub(flag_sec1_sub),
.blink_sec1_en(blink_sec1_en),
.flag_min0_add(flag_min0_add),
.flag_min0_sub(flag_min0_sub),
.blink_min0_en(blink_min0_en),
.flag_min1_add(flag_min1_add),
.flag_min1_sub(flag_min1_sub),
.blink_min1_en(blink_min1_en),
.flag_hour0_add(flag_hour0_add),
.flag_hour0_sub(flag_hour0_sub),
.blink_hour0_en(blink_hour0_en),
.flag_hour1_add(flag_hour1_add),
.flag_hour1_sub(flag_hour1_sub),
.blink_hour1_en(blink_hour1_en)
);
time_ctrl time_ctrl_inst(
.clk(clk),
.rst_n(rst_n),
.one_sec_clren(one_sec_clren),
.flag_sec0_add(flag_sec0_add),
.flag_sec0_sub(flag_sec0_sub),
.blink_sec0_en(blink_sec0_en),
.flag_sec1_add(flag_sec1_add),
.flag_sec1_sub(flag_sec1_sub),
.blink_sec1_en(blink_sec1_en),
.flag_min0_add(flag_min0_add),
.flag_min0_sub(flag_min0_sub),
.blink_min0_en(blink_min0_en),
.flag_min1_add(flag_min1_add),
.flag_min1_sub(flag_min1_sub),
.blink_min1_en(blink_min1_en),
.flag_hour0_add(flag_hour0_add),
.flag_hour0_sub(flag_hour0_sub),
.blink_hour0_en(blink_hour0_en),
.flag_hour1_add(flag_hour1_add),
.flag_hour1_sub(flag_hour1_sub),
.blink_hour1_en(blink_hour1_en),
.data_out(data_out)
);
seg_driver seg_driver_inst(
.clk(clk),
.rst_n(rst_n),
.data_in(data_out),
.sel(sel),
.seg(seg)
);
endmodule
仿真文件,对相应延时计数器处理后开始仿真
`timescale 1ns/1ps
module digital_clock_tb;
reg clk;
reg rst_n;
reg key_adjust;
reg key_add;
reg key_sub;
wire [2:0] sel;
wire [7:0] seg;
digital_clock digital_clock_inst(
.clk(clk),
.rst_n(rst_n),
.key_adjust(key_adjust),
.key_add(key_add),
.key_sub(key_sub),
.sel(sel),
.seg(seg)
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rst_n = 0;
key_adjust = 1;
key_add = 1;
key_sub = 1;
#200
rst_n = 1;
#1000
key_add = 0;
#200
key_add = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_adjust = 0;
#200
key_adjust = 1;
#1000
key_add = 0;
#200
key_add = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_adjust = 0;
#200
key_adjust = 1;
#1000
key_add = 0;
#200
key_add = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_adjust = 0;
#200
key_adjust = 1;
#1000
key_add = 0;
#200
key_add = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_adjust = 0;
#200
key_adjust = 1;
#1000
key_add = 0;
#200
key_add = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_adjust = 0;
#200
key_adjust = 1;
#1000
key_add = 0;
#200
key_add = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_adjust = 0;
#200
key_adjust = 1;
#1000
key_add = 0;
#200
key_add = 1;
#1000
key_sub = 0;
#200
key_sub = 1;
#1000
key_adjust = 0;
#200
key_adjust = 1;
#10000
$stop;
end
endmodule
三、仿真验证
仿真文件
按下模式切换,观察sec0信号,1s信号产生停止,数据一直保持在1。时钟停止等待按键操作。
按下一次模式切换按键,调整小时十位数据,观察hour1信号,add一次后由0变1,sub两次后变到2,满足设计。下图为放大后的图像。
再按下一次模式切换按键,当前小时十位为2,调整小时个位数据,观察hour0信号,add一次后由0变1,sub两次后变到3,满足设计。
切换回自动模式
当前时刻为23:59:28,下一秒时间为23:59:29,
观察上图,23:59:59到00:00:00,自动模式下时间数据也是正确的。