引言
完全基于标准计数器进行设计,结构简单,采用多个计数器并通过各个计数器之间的“结束条件”与“加一条件”的组合逻辑进行拼接,实现可调时钟与可调闹钟。。
按钮信号处理
在按钮信号处理部分,由于使用的开发板按钮按下表示低电平,所以对输入信号进行取反后再使用,利用输入按钮信号key_1、key_2、key_3的逻辑或结果作为0.5s计数器“加一条件”,输出该计数器的结束信号,并与输入按钮信号key_1、key_2、key_3分别进行逻辑相与并输出信号,此部分做法类似于按键消抖,同时可以实现按钮输入信号的截断,用处理后的按钮信号控制后续计数器时方便调整,例如调整时钟与闹钟大小时不用多次按下按钮,此设计依据个人习惯进行设计。用处理后的按钮信号分别作为计数器“state_sys”和计数器“move”的“加一条件”,并用计数器“state_sys”的状态指示系统的操作状态,用计数器“move”的状态控制“时”、“分”、“秒”的分别调整以及关闭蜂鸣器。在此部分还使用4个led灯对系统状态进行指示便于操作。。
时钟功能计数器设计
在此部分设计中使用一个标准计数器对时钟信号进行计数50_000_000次,得到1s的频率用于时钟的正常计时。在控制“时”、“分”、“秒”的计数器部分,对标准计数器进行调整,增加stop状态,在此状态下进行计数器的手动调整,通过对按钮信号与计数器“state_sys”和计数器“move”的状态进行组合逻辑得到信号来控制该部分计数器。
闹钟功能设计
完全采用标准计数器进行设计,不再过多赘述。最后比较闹钟计数器与时钟计数器得到中间信号,控制蜂鸣器鸣叫。
数码管扫描与显示译码
使用一个标准计数器对时钟信号进行计数50_000次,得到1ms的频率作为数码管扫描频率,再使用一个标准计数器“cnt_sel”用来指示显示译码部分的信号流入并控制数码管扫描。用计数器“state_sys”的状态指示显示译码器中间参量的信号传递,用取余和求模的方法将“时”、“分”、“秒”计数器的高低位进行分离。
说明
使用ALINX公司AX301开发板进行验证,蜂鸣器只需要给低电平就能响,要实现不同效果鸣叫可以自己加PWM模块。仿真部分由于电脑配置不了modelsim,故而使用Robei EDA进行验证,做一点验证了一点就不展示了。
上代码
/
//*********************************************************//
//* Author : Petter *//
//* Time : 2022.03.28 *//
//* IC : EP4CE6F17C8 *//
//* Project : clock_test *//
//* Module : clock_test *//
//* *//
//*********************************************************//
/
/****************************************************************************************************************/
module clock_test(
input clk,
input rst_n,
input key_1, //system control
input key_2, //select location to change
input key_3, //add '1'
output reg [5:0] sel, //位选输出
output reg [6:0] ment, //段选输出
output reg pot, //小数点
output reg ring, //蜂鸣器
output reg [3:0] led //led_0:reset instruction led, led_1:hour instruction led,
//led_2:minute instruction led, led_3:second instruction led,
);
reg [23:0] cnt_HALF; //24_999_999*0.02us=0.5s,用于按键消抖
reg [3:0] state_sys; //'0':复位态;'1':state run;'2':state change;'3':state alarm
reg [3:0] move; //'0':close buzzer;'1':change second;'2':change minute;'3':change hour
wire add; //控制各位数据加'1'
wire add_cnt_HALF;
wire end_cnt_HALF;
wire add_state_sys;
wire end_state_sys;
wire add_move;
wire end_move;
reg [27:0] cnt_div_s; //49_999_999 1s
reg [19:0] cnt_div_ms; //49_999 1ms,数码管扫描频率
reg [3:0] cnt_sel; //数码管扫描计数器
reg [7:0] cnt_s; //时钟second计数器
reg [7:0] cnt_min; //时钟minute计数器
reg [7:0] cnt_h; //时钟hour计数器
reg [3:0] data_sL; //second低位
reg [3:0] data_sH; //second高位
reg [3:0] data_minL; //minute低位
reg [3:0] data_minH; //minute高位
reg [3:0] data_hL; //hour低位
reg [3:0] data_hH; //hour高位
reg [3:0] data_ment; //数码管数据传递中间参量
wire add_cnt_div_s;
wire end_cnt_div_s;
wire add_cnt_div_ms;
wire end_cnt_div_ms;
wire add_cnt_sel;
wire end_cnt_sel;
wire add_cnt_s;
wire add_cnt_min;
wire add_cnt_h;
wire end_cnt_s;
wire end_cnt_min;
wire end_cnt_h;
wire stop_cnt_s;
wire stop_cnt_min;
wire stop_cnt_h;
wire change_cnt_s;
wire change_cnt_min;
wire change_cnt_h;
reg [7:0] alarm_s; //闹钟second计数器
reg [7:0] alarm_min; //闹钟minute计数器
reg [7:0] alarm_h; //闹钟hour计数器
wire add_alarm_s;
wire add_alarm_min;
wire add_alarm_h;
wire end_alarm_s;
wire end_alarm_min;
wire end_alarm_h;
wire flag_s;
wire flag_min;
wire flag_h;
wire flag_ring; //蜂鸣器鸣叫信号
wire clock; //标志数码管显示数据为时钟
wire alarm; //标志数码管显示数据为闹钟
/****************************************************************************************************************/
//按钮信号处理部分并用led对系统状态进行指示
//cnt_HALF
always@(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt_HALF <= 24'd0; end
else if(add_cnt_HALF) begin
if(end_cnt_HALF) begin
cnt_HALF <= 24'd0; end
else begin
cnt_HALF <= cnt_HALF + 24'd1; end
end
end
assign add_cnt_HALF = (key_1 == 1'b0) || (key_2 == 1'b0) || (key_3 == 1'b0 ); //key低电平有效
assign end_cnt_HALF = add_cnt_HALF && cnt_HALF == 24'd24_999_999;
//state_sys
always@(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
state_sys <= 4'd0; end
else if(add_state_sys ) begin
if(end_state_sys ) begin
state_sys <= 4'd0; end
else begin
state_sys <= state_sys + 4'd1; end
end
end
assign add_state_sys = end_cnt_HALF && ~key_1;
assign end_state_sys = add_state_sys && state_sys == 4'd3;
//move
always@(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
move <= 4'd0; end
else if(add_move ) begin
if(end_move ) begin
move <= 4'd0; end
else begin
move <= move + 4'd1; end
end
end
assign add_move = end_cnt_HALF && ~key_2;
assign end_move = add_move && move == 4'd3;
//add
assign add = end_cnt_HALF && ~key_3;
//led
always@(posedge clk )
begin
if(state_sys == 2 || state_sys == 3) begin
case(move)
4'd0 : begin
led <= 4'b0001; end
4'd1 : begin
led <= 4'b1000; end
4'd2 : begin
led <= 4'b0100; end
4'd3 : begin
led <= 4'b0010; end
default : begin
led <= 4'b0001; end
endcase
end
else if(state_sys == 1) begin
led <= 4'b1110; end
end
/****************************************************************************************************************/
//时钟功能计数器
//1s计数器
//cnt_div_s
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt_div_s <= 28'd0; end
else if(add_cnt_div_s) begin
if(end_cnt_div_s) begin
cnt_div_s <= 28'd0; end
else begin
cnt_div_s <= cnt_div_s + 28'd1; end
end
end
assign add_cnt_div_s = state_sys == 1 || state_sys == 3; //'1'&'3'态驱动时钟,'0'态用来复位后清零
assign end_cnt_div_s = add_cnt_div_s && cnt_div_s == 28'd49_999_999; //28'd1_999_999;
//second计数器
//cnt_s
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt_s <= 8'd0; end
else if(add_cnt_s) begin
if(end_cnt_s) begin
cnt_s <= 8'd0; end
else begin
cnt_s <= cnt_s + 8'd1; end
end
else if(stop_cnt_s) begin
if(change_cnt_s) begin
cnt_s <= cnt_s + 8'd1; end
else if(cnt_s== 8'd60) begin
cnt_s <= 8'd0; end
else begin
cnt_s <= cnt_s; end
end
end
assign add_cnt_s = end_cnt_div_s;
assign end_cnt_s = add_cnt_s && cnt_s == 8'd59;
assign stop_cnt_s = state_sys == 2;
assign change_cnt_s = move == 1 && add;
//minute计数器
//cnt_min
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt_min <= 8'd0; end
else if(add_cnt_min) begin
if(end_cnt_min) begin
cnt_min <= 8'd0; end
else begin
cnt_min <= cnt_min + 8'd1; end
end
else if(stop_cnt_min) begin
if(change_cnt_min) begin
cnt_min <= cnt_min + 8'd1; end
else if(cnt_min== 8'd60) begin
cnt_min <= 8'd0; end
else begin
cnt_min <= cnt_min; end
end
end
assign add_cnt_min = end_cnt_s;
assign end_cnt_min = (add_cnt_min && cnt_min== 8'd59) || (add_cnt_min && cnt_min == 8'd59 && cnt_s == 8'd59);
assign stop_cnt_min = state_sys == 2;
assign change_cnt_min = move == 2 && add;
//hour计数器
//cnt_h
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt_h <= 8'd0; end
else if(add_cnt_h) begin
if(end_cnt_h) begin
cnt_h <= 8'd0; end
else begin
cnt_h <= cnt_h + 8'd1; end
end
else if(stop_cnt_h) begin
if(change_cnt_h) begin
cnt_h <= cnt_h + 8'd1; end
else if( cnt_h== 8'd24) begin
cnt_h <= 8'd0; end
else begin
cnt_h <= cnt_h; end
end
end
assign add_cnt_h = end_cnt_min && end_cnt_s;
assign end_cnt_h = add_cnt_h && cnt_h == 8'd23 && cnt_min == 8'd59 && cnt_s == 8'd59;
assign stop_cnt_h = state_sys == 2;
assign change_cnt_h = move == 3 && add;
/****************************************************************************************************************/
//闹钟功能计数器
//alarm_s
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
alarm_s <= 8'd0; end
else if(add_alarm_s) begin
if(end_alarm_s) begin
alarm_s <= 8'd0; end
else begin
alarm_s <= alarm_s + 8'd1; end
end
end
assign add_alarm_s = move == 1 && state_sys == 3 && add;
assign end_alarm_s = add_alarm_s && alarm_s == 8'd59;
//alarm_min
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
alarm_min <= 8'd0; end
else if(add_alarm_min) begin
if(end_alarm_min) begin
alarm_min <= 8'd0; end
else begin
alarm_min <= alarm_min + 8'd1; end
end
end
assign add_alarm_min = move == 2 && state_sys == 3 && add;
assign end_alarm_min = add_alarm_min && alarm_min == 8'd59;
//alarm_h
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
alarm_h <= 8'd6; end
else if(add_alarm_h) begin
if(end_alarm_h) begin
alarm_h <= 8'd0; end
else begin
alarm_h <= alarm_h + 8'd1; end
end
end
assign add_alarm_h = move == 3 && state_sys == 3 && add;
assign end_alarm_h = add_alarm_h && alarm_h == 8'd23;
//ring
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin //强制复位
ring <= 1'b1; end
else if(flag_ring) begin
ring <= 1'b0; end
else if(move == 0) begin //手动关闭buzzer
ring <= 1'b1; end
end
assign flag_s = cnt_s == alarm_s;
assign flag_min = cnt_min == alarm_min;
assign flag_h = cnt_h == alarm_h;
assign flag_ring = flag_s && flag_min && flag_h;
/****************************************************************************************************************/
//数码管显示控制与译码
assign clock = state_sys == 1 || state_sys == 2;
assign alarm = state_sys == 3;
//高低位数据分离
always@(posedge clk)
begin
if(clock) begin
data_sL <= cnt_s % 8'd10;
data_sH <= cnt_s / 8'd10;
data_minL <= cnt_min % 8'd10;
data_minH <= cnt_min / 8'd10;
data_hL <= cnt_h % 8'd10;
data_hH <= cnt_h / 8'd10; end
else if(alarm) begin
data_sL <= alarm_s % 8'd10;
data_sH <= alarm_s / 8'd10;
data_minL <= alarm_min % 8'd10;
data_minH <= alarm_min / 8'd10;
data_hL <= alarm_h % 8'd10;
data_hH <= alarm_h / 8'd10; end
end
//1ms计数器
//cnt_div_ms
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt_div_ms <= 20'd0; end
else if(add_cnt_div_ms) begin
if(end_cnt_div_ms) begin
cnt_div_ms <= 20'd0; end
else begin
cnt_div_ms <= cnt_div_ms + 20'd1; end
end
end
assign add_cnt_div_ms = rst_n;
assign end_cnt_div_ms = add_cnt_div_ms && cnt_div_ms == 20'd49_999; //1ms
//位选计数器
//cnt_sel
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt_sel <= 4'd0; end
else if(add_cnt_sel) begin
if(end_cnt_sel) begin
cnt_sel <= 4'd0; end
else begin
cnt_sel <= cnt_sel + 4'd1; end
end
end
assign add_cnt_sel = end_cnt_div_ms;
assign end_cnt_sel = add_cnt_sel && cnt_sel == 4'd5;
//数据传递
always@(posedge clk)
begin
case(cnt_sel)
4'd0 : begin data_ment <= data_sL;
pot<= 1;end
4'd1 : begin data_ment <= data_sH;
pot<= 1;end
4'd2 : begin data_ment <= data_minL;
pot<= 0;end
4'd3 : begin data_ment <= data_minH;
pot<= 1;end
4'd4 : begin data_ment <= data_hL;
pot<= 0; end
4'd5 : begin data_ment <= data_hH;
pot<= 1;end
default data_ment <= 4'd0;
endcase
end
//数码管扫描
always@(posedge clk)
begin
case(cnt_sel)
4'd0 : sel <= 6'b111_110;
4'd1 : sel <= 6'b111_101;
4'd2 : sel <= 6'b111_011;
4'd3 : sel <= 6'b110_111;
4'd4 : sel <= 6'b101_111;
4'd5 : sel <= 6'b011_111;
default : sel <= 6'b111_110;
endcase
end
//显示译码
always@(posedge clk)
begin
case(data_ment)
4'd0 : ment <= 7'b1_000_000;
4'd1 : ment <= 7'b1_111_001;
4'd2 : ment <= 7'b0_100_100;
4'd3 : ment <= 7'b0_110_000;
4'd4 : ment <= 7'b0_011_001;
4'd5 : ment <= 7'b0_010_010;
4'd6 : ment <= 7'b0_000_010;
4'd7 : ment <= 7'b1_111_000;
4'd8 : ment <= 7'b0_000_000;
4'd9 : ment <= 7'b0_010_000;
default : ment <= 7'b1_000_000;
endcase
end
/****************************************************************************************************************/
endmodule
结语
设计简单,没有分模块,能力有限,恳请大佬批评指正。