6、按键可校准数码管时钟
0赞
发表于 7/9/2016 10:23:42 AM
阅读(2321)
/*实验有六个数码管,四个按键(一个系统复位按键、一个暂停/启动按键、一个时钟校准时的位选按键,一个对数码管上具体数字调节的按键)。
要求没有按键按下时,六个数码管按照24小时制进行计数。一旦暂停/启动按键按下,系统自动进入默认的“秒”数码管的个位调节,随后可以通过
位选按键选择你所需要校准的那个数码管*/
module seg8_stopwatch(clk, rst_n, En, seg, sel, key0, key1, key2);
input clk; //50MHz
input rst_n;
input En; //使能信号
input key0;
input key1;
input key2;
output [5:0]sel; //6位
output reg[7:0]seg; //8段
reg [14:0]cnt1; //为了得到数码管扫描始终clk_1k
reg [25:0]cnt2; //为了得到计数1s(即second)
reg clk_1k;
reg second;
reg [7:0]disp_data_m; //定义2个数码管,每四位对应一个“秒”数码管上要显示的数字,[3:0]低位,[7:4]高位
reg [7:0]disp_data_f; //定义2个数码管,每四位对应一个“分”数码管上要显示的数字,[3:0]低位,[7:4]高位
reg [7:0]disp_data_s; //定义2个数码管,每四位对应一个“时”数码管上要显示的数字,[3:0]低位,[7:4]高位
reg [5:0]sel_r; //配合En使用,可以选择全部关闭数码管
reg [3:0]data_tmp; //暂时寄存软件工具输入的数据disp_data
reg cn; //“秒”计数到59之后,需要进1给“分”,cn一个时钟高电平
reg cm; //“分”计数到59之后,需要进1给“时”,cm一个时钟高电平
reg key0_en; //系统时钟暂停/启动用的使能信号
reg [2:0]disp_drive; //时钟校准时用于位选
wire key0_nedge; //key0的下降沿
wire key1_nedge; //key1的下降沿
wire key2_nedge; //key2的下降沿
wire cn_pedge;
wire cm_pedge;
//--------------------数码管扫描时钟---------------------
//计数器计数
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt1 <= 15'd0;
else if(!En)
cnt1 <= 15'd0;
else if(cnt1 == 15'd24_999)
cnt1 <= 15'd0;
else
cnt1 <= cnt1 + 1'b1;
//分频计数后产生clk_1k,1ms数码管扫描时钟
always@(posedge clk or negedge rst_n)
if(!rst_n)
clk_1k <= 1'b0;
else if(cnt1 == 15'd24_999)
clk_1k <= ~clk_1k;
else
clk_1k <= clk_1k;
//-------------------秒钟计数,得到1s------------------------
//分频计数,计数1s
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt2 <= 26'd0;
else if(cnt2 == 26'd49_999_999)
//else if(cnt2 == 26'd49_999)
cnt2 <= 26'd0;
else if(key0_en == 1)//计时暂停
cnt2 <= cnt2;
else
cnt2 <= cnt2 + 1'b1;
always@(posedge clk or negedge rst_n)
if(!rst_n)
second <= 1'b0;
else if(cnt2 == 26'd49_999_999)
//else if(cnt2 == 26'd49_999)
second <= 1'b1;
else
second <= 1'b0;
//--------------------显示处理,位选和段选--------------------------
//6个数码管依次从左往右点亮,低电平点亮
always@(posedge clk_1k or negedge rst_n)
if(!rst_n)
sel_r <= 6'b11_1110;
else if(sel_r == 6'b01_1111)
sel_r <= 6'b11_1110;
else
sel_r <= {sel_r[4:0],sel_r[5]}; //右移,注意这里用"<<"以为要注意补位的情况
//位选,当sel_r等于对应的值时点亮对应的数码管,数码管显示软件工具输入的值,低电平点亮
always@(*)
case(sel_r)
6'b11_1110: data_tmp = disp_data_m[3:0];//时
6'b11_1101: data_tmp = disp_data_m[7:4];//时
6'b11_1011: data_tmp = disp_data_f[3:0];//分
6'b11_0111: data_tmp = disp_data_f[7:4];//分
6'b10_1111: data_tmp = disp_data_s[3:0];//秒
6'b01_1111: data_tmp = disp_data_s[7:4];//秒
default:data_tmp = 4'b0000;
endcase
//段选,显示0-f在数码管上,板子上的是共阳数码管,低电平点亮
always@(*)
case(data_tmp)
4'h0: seg = 8'b1100_0000;
4'h1: seg = 8'b1111_1001;
4'h2: seg = 8'b1010_0100;
4'h3: seg = 8'b1011_0000;
4'h4: seg = 8'b1001_1001;
4'h5: seg = 8'b1001_0010;
4'h6: seg = 8'b1000_0010;
4'h7: seg = 8'b1111_1000;
4'h8: seg = 8'b1000_0000;
4'h9: seg = 8'b1001_0000;
default: seg = 8'b1100_0000;
endcase
//-----------------------------计数处理--------------------------------
//“秒”的处理
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
disp_data_m[3:0] <= 4'd0;
disp_data_m[7:4] <= 4'd0;
cn <= 0;
end
else if(key0_en == 1 && disp_drive == 3'b000) begin //秒个位调节
if(key2_nedge == 1) begin
if(disp_data_m[3:0] == 4'd9)
disp_data_m[3:0] <= 4'd0;
else
disp_data_m[3:0] <= disp_data_m[3:0] + 1'b1;
end
end
else if(key0_en == 1 && disp_drive == 3'b001) begin //秒十位调节
if(key2_nedge == 1) begin
if(disp_data_m[7:4] == 4'd5)
disp_data_m[7:4] <= 4'd0;
else
disp_data_m[7:4] <= disp_data_m[7:4] + 1'b1;
end
end
else if(key0_en == 0 && second == 1)begin //暂停/启动按键没有按下时的正常计数
cn <= 0;
disp_data_m[3:0] <= disp_data_m[3:0] + 1'b1;
if(disp_data_m[3:0] == 4'd9) begin
disp_data_m[3:0] <= 4'd0;
disp_data_m[7:4] <= disp_data_m[7:4] + 1'b1;
if(disp_data_m[7:4] == 4'd5) begin
disp_data_m[7:4] <= 4'd0;
cn <= 1;
end
end
end
//“分”的处理,与“秒的处理”基本一样
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
disp_data_f[3:0] <= 4'd0;
disp_data_f[7:4] <= 4'd0;
cm <= 0;
end
else if(key0_en == 1 && disp_drive == 3'b010) begin //分个位调节
if(key2_nedge == 1) begin
if(disp_data_f[3:0] == 4'd9)
disp_data_f[3:0] <= 4'd0;
else
disp_data_f[3:0] <= disp_data_f[3:0] + 1'b1;
end
end
else if(key0_en == 1 && disp_drive == 3'b011) begin //分十位调节
if(key2_nedge == 1) begin
if(disp_data_f[7:4] == 4'd5)
disp_data_f[7:4] <= 4'd0;
else
disp_data_f[7:4] <= disp_data_f[7:4] + 1'b1;
end
end
else if(key0_en == 0 && cn_pedge == 1) begin //暂停/启动按键没有按下时的正常计数
cm <= 0;
disp_data_f[3:0] <= disp_data_f[3:0] + 1'b1;
if(disp_data_f[3:0] == 4'd9) begin
disp_data_f[3:0] <= 4'd0;
disp_data_f[7:4] <= disp_data_f[7:4] + 1'b1;
if(disp_data_f[7:4] == 4'd5) begin
disp_data_f[7:4] <= 4'd0;
cm <= 1;
end
end
end
//“时”的处理
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
disp_data_s[3:0] <= 4'd0;
disp_data_s[7:4] <= 4'd0;
end
else if(key0_en == 1 && disp_drive == 3'b100) begin //时个位调节
if(key2_nedge == 1) begin
if(disp_data_s[3:0] == 4'd9)
disp_data_s[3:0] <= 4'd0;
else
disp_data_s[3:0] <= disp_data_s[3:0] + 1'b1;
end
end
else if(key0_en == 1 && disp_drive == 3'b101) begin //时十位调节
if(key2_nedge == 1) begin
if(disp_data_s[7:4] == 4'd2)
disp_data_s[7:4] <= 4'd0;
else
disp_data_s[7:4] <= disp_data_s[7:4] + 1'b1;
end
end
else if(key0_en == 0 && disp_data_s[7:4] == 4'd2 && disp_data_s[3:0] >= 4'd3) begin //暂停/启动按键没有按下时的正常计数
disp_data_s[3:0] <= 4'd0;
disp_data_s[7:4] <= 4'd0;
end
else if(key0_en ==0 && cm_pedge == 1)begin //暂停/启动按键没有按下时的正常计数
disp_data_s[3:0] <= disp_data_s[3:0] + 1'b1;
if(disp_data_s[3:0] == 4'd9) begin
disp_data_s[3:0] <= 4'd0;
disp_data_s[7:4] <= disp_data_s[7:4] + 1'b1;
end
end
assign sel = (En)?sel_r:6'b11_1111;
//----------------------按键处理---------------------------
//key0是暂停/启动按键,每次系统复位或者断电重启后的第一按下没用,从第二次之后的按键都是正常的
reg key0_clk;
//reg key0_en;这里需要放到最开始的位置,否则MODELSIM会报错
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
key0_clk <= 0;
key0_en <= 0;
end
else if(key0_nedge) begin
key0_clk <= ~key0_clk;
if(key0_clk == 1)
key0_en <= 1;
else
key0_en <= 0;
end
//key1是设置按键
//reg [2:0]disp_drive;这里需要放到最开始的位置,否则MODELSIM会报错
always@(posedge clk or negedge rst_n)
if(!rst_n)
disp_drive <= 3'b000;
else begin
if(key0_en == 1 && key1_nedge == 1)
disp_drive <= disp_drive + 1'b1;
else if(disp_drive == 3'b110)
disp_drive <= 3'b000;
end
//key2是调节按键
//-----------------key0/key1/key2的边沿检测----------------------
//key0按键的边沿检测
reg key0_sa, key0_sb;
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
key0_sa <= 1'b0;
key0_sb <= 1'b0;
end
else begin
key0_sa <= key0;
key0_sb <= key0_sa;
end
reg key0_tmpa, key0_tmpb;
//wire key0_nedge;这里需要放到最开始的位置,否则MODELSIM会报错
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
key0_tmpa <= 1'b0;
key0_tmpb <= 1'b0;
end
else begin
key0_tmpa <= key0_sb;
key0_tmpb <= key0_tmpa;
end
assign key0_nedge = key0_tmpb & ~key0_tmpa;
//key1按键的边沿检测
reg key1_sa, key1_sb;
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
key1_sa <= 1'b0;
key1_sb <= 1'b0;
end
else begin
key1_sa <= key1;
key1_sb <= key1_sa;
end
reg key1_tmpa, key1_tmpb;
//wire key1_nedge;这里需要放到最开始的位置,否则MODELSIM会报错
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
key1_tmpa <= 1'b0;
key1_tmpb <= 1'b0;
end
else begin
key1_tmpa <= key1_sb;
key1_tmpb <= key1_tmpa;
end
assign key1_nedge = key1_tmpb & ~key1_tmpa;
//key2按键的边沿检测
reg key2_sa, key2_sb;
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
key2_sa <= 1'b0;
key2_sb <= 1'b0;
end
else begin
key2_sa <= key2;
key2_sb <= key2_sa;
end
reg key2_tmpa, key2_tmpb;
//wire key2_nedge;这里需要放到最开始的位置,否则MODELSIM会报错
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
key2_tmpa <= 1'b0;
key2_tmpb <= 1'b0;
end
else begin
key2_tmpa <= key2_sb;
key2_tmpb <= key2_tmpa;
end
assign key2_nedge = key2_tmpb & ~key2_tmpa;
//cn的边沿检测
reg cn_sa, cn_sb;
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
cn_sa <= 1'b0;
cn_sb <= 1'b0;
end
else begin
cn_sa <= cn;
cn_sb <= cn_sa;
end
reg cn_tmpa, cn_tmpb;
//wire cn_pedge;这里需要放到最开始的位置,否则MODELSIM会报错
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
cn_tmpa <= 1'b0;
cn_tmpb <= 1'b0;
end
else begin
cn_tmpa <= cn_sb;
cn_tmpb <= cn_tmpa;
end
assign cn_pedge = ~cn_tmpb & cn_tmpa;
//cm的边沿检测
reg cm_sa, cm_sb;
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
cm_sa <= 1'b0;
cm_sb <= 1'b0;
end
else begin
cm_sa <= cm;
cm_sb <= cm_sa;
end
reg cm_tmpa, cm_tmpb;
//wire cm_pedge;这里需要放到最开始的位置,否则MODELSIM会报错
always@(posedge clk or negedge rst_n)
if(!rst_n) begin
cm_tmpa <= 1'b0;
cm_tmpb <= 1'b0;
end
else begin
cm_tmpa <= cm_sb;
cm_tmpb <= cm_tmpa;
end
assign cm_pedge = ~cm_tmpb & cm_tmpa;
endmodule按键消抖模块(这块直接用的小梅哥的)

module key_filter(clk, rst_n, key_in, key_flag, key_state); input clk; input rst_n; input key_in; output reg key_flag; //每次按键从高电平到低电平或者从低电平到高电平发生,key_flag输出一个高电平 output reg key_state; //从工程附带的图片中可以看到,key_state可以看作是一个没有抖动的按键 localparam IDLE =4'b0001, //按键没有按下的状态,初始或者结束 FILTER0 =4'b0010, //按键按下过程中的抖动部分 DOWN =4'b0100, //按键处于低电平时间段 FILTER1 =4'b1000; //按键释放过程中的抖动部分 reg [19:0]cnt; reg [3:0]state; reg en_cnt; //使能计数寄存器,用于抖动部分计数的开关 reg cnt_full; //抖动部分计数满标志信号 //---------------------边沿检测------------------------------ //对外部输入的异步信号进行同步处理 reg key_in_sa; reg key_in_sb; always@(posedge clk or negedge rst_n) if(!rst_n) begin key_in_sa <= 1'b0; key_in_sb <= 1'b0; end else begin key_in_sa <= key_in; key_in_sb <= key_in_sa; end //使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态 reg key_tmpa; reg key_tmpb; wire pedge, nedge; always@(posedge clk or negedge rst_n) if(!rst_n) begin key_tmpa <= 1'b0; key_tmpb <= 1'b0; end else begin key_tmpa <= key_in_sb; key_tmpb <= key_tmpa; end assign nedge = key_tmpb & ~key_tmpa; assign pedge = ~key_tmpb & key_tmpa; //------------------一段式状态机-------------------------- // always@(posedge clk or negedge rst_n) if(!rst_n) begin state <= IDLE; en_cnt <= 1'b0; key_flag <= 1'b0; key_state <= 1'b1; end else begin case(state) IDLE: begin key_flag <= 1'b0; if(nedge) begin state <= FILTER0; en_cnt <= 1'b1;//检测到抖动,开启计数 end else state <= IDLE; end FILTER0: if(cnt_full) begin state <= DOWN; en_cnt <= 1'b0; key_flag <= 1'b1; key_state <= 1'b0; end else if(pedge) begin//如果检测到上升沿,说明这是一个抖动 state <= IDLE; en_cnt <= 1'b0; end else state <= FILTER0; DOWN: begin key_flag <= 1'b0; if(pedge) begin state <= FILTER1; en_cnt <= 1'b1; end else state <= DOWN; end FILTER1: if(cnt_full) begin state <= IDLE; key_flag <= 1'b1; key_state <= 1'b1; end else if(nedge) begin en_cnt <= 1'b0; state <= DOWN; end else state <= FILTER1; default: begin state <= IDLE; en_cnt <= 1'b0; key_flag <= 1'b0; key_state <= 1'b1; end endcase end //en_cnt开关计数器 always@(posedge clk or negedge rst_n) if(!rst_n) cnt <= 20'd0; else if(en_cnt) cnt <= cnt + 1'b1; else cnt <= 20'd0; //计数满20ms,输出一个计数满标志信号cnt_full always@(posedge clk or negedge rst_n) if(!rst_n) cnt_full <= 1'b0; else if(cnt == 20'd999_999) cnt_full <= 1'b1; else cnt_full <= 1'b0; endmodule
顶层模块
/*这里先写好按键消抖部分key_filter,然后将三个输入按键例化消抖后,得到消 除抖动后的三个分别对应的按键key_state0、key_state1、key_state2,连接回 seg8_stopwatch的三个输入按键key0、key1、key2进行按键调节部分编写*/ module seg8_stopwatch_top(clk, rst_n, seg, sel, key0, key1, key2); input clk; //50MHz input rst_n; input key0; input key1; input key2; output [5:0]sel; //6位 output [7:0]seg; //8段 wire key_flag0, key_state0; wire key_flag1, key_state1; wire key_flag2, key_state2; key_filter key_filter0( .clk(clk), .rst_n(rst_n), .key_in(key0), .key_flag(key_flag0), .key_state(key_state0) ); key_filter key_filter1( .clk(clk), .rst_n(rst_n), .key_in(key1), .key_flag(key_flag1), .key_state(key_state1) ); key_filter key_filter2( .clk(clk), .rst_n(rst_n), .key_in(key2), .key_flag(key_flag2), .key_state(key_state2) ); seg8_stopwatch seg8_stopwatch( .clk(clk), .rst_n(rst_n), .key0(key_state0), .key1(key_state1), .key2(key_state2), .En(1'b1), .seg(seg), .sel(sel) ); endmodule
