文章目录
- 前言
- 实验手册
- 一、实验目的
- 二、实验原理
- 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 2023
package require ::quartus::project
set_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;
else
cnt_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;
else
key_flag<=1'b0;
//有效长信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_out <= 1'b0;
end
else if(key_flag == 1'b1)
key_out <= ~key_out;
else
key_out <= key_out;
end
endmodule
计数器模块(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) begin
if (!rst_n) begin
cnt_10ms <= 26'd0;
end
else if (cnt_10ms == MAX_10ms) begin
cnt_10ms <= 26'd0;
end
else begin
cnt_10ms <= cnt_10ms + 1'd1;
end
end
//1秒计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_1s <= 26'd0;
end
else if (cnt_1s == SEC - 1'd1) begin
cnt_1s <= 26'd0;
end
else begin
cnt_1s <= cnt_1s + 1'd1;
end
end
//1天计时器_电子时钟
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_day <= 17'd0;
end
else if(cnt_day >= MAXDAY - 1'd1)begin
cnt_day <= 17'd0;
end
else if( cnt_1s == SEC -1'd1)begin
cnt_day <= cnt_day + 1'd1;
end
else if(n_state == A1) begin
cnt_day <= cnt_day + 17'd3600;
end
else if(n_state == M1) begin
cnt_day <= cnt_day - 17'd3600;
end
else if (c_state== S1 &&key_flag[2] && (cnt_day / 3600)== 0) begin
cnt_day <= cnt_day + 17'd82800;
end
else if(n_state == A2) begin
cnt_day <= cnt_day + 17'd60;
end
else if(n_state == M2) begin
cnt_day <= cnt_day - 17'd60;
end
else if(n_state == A3) begin
cnt_day <= cnt_day + 17'd1;
end
else if(n_state == M3) begin
cnt_day <= cnt_day - 17'd1;
end
else begin
cnt_day <= cnt_day;
end
end
//1天计时器_r_闹钟空间
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_day_r <= 17'd0;
end
else if(cnt_day_r >= MAXDAY - 1'd1)begin
cnt_day_r <= 17'd0;
end
else if(n_state == A4) begin
cnt_day_r <= cnt_day_r + 17'd3600;
end
else if(n_state == M4) begin
cnt_day_r <= cnt_day_r - 17'd3600;
end
else if (key_flag[2] && (cnt_day_r / 3600)== 0) begin
cnt_day_r <= cnt_day_r + 17'd82800;
end
else if(n_state == A5) begin
cnt_day_r <= cnt_day_r + 17'd60;
end
else if(n_state == M5) begin
cnt_day_r <= cnt_day_r - 17'd60;
end
else if(n_state == A6) begin
cnt_day_r <= cnt_day_r + 17'd10;
end
else if(n_state == M6) begin
cnt_day_r <= cnt_day_r - 17'd10;
end
else begin
cnt_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) begin
if (!rst_n) begin
c_state <= IDLE;
end
else begin
c_state <= n_state;
end
end
//数码管选位状态机,第二段
always @(*) begin
case (c_state)
//空闲状态
IDLE :begin
if (key_flag[0]) begin
n_state = S1;
end
else if (key_flag[3]) begin
n_state = R1;
end
else begin
n_state = IDLE;
end
end
//闹钟状态
R1 :begin
if (key_flag[0]) begin
n_state = P1;
end
else if (key_flag[3]) begin
n_state = IDLE;
end
else begin
n_state = R1;
end
end
//换位状态:时位
S1 :begin
if (key_flag[0]) begin
n_state = S2;
end
else if (key_flag[1]) begin
n_state = A1;
end
else if (key_flag[2]) begin
n_state = M1;
end
else begin
n_state = S1;
end
end
//换位状态:分位
S2 :begin
if (key_flag[0]) begin
n_state = S3;
end
else if (key_flag[1]) begin
n_state = A2;
end
else if (key_flag[2]) begin
n_state = M2;
end
else begin
n_state = S2;
end
end
//换位状态:秒位
S3 :begin
if (key_flag[0]) begin
n_state = IDLE;
end
else if (key_flag[1]) begin
n_state = A3;
end
else if (key_flag[2]) begin
n_state = M3;
end
else begin
n_state = S3;
end
end
//时位加一
A1 :begin
if (c_state == A1) begin
n_state = S1;
end
else begin
n_state = A1;
end
end
//时位减一
M1 :begin
if (c_state == M1) begin
n_state = S1;
end
else begin
n_state = M1;
end
end
//分位加一
A2 :begin
if (c_state == A2) begin
n_state = S2;
end
else begin
n_state = A2;
end
end
//分位减一
M2 :begin
if (c_state == M2) begin
n_state = S2;
end
else begin
n_state = M2;
end
end
//秒位加一
A3 :begin
if (c_state == A3) begin
n_state = S3;
end
else begin
n_state = A3;
end
end
//秒位减一
M3 :begin
if (c_state == M3) begin
n_state = S3;
end
else begin
n_state = M3;
end
end
//闹钟空间:时位
P1 :begin
if (key_flag[0]) begin
n_state = P2;
end
else if (key_flag[1]) begin
n_state = A4;
end
else if (key_flag[2]) begin
n_state = M4;
end
else begin
n_state = P1;
end
end
//闹钟空间:分位
P2 :begin
if (key_flag[0]) begin
n_state = P3;
end
else if (key_flag[1]) begin
n_state = A5;
end
else if (key_flag[2]) begin
n_state = M5;
end
else begin
n_state = P2;
end
end
//闹钟空间:秒位
P3 :begin
if (key_flag[0]) begin
n_state = R1;
end
else if (key_flag[1]) begin
n_state = A6;
end
else if (key_flag[2]) begin
n_state = M6;
end
else begin
n_state = P3;
end
end
//闹钟空间:时位加一
A4 :begin
if (c_state == A4) begin
n_state = P1;
end
else begin
n_state = A4;
end
end
//闹钟空间:时位减一
M4 :begin
if (c_state == M4) begin
n_state = P1;
end
else begin
n_state = M4;
end
end
//闹钟空间:分位加一
A5 :begin
if (c_state == A5) begin
n_state = P2;
end
else begin
n_state = A5;
end
end
//闹钟空间:分位减一
M5 :begin
if (c_state == M5) begin
n_state = P2;
end
else begin
n_state = M5;
end
end
//闹钟空间:秒位加一
A6 :begin
if (c_state == A6) begin
n_state = P3;
end
else begin
n_state = A6;
end
end
//闹钟空间:秒位减一
M6 :begin
if (c_state == M6) begin
n_state = P3;
end
else begin
n_state = M6;
end
end
default: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) begin
if (!rst_n) begin
cnt_1s <= 1'd0;
end
else if (cnt_1s == MAX_1s) begin
cnt_1s <= 1'd0;
end
else begin
cnt_1s <= cnt_1s + 1;
end
end
//5s计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_5s <= 1'd0;
end
else if (cnt_5s == MAX_5s) begin
cnt_5s <= 1'd0;
end
else if (cnt_1s == MAX_1s)begin
cnt_5s <= cnt_5s + 1;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
music_delay <= 1'd0;
end
else if (cnt_5s == MAX_5s) begin
music_delay <= 1'd0;
end
else if (music_flag)begin
music_delay <= 1'd1;
end
end
//0.3s计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_0_3s <= 1'd0;
end
else if (cnt_0_3s == MAX_0_3s) begin
cnt_0_3s <= 1'd0;
end
else begin
cnt_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)begin
if(!rst_n)begin
music_cnt <= 16'd0;
end
else if(music_cnt == music)begin
music_cnt <= 16'd0;
end
else begin
music_cnt <= music_cnt + 1'd1;
end
end
//乐谱1每隔0.5s音符切换
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_7 <= 1'd0;
end
else if (cnt_7 == MAX_7 && cnt_0_3s == MAX_0_3s) begin
cnt_7 <= 1'd0;
end
else if (cnt_0_3s == MAX_0_3s) begin
cnt_7 <= cnt_7 + 1'd1;
end
else begin
cnt_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) begin
if (!rst_n) begin
music <= 1'd0;
end
//else if(key_out[1])begin
case (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)begin
if(!rst_n)begin
flag <= 1'b0;
end
else if (music_delay)begin
flag <= (music_cnt >= duty) ? 1'b1 : 1'b0;
end
else begin
flag <= 1'b0;
end
end
endmodule
蜂鸣器发声模块(beep)
module beep (
input clk,
input rst_n,
input flag,
output reg beep
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
beep <= 1'b1;
end
else if (flag) begin
beep <= 1'b0;
end
else begin
beep <= 1'b1;
end
end
endmodule
数码管驱动模块(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) begin
if(!rst_n)begin
cnt_1s <= 10'd0;
end
else if(cnt_1s == MAX_1s)begin
cnt_1s <= 10'd0;
end
else begin
cnt_1s <= cnt_1s + 1'd1;
end
end
//20us计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_delay <= 10'd0;
end
else if(cnt_delay == CNT_DELAY_MAX - 1'd1)begin
cnt_delay <= 10'd0;
end
else begin
cnt_delay <= cnt_delay + 1'd1;
end
end
//该板块无法同时变化,只能依次闪烁,该方法为依次闪烁的间隔,人肉眼无法判断 20us
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sel <= 6'b111_110;
end
else if (cnt_delay == CNT_DELAY_MAX - 1'd1) begin
sel <= {sel[4:0],sel[5]};
end
else begin
sel <= sel ;
end
end
always @(*) begin
//若处于时位,则相应的位选信号闪烁
if (c_state == S1) begin
case (sel)
6'b111110:begin
if (cnt_1s <= MAX_1s/2) begin
cnt_num = 9'd20;
end
else cnt_num = hour / 10;
end
6'b111101: begin
if (cnt_1s <= MAX_1s/2) begin
cnt_num = 9'd20;
end
else 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;
endcase
end
//若处于分位,则相应的位选信号闪烁
else if (c_state == S2) begin
case (sel)
6'b111110: cnt_num = hour / 10;
6'b111101: cnt_num = hour % 10 + 10;
6'b111011: begin
if (cnt_1s <= MAX_1s/2) begin
cnt_num = 9'd20;
end
else cnt_num = min / 10;
end
6'b110111: begin
if (cnt_1s <= MAX_1s/2) begin
cnt_num = 9'd20;
end
else cnt_num = min % 10 + 10;
end
6'b101111: cnt_num = sec / 10;
6'b011111: cnt_num = sec % 10;
default: cnt_num = 4'd0;
endcase
end
//若处于秒位,则相应的位选信号闪烁
else if (c_state == S3) 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: begin
if (cnt_1s <= MAX_1s/2) begin
cnt_num = 9'd20;
end
else cnt_num = sec / 10;
end
6'b011111: begin
if (cnt_1s <= MAX_1s/2) begin
cnt_num = 9'd20;
end
else cnt_num = sec % 10;
end
default: cnt_num = 4'd0;
endcase
end
else 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;
endcase
end
end
//通过数字num解析出seg值
always @(*) begin
case (cnt_num)
9'd0:begin seg <= ZERO ; end
9'd1:begin seg <= ONE ; end
9'd2:begin seg <= TWO ; end
9'd3:begin seg <= THREE ; end
9'd4:begin seg <= FOUR ; end
9'd5:begin seg <= FIVE ; end
9'd6:begin seg <= SIX ; end
9'd7:begin seg <= SEVEN ; end
9'd8:begin seg <= EIGHT ; end
9'd9:begin seg <= NINE ; end
9'd10:begin seg <= {point[7],ZERO[6:0]} ; end
9'd11:begin seg <= {point[7],ONE[6:0]} ; end
9'd12:begin seg <= {point[7],TWO[6:0]} ; end
9'd13:begin seg <= {point[7],THREE[6:0]} ; end
9'd14:begin seg <= {point[7],FOUR[6:0]} ; end
9'd15:begin seg <= {point[7],FIVE[6:0]} ; end
9'd16:begin seg <= {point[7],SIX[6:0]} ; end
9'd17:begin seg <= {point[7],SEVEN[6:0]} ; end
9'd18:begin seg <= {point[7],EIGHT[6:0]} ; end
9'd19:begin seg <= {point[7],NINE[6:0]} ; end
9'd20:begin seg <= 8'b11111111 ; end
default: seg <= ZERO;
endcase
end
//数码管选位状态机,第一段
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
c_state <= IDLE;
end
else begin
c_state <= n_state;
end
end
//数码管选位状态机,第二段
always @(*) begin
case (c_state)
//空闲状态
IDLE :begin
if (key_flag[0]) begin
n_state = S1;
end
else begin
n_state = IDLE;
end
end
//数码管时位
S1 :begin
if (key_flag[0]) begin
n_state = S2;
end
else begin
n_state = S1;
end
end
//数码管分位
S2 :begin
if (key_flag[0]) begin
n_state = S3;
end
else begin
n_state = S2;
end
end
//数码管秒位
S3 :begin
if (key_flag[0]) begin
n_state = IDLE;
end
else begin
n_state = S3;
end
end
default:n_state = IDLE;
endcase
end
endmodule
顶层模块(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
八、总结
虽然该代码仍有许多不足,但如果对各位有所帮助,还请各位不要吝啬手中的点赞和关注。