文章目录
- 前言
- 实验手册
- 一、实验目的
- 二、实验原理
- 1.理论原理
- 2.硬件原理
- 三、系统架构设计
- 四、模块说明
- 1.模块端口信号列表
- 按键消抖模块(key)
- 计数器模块(counter)
- 蜂鸣器乐谱模块(music)
- 蜂鸣器发声模块(beep)
- 数码管驱动模块(seg_driver)
- 顶层模块(top)
- 2.状态转移图
- 3.时序图
- 五、仿真波形图
- 六、引脚分配
- tcl引脚分配文件
- 七、代码编写
- 按键消抖模块(key)
- 计数器模块(counter)
- 蜂鸣器乐谱模块(music)
- 蜂鸣器发声模块(beep)
- 数码管驱动模块(seg_driver)
- 顶层模块(top)
- 八、总结
前言
本次实验是本人全部用状态机实现的,所以导致我的状态机空间有很多状态,有一部分状态可以进行修改,不过我认为全部写成状态机更有利于本人的理解,可能有人会觉得很绕,不过根据本人自身所画的状态图,就能较为清晰明了,本文结尾会附上所有代码,如有需要自取即可,但我希望不要一味的复制粘贴,理解之后手敲一遍更能提升自己,如需要工程文件私信我或者评论区找我,现写下此博客以便日后复习。
该开发板为EP4CE6F17C8
实验手册
一、实验目的
1、完成基本24时计数显示2、显示时间可调整
3、设置可调闹钟-闹钟触发时蜂鸣器响起
二、实验原理
1.理论原理
蜂鸣器原理输入一定频率的方波或者PWM信号,蜂鸣器就可以发出声音。输入不同频率的信号,蜂鸣器可以发出不同音色的声音
电子时钟原理是在数码管中由于有位选信号和段选信号的存在,我们无法做到将数码管同时进行变换,所以我们通过快速的切换和刷新,使得我们看到的是连续的多个数码管显示结果。通过控制位选信号和段选信号,可以实现不同的数字、字母或符号的动态显示。
将按键进行按键消抖获得稳定信号。
使用按键通过状态机切换状态控制位选信号,在相应的位选信号下,控制时位,分位,秒位的数值加一或减一。
利用相同原理使用状态机切换状态——闹钟设置状态和常规电子时钟状态,闹钟设置状态下对时分秒位的操作与电子时钟状态一致
闹钟状态中将设定的值与电子时钟的值比对,如果相等则传给蜂鸣器一个信号,并将该信号持续5s,是蜂鸣器发声五秒
2.硬件原理
我所用的板子自带的蜂鸣器是无源蜂鸣器——内部无振荡源,需要通以方波、PWM信号才能发出声音,以及是低电平有效
开发板上的数码管数码管位选和段选信号都是低电平有效。
位选信号根据需要的显示位数进行生成(该实验所用的开发板可用6个位选信号),它们用于选择要显示的数码管。
段选信号根据需要的显示内容进行生成,它们用于控制数码管的七段LED灯以及小数点LED灯的亮灭状态(该实验所用的开发板可用8个段选信号)。
三、系统架构设计
本次实验有按键消抖模块、计数器模块、蜂鸣器模块、音符模块、数码管驱动模块
按键消抖模块将按键信号转化为稳定的有效信号
计数器模块通过输入的有效按键信号,key3切换闹钟和电子时钟状态,以及key1加,key2减,时位、分位、秒位的加减,比对闹钟空间和电子时钟的时间数值,若相等则传出一个蜂鸣器信号给音符模块,再将hour、min、sec数值输入进数码管驱动模块进行显示。
数码管驱动模块,将计数器模块产生的时分秒显示出来,以及key0切换时位、分位、秒位、空闲状态的切换。
音符模块,接收到闹钟信号时将pwm波发给蜂鸣器发声模块,持续5s
蜂鸣器模块,接收到音符模块的pwm波,发声
顶层模块,统合所有模块。
四、模块说明
1.模块端口信号列表
按键消抖模块(key)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | key_in | 按键信号 |
output | reg | key_flag | 稳定按键信号 |
计数器模块(counter)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | key_flag | 稳定按键信号 |
output | wire | hour | 小时数 |
output | wire | min | 分钟数 |
output | wire | sec | 秒钟数 |
output | wire | music_flag | 闹钟信号 |
蜂鸣器乐谱模块(music)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | music_flag | 闹钟到时信号 |
output | reg | flag | 蜂鸣器发声信号 |
蜂鸣器发声模块(beep)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | flag | 控制蜂鸣器发声信号 |
output | reg | beep | 蜂鸣器发声信号 |
数码管驱动模块(seg_driver)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | hour | 小时数 |
input | wire | min | 分钟数 |
input | wire | sec | 秒钟数 |
input | wire | key_flag | 稳定按键信号 |
output | reg | seg | 数码管段选信号 |
output | reg | sel | 数码管位选信号 |
顶层模块(top)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | key_in | 按键信号 |
output | wire | sel | 数码管位选信号 |
output | wire | beep | 蜂鸣器发声信号 |
output | wire | seg | 数码管段选信号 |
2.状态转移图
闹钟空间时分秒位也有相同的加一减一,这里本人偷懒了没有画全。
3.时序图
五、仿真波形图
无仿真波形
六、引脚分配
tcl引脚分配文件
# Copyright (C) 2018 Intel Corporation. All rights reserved.
# Your use of Intel Corporation's design tools, logic functions
# and other software and tools, and its AMPP partner logic
# functions, and any output files from any of the foregoing
# (including device programming or simulation files), and any
# associated documentation or information are expressly subject
# to the terms and conditions of the Intel Program License
# Subscription Agreement, the Intel Quartus Prime License Agreement,
# the Intel FPGA IP License Agreement, or other applicable license
# agreement, including, without limitation, that your use is for
# the sole purpose of programming logic devices manufactured by
# Intel and sold by Intel or its authorized distributors. Please
# refer to the applicable agreement for further details.# Quartus Prime Version 18.1.0 Build 625 09/12/2018 SJ Standard Edition
# File: D:\intelFPGA\code\dig_clock\tcl\dig_clock.tcl
# Generated on: Mon Aug 07 16:06:21 2023package require ::quartus::projectset_location_assignment PIN_A5 -to seg[7]
set_location_assignment PIN_B8 -to seg[6]
set_location_assignment PIN_A7 -to seg[5]
set_location_assignment PIN_B6 -to seg[4]
set_location_assignment PIN_B5 -to seg[3]
set_location_assignment PIN_A6 -to seg[2]
set_location_assignment PIN_A8 -to seg[1]
set_location_assignment PIN_B7 -to seg[0]
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_A4 -to sel[0]
set_location_assignment PIN_B4 -to sel[1]
set_location_assignment PIN_A3 -to sel[2]
set_location_assignment PIN_B3 -to sel[3]
set_location_assignment PIN_A2 -to sel[4]
set_location_assignment PIN_B1 -to sel[5]
set_location_assignment PIN_E15 -to key_in[0]
set_location_assignment PIN_E16 -to key_in[1]
set_location_assignment PIN_M16 -to key_in[2]
set_location_assignment PIN_M15 -to key_in[3]
set_location_assignment PIN_J1 -to beep
七、代码编写
按键消抖模块(key)
module key
(input wire clk,input wire rst_n,input wire key_in,output reg key_out,output reg key_flag
);parameter CNT_MAX =20'd999_999; //20ms计数reg [19:0] cnt_20ms;
//reg key_flag;
//20ms消抖
always@(posedge clk or negedge rst_n)if(!rst_n)cnt_20ms<=20'b0;else if(key_in==1'b1)cnt_20ms<=20'd0;else if(cnt_20ms==CNT_MAX)cnt_20ms<=CNT_MAX;elsecnt_20ms<=cnt_20ms+20'd1;
//取单个脉冲信号
always@(posedge clk or negedge rst_n)if(!rst_n)key_flag<=1'b0;else if(cnt_20ms==(CNT_MAX-20'd1))key_flag<=1'b1;elsekey_flag<=1'b0;
//有效长信号
always @(posedge clk or negedge rst_n)beginif(!rst_n)beginkey_out <= 1'b0;endelse if(key_flag == 1'b1)key_out <= ~key_out;elsekey_out <= key_out;
endendmodule
计数器模块(counter)
module counter (input wire clk ,input wire rst_n ,input wire [3:0] key_flag,output wire [4:0] hour ,output wire [5:0] min ,output wire [5:0] sec ,output wire music_flag
);reg [8:0] cnt_min ;//存放分钟加减
reg [8:0] cnt_sec ;//存放秒钟加减
parameter MAXDAY = 17'd86400;//24小时 24*60*60
parameter SEC = 26'd50_000_000;//1s
reg [5:0] c_state;//现态,数码管选位加减
reg [5:0] n_state;//次态,数码管选位加减
parameter IDLE = 6'd0, //空闲状态S1 = 6'd1, //数码管时位S2 = 6'd2, //数码管分位S3 = 6'd3, //数码管秒位A1 = 6'd4, //时位加M1 = 6'd5, //时位减A2 = 6'd6, //分位加M2 = 6'd7, //分位减A3 = 6'd8, //秒位减M3 = 6'd9, //秒位减R1 = 6'd10, //闹钟功能空间 P1 = 6'd11, //数码管时位P2 = 6'd12, //数码管分位P3 = 6'd13, //数码管秒位A4 = 6'd14, //时位加M4 = 6'd15, //时位减A5 = 6'd16, //分位加M5 = 6'd17, //分位减A6 = 6'd18, //秒位减M6 = 6'd19; //秒位减
reg [25:0] cnt_1s ;
reg [25:0] cnt_1s_r ;
reg [16:0] cnt_day ;
reg [16:0] cnt_day_r ;
reg [31:0] cnt_10ms ;
wire [4:0] hour_r ;//存放时钟加减
wire [5:0] min_r ;
wire [5:0] sec_r ;
parameter MAX_10ms = 32'd499_999;
//10ms计数器
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_10ms <= 26'd0;endelse if (cnt_10ms == MAX_10ms) begincnt_10ms <= 26'd0;endelse begincnt_10ms <= cnt_10ms + 1'd1;end
end
//1秒计数器
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_1s <= 26'd0;endelse if (cnt_1s == SEC - 1'd1) begincnt_1s <= 26'd0;endelse begincnt_1s <= cnt_1s + 1'd1;end
end
//1天计时器_电子时钟
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_day <= 17'd0;end else if(cnt_day >= MAXDAY - 1'd1)begincnt_day <= 17'd0;end else if( cnt_1s == SEC -1'd1)begincnt_day <= cnt_day + 1'd1;endelse if(n_state == A1) begincnt_day <= cnt_day + 17'd3600;endelse if(n_state == M1) begincnt_day <= cnt_day - 17'd3600;endelse if (c_state== S1 &&key_flag[2] && (cnt_day / 3600)== 0) begincnt_day <= cnt_day + 17'd82800;endelse if(n_state == A2) begincnt_day <= cnt_day + 17'd60;endelse if(n_state == M2) begincnt_day <= cnt_day - 17'd60;endelse if(n_state == A3) begincnt_day <= cnt_day + 17'd1;endelse if(n_state == M3) begincnt_day <= cnt_day - 17'd1;endelse begincnt_day <= cnt_day;end
end
//1天计时器_r_闹钟空间
always@(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_day_r <= 17'd0;end else if(cnt_day_r >= MAXDAY - 1'd1)begincnt_day_r <= 17'd0;end else if(n_state == A4) begincnt_day_r <= cnt_day_r + 17'd3600;endelse if(n_state == M4) begincnt_day_r <= cnt_day_r - 17'd3600;endelse if (key_flag[2] && (cnt_day_r / 3600)== 0) begincnt_day_r <= cnt_day_r + 17'd82800;endelse if(n_state == A5) begincnt_day_r <= cnt_day_r + 17'd60;endelse if(n_state == M5) begincnt_day_r <= cnt_day_r - 17'd60;endelse if(n_state == A6) begincnt_day_r <= cnt_day_r + 17'd10;endelse if(n_state == M6) begincnt_day_r <= cnt_day_r - 17'd10;endelse begincnt_day_r <= cnt_day_r;end
end
//如果处于闹钟空间则输出闹钟空间的值,否则输出电子时钟的值
assign hour = (c_state == R1||c_state == P1||c_state == P2||c_state == P3)?hour_r:cnt_day / 3600;
assign min = (c_state == R1||c_state == P1||c_state == P2||c_state == P3)?min_r:cnt_day % 3600 / 60;
assign sec = (c_state == R1||c_state == P1||c_state == P2||c_state == P3)?sec_r:cnt_day % 3600 % 60;assign hour_r = cnt_day_r /3600;
assign min_r = cnt_day_r % 3600 / 60;
assign sec_r = cnt_day_r % 3600 % 60;
//如果闹钟空间的值除了零以外的时候和电子时钟的值相等,则输出音乐信号给乐谱模块
assign music_flag = (cnt_day == cnt_day_r&&cnt_day_r != 0)?1:0;
//数码管选位状态机,第一段
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginc_state <= IDLE;endelse beginc_state <= n_state;end
end
//数码管选位状态机,第二段
always @(*) begincase (c_state)//空闲状态IDLE :beginif (key_flag[0]) beginn_state = S1;endelse if (key_flag[3]) beginn_state = R1;endelse beginn_state = IDLE;endend//闹钟状态R1 :beginif (key_flag[0]) beginn_state = P1;endelse if (key_flag[3]) beginn_state = IDLE;endelse beginn_state = R1;endend//换位状态:时位S1 :beginif (key_flag[0]) beginn_state = S2;endelse if (key_flag[1]) beginn_state = A1;endelse if (key_flag[2]) beginn_state = M1;endelse beginn_state = S1;endend //换位状态:分位S2 :beginif (key_flag[0]) beginn_state = S3;endelse if (key_flag[1]) beginn_state = A2;endelse if (key_flag[2]) beginn_state = M2;endelse beginn_state = S2;endend//换位状态:秒位 S3 :beginif (key_flag[0]) beginn_state = IDLE;endelse if (key_flag[1]) beginn_state = A3;endelse if (key_flag[2]) beginn_state = M3;endelse beginn_state = S3;endend//时位加一A1 :beginif (c_state == A1) beginn_state = S1;endelse beginn_state = A1;endend//时位减一 M1 :beginif (c_state == M1) beginn_state = S1;endelse beginn_state = M1;endend//分位加一A2 :beginif (c_state == A2) beginn_state = S2;endelse beginn_state = A2;endend//分位减一 M2 :beginif (c_state == M2) beginn_state = S2;endelse beginn_state = M2;endend//秒位加一A3 :beginif (c_state == A3) beginn_state = S3;endelse beginn_state = A3;endend//秒位减一 M3 :beginif (c_state == M3) beginn_state = S3;endelse beginn_state = M3;endend//闹钟空间:时位P1 :beginif (key_flag[0]) beginn_state = P2;endelse if (key_flag[1]) beginn_state = A4;endelse if (key_flag[2]) beginn_state = M4;endelse beginn_state = P1;endend//闹钟空间:分位 P2 :beginif (key_flag[0]) beginn_state = P3;endelse if (key_flag[1]) beginn_state = A5;endelse if (key_flag[2]) beginn_state = M5;endelse beginn_state = P2;endend//闹钟空间:秒位 P3 :beginif (key_flag[0]) beginn_state = R1;endelse if (key_flag[1]) beginn_state = A6;endelse if (key_flag[2]) beginn_state = M6;endelse beginn_state = P3;endend//闹钟空间:时位加一 A4 :beginif (c_state == A4) beginn_state = P1;endelse beginn_state = A4;endend//闹钟空间:时位减一 M4 :beginif (c_state == M4) beginn_state = P1;endelse beginn_state = M4;endend//闹钟空间:分位加一 A5 :beginif (c_state == A5) beginn_state = P2;endelse beginn_state = A5;endend//闹钟空间:分位减一 M5 :beginif (c_state == M5) beginn_state = P2;endelse beginn_state = M5;endend//闹钟空间:秒位加一 A6 :beginif (c_state == A6) beginn_state = P3;endelse beginn_state = A6;endend//闹钟空间:秒位减一 M6 :beginif (c_state == M6) beginn_state = P3;endelse beginn_state = M6;endenddefault:n_state = IDLE;endcase
end
endmodule
蜂鸣器乐谱模块(music)
module music (input clk,input rst_n,input [3:0] key_out,input music_flag,output reg flag
);parameter MAX_0_3s = 25'd14_999_999;
reg [31:0] cnt_0_3s;
parameter MAX_1s = 32'd49_999_999;
reg [31:0] cnt_1s ;//1秒计数器
parameter MAX_5s = 32'd5;
reg [31:0] cnt_5s ;//1秒计数器parameter MAX_7 = 6'd33;//乐谱
reg [15:0] cnt_7; //乐谱1寄存器
//parameter MAX_two_tiger = 6'd33;
//reg [15:0] two_tiger;//乐谱2寄存器
//parameter MAX_300ms = 25'd14_999_999;
//reg [31:0] cnt_300ms;
reg [15:0] music_cnt;//音符音频计数器
wire[15:0] duty;//占空比
reg [15:0] music;//音符数据寄存器
reg music_delay;parameter DO = 16'd47750 ;//1
parameter RE = 16'd42250 ;//2
parameter MI = 16'd37900 ;//3
parameter FA = 16'd37550 ;//4
parameter SO = 16'd31850 ;//5
parameter LA = 16'd28400 ;//6
parameter XI = 16'd25400 ;//7
//1s计数器
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_1s <= 1'd0;endelse if (cnt_1s == MAX_1s) begincnt_1s <= 1'd0;endelse begincnt_1s <= cnt_1s + 1;end
end
//5s计数器
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_5s <= 1'd0;endelse if (cnt_5s == MAX_5s) begincnt_5s <= 1'd0;endelse if (cnt_1s == MAX_1s)begincnt_5s <= cnt_5s + 1;end
end
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginmusic_delay <= 1'd0;endelse if (cnt_5s == MAX_5s) beginmusic_delay <= 1'd0;endelse if (music_flag)beginmusic_delay <= 1'd1;end
end
//0.3s计数器
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_0_3s <= 1'd0;endelse if (cnt_0_3s == MAX_0_3s) begincnt_0_3s <= 1'd0;endelse begincnt_0_3s <= cnt_0_3s + 1;end
end
//300ms计数器
//always @(posedge clk or negedge rst_n) begin
// if (!rst_n) begin
// cnt_300ms <= 1'd0;
// end
// else if (cnt_300ms == MAX_300ms) begin
// cnt_300ms <= 1'd0;
// end
// else begin
// cnt_300ms <= cnt_300ms + 1;
// end
//end
//音符频率计数器
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginmusic_cnt <= 16'd0;end else if(music_cnt == music)beginmusic_cnt <= 16'd0;endelse beginmusic_cnt <= music_cnt + 1'd1;end
end//乐谱1每隔0.5s音符切换
always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_7 <= 1'd0;endelse if (cnt_7 == MAX_7 && cnt_0_3s == MAX_0_3s) begincnt_7 <= 1'd0;endelse if (cnt_0_3s == MAX_0_3s) begincnt_7 <= cnt_7 + 1'd1;endelse begincnt_7 <= cnt_7;end
end//乐谱2的切换间隔为300ms
//always @(posedge clk or negedge rst_n) begin
// if (!rst_n) begin
// two_tiger <= 1'd0;
// end
// else if (two_tiger == MAX_two_tiger && cnt_300ms == MAX_300ms) begin
// two_tiger <= 1'd0;
// end
// else if (cnt_300ms == MAX_0_5s) begin
// two_tiger <= two_tiger + 1'd1;
// end
// else begin
// two_tiger <= two_tiger;
// end
//end
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginmusic <= 1'd0;end//else if(key_out[1])begincase (cnt_7)6'd0: music <= DO;6'd1: music <= RE;6'd2: music <= MI; 6'd3: music <= DO; 6'd4: music <= DO; 6'd5: music <= RE; 6'd6: music <= MI; 6'd7: music <= DO; 6'd8: music <= MI; 6'd9: music <= FA; 6'd10: music <= SO;6'd11: music <= MI;6'd12: music <= FA;6'd13: music <= SO;6'd14: music <= SO;6'd15: music <= LA;6'd16: music <= SO;6'd17: music <= FA;6'd18: music <= MI;6'd19: music <= DO;6'd20: music <= SO;6'd21: music <= LA;6'd22: music <= SO;6'd23: music <= FA;6'd24: music <= MI;6'd25: music <= DO;6'd26: music <= RE;6'd27: music <= SO;6'd28: music <= DO;6'd29: music <= DO;6'd30: music <= RE;6'd31: music <= SO;6'd32: music <= DO;6'd33: music <= DO;default:music <= DO;endcase//end//else if(!key_out[1])begin// case(cnt_7)// 10'd0: music <= DO ;// 10'd1: music <= RE ;// 10'd2: music <= MI ;// 10'd3: music <= FA ;// 10'd4: music <= SO ;// 10'd5: music <= LA ;// 10'd6: music <= XI ;// 10'd7: music <= DO ;// 10'd8: music <= RE ;// 10'd9: music <= MI ;// 10'd10: music <= FA ;// 10'd11: music <= SO ;// 10'd12: music <= LA ;// 10'd13: music <= XI ;// 10'd14: music <= DO ;// 10'd15: music <= RE ;// 10'd16: music <= MI ;// 10'd17: music <= FA ;// 10'd18: music <= SO ;// 10'd19: music <= LA ;// 10'd20: music <= XI ;// default: music <= DO;// endcase//end
end
assign duty = music >> 1;//占空比50%
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginflag <= 1'b0;end else if (music_delay)beginflag <= (music_cnt >= duty) ? 1'b1 : 1'b0; endelse beginflag <= 1'b0;end
endendmodule
蜂鸣器发声模块(beep)
module beep (input clk,input rst_n,input flag,output reg beep
);always @(posedge clk or negedge rst_n) beginif (!rst_n) beginbeep <= 1'b1; endelse if (flag) beginbeep <= 1'b0;endelse beginbeep <= 1'b1;end
endendmodule
数码管驱动模块(seg_driver)
module seg_driver (input wire clk ,input wire rst_n ,input wire [4:0] hour ,input wire [5:0] min ,input wire [5:0] sec , input wire [3:0] key_flag,output reg [7:0] seg , //段选信号output reg [5:0] sel //位选信号);reg [8:0] cnt_num ;
reg [19:0] cnt_delay ;
reg [7:0] point ;//小数点
reg [5:0] c_state;//现态,数码管选位加减
reg [5:0] n_state;//次态,数码管选位加减
parameter IDLE = 6'd0, //空闲状态S1 = 6'd1, //数码管时位S2 = 6'd2, //数码管分位S3 = 6'd3; //数码管秒位parameter MAX_1s = 32'd24_999_999;
reg [31:0] cnt_1s ;//1秒计数器parameter CNT_DELAY_MAX= 10'd1000 ;
parameter ZERO = 8'b1100_0000 ,ONE = 8'b1111_1001 ,TWO = 8'b1010_0100 ,THREE = 8'b1011_0000 ,FOUR = 8'b1001_1001 ,FIVE = 8'b1001_0010 ,SIX = 8'b1000_0010 ,SEVEN = 8'b1111_1000 ,EIGHT = 8'b1000_0000 ,NINE = 8'b1001_0000 ;//1s计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_1s <= 10'd0;endelse if(cnt_1s == MAX_1s)begincnt_1s <= 10'd0;endelse begincnt_1s <= cnt_1s + 1'd1;end
end
//20us计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_delay <= 10'd0;endelse if(cnt_delay == CNT_DELAY_MAX - 1'd1)begincnt_delay <= 10'd0;endelse begincnt_delay <= cnt_delay + 1'd1;end
end//该板块无法同时变化,只能依次闪烁,该方法为依次闪烁的间隔,人肉眼无法判断 20us
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginsel <= 6'b111_110;endelse if (cnt_delay == CNT_DELAY_MAX - 1'd1) beginsel <= {sel[4:0],sel[5]};endelse beginsel <= sel ;end
endalways @(*) begin//若处于时位,则相应的位选信号闪烁if (c_state == S1) begincase (sel)6'b111110:beginif (cnt_1s <= MAX_1s/2) begincnt_num = 9'd20;endelse cnt_num = hour / 10;end 6'b111101: beginif (cnt_1s <= MAX_1s/2) begincnt_num = 9'd20;endelse cnt_num = hour % 10 + 10;end 6'b111011: cnt_num = min / 10;6'b110111: cnt_num = min % 10 + 10;6'b101111: cnt_num = sec / 10;6'b011111: cnt_num = sec % 10;default: cnt_num = 4'd0;endcaseend//若处于分位,则相应的位选信号闪烁else if (c_state == S2) begincase (sel)6'b111110: cnt_num = hour / 10;6'b111101: cnt_num = hour % 10 + 10;6'b111011: beginif (cnt_1s <= MAX_1s/2) begincnt_num = 9'd20;endelse cnt_num = min / 10;end 6'b110111: beginif (cnt_1s <= MAX_1s/2) begincnt_num = 9'd20;endelse cnt_num = min % 10 + 10;end6'b101111: cnt_num = sec / 10;6'b011111: cnt_num = sec % 10;default: cnt_num = 4'd0;endcaseend//若处于秒位,则相应的位选信号闪烁else if (c_state == S3) begincase (sel)6'b111110: cnt_num = hour / 10;6'b111101: cnt_num = hour % 10 + 10;6'b111011: cnt_num = min / 10;6'b110111: cnt_num = min % 10 + 10;6'b101111: beginif (cnt_1s <= MAX_1s/2) begincnt_num = 9'd20;endelse cnt_num = sec / 10;end6'b011111: beginif (cnt_1s <= MAX_1s/2) begincnt_num = 9'd20;endelse cnt_num = sec % 10;enddefault: cnt_num = 4'd0;endcaseendelse begin //其他情况数码管正常显示 case (sel)6'b111110: cnt_num = hour / 10;6'b111101: cnt_num = hour % 10 + 10;6'b111011: cnt_num = min / 10;6'b110111: cnt_num = min % 10 + 10;6'b101111: cnt_num = sec / 10;6'b011111: cnt_num = sec % 10;default: cnt_num = 4'd0;endcaseend
end//通过数字num解析出seg值
always @(*) begincase (cnt_num)9'd0:begin seg <= ZERO ; end9'd1:begin seg <= ONE ; end9'd2:begin seg <= TWO ; end9'd3:begin seg <= THREE ; end9'd4:begin seg <= FOUR ; end9'd5:begin seg <= FIVE ; end9'd6:begin seg <= SIX ; end9'd7:begin seg <= SEVEN ; end9'd8:begin seg <= EIGHT ; end9'd9:begin seg <= NINE ; end9'd10:begin seg <= {point[7],ZERO[6:0]} ; end9'd11:begin seg <= {point[7],ONE[6:0]} ; end9'd12:begin seg <= {point[7],TWO[6:0]} ; end9'd13:begin seg <= {point[7],THREE[6:0]} ; end9'd14:begin seg <= {point[7],FOUR[6:0]} ; end9'd15:begin seg <= {point[7],FIVE[6:0]} ; end9'd16:begin seg <= {point[7],SIX[6:0]} ; end9'd17:begin seg <= {point[7],SEVEN[6:0]} ; end9'd18:begin seg <= {point[7],EIGHT[6:0]} ; end9'd19:begin seg <= {point[7],NINE[6:0]} ; end9'd20:begin seg <= 8'b11111111 ; enddefault: seg <= ZERO;endcase
end//数码管选位状态机,第一段
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginc_state <= IDLE;endelse beginc_state <= n_state;end
end
//数码管选位状态机,第二段
always @(*) begincase (c_state)//空闲状态IDLE :beginif (key_flag[0]) beginn_state = S1;endelse beginn_state = IDLE;endend//数码管时位S1 :beginif (key_flag[0]) beginn_state = S2;endelse beginn_state = S1;endend //数码管分位 S2 :beginif (key_flag[0]) beginn_state = S3;endelse beginn_state = S2;endend//数码管秒位 S3 :beginif (key_flag[0]) beginn_state = IDLE;endelse beginn_state = S3;endenddefault:n_state = IDLE;endcase
endendmodule
顶层模块(top)
module top(input wire clk,input wire rst_n,input wire [3:0] key_in,output wire [7:0] seg,output wire [5:0] sel,output wire beep
);
wire [3:0] key_flag;
wire [4:0] hour;
wire [5:0] min;
wire [5:0] sec;
wire music_flag;
wire flag;key u_key//key_fsm
(/* input wire */ .clk (clk ) , /* input wire */ .rst_n (rst_n ) , /* input wire */ .key_in (key_in[0]) ,/* output reg */ .key_flag (key_flag[0]) );key u_key1
(/* input wire */ .clk (clk ) , /* input wire */ .rst_n (rst_n ) , /* input wire */ .key_in (key_in[1]) , /* output reg */ .key_flag (key_flag[1]) );key u_key2
(/* input wire */ .clk (clk ) , /* input wire */ .rst_n (rst_n ) , /* input wire */ .key_in (key_in[2]) , /* output reg */ .key_flag (key_flag[2]) );key u_key3
(/* input wire */ .clk (clk ) , /* input wire */ .rst_n (rst_n ) , /* input wire */ .key_in (key_in[3]) , /* output reg */ .key_flag (key_flag[3])
);
beep u_beep (/* input */.clk (clk ),/* input */.rst_n(rst_n),/* input */.flag (flag ),/* output */.beep (beep )
);
music u_music(/* input */.clk (clk ) ,/* input */.rst_n (rst_n ) ,/* input */.key_out (key_out ) ,/* input */.music_flag(music_flag) ,/* output */.flag(flag)
);
counter u_counter(.clk (clk) ,.rst_n (rst_n) ,.hour (hour) ,.key_flag (key_flag),.music_flag (music_flag),.min (min) ,.sec (sec)
);
seg_driver u_seg_driver(.clk (clk) ,.rst_n (rst_n) ,.hour (hour) ,.key_flag (key_flag),.min (min) ,.sec (sec) ,.seg (seg) ,.sel (sel)
);
endmodule
八、总结
虽然该代码仍有许多不足,但如果对各位有所帮助,还请各位不要吝啬手中的点赞和关注。