FPGA学习之 状态机实现数码管的数字时钟
Posted 满足没有
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FPGA学习之 状态机实现数码管的数字时钟相关的知识,希望对你有一定的参考价值。
FPGA学习之 状态机实现数字时钟
开发板型号:EP4CE6F17C8
六位数码管原理图:
由图可知,数码管段选和片选均为低电平有效。
由于人眼的视觉残留,我们控制一定频率对每一位数码管进行刷新,就能实现显示6位数字时钟。
项目功能描述:
1.实现数字时钟自动计时,设置6个计数器,分别计数时分秒的十位和个位
2.时间设置功能
3.闹钟设置
4.按键实现模式切换(主要是 初始计时、时间设置、闹钟设置)
5.蜂鸣器发声(设置的闹钟时间到来时,蜂鸣器按一定频率播放”两只老虎“)
代码:
顶层模块:
module top(
input clk ,
input rst_n ,
input [2:0] key_in ,
output [5:0] sel ,
output [7:0] dig ,
output beep
);
wire [23:0] time_data;//显示数据
wire [2:0] key_out;//按键消抖输出
wire alarm_clk_flag;//闹钟响标志
wire [5:0] shan_shuo;
//数字时钟显示驱动
seg_driver u_seg_driver(
.clk (clk ),
.rst_n (rst_n ),
.din (time_data ),
.sel (sel ),
.dig (dig ),
.shan_shuo(shan_shuo)
);
//按键消抖
fsm_key_debounce # (.KEY_W(3)) u_fsm_key_debounce(
.clk (clk ),
.rst_n (rst_n ),
.key_in (key_in ),
.key_out (key_out )
);
//控制模块
control u_control(
.clk (clk ),
.rst_n (rst_n ),
.key (key_out ),
.time_data (time_data ),
.alarm_clk_flag (alarm_clk_flag ),
.shan_shuo (shan_shuo)
);
//蜂鸣器
beep u_beep(
.clk (clk ),
.rst_n (rst_n ),
.alarm_clk_flag (alarm_clk_flag ),
.beep (beep )
);
endmodule
按键消抖模块
module fsm_key_debounce # (parameter KEY_W = 3,TIME_20MS = 1_000_000)(
input clk ,
input rst_n ,
input [KEY_W - 1:0] key_in ,
output [KEY_W - 1:0] key_out
);
//参数定义
localparam IDLE = 4'b0001;//初始状态
localparam DOWN = 4'b0010;//按键按下抖动
localparam HOLD = 4'b0100;//按键按下后稳定
localparam UP = 4'b1000;//按键上升抖动
//信号定义
reg [3:0] state_c;//现态
reg [3:0] state_n;//次态
//状态转移条件定义
wire idle2down;
wire down2idle;
wire down2hold;
wire hold2up ;
wire up2idle ;
reg [KEY_W - 1:0] key_r0;//同步
reg [KEY_W - 1:0] key_r1;//打拍
wire [KEY_W - 1:0] nedge;//下降沿
wire [KEY_W - 1:0] pedge;//上升沿
//20ms计数器
reg [19:0] cnt_20ms;
wire add_cnt_20ms;
wire end_cnt_20ms;
reg [KEY_W - 1:0] key_out_r;//输出寄存
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always@(*)begin
case(state_c)
IDLE:begin
if(idle2down)begin
state_n = DOWN;
end
else begin
state_n = state_c;
end
end
DOWN:begin
if(down2idle)begin
state_n = IDLE;
end
else if(down2hold)begin
state_n = HOLD;
end
else begin
state_n = state_c;
end
end
HOLD:begin
if(hold2up)begin
state_n = UP;
end
else begin
state_n = state_c;
end
end
UP:begin
if(up2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default:state_n = state_c;
endcase
end
assign idle2down = (state_c == IDLE) && nedge;//检测到下降沿
assign down2idle = (state_c == DOWN) && (pedge&& end_cnt_20ms);//计时未到20ms时且出现上升沿表示按键意外抖动,回到初始态
assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);//计时到20ms时没有出现上升沿标志按键按下后保持稳定
assign hold2up = (state_c == HOLD) && (pedge);//检测到上升沿跳转到上升态
assign up2idle = (state_c == UP) && end_cnt_20ms;//计数器计数到20ms跳转到初始态
//20ms计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20ms <= 0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= 0;
end
else begin
cnt_20ms <= cnt_20ms + 1'b1;
end
end
end
assign add_cnt_20ms = state_c == DOWN || state_c == UP;//当按键按下或上弹时开始计数
assign end_cnt_20ms = add_cnt_20ms && ((cnt_20ms == TIME_20MS - 1) || pedge);//当计数到最大值或检测到上升沿计数器清零
//同步打拍
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= {KEY_W{1'b1}};
key_r1 <= {KEY_W{1'b1}};
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
end
end
assign nedge = ~key_r0 & key_r1;//检测下降沿
assign pedge = key_r0 & ~key_r1;//检测上升沿
//按键赋值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_out_r <= {KEY_W{1'b0}};
end
else if(state_c == HOLD && hold2up)begin
key_out_r <= ~key_r1;
end
else begin
key_out_r <= {KEY_W{1'b0}};
end
end
assign key_out = key_out_r;
endmodule
蜂鸣器模块
module beep(
input clk ,
input rst_n ,
input alarm_clk_flag ,
output beep
);
parameter M1 = 17'd95600;
parameter M2 = 17'd85150;
parameter M3 = 17'd75850;
parameter M4 = 17'd71600;
parameter M5 = 17'd63750;
parameter M6 = 17'd56800;
parameter M7 = 17'd50600;
reg beep_r;
reg alarm_clk_flag_r;
reg [16:0] cnt0;//音符周期计数器
wire add_cnt0;
wire end_cnt0;
reg [8:0] cnt1;//音符重复次数计数器
wire add_cnt1;
wire end_cnt1;
reg [4:0] cnt2;//音符总次数
wire add_cnt2;
wire end_cnt2;
reg [1:0] cnt3;//音乐时间计数器 7次完结
wire add_cnt3;
wire end_cnt3;
reg [16:0] preset_note;//预设音符周期数
wire [16:0] preset_duty;//占空比
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
alarm_clk_flag_r <= 1'b0;
end
else if(alarm_clk_flag)begin
alarm_clk_flag_r <= 1'b1;
end
else if(cnt3 == 1)begin
alarm_clk_flag_r <= 1'b0;
end
end
//音符周期计数
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 17'b0;
end
else if(add_cnt0)begin
if(end_cnt0)begin
cnt0 <= 17'b0;
end
else begin
cnt0 <= cnt0 +1'b1;
end
end
end
assign add_cnt0 = 1'b1;
assign end_cnt0 = add_cnt0 && (cnt0 == preset_note - 1);
//音符重复次数
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 9'b0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt1 <= 9'b0;
end
else begin
cnt1 <= cnt1 +1'b1;
end
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && (cnt1 == 299);
//音符总次数
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 5'b0;
end
else if(add_cnt2)begin
if(end_cnt2)begin
cnt2 <= 5'b0;
end
else begin
cnt2 <= cnt2 +1'b1;
end
end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && (cnt2 == 31);
//音乐时间计数器 响3次
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt3 <= 0;
end
else if(add_cnt3)begin
if(end_cnt3)begin
cnt3 <= 0;
end
else begin
cnt3 <= cnt3 + 1;
end
end
end
assign add_cnt3 = end_cnt2;
assign end_cnt3 = add_cnt3 && (cnt3 == 4 - 1);
//给音符周期赋值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
preset_note <= 17'b0;
end
else begin
case(cnt2)
6'd0 : preset_note <= M1;
6'd1 : preset_note <= M2;
6'd2 : preset_note <= M3;
6'd3 : preset_note <= M1;
6'd4 : preset_note <= M1;
6'd5 : preset_note <= M2;
6'd6 : preset_note <= M3;
6'd7 : preset_note <= M1;
6'd8 : preset_note <= M3;
6'd9 : preset_note <= M4;
6'd10 : preset_note <= M5;
6'd11 : preset_note <= M3;
6'd12 : preset_note <= M4;
6'd13 : preset_note <= M5;
6'd14 : preset_note <= M5;
6'd15 : preset_note <= M6;
6'd16 : preset_note <= M5;
6'd17 : preset_note <= M4;
6'd18 : preset_note <= M3;
6'd19 : preset_note <= M1;
6'd20 : preset_note <= M5;
6'd21 : preset_note <= M6;
6'd22 : preset_note <= M5;
6'd23 : preset_note <= M4;
6'd24 : preset_note <= M3;
6'd25 : preset_note <= M1;
6'd26 : preset_note <= M2;
6'd27 : preset_note <= M5;
6'd28 : preset_note <= M1;
6'd29 : preset_note <= M2;
6'd30 : preset_note <= M5;
6'd31 : preset_note <= M1;
default : preset_note <= M1;
endcase
end
end
//给蜂鸣器赋值,并设定占空比
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
beep_r <= 1'b1;
end
else if(cnt3 == 2'd1)begin
beep_r <= 1'b1;
end
else if(alarm_clk_flag_r)begin
if(cnt0 <= preset_duty)begin
beep_r <= 1'b0;
end
else begin
beep_r <= 1'b1;
end
end
else begin
beep_r <= 1'b1;
end
end
assign preset_duty = preset_note >> 1;
assign beep = beep_r;
endmodule
数字时钟显示:
module seg_driver(
input clk ,
input rst_n ,
input [23:0] din ,
output reg [5:0] sel ,
output reg [7:0] dig ,
input [5:0] shan_shuo
);
parameter TIME_10MS = 25_000;
parameter TIME_500MS = 25_000_000;
//共阳极数码管 低电平点亮
localparam ZERO = 7'b100_0000,
ONE = 7'b111_1001,
TWO = 7'b010_0100,
THREE = 7'b011_0000,
FOUR = 7'b001_1001,
FIVE = 7'b001_0010,
SIX = 7'b000_0010,
SEVEN = 7'b111_1000,
EIGHT = 7'b000_0000,
NINE = 7'b001_0000,
OFF = 7'b111_1111;
//信号定义
reg dot;
reg [19:0] cnt;//扫描计数器
wire add_cnt;
wire end_cnt;
reg [3:0] data;//根据片选信号选择高四位还是第四位
//刷新频率计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
assign add_cnt = 1'b1;
assign end_cnt = add_cnt && (cnt == TIME_10MS - 1);
//片选信号选择
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel <= 6'b111_110;;
end
else if(end_cnt)begin
sel <= {sel[0],sel[5:1]};
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
data <= 4'b0;
end
else begin
case(sel)
6'b111_110: begin data <= shan_shuo[0]? 7'd10:din [3:0] ;dot <= 1'b1;end
6'b111_101: begin data <= shan_shuo[1]? 7'd10:din [7:4] ;dot <= 1'b1;end
6'b111_011: begin data <= shan_shuo[2]? 7'd10:din [11:8] ;dot <= 1'b0;end
6'b110_111: begin data <= shan_shuo[3]? 7'd10:din [15:12] ;dot <= 1'b1;end
6'b101_111: begin data <= shan_shuo[4]? 7'd10:din [19:16] ;dot <= 1'b0;end
6'b011_111: begin data <= shan_shuo[5]? 7'd10:din [23:20] ;dot <= 1'b1;end
default: data <= din[3:0];
endcase
end
end
always @(*) begin
case(data)
4'd0:dig = {dot,ZERO } ;
4'd1:dig = {dot,ONE } ;
4'd2:dig = {dot,TWO } ;
4'd3:dig = {dot,THREE} ;
4'd4:dig = {dot,FOUR } ;
4'd5:dig = {dot,FIVE } ;
4'd6:dig = {dot,SIX } ;
4'd7:dig = {dot,SEVEN} ;
4'd8:dig = {dot,EIGHT} ;
4'd9:dig = {dot,NINE } ;
4'd10:dig = {dot,OFF};
default: dig = {dot,ZERO};
endcase
end
endmodule
控制模块
控制模块主要设计了3组寄存器,分别寄存 自动计数的时间、手动设置的时间、闹钟时间
自动计数就不说了。
设置时间:key[0]进入设置时间,再次按下退出,并将设置的时间赋值给自动计数寄存器。key[2]控制选中时钟位闪烁,key[1]对选中的位进行加数。
闹钟时间:key[1]进入设置时间,再次按下退出,并保存时间。key[2]进行选位,key[0]控制加数。
module control(
input clk ,
input rst_n ,
input [2:0] key ,
output [23:0] time_data ,
output alarm_clk_flag ,
output [5:0] shan_shuo
);
localparam TIME_1S = 50_000_000;
localparam TIME_200MS = 10_000_000;
localparam IDLE = 3'b001;
localparam SET_TIME = 3'b010;
localparam SET_ALARM = 3'b100;
wire idle2set_time;
wire idle2set_alarm;
wire set_time2idle;
wire set_alarm2idle;
reg [2:0] state_c;
reg [2:0] state_n;
reg [5:0] shanshuo_r;//闪烁标志,选中位0/1不停变换
reg set_time_flag;//设置时间标志
reg set_alarm_flag;//设置闹钟标志
reg [23:0] time_data_r;
reg [24:0] cnt_200ms;
wire add_cnt_200ms;
wire end_cnt_200ms;
reg [25:0] cnt_1s;//1s计数器
wire add_cnt_1s;
wire end_cnt_1s;
reg [3:0] cnt0;//s个位计数器
wire add_cnt0;
wire end_cnt0;
reg [3:0] cnt1;//s十位计数器
wire add_cnt1;
wire end_cnt1;
reg [3:0] cnt2;//m个位计数器
wire add_cnt2;
wire end_cnt2;
reg [3:0] cnt3;//m十位计数器
wire add_cnt3;
wire end_cnt3;
reg [3:0] cnt4;//h个位计数器
wire add_cnt4;
wire end_cnt4;
reg [3:0] cnt5;//h十位计数器
wire add_cnt5;
wire end_cnt5;
//设置时间寄存器
reg [3:0] set_time_cnt0;//s个位计数器
reg [3:0] set_time_cnt1;//s十位计数器
reg [3:0] set_time_cnt2;//m个位计数器
reg [3:0] set_time_cnt3;//m十位计数器
reg [3:0] set_time_cnt4;//h个位计数器
reg [3:0] set_time_cnt5;//h十位计数器
reg [2:0] key_sel_time;//设置时间片选记录
wire add_key_sel_time;
wire end_key_sel_time;
reg [2:0] key_sel_alarm;//设置闹钟片选记录
wire add_key_sel_alarm;
wire end_key_sel_alarm;
//设置闹钟寄存器
reg [23:0] alarm;//保存闹钟数据
reg [3:0] set_alarm_cnt0;//s个位计数器
reg [3:0] set_alarm_cnt1;//s十位计数器
reg [3:0] set_alarm_cnt2;//m个位计数器
reg [3:0] set_alarm_cnt3;//m十位计数器
reg [3:0] set_alarm_cnt4;//h个位计数器
reg [3:0] set_alarm_cnt5;//h十位计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*) begin
case(state_c)
IDLE:begin
if(idle2set_time)begin
state_n <= SET_TIME;
end
else if(idle2set_alarm)begin
state_n <= SET_ALARM;
end
else begin
state_n <= state_c;
end
end
SET_TIME:begin
if(set_time2idle)begin
state_n <= IDLE;
end
else begin
state_n <= state_c;
end
end
SET_ALARM:begin
if(set_alarm2idle)begin
state_n <= IDLE;
end
else begin
state_n <= state_c;
end
end
default:state_n = state_c;
endcase
end
assign idle2set_time = (state_c == IDLE) && (set_time_flag);
assign idle2set_alarm = (state_c == IDLE) && (set_alarm_flag);
assign set_time2idle = (state_c == SET_TIME) && (!set_time_flag);
assign set_alarm2idle = (state_c == SET_ALARM)&& (!set_alarm_flag);
//200ms计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_200ms <= 25'b0;
end
else if(add_cnt_200ms)begin
if(end_cnt_200ms)begin
cnt_200ms <= 26'b0;
end
else begin
cnt_200ms <= cnt_200ms + 1'b1;
end
end
end
assign add_cnt_200ms = 1'b1;
assign end_cnt_200ms = add_cnt_200ms && (cnt_200ms == TIME_200MS - 1);
//1s计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1s <= 26'b0;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)begin
cnt_1s <= 26'b0;
end
else begin
cnt_1s <= cnt_1s + 1'b1;
end
end
end
assign add_cnt_1s = 1'b1;
assign end_cnt_1s = add_cnt_1s && (cnt_1s == TIME_1S - 1);
//s个位计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 4'b0;
end
else if(add_cnt0)begin
if(end_cnt0)begin
cnt0 <= 4'b0;
end
else begin
cnt0 <= cnt0 + 1'b1;
end
end
else if(set_time2idle)begin
cnt0 <= set_time_cnt0;
end
end
assign add_cnt0 = end_cnt_1s;
assign end_cnt0 = add_cnt0 && (cnt0 == 10 - 1);
//s十位计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 4'b0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt1 <= 4'b0;
end
else begin
cnt1 <= cnt1 + 1'b1;
end
end
else if(set_time2idle)begin
cnt1 <= set_time_cnt1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && (cnt1 == 6 - 1);
//m个位计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 4'b0;
end
else if(add_cnt2)begin
if(end_cnt2)begin
cnt2 <= 4'b0;
end
else begin
cnt2 <= cnt2 + 1'b1;
end
end
else if(set_time2idle)begin
cnt2 <= set_time_cnt2;
end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && (cnt2 == 10 - 1);
//m十位计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt3 <= 4'b0;
end
else if(add_cnt3)begin
if(end_cnt3)begin
cnt3 <= 4'b0;
end
else begin
cnt3 <= cnt3 + 1'b1;
end
end
else if(set_time2idle)begin
cnt3 <= set_time_cnt3;
end
end
assign add_cnt3 = end_cnt2;
assign end_cnt3 = add_cnt3 && (cnt3 == 6 - 1);
//h个位计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt4 <= 4'b0;
end
else if(add_cnt4)begin
if(end_cnt4)begin
cnt4 <= 4'b0;
end
else begin
cnt4 <= cnt4 + 1'b1;
end
end
else if(set_time2idle)begin
cnt4 <= set_time_cnt4;
end
end
assign add_cnt4 = end_cnt3;
assign end_cnt4 = add_cnt4 && (cnt4 == (cnt5 == 2)?(4 - 1):(10 - 1));
//h十位计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt5 <= 4'b0;
end
else if(add_cnt5)begin
if(end_cnt5)begin
cnt5 <= 4'b0;
end
else begin
cnt5 <= cnt5 + 1'b1;
end
end
else if(set_time2idle)begin
cnt5 <= set_time_cnt5;
end
end
assign add_cnt5 = end_cnt4;
assign end_cnt5 = add_cnt5 && (cnt5 == 2);
//选中位反转
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
shanshuo_r <= 0;
end
else if(state_c == IDLE)begin
shanshuo_r <= 6'b000_000;
end
else if(state_c == SET_TIME)begin
case(key_sel_time)
3'd0:begin
shanshuo_r[5] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[0] <= ~shanshuo_r[0];
end
end
3'd1:begin
shanshuo_r[0] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[1] <= ~shanshuo_r[1];
end
end
3'd2:begin
shanshuo_r[1] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[2] <= ~shanshuo_r[2];
end
end
3'd3:begin
shanshuo_r[2] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[3] <= ~shanshuo_r[3];
end
end
3'd4:begin
shanshuo_r[3] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[4] <= ~shanshuo_r[4];
end
end
3'd5:begin
shanshuo_r[4] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[5] <= ~shanshuo_r[5];
end
end
default:shanshuo_r <= shanshuo_r;
endcase
end
else if(state_c == SET_ALARM)begin
case(key_sel_alarm)
3'd0:begin
shanshuo_r[5] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[0] <= ~shanshuo_r[0];
end
end
3'd1:begin
shanshuo_r[0] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[1] <= ~shanshuo_r[1];
end
end
3'd2:begin
shanshuo_r[1] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[2] <= ~shanshuo_r[2];
end
end
3'd3:begin
shanshuo_r[2] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[3] <= ~shanshuo_r[3];
end
end
3'd4:begin
shanshuo_r[3] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[4] <= ~shanshuo_r[4];
end
end
3'd5:begin
shanshuo_r[4] <= 0;
if(end_cnt_200ms)begin
shanshuo_r[5] <= ~shanshuo_r[5];
end
end
default:shanshuo_r <= shanshuo_r;
endcase
end
end
assign shan_shuo = shanshuo_r;
//-----------------------------设置时间------------------------------------------------------------------------
//时间设置标志
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_time_flag <= 1'b0;
end
else if((state_c == IDLE) && key[0])begin
set_time_flag <= 1'b1;
end
else if((state_c == SET_TIME) && key[0])begin
set_time_flag <= 1'b0;
end
end
//时间设置寄存器赋值
//s个位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_time_cnt0 <= 0;
end
else if(idle2set_time)begin
set_time_cnt0 <= cnt0;
end
else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd0)begin
if(set_time_cnt0 == 10 -1)begin
set_time_cnt0 <= 0;
end
else begin
set_time_cnt0 <= set_time_cnt0 + 1;
end
end
end
//s十位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_time_cnt1 <= 0;
end
else if(idle2set_time)begin
set_time_cnt1 <= cnt1;
end
else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd1)begin
if(set_time_cnt1 == 6 - 1)begin
set_time_cnt1 <= 0;
end
else begin
set_time_cnt1 <= set_time_cnt1 + 1;
end
end
end
//m个位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_time_cnt2 <= 0;
end
else if(idle2set_time)begin
set_time_cnt2 <= cnt2;
end
else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd2)begin
if(set_time_cnt2 == 10 - 1)begin
set_time_cnt2 <= 0;
end
else begin
set_time_cnt2 <= set_time_cnt2 + 1;
end
end
end
//m十位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_time_cnt3 <= 0;
end
else if(idle2set_time)begin
set_time_cnt3 <= cnt3;
end
else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd3)begin
if(set_time_cnt3 == 6 - 1)begin
set_time_cnt3 <= 0;
end
else begin
set_time_cnt3 <= set_time_cnt3 + 1;
end
end
end
//h个位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_time_cnt4 <= 0;
end
else if(idle2set_time)begin
set_time_cnt4 <= cnt4;
end
else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd4)begin
if(set_time_cnt4 == ((set_time_cnt5 == 2)?(4 - 1):(10 - 1)))begin
set_time_cnt4 <= 0;
end
else begin
set_time_cnt4 <= set_time_cnt4 + 1;
end
end
end
//h十位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_time_cnt5 <= 0;
end
else if(idle2set_time)begin
set_time_cnt5 <= cnt5;
end
else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd5)begin
if(set_time_cnt5 == (set_time_cnt4>= 4 ? 1 : 2))begin
set_time_cnt5 <= 0;
end
else begin
set_time_cnt5 <= set_time_cnt5 + 1;
end
end
end
//设置时间片选计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_sel_time <= 0;
end
else if(add_key_sel_time)begin
if(end_key_sel_time)begin
key_sel_time <= 0;
end
else begin
key_sel_time <= key_sel_time + 1;
end
end
end
assign add_key_sel_time = key[2] && (state_c == SET_TIME);
assign end_key_sel_time = add_key_sel_time && (key_sel_time == 6 - 1);
//---------------------------设置闹钟------------------------------------------------------------------
//闹钟设置标志
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_alarm_flag <= 1'b0;
end
else if((state_c == IDLE) && key[1])begin
set_alarm_flag <= 1'b1;
end
else if((state_c == SET_ALARM) && key[1])begin
set_alarm_flag <= 1'b0;
end
end
//设置闹钟片选计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_sel_alarm <= 0;
end
else if(add_key_sel_alarm)begin
if(end_key_sel_alarm)begin
key_sel_alarm <= 0;
end
else begin
key_sel_alarm <= key_sel_alarm + 1;
end
end
end
assign add_key_sel_alarm = key[2] && (state_c == SET_ALARM);
assign end_key_sel_alarm = add_key_sel_alarm && (key_sel_alarm == 6 - 1);
//s个位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_alarm_cnt0 <= 0;
end
else if(idle2set_alarm)begin
set_alarm_cnt0 <= cnt0;
end
else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd0)begin
if(set_alarm_cnt0 == 10 -1)begin
set_alarm_cnt0 <= 0;
end
else begin
set_alarm_cnt0 <= set_alarm_cnt0 + 1;
end
end
end
//s十位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_alarm_cnt1 <= 0;
end
else if(idle2set_alarm)begin
set_alarm_cnt1 <= cnt1;
end
else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd1)begin
if(set_alarm_cnt1 == 6 -1)begin
set_alarm_cnt1 <= 0;
end
else begin
set_alarm_cnt1 <= set_alarm_cnt1 + 1;
end
end
end
//m个位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_alarm_cnt2 <= 0;
end
else if(idle2set_alarm)begin
set_alarm_cnt2 <= cnt2;
end
else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd2)begin
if(set_alarm_cnt2 == 10 -1)begin
set_alarm_cnt2 <= 0;
end
else begin
set_alarm_cnt2 <= set_alarm_cnt2 + 1;
end
end
end
//m十位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_alarm_cnt3 <= 0;
end
else if(idle2set_alarm)begin
set_alarm_cnt3 <= cnt3;
end
else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd3)begin
if(set_alarm_cnt3 == 6 -1)begin
set_alarm_cnt3 <= 0;
end
else begin
set_alarm_cnt3 <= set_alarm_cnt3 + 1;
end
end
end
//h个位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_alarm_cnt4 <= 0;
end
else if(idle2set_alarm)begin
set_alarm_cnt4 <= cnt4;
end
else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd4)begin
if(set_alarm_cnt4 == ((set_alarm_cnt5 == 2)?(4 - 1):(10 - 1)))begin
set_alarm_cnt4 <= 0;
end
else begin
set_alarm_cnt4 <= set_alarm_cnt4 + 1;
end
end
end
//h十位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
set_alarm_cnt5 <= 0;
end
else if(idle2set_alarm)begin
set_alarm_cnt5 <= cnt5;
end
else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd5)begin
if(set_alarm_cnt5 == (set_alarm_cnt4 >=4 ? 1 : 2))begin
set_alarm_cnt5 <= 0;
end
else begin
set_alarm_cnt5 <= set_alarm_cnt5 + 1;
end
end
end
//保存闹钟
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
alarm <= {4'd2,4'd3,4'd5,4'd9,4'd5,4'd9};
end
else if(set_alarm2idle)begin
alarm <= {set_alarm_cnt5,set_alarm_cnt4,set_alarm_cnt3,set_alarm_cnt2,set_alarm_cnt1,set_alarm_cnt0};
end
end
//数码管显示
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
time_data_r <= 0;
end
else begin
case(state_c)
IDLE: time_data_r <= {cnt5,cnt4,cnt3,cnt2,cnt1,cnt0};
SET_TIME: time_data_r <= {set_time_cnt5,set_time_cnt4,set_time_cnt3,set_time_cnt2,set_time_cnt1,set_time_cnt0};
SET_ALARM:time_data_r <= {set_alarm_cnt5,set_alarm_cnt4,set_alarm_cnt3,set_alarm_cnt2,set_alarm_cnt1,set_alarm_cnt0};
default: time_data_r <= {cnt5,cnt4,cnt3,cnt2,cnt1,cnt0};
endcase
end
end
assign time_data = time_data_r;
//闹钟标志信号
assign alarm_clk_flag = ((state_c == IDLE) && (alarm == {cnt5,cnt4,cnt3,cnt2,cnt1,cnt0}));
endmodule
以上是关于FPGA学习之 状态机实现数码管的数字时钟的主要内容,如果未能解决你的问题,请参考以下文章