功能如下:
1. 6个数码管显示 时、分、秒。
2. 按键控制计时的暂停与启动。
设计思路如下:
1.独立按键的分析与编程思路。
如图所示,由于机械的固有震动属性,在按键按下来的过程中以及松开按键的过程中都会产生震动,因此代码设计务必进行相关的处理。
已知FPGA连接按键的引脚,在按键未按下的时候被拉高。
key_en==1 表示按一下按键,下面给出这个信号的设计思路。
当按键被按下,检测到按键低电平的时间不低于20ms,为有效,低于20ms的低电平信号被认为是毛刺。
key_en 这个信号,在稳定期产生。且应该是只产生一拍,也就是当按键按下,FPGA检测到按下的有效性后key_en由低电平变高电平,松开就变成低电平。代码里处理成检测到按下的有效性后,只维持一个时钟周期高电平,这样是最合理的。
如何确定稳定期? 低电平持续20ms 如何知道持续20ms? 用计数器count_20ms 低电平计数,高电平清零,计到20ms时保持。
哪个时刻产生?开始、中间、结尾?
开始,前一时刻不够20ms,后一时刻够20ms 中间,中间不能确定 结尾,前一时钟20ms,后一时钟key高电平
flag_20ms=1 表示计数到20ms flag_20ms_ff0 表示前一时钟指示
如果 key_an==1 产生时刻在开始
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_20ms<=15'd0;
end
else if(key)begin
count_20ms<=15'd0;
end
else if(count_20ms>=19999&&count_1us==TIMES_1us)begin //这个应该为
count_20ms<=count_20ms;
end
else if(count_1us==TIMES_1us&&count_20ms<19999)begin
count_20ms<=count_20ms+1'b1;
end
end
always @(*) begin
if(count_20ms>=19999)
flag_20ms<=1'b1;
else
flag_20ms<=1'b0;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_20ms_ff0<=1'b0;
end
else
flag_20ms_ff0 <= flag_20ms;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_en<=1'b0;
end
else if(flag_20ms&&flag_20ms_ff0==1'b0)
key_en <= 1'b1;
else
key_en <= 1'b0;
end
如果key_an==1产生在结尾
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_en<=1'b0;
end
else if(flag_20ms==1'b0&&flag_20ms_ff0)
key_en <= 1'b1;
else
key_en <= 1'b0;
end
2.计时部分设计思路:
时 24进制计数器 分 60进制计数器 秒 60进制计数器
1. 设计1s计数器 parameter TIMES_1s=4999_9999 26位寄存器 count_1s ,这个计时器时最基础的
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_1s<=26'd0 ;
end
else if(count_1s==TIMES_1S)
count_1s<=26'd0 ;
else if(pause==1'b1)
count_1s<=count_1s+1'b1;
else
count_1s<=count_1s ;
end
2. 设计秒计数器,即60进制计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_60s<=6'd0;
end
else if(count_60s==59&&count_1s==TIMES_1S)
count_60s<=6'd0;
else if(count_1s==TIMES_1S)
count_60s<=count_60s+1'b1;
end
3. 设计分计数器,即60进制计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_60m<=6'd0;
end
else if(count_60m==59&&count_60s==59&&count_1s==TIMES_1S)
count_60m<=6'd0;
else if(count_60s==59&&count_1s==TIMES_1S)
count_60m<=count_60m+1'b1;
end
4. 设计时计数器,即24进制计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_24h<=5'd0;
end
else if(count_60m==59&&count_60s==59&&count_1s==TIMES_1S)begin
if(count_24h==23)
count_24h<=5'd0;
else
count_24h<=count_24h+1'b1;
end
end
3. 如何暂停计时器与启动计时器
设计一个1位 暂停与启动信号 pause 当pause==1时启动计时 当pause==0时暂停计时功能。
用这个信号控制1s计时器就行了,因为秒,时,分计时都与这个1s计时器有关。
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_1s<=26'd0 ;
end
else if(count_1s==TIMES_1S)
count_1s<=26'd0 ;
else if(pause==1'b1)
count_1s<=count_1s+1'b1;
else
count_1s<=count_1s ;
end
pause信号的产生:
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
pause<=1'b1;
end
else if(pause==1'b1&&key_en)
pause <= 1'b0;
else if(pause==1'b0&&key_en)
pause <= 1'b1;
end
完毕 完整代码如下所示:
module miaobiao(
clk ,
rst_n ,
segment ,
seg_sel ,
key
);
//can shu
parameter DATA_W = 8;
parameter TIMES_1S = 4999_999;
parameter TIMES_1us= 49 ;
parameter _0 = 8'b1100_0000, _1 = 8'b1111_1001, _2 = 8'b1010_0100,
_3 = 8'b1011_0000, _4 = 8'b1001_1001, _5 = 8'b1001_0010,
_6 = 8'b1000_0010, _7 = 8'b1111_1000, _8 = 8'b1000_0000,
_9 = 8'b1001_0000;
//输入信号定义
input key ;
input clk ;
input rst_n ;
output[DATA_W-1:0] segment ;
output[DATA_W-3:0] seg_sel ;
reg [DATA_W-1:0] segment ;
reg [DATA_W-3:0] seg_sel ;
//一些中间变量
reg [22:0] count_1s ;
reg [5:0] count_1us ;
reg [6:0] count_120us ;
//miao
reg [5:0] count_60s ;
//fen
reg [5:0] count_60m ;
//shi
reg [4:0] count_24h ;
//20ms 定时器
reg [14:0] count_20ms ;
//按键 打两拍
reg key_reg1 ;
reg key_reg2 ;
reg key_flag ;
reg[DATA_W-1:0] s_ge ;
reg[DATA_W-1:0] s_shi ;
reg[DATA_W-1:0] m_ge ;
reg[DATA_W-1:0] m_shi ;
reg[DATA_W-1:0] h_ge ;
reg[DATA_W-1:0] h_shi ;
//停止与启动信号 按键
reg pause ;
reg flag_20ms ;
reg key_en ;
reg flag_20ms_ff0 ;
/********************************************************************************************/
//1s timer
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_1s<=23'd0 ;
end
else if(count_1s==TIMES_1S)
count_1s<=23'd0 ;
else if(pause==1'b1)
count_1s<=count_1s+1'b1;
else
count_1s<=count_1s ;
end
/********************************************************************************************/
//逐一设计每个输出信号
//列扫描 刷新时间为20us 总共用了6个数码管,设计一个计时器 6*20us=120us
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_1us<=6'd0;
end
else if(count_1us==TIMES_1us)
count_1us<=6'd0;
else
count_1us<=count_1us+1'b1;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_120us<=7'd0;
end
else if(count_120us==119&&count_1us==TIMES_1us)
count_120us<=7'd0;
else if(count_1us==TIMES_1us)
count_120us<=count_120us+1'b1;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
seg_sel<=6'b111_110 ;
end
else if(count_120us==0)
seg_sel<=6'b111_110 ;
else if(count_120us==19&&count_1us==TIMES_1us)
seg_sel<=6'b111_101 ;
else if(count_120us==39&&count_1us==TIMES_1us)
seg_sel<=6'b111_011 ;
else if(count_120us==59&&count_1us==TIMES_1us)
seg_sel<=6'b110_111 ;
else if(count_120us==79&&count_1us==TIMES_1us)
seg_sel<=6'b101_111 ;
else if(count_120us==99&&count_1us==TIMES_1us)
seg_sel<=6'b011_111 ;
end
/********************************************************************************************/
//shi 设计一个24进制计数器
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_24h<=5'd0;
end
else if(count_60m==59&&count_60s==59&&count_1s==TIMES_1S)begin
if(count_24h==23)
count_24h<=5'd0;
else
count_24h<=count_24h+1'b1;
end
end
//shi_ge
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
h_ge<=4'd0;
end
else if(seg_sel==6'b101_111)begin
case (count_24h%10)
0:
h_ge<=_0;
1:
h_ge<=_1;
2:
h_ge<=_2;
3:
h_ge<=_3;
4:
h_ge<=_4;
5:
h_ge<=_5;
6:
h_ge<=_6;
7:
h_ge<=_7;
8:
h_ge<=_8;
9:
h_ge<=_9;
endcase
end
end
//shi_shi
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
h_shi<=4'd0;
end
else if(seg_sel==6'b011_111)begin
case (count_24h/10)
0:
h_shi<=_0;
1:
h_shi<=_1;
2:
h_shi<=_2;
3:
h_shi<=_3;
4:
h_shi<=_4;
5:
h_shi<=_5;
6:
h_shi<=_6;
7:
h_shi<=_7;
8:
h_shi<=_8;
9:
h_shi<=_9;
endcase
end
end
/********************************************************************************************/
//fen 设计一个60进制计数器 count_60m
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_60m<=6'd0;
end
else if(count_60m==59&&count_60s==59&&count_1s==TIMES_1S)
count_60m<=6'd0;
else if(count_60s==59&&count_1s==TIMES_1S)
count_60m<=count_60m+1'b1;
end
//fen_ge
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
m_ge<=4'd0;
end
else if(seg_sel==6'b111_011)begin
case (count_60m%10)
0:
m_ge<=_0;
1:
m_ge<=_1;
2:
m_ge<=_2;
3:
m_ge<=_3;
4:
m_ge<=_4;
5:
m_ge<=_5;
6:
m_ge<=_6;
7:
m_ge<=_7;
8:
m_ge<=_8;
9:
m_ge<=_9;
endcase
end
end
//fen_shi
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
m_shi<=4'd0;
end
else if(seg_sel==6'b110_111)begin
case (count_60m/10)
0:
m_shi<=_0;
1:
m_shi<=_1;
2:
m_shi<=_2;
3:
m_shi<=_3;
4:
m_shi<=_4;
5:
m_shi<=_5;
6:
m_shi<=_6;
7:
m_shi<=_7;
8:
m_shi<=_8;
9:
m_shi<=_9;
endcase
end
end
/********************************************************************************************/
//miao 设计一个60计数器 count_60s
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_60s<=6'd0;
end
else if(count_60s==59&&count_1s==TIMES_1S)
count_60s<=6'd0;
else if(count_1s==TIMES_1S)
count_60s<=count_60s+1'b1;
end
//miao_ge
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
s_ge<=4'd0;
end
else if(seg_sel==6'b111_110)begin
case (count_60s%10)
0:
s_ge<=_0;
1:
s_ge<=_1;
2:
s_ge<=_2;
3:
s_ge<=_3;
4:
s_ge<=_4;
5:
s_ge<=_5;
6:
s_ge<=_6;
7:
s_ge<=_7;
8:
s_ge<=_8;
9:
s_ge<=_9;
endcase
end
end
//miaoshi
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
s_shi<=4'd0;
end
else if(seg_sel==6'b111_101)begin
case (count_60s/10)
0:
s_shi<=_0;
1:
s_shi<=_1;
2:
s_shi<=_2;
3:
s_shi<=_3;
4:
s_shi<=_4;
5:
s_shi<=_5;
6:
s_shi<=_6;
7:
s_shi<=_7;
8:
s_shi<=_8;
9:
s_shi<=_9;
endcase
end
end
/********************************************************************************************/
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_reg1<=1'b1;
key_reg2<=1'b1;
end
else begin
key_reg1<=key ;
key_reg2<=key_reg1 ;
end
end
//按键输入
//20ms 定时器
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
count_20ms<=15'd0;
end
else if(key_reg2)begin
count_20ms<=15'd0;
end
else if(count_20ms==19999&&count_1us==TIMES_1us)begin
count_20ms<=count_20ms;
end
else if(count_1us==TIMES_1us)begin
count_20ms<=count_20ms+1'b1;
end
end
always @(*) begin
if(count_20ms>=19999)
flag_20ms<=1'b1;
else
flag_20ms<=1'b0;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_20ms_ff0<=1'b0;
end
else
flag_20ms_ff0 <= flag_20ms;
end
// always @(posedge clk or negedge rst_n)begin
// if(rst_n==1'b0)begin
// key_en<=1'b0;
// end
// else if(flag_20ms&&flag_20ms_ff0==1'b0)
// key_en <= 1'b1;
// else
// key_en <= 1'b0;
// end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_en<=1'b0;
end
else if(flag_20ms==1'b0&&flag_20ms_ff0==1)
key_en <= 1'b1;
else
key_en <= 1'b0;
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
pause<=1'b1;
end
else if(pause==1'b1&&key_en)
pause <= 1'b0;
else if(pause==1'b0&&key_en)
pause <= 1'b1;
end
/********************************************************************************************/
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment<=8'd0;
end
else begin
case(seg_sel)
6'b111_110:
segment<=s_ge;
6'b111_101:
segment<=s_shi;
6'b111_011:
segment<=m_ge;
6'b110_111:
segment<=m_shi;
6'b101_111:
segment<=h_ge;
6'b011_111:
segment<=h_shi;
default:
segment<=segment;
endcase
end
end
endmodule