用FPGA设计一个数字闹钟应该说是一个比较综合的小系统,包含了按键、数码
管、状态机等内容,本文主要是讲述三键输入的数字闹钟总体的设计,整个作品和小时候两三块一个的电子手表十分类似
功能描述
1 用四个数码管正常显示时、分高低位,实现正常显示时间。
2 具备调分调时功能
3 闹钟功能
功能并不复杂,我们现在来分析一下需要哪些模块。
首先是时钟功能,正常显示时间肯定是必须的,为实现这一可以设计一个60进制计数器和一个24进制计数器,当然也可以根据逻辑直接书写出来,但无论是什么办法,这肯定需要一个模块来实现它。
第二就是调分调时了,说白了就是置数,要置数,那么就必须有一个区域去控制数据,也需要一个地方存储数据,然后将置数的数据传给时钟,所以它应该与键盘的联系,内部有存储器。
第三是闹钟,闹钟不难想,比较器,我设定闹钟时间,然后与时钟的时间比较,如果两者相同,输出闹钟信号,就是如此。
最后的便是显示电路,主要是数码管的显示原理,驱动数码管显示时间。
就是这几样部分,貌似这么一说,确实没有什么东西,一个系统就是需要输入和输出相互协调好,这里面的逻辑必须是相互对应的,不出矛盾,个人认为,这是设计的难度所在。
整体设计图
模块讲解
键盘模块(key)
输入 | 功能说明 | 输出 | 功能说明 |
---|---|---|---|
add_in | 加 | c_add | 时钟加 |
aub_in | 减 | c_sub | 时钟减 |
model_in | 模式控制端 | a_hour | 调闹钟小时 |
clk | 时钟 | a_minute | 调闹钟分钟 |
rst_n | 复位 | cnt_control | 计数器开关 |
Display_Model | 显示控制 | ||
Time_model | 调时钟转换信号 |
细节讲解
model:模式的选择寄存器
整个闹钟系统我设置为五个模式,所以model需要是三位的[2:0]
00:时钟正常走时
01:时钟调分模式,该模式下时钟的计数器停止,时钟是不走的,同时显示模式也会转到调时钟模式。
10:时钟调时模式,与调分模式类似。
11:闹钟调分设置模式,此时时钟走时,显示模式为闹钟模式。
100:闹钟调时模式,与调分时类似。
cnt_control:计数器开关
正常走时和调闹钟模式下,计数器开,cnt_control = 0;
当进入调分和调时模式,计数器关闭,cnt_control = 1。
Time_Model:调时钟转换信号
这个是连接时钟模块(clock)的,是调分模式和调时模式的切换信号。
Display_Model:显示控制
正常走时,进入调分和调时模式时,停止走时,整个过程我设置为同一种显示模式;
闹钟模式下,显示模式转换;
所以一共是两种模式,一根线足以。
代码展示
module key(
input clk,
input rst_n,
input add_in,//增加按钮
input sub_in,//减去按钮
input model_in,//模式选择
output reg Display_Model,
//时钟与闹钟显示选择,0 代表时钟,1代表闹钟
output reg cnt_control, //控制定时器
output reg [2:0]Time_model,
output reg c_add, //控制时钟 加
output reg c_sub, //控制时钟 减
output reg a_add, //控制闹钟 加
output reg a_sub //控制闹钟 减
);
/************************************/
parameter T40MS = 20'd40_000;
parameter T1S = 30'd1_000_000;
/************************************/
/// add_in 按键 /////
reg add;
reg [19:0]cnt_a;
reg [1:0]a;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt_a <= 0;
a <= 0;
add <= 1;
end
else
begin
case(a)
0:begin
if(cnt_a < T40MS)
// 按下时间大于40MS 认为有效
begin
if(!add_in)
cnt_a = cnt_a+1'b1;
else
cnt_a = 0;
end
else //counter> 40MS ,说明确实按键是按下了
begin
add = 0;
// 给冲击信号 ,0~1 是上升沿
a = 1;
//确定按键按下,转到状态 1
cnt_a = 0; //计数器清零
end
end
1:begin
add = 1; //产生尖脉冲
if(cnt_a < T40MS)
// 按下时间大于40MS 松开有效
begin
if(add_in)
cnt_a = cnt_a+1'b1;
else
cnt_a = 0;
end
else
begin
a = 0;
// 若松开,回到状态 0 ,等待下次按键到来
cnt_a = 0;
end
end
default : a = 1;
endcase
end
end
//////////////////////////////////////////////////////////
/// sub_in 按键 ///
reg sub;
reg [19:0]cnt_s;
reg [1:0]s;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt_s <= 0;
s <= 0;
sub <= 1;
end
else
begin
case(s)
0:begin
if(cnt_s < T40MS)
// 按下时间大于40MS 认为有效
begin
if(!sub_in)
cnt_s = cnt_s+1'b1;
else
cnt_s = 0;
end
else //counter> 40MS ,说明确实按键是按下了
begin
sub = 0;
// 给冲击信号 ,0~1 是上升沿
s = 1;
//确定按键按下,转到状态 1
cnt_s = 0; //计数器清零
end
end
1:begin
sub = 1; //产生尖脉冲
if(cnt_s < T40MS)
// 按下时间大于40MS 松开有效
begin
if(sub_in)
cnt_s = cnt_s+1'b1;
else
cnt_s = 0;
end
else
begin
s = 0;
// 若松开,回到状态 0 ,等待下次按键到来
cnt_s = 0;
end
end
default : s = 1;
endcase
end
end
////////////////////////////////////////////////////////////
/// model_in 按键 ///
reg model;
reg [19:0]cnt_m;
reg [1:0]m;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt_m <= 0;
m <= 0;
model <= 1;
end
else
begin
case(m)
0:begin
if(cnt_m < T40MS)
// 按下时间大于40MS 认为有效
begin
if(!model_in)
cnt_m = cnt_m+1'b1;
else
cnt_m = 0;
end
else //counter> 40MS ,说明确实按键是按下了
begin
model = 0;
// 给冲击信号 ,0~1 是上升沿
m = 1;
//确定按键按下,转到状态 1
cnt_m = 0; //计数器清零
end
end
1:begin
model = 1; //产生尖脉冲
if(cnt_m < T40MS)
// 按下时间大于40MS 松开有效
begin
if(model_in)
cnt_m = cnt_m+1'b1;
else
cnt_m = 0;
end
else
begin
m = 0;
// 若松开,回到状态 0 ,等待下次按键到来
cnt_m = 0;
end
end
default : m = 1;
endcase
end
end
////////////////////////////////////////////////////////////
/************************************************/
reg [2:0]type;
//00:时钟正常跑模式
//01:时钟调分模式,在该模式时间计数器停止计数
//10: 时钟调时模式,在该模式时间计数器停止计数
//11:闹钟调分模式,在该模式时间计数器正常计数
//100:闹钟调时模式,在该模式时间计数器正常计数
/************************************************/
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
Display_Model <= 1'b0;
a_add <= 1'b0;
a_sub <= 1'b0;
c_add <= 1'b0;
c_sub <= 1'b0;
Time_model <= 3'b000;
type <= 3'b000;
cnt_control <= 1'b1;//启动计数
end
else
begin
if(!model)
begin
if(type == 3'b100)
type = 3'b000;
else
begin
type = type + 1'b1;
end
end
case(type)
//时钟正常开始跑
3'b000:
begin
Time_model <= 3'b000;
cnt_control <= 1'b1;//启动计数
Display_Model <= 1'b0;
a_add <= 1'b0;
a_sub <= 1'b0;
c_add <= 1'b0;
c_sub <= 1'b0;
end
//调分模式
3'b001:
begin
cnt_control <= 1'b0; //关闭计数
Time_model <= 3'b001;
Display_Model <= 1'b0;
if(!add)//加
begin
c_add <=1'b1 ;
end
else
begin
c_add <= 1'b0;
end
if(!sub)//减
begin
c_sub <= 1'b1;
end
else
begin
c_sub <= 1'b0;
end
end
//调时模式
3'b010:
begin
cnt_control <= 1'b0;//关闭计数
Time_model <= 2'b010;
Display_Model <= 1'b0;
if(!add)//加
begin
c_add <=1'b1 ;
end
else
begin
c_add <= 1'b0;
end
if(!sub)//减
begin
c_sub <= 1'b1;
end
else
begin
c_sub <= 1'b0;
end
end
//调分模式
3'b011:
begin
cnt_control <= 1'b1; //kaijishu
Time_model <= 3'b011;
Display_Model <= 1'b1;
if(!add)//加
begin
a_add <=1'b1 ;
end
else
begin
a_add <= 1'b0;
end
if(!sub)//减
begin
a_sub <= 1'b1;
end
else
begin
a_sub <= 1'b0;
end
end
//调时模式
3'b100:
begin
cnt_control <= 1'b1;//关闭计数
Time_model <= 3'b100;
Display_Model <= 1'b1;
if(!add)//加
begin
a_add <=1'b1 ;
end
else
begin
a_add <= 1'b0;
end
if(!sub)//减
begin
a_sub <= 1'b1;
end
else
begin
a_sub <= 1'b0;
end
end
default:type <= 3'b000;
endcase
end
endmodule