开发板:此款开发板使用的是 ALTERA 公司的 Cyclone IV 系列 FPGA,型号为 EP4CE6F17C8, 256 个引脚的 FBGA 封装。
题目:在EDA开发板上实现电子时钟功能
要求:实现电子时钟程序编写,实现在7段数码管显示时、分、秒,使用4x4矩阵按键模拟调节时钟指令输入按键,并实现整点报时功能。按键功能包括但不限以下功能:选择(时分秒选择按键、可以一一对应,也可以只用1个按键)、复位、+(时分秒加)、-(时分秒减)。
程序设计步骤:
1、七段数码管显示
1 |
2. |
4 |
6. |
5 |
7 |
图1 开机显示画面,其中.为时分秒的间隔
2、 数据输入:在图1所示的状态下,用4x4矩阵按键来进行时分秒的调节。
3、整点报时:到整点时,自动播放一段音乐。
4、一键复原:完成时间调节后,按“复原”按键,七段数码管显示最初的状态,回到图1所示状态。
以上既是该电子时钟的设计要求。
功能设计部分,分为以下7个模块。
EDA_clock | 顶层模块文件 | 将各个模块进行整合 |
clk1hz | 时钟频率 | 将50MHz晶振频率转化为1秒钟的频率,1Hz=1s |
cnt24 | 24计数器 | 时钟中 “时”的计数,并判断 |
cnt60 | 60计数器 | 时钟中“分”、“秒”的计数,并判断个位十位实时状态 |
key4x4_clock | 4x4矩阵键盘 | 矩阵键盘功能设置 |
seg_dynamic | 七段数码管 | 接收当前计数器值 |
buzzer | 蜂鸣器控制 | 当整点时播放音乐3秒钟 |
对于以下各个代码块有较为详细的标注,请自行对照理解,有疑惑的可以评论区,看到回复
1. EDA_clock.v
创建工程后,该模块为顶层文件
代码如下:
/**
* @copyright: 2782978674@qq.com
* @author: xietiancheng
* @date: 2023.6.4
*
**/
module EDA_clock(
input wire sys_clk,
input wire rst_n,
input wire [3:0] key_in_y, // 输入矩阵键盘的列信号(KEY0~KEY3)
output wire [3:0] key_out_x, // 输出矩阵键盘的行信号(KEY4~KEY7)
output wire [5:0] sel , //数码管位选信号
output wire [7:0] seg , //数码管段选信号
output wire buzzer
);
wire clk_1hz;
wire count_s;
wire count_m;
wire [6:0] Sec; //秒
wire [6:0] Sec_out; //秒输出
wire [6:0] Min; //分
wire [6:0] Min_out; //分输出
wire [6:0] Hour; //时
wire [6:0] Hour_out; //时输出
wire [4:0] Sec1; //秒的个位
wire [4:0] Sec2; //秒的十位
wire [4:0] Min1; //分的个位
wire [4:0] Min2; //分的十位
wire [4:0] Hour1; //时的个位
wire [4:0] Hour2; //时的十位
wire en_s; //秒的使能信号
wire en_m; //分的使能信号
wire en_h; //时的使能信号
wire time_en;//当设置时间时,设置使能信号,当有效时时间停止,进行时钟各位赋值。
// assign en_s=1'b1;
assign en_m=count_s && en_s;
assign en_h= (Min_out == 7'd59 && Sec_out == 7'd58)?1:0;
//1hz代表一秒钟,时钟频率设置
clk1hz
#(
.N (26'd25_000_000 ) //代表
)
clk1hz_inst
(
.clk(sys_clk),
.rst_n(rst_n),
.clk_out(clk_1hz)
);
/*********秒和分使用使能信号en_s和en_m来控制*********/
//秒的代码
cnt60 cnt60_inst1(
.clk(clk_1hz),
.rst_n(rst_n),
.en(en_s),
.time_en(time_en),
.cnt0(Sec),
.cnt_out(Sec_out),
.cnt1(Sec1), //秒的个位
.cnt2(Sec2), //秒的十位
.flag(count_s) //秒的使能信号
);
//分的代码
cnt60 cnt60_inst2(
.clk(clk_1hz),
.rst_n(rst_n),
.en(en_m),
.time_en(time_en),
.cnt0(Min),
.cnt_out(Min_out),
.cnt1(Min1), //分的个位
.cnt2(Min2), //分的十位
.flag(count_m)
);
//时间的设置
cnt24 cnt24_inst(
.clk(clk_1hz),
.rst_n(rst_n),
.en(en_h),
.time_en(time_en),
.cnt0(Hour),
.cnt_out(Hour_out),
.cnt1(Hour1),
.cnt2(Hour2)
);
//数码管控制显示模块
seg_dynamic seg_dynamic_inst
(
.clk(sys_clk),
.rst_n(rst_n),
.Sec1(Sec1), //秒的个位
.Sec2(Sec2), //秒的十位
.Min1(Min1), //分的个位
.Min2(Min2), //分的十位
.Hour1(Hour1), //时的个位
.Hour2(Hour2), //时的十位
.sel (sel), //数码管位选信号
.seg(seg) //数码管段选信号
);
//矩阵键盘设置调节时间
key4x4_clock key4x4_clock_inst(
.clk(sys_clk) ,
.rst_n(rst_n) ,
.Sec_out (Sec_out) ,
.Min_out (Min_out) ,
.Hour_out (Hour_out) ,
.key_in_y(key_in_y) , // 输入矩阵键盘的列信号(KEY0~KEY3)
.key_out_x(key_out_x) , // 输出矩阵键盘的行信号(KEY4~KEY7)
.Sec(Sec) , //秒修改
.Min(Min) , //分修改
.Hour(Hour) , //时修改
.en(en_s) , //时钟是否运行的使能信号
.time_en(time_en)
);
buzzer buzzer_inst(
.clk (sys_clk),
.rst_n (rst_n ),
.Sec_out(Sec_out),
.Min_out(Min_out),
.buzzer (buzzer)
);
endmodule
/**********************************************************
quartusII综合报错(Error (10028): Can't resolve multiple constant drivers for net "txd_cnt[3]")
出现这个错误的原因在于,在不同的always逻辑块中,对同一个reg变量进行了赋值。
在多个alwasy逻辑块同时并行工作的时候,会出现冲突。解决的办法就是,对于一个变量,只在一个always块中,进行赋值。
***********************************************************/
2. clk1hz.v
将50MHz转化为1Hz的频率,可通过parameter参数N进行更改转化为其他频率
代码如下:
module clk1hz
#(parameter N = 26'd25_000_000)
(
input clk,
input rst_n,
output reg clk_out
);
reg [32:0] tmp;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
tmp <= 0;
clk_out<=0;
end
else if (tmp == N-1)
begin
clk_out<= ~clk_out;
tmp <= 0;
end
else
begin
tmp <= tmp+1;
clk_out <=clk_out;
end
end
endmodule
3. cnt24.v
代码如下:
module cnt24
// #(parameter CNT = 5'd0)
(
input wire clk ,
input wire rst_n ,
input wire en ,
input wire time_en ,
input wire [6:0] cnt0,
output reg [6:0] cnt_out, //控制cnt的传入传出
output reg [4:0] cnt1,
output reg [4:0] cnt2
);
reg [6:0] cnt;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) begin
cnt<=cnt0;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
else
begin
if(en==1)
begin
if(cnt<23)
begin
cnt<=cnt+1;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
else begin
cnt<=0;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
end
else if(time_en==1'b1)begin
cnt <=cnt0;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
else begin //if(en_m==0 &&en == 0)begin
cnt<=cnt;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
end
end
endmodule
/*
if(cnt1<10)
begin
cnt1 <= cnt1+1;
end
else
begin
cnt1<= 0;
cnt2<=cnt2+1;
end
*/
4. cnt60.v
代码如下:
module cnt60
(
input wire clk,
input wire rst_n,
input wire en,
input wire time_en, //修改时钟值的使能信号
input wire [6:0] cnt0, //控制cnt的传入传出
output reg [6:0] cnt_out, //控制cnt的传入传出
output reg [4:0] cnt1,
output reg [4:0] cnt2,
output reg flag
);
reg [6:0] cnt; //时钟始终变化着运行着的值
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt<=cnt0;
//控制个位和十位的变化
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
else
begin
if(en==1)
begin
if(cnt<59)
begin
cnt <=cnt +1;
//控制个位和十位的变化
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
else begin
cnt <= 0;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
end
else if(time_en==1'b1)begin
cnt <=cnt0 ;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
else begin
cnt <=cnt;
cnt1<=cnt%10;
cnt2<=cnt/10;
cnt_out<=cnt;
end
end
end
//当cnt到达58时flag的变化
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
flag<=0;
else if(cnt <58)
flag <=0;
else if(cnt == 58)
flag <= 1;
else flag <= 0;
end
endmodule
5. key4x4_clock.v
代码如下:
`timescale 1ns / 1ps //精度 可不用
module key4x4_clock(
input wire clk,
input wire rst_n,
input wire [6:0] Sec_out,
input wire [6:0] Min_out,
input wire [6:0] Hour_out,
input wire [3:0] key_in_y, // 输入矩阵键盘的列信号(KEY0~KEY3)
output reg [3:0] key_out_x, // 输出矩阵键盘的行信号(KEY4~KEY7)
output reg [6:0] Sec, //秒修改
output reg [6:0] Min, //分修改
output reg [6:0] Hour, //时修改
output reg en , //时钟是否运行的使能信号
output reg time_en //时钟是否运行的使能信号
);
//寄存器定义
reg [19:0] count;
reg [1:0] count1; //时钟修改的时候,在修改时它的值等于当前已经变化后的值,count1作为该修改值的使能信号
//==============================================
// 输出矩阵键盘的行信号,20ms 扫描矩阵键盘一次,采样频率小于按键毛刺频率,相当于滤除掉了高频毛刺信号。
//==============================================
always @(posedge clk or negedge rst_n) //检测时钟的上升沿和复位的下降沿
begin
if(!rst_n) begin //复位信号低有效
count <= 20'd0; //计数器清 0
key_out_x <= 4'b1111;
end
else begin
if(count == 20'd0) //0ms 扫描第一行矩阵键盘
begin
key_out_x <= 4'b1110; //开始扫描第一行矩阵键盘,第一行输出0
count <= count + 20'b1; //计数器加1
end
else if(count == 20'd249_999) //5ms 扫描第二行矩阵键盘,5ms 计数(50M/200-1=249_999)
begin
key_out_x <= 4'b1101; //开始扫描第二行矩阵键盘,第二行输出 0
count <= count + 20'b1; //计数器加 1
end
else if(count ==20'd499_999) //10ms扫描第三行矩阵键盘 ,10ms计数(50M/100-1=499_999)
begin
key_out_x <= 4'b1011; //扫描第三行矩阵键盘,第三行输出0
count <= count + 20'b1; //计数器加1
end
else if(count ==20'd749_999) //15ms扫描第四行矩阵键盘 ,15ms 计 数(50M/67.7-1=749_999)
begin
key_out_x <= 4'b0111; //扫描第四行矩阵键盘,第四行输出0
count <= count + 20'b1; //计数器加 1
end
else if(count ==20'd999_999) //20ms 计数(50M/50-1=999_999)
begin
count <= 0; //计数器为 0
end
else
count <= count + 20'b1; //计数器加 1
end
end
//====================================================
// 采样列的按键信号
//====================================================
reg [3:0] key_h1_scan; //第一行按键扫描值 KEY
reg [3:0] key_h1_scan_r; //第一行按键扫描值寄存器 KEY
reg [3:0] key_h2_scan; //第二行按键扫描值 KEY
reg [3:0] key_h2_scan_r; //第二行按键扫描值寄存器 KEY
reg [3:0] key_h3_scan; //第三行按键扫描值 KEY
reg [3:0] key_h3_scan_r; //第三行按键扫描值寄存器 KEY
reg [3:0] key_h4_scan; //第四行按键扫描值 KEY
reg [3:0] key_h4_scan_r; //第四行按键扫描值寄存器 KEY
always @(posedge clk)
begin
if(!rst_n) begin //复位信号低有效
key_h1_scan <= 4'b1111;
key_h2_scan <= 4'b1111;
key_h3_scan <= 4'b1111;
key_h4_scan <= 4'b1111;
end
else begin
if(count == 20'd124_999) //2.5ms 扫描第一行矩阵键盘值
key_h1_scan<=key_in_y; //扫描第一行的矩阵键盘值
else if(count == 20'd374_999) //7.5ms 扫描第二行矩阵键盘值
key_h2_scan<=key_in_y; //扫描第二行的矩阵键盘值
else if(count == 20'd624_999) //12.5ms 扫描第三行矩阵键盘值
key_h3_scan<=key_in_y; //扫描第三行的矩阵键盘值
else if(count == 20'd874_999) //17.5ms 扫描第四行矩阵键盘值
key_h4_scan<=key_in_y; //扫描第四行的矩阵键盘值
end
end
//====================================================
// 按键信号锁存一个时钟节拍
//====================================================
always @(posedge clk)
begin
key_h1_scan_r <= key_h1_scan;
key_h2_scan_r <= key_h2_scan;
key_h3_scan_r <= key_h3_scan;
key_h4_scan_r <= key_h4_scan;
end
wire [3:0] flag_h1_key = key_h1_scan_r[3:0] & (~key_h1_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
wire [3:0] flag_h2_key = key_h2_scan_r[3:0] & (~key_h2_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
wire [3:0] flag_h3_key = key_h3_scan_r[3:0] & (~key_h3_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
wire [3:0] flag_h4_key = key_h4_scan_r[3:0] & (~key_h4_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
/* 状态参数的定义 --- 可以使用独热码 */
parameter IDLE = 5'b00001, //初始状态(也是终态) 使能信号正常,可以初次进行时钟正常运行
ONE = 5'b00010, //设置秒的个十位
TWO = 5'b00100, //设置分
THREE = 5'b01000, //设置时
SETTING = 5'b10000; //时间设置键 使能信号失效
/**************按键排列******************
K1设置键 K2设置秒 K3设置分 K4设置时
K5确认键
K9时间加 K10时间减
*****************************************/
reg [4:0] state ; //状态跳变
//状态跳转
always@ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
state <=IDLE;
else
case(state)
IDLE: if(flag_h1_key[0]==1'b1)
state <= SETTING;
else
state <= IDLE;
SETTING:if(flag_h1_key[1]==1'b1)
state <= ONE;
else if(flag_h1_key[2]==1'b1)
state <= TWO;
else if(flag_h1_key[3]==1'b1)
state <= THREE;
else if(flag_h2_key[0]==1'b1)
state <= IDLE;
else
state <= SETTING;
ONE: if(flag_h1_key[2])
state <= TWO;
else if(flag_h1_key[3]==1'b1)
state <= THREE;
else if(flag_h2_key[0]==1'b1)
state <= IDLE;
else
state <= ONE;
TWO: if(flag_h1_key[1])
state <= ONE;
else if(flag_h1_key[3]==1'b1)
state <= THREE;
else if(flag_h2_key[0]==1'b1)
state <= IDLE;
else
state <= TWO;
THREE: if(flag_h1_key[1]==1'b1)
state <= ONE;
else if(flag_h1_key[2]==1'b1)
state <= TWO;
else if(flag_h2_key[0]==1'b1)
state <= IDLE;
else
state <= THREE;
default:state <=IDLE;
endcase
end
/* 描述状态IDLE和SETTING的输出 */
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)begin
en <= 1'b0;
time_en <= 1'b0;
count1 <=1'b0;
end
else if(state <= IDLE) begin //相当于确认按键,按下以后使能信号有效,时钟继续运行
en <= 1'b1;
time_en <= 1'b0;
end
else if(state <= SETTING)begin
en <= 1'b0;
time_en <= 1'b1;
count1 <= 1'b1;
end
else if(flag_h2_key[0]==1'b1)
count1 <= 1'b0;
else begin
en <= en;
end
end
/* 描述ONE状态的输出 */
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)begin
Sec <= 7'd57;
end
else if(state==SETTING && count1 ==1'b0)begin
Sec <= Sec_out;
end
else if (state == ONE && flag_h3_key[0] == 1'b1 && Sec < 59)
Sec <=Sec + 1'b1;
else if (state == ONE && flag_h3_key[1] == 1'b1 && Sec >0)
Sec <=Sec - 1'b1;
else if(state == ONE && flag_h3_key[0] == 1'b1 && Sec == 7'd59)
Sec<= 7'd0;
else if(state == ONE && flag_h3_key[1] == 1'b1 && Sec == 7'd0)
Sec<=7'd59;
else
Sec <=Sec;
end
/* 描述TWO状态的输出 */
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)begin
Min <= 7'd46;
end
else if(state==SETTING && count1 ==1'b0)begin
Min <= Min_out;
end
else if (state == TWO && flag_h3_key[0] == 1'b1 && Min < 59)
Min <= Min + 1'b1;
else if (state == TWO && flag_h3_key[1] == 1'b1 && Min >0)
Min <= Min - 1'b1;
else if(state == TWO && flag_h3_key[0] == 1'b1 && Min == 7'd59)
Min<= 7'd0;
else if(state == TWO && flag_h3_key[1] == 1'b1 && Min == 7'd0)
Min<=7'd59;
else
Min <= Min;
end
/* 描述THREE状态的输出 */
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)begin
Hour<= 7'd12;
end
else if(state==SETTING && count1 ==1'b0)begin
Hour <= Hour_out;
end
else if (state == THREE && flag_h3_key[0] == 1'b1 && Hour < 7'd23)
Hour <=Hour + 1'b1;
else if (state == THREE && flag_h3_key[1] == 1'b1 && Hour > 7'd0)
Hour <=Hour - 1'b1;
else if(state == THREE && flag_h3_key[0] == 1'b1 && Hour == 7'd23)
Hour<= 7'd0;
else if(state == THREE && flag_h3_key[1] == 1'b1 && Hour == 7'd0)
Hour<=7'd23;
else
Hour <=Hour;
end
endmodule
6. seg_dynamic.v
代码如下:
module seg_dynamic
(
input wire clk,
input wire rst_n,
input wire [4:0] Sec1, //秒的个位
input wire [4:0] Sec2, //秒的十位
input wire [4:0] Min1, //分的个位
input wire [4:0] Min2, //分的十位
input wire [4:0] Hour1, //时的个位
input wire [4:0] Hour2, //时的十位
output reg [5:0] sel , //数码管位选信号
output reg [7:0] seg //数码管段选信号
);
//十六进制数显示编码
parameter
SEG_0 = 8'b1100_0000,
SEG_1 = 8'b1111_1001,
SEG_2 = 8'b1010_0100,
SEG_3 = 8'b1011_0000,
SEG_4 = 8'b1001_1001,
SEG_5 = 8'b1001_0010,
SEG_6 = 8'b1000_0010,
SEG_7 = 8'b1111_1000,
SEG_8 = 8'b1000_0000,
SEG_9 = 8'b1001_0000,
SEG_A = 8'b1000_1000, SEG_B = 8'b1000_0011,
SEG_C = 8'b1100_0110, SEG_D = 8'b1010_0001,
SEG_E = 8'b1000_0110, SEG_F = 8'b1000_1110;
parameter IDLE = 8'b1111_1111; //不显示状态
//reg define
reg [23:0] data_reg ; //待显示数据寄存器
reg [15:0] cnt_1ms ; //1ms计数器
reg flag_1ms ; //1ms标志信号
reg [2:0] cnt_sel ; //数码管位选计数器
reg [5:0] sel_reg ; //位选信号
reg [3:0] data_disp ; //当前数码管显示的数据
parameter CNT_MAX = 16'd49_999; //数码管刷新时间计数最大值
reg [7:0] led_SG ; //秒的个位
reg [7:0] led_SS ; //秒的十位
reg [7:0] led_MG ; //分的个位
reg [7:0] led_MS ; //分的十位
reg [7:0] led_HG ; //时的个位
reg [7:0] led_HS ; //时的十位
/****************************************************
*****************数码管段选信号**********************
******************************************************/
//七段数码管显示秒的个位
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
led_SG<=IDLE;
else begin
case(Sec1)
0: led_SG<=SEG_0;
1: led_SG<=SEG_1;
2: led_SG<=SEG_2;
3: led_SG<=SEG_3;
4: led_SG<=SEG_4;
5: led_SG<=SEG_5;
6: led_SG<=SEG_6;
7: led_SG<=SEG_7;
8: led_SG<=SEG_8;
9: led_SG<=SEG_9;
default led_SG<=IDLE;
endcase
end
end
//七段数码管显示秒的十位
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
led_SS<=IDLE;
else begin
case(Sec2)
0: led_SS<=SEG_0;
1: led_SS<=SEG_1;
2: led_SS<=SEG_2;
3: led_SS<=SEG_3;
4: led_SS<=SEG_4;
5: led_SS<=SEG_5;
6: led_SS<=SEG_6;
7: led_SS<=SEG_7;
8: led_SS<=SEG_8;
9: led_SS<=SEG_9;
default led_SS<=IDLE;
endcase
end
end
//七段数码管显示分的个位
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
led_MG<=IDLE;
else begin
case(Min1)
0: led_MG<=8'b0100_0000;
1: led_MG<=8'b0111_1001;
2: led_MG<=8'b0010_0100;
3: led_MG<=8'b0011_0000;
4: led_MG<=8'b0001_1001;
5: led_MG<=8'b0001_0010;
6: led_MG<=8'b0000_0010;
7: led_MG<=8'b0111_1000;
8: led_MG<=8'b0000_0000;
9: led_MG<=8'b0001_0000;
default led_MG<=8'b0111_1111;
endcase
end
end
//七段数码管显示分的十位
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
led_MS<=IDLE;
else begin
case(Min2)
0: led_MS<=SEG_0;
1: led_MS<=SEG_1;
2: led_MS<=SEG_2;
3: led_MS<=SEG_3;
4: led_MS<=SEG_4;
5: led_MS<=SEG_5;
6: led_MS<=SEG_6;
7: led_MS<=SEG_7;
8: led_MS<=SEG_8;
9: led_MS<=SEG_9;
default led_MS<=IDLE;
endcase
end
end
//七段数码管显示时的个位
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
led_HG <= IDLE;
else begin
case(Hour1)
0: led_HG <= 8'b0100_0000;
1: led_HG <= 8'b0111_1001;
2: led_HG <= 8'b0010_0100;
3: led_HG <= 8'b0011_0000;
4: led_HG <= 8'b0001_1001;
5: led_HG <= 8'b0001_0010;
6: led_HG <= 8'b0000_0010;
7: led_HG <= 8'b0111_1000;
8: led_HG <= 8'b0000_0000;
9: led_HG <= 8'b0001_0000;
default led_HG <= 8'b0111_1111;
endcase
end
end
//七段数码管显示时的十位
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
led_HS<=IDLE;
else begin
case(Hour2)
0: led_HS<=SEG_0;
1: led_HS<=SEG_1;
2: led_HS<=SEG_2;
3: led_HS<=SEG_3;
// 4: led_HS<=SEG_4;
// 5: led_HS<=SEG_5;
// 6: led_HS<=SEG_6;
// 7: led_HS<=SEG_7;
// 8: led_HS<=SEG_8;
// 9: led_HS<=SEG_9;
default led_HS<=IDLE;
endcase
end
end
/****************************************************
*****************数码管位选信号**********************
******************************************************/
//cnt_1ms:1ms循环计数
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
cnt_1ms <= 16'd0;
else if(cnt_1ms == CNT_MAX)
cnt_1ms <= 16'd0;
else
cnt_1ms <= cnt_1ms + 1'b1;
//flag_1ms:1ms标志信号
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
flag_1ms <= 1'b0;
else if(cnt_1ms == CNT_MAX - 1'b1)
flag_1ms <= 1'b1;
else
flag_1ms <= 1'b0;
//cnt_sel:从0到5循环数,用于选择当前显示的数码管
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
cnt_sel <= 3'd0;
else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
cnt_sel <= 3'd0;
else if(flag_1ms == 1'b1)
cnt_sel <= cnt_sel + 1'b1;
else
cnt_sel <= cnt_sel;
//数码管位选信号寄存器
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
sel_reg <= 6'b000_000;
else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))
sel_reg <= 6'b000_001;
else if(flag_1ms == 1'b1)
sel_reg <= sel_reg << 1;
else
sel_reg <= sel_reg;
//控制数码管的位选信号,使六个数码管轮流显示
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
seg <= 8'hff;
else if( flag_1ms == 1'b1)
case(cnt_sel)
3'd0: seg <= led_HS; //给第6个数码管赋十万位值
3'd1: seg <= led_HG; //给第5个数码管赋万位值
3'd2: seg <= led_MS; //给第4个数码管赋千位值
3'd3: seg <= led_MG ; //给第3个数码管赋百位值
3'd4: seg <= led_SS ; //给第2个数码管赋十位值
3'd5: seg <= led_SG ; //给第1个数码管赋个位值
default:seg <= 8'hff;
endcase
else
seg <= seg;
//sel:数码管位选信号赋值
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
sel <= 6'b111_111;
else
sel <= ~sel_reg;
endmodule
7. buzzer.v
buzzer==0响否则=1不响,歌曲放了两首,都能使用,自行选择,但意义不大。
代码如下:
module buzzer(
input wire clk , //系统时钟50MHz
input wire rst_n ,
input wire [6:0] Sec_out ,
input wire [6:0] Min_out ,
// input wire Min1 ,
// input wire Min2 ,
output reg buzzer //蜂鸣器输出端
);
// assign buzzer =( Sec_out==1'b0 && Min_out==1'b0 )?0:1; //只响一秒钟
/**********--------------森林狂想曲-----------------************************/
//模块名称song
reg beep_r; //寄存器
reg[7:0] state; //乐谱状态机
reg[16:0]count,count_end;
reg[23:0]count1;
//乐谱参数:D=F/2K (D:参数,F:时钟频率,K:音高频率)
parameter L_3 = 17'd75850, //低音3
L_5 = 17'd63776, //低音5
L_6 = 17'd56818, //低音6
L_7 = 17'd50618, //低音7
M_1 = 17'd47774, //中音1
M_2 = 17'd42568, //中音2
M_3 = 17'd37919, //中音3
M_5 = 17'd31888, //中音5
M_6 = 17'd28409, //中音6
H_1 = 17'd23889; //高音1
parameter TIME = 12000000; //控制每一个音的长短(250ms)
// assign buzzer = beep_r; //输出音乐
//★★★以下两个时序逻辑模块较为重点关注
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
buzzer <= 1'b1;
end
else if(Min_out==1'b0 && Sec_out<=7'd3) //整点报时并3s后停止
buzzer <= beep_r;
else
buzzer <=1'b1;
end
always@(posedge clk) begin
count <= count + 1'b1; //计数器加1
if(count == count_end) begin
count <= 17'h0; //计数器清零
beep_r <= !beep_r; //输出取反
end
end
//曲谱 产生分频的系数并描述出曲谱
always @(posedge clk) begin
if(count1 < TIME) //一个节拍250mS
count1 = count1 + 1'b1;
else begin
count1 = 24'd0;
if(state == 8'd63)
state = 8'd0;
else
state = state + 1'b1;
case(state)
8'd0:count_end = L_6;
8'd1:count_end=M_1;
8'd2:count_end=M_3;
8'D3:count_end=M_5;
8'D4,8'D5:count_end=M_3;
8'D6:count_end=M_3;
8'D7:count_end=M_2;
8'D8,8'D9:count_end=M_3;
8'D10:count_end=M_3;
8'D11:count_end=M_2;
8'D12,8'D13:count_end=M_3;
8'D14:count_end=L_6;
8'D15:count_end=L_7;
8'D16:count_end=M_1;
8'D17:count_end=M_3;
8'D18:count_end=M_2;
8'D19:count_end=M_1;
8'D20,8'D21:count_end=L_6;
8'D22,8'D23:count_end=L_5;
8'D24,8'D25,8'D26,8'D27,8'D28,8'D29,8'D30,8'D31:count_end=L_3;
8'd32:count_end = L_6;
8'd33:count_end=M_1;
8'd34:count_end=M_3;
8'D35:count_end=M_5;
8'D36,8'D37:count_end=M_3;
8'D38:count_end=M_3;
8'D39:count_end=M_2;
8'D40,8'D41:count_end=M_3;
8'D42:count_end=M_3;
8'D43:count_end=M_2;
8'D44,8'D45:count_end=M_3;
8'D46:count_end=L_6;
8'D47:count_end=L_7;
8'D48:count_end=M_1;
8'D49:count_end=M_3;
8'D50:count_end=M_2;
8'D51:count_end=M_1;
8'D52,8'D53:count_end=L_6;
8'D54,8'D55:count_end=L_5;
8'D56,8'D57,8'D58,8'D59,8'D60,8'D61:count_end=L_6;
8'D62:count_end=L_6;
8'D63:count_end=L_7;
default: count_end = 16'h0;
endcase
end
end
/************************************************************************
-------------------------两只老虎----------------------------------------
//中间信号定义
reg [16:0] cnt0 ; //产生PWM的计数器
wire add_cnt0;
wire end_cnt0;
reg [7:0] cnt1 ; //每个音符持续时间的计数器
wire add_cnt1;
wire end_cnt1;
reg [5:0] cnt2 ; //《两只老虎》共32个音节
wire add_cnt2;
wire end_cnt2;
reg [16:0] pre_set ; //存放每个音节的频率在系统中的时钟个数
//每个音符对应的系统周期计数,中音
localparam M1=95602, //音符1do
M2=85178, //音符rui
M3=75872, //音符mi
M4=71633, //音符fa
M5=63775, //音符so
M6=56818, //音符la
M7=50607; //音符xi
//每个音符对应的系统周期计数,低音音符so,频率392
//周期是1/392s,换算成ns是2551020ns,每个
//系统时钟周期是20ns,所以上述是2551020/20个系统周期个数127,551
localparam D5=127551; //音符so,低音
//每个音节的频率在系统时钟周期下对应的系统周期个数
//--------------------------------------------
//比如:音符1的频率是523HZ,它的周期是1/523s,换算成ns是1912045ns,每个
//系统时钟周期是20ns,所以上述是1912045/20个系统周期个数,即cnt0的计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0<=0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0<=0;
else
cnt0<=cnt0+1;
end
end
assign add_cnt0=1'b1;
assign end_cnt0=add_cnt0 && cnt0==pre_set-1;
// assign buzzer=(cnt0>=(pre_set/2) && Min_out==1'b0)?1:0; //每个音符的占空比为50%
//每个音符持续一段时间
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
buzzer <= 1'b1;
end
else if(cnt0>=(pre_set/2) && Min_out==1'b0 && Sec_out<=7'd3)
buzzer <= 1'b0;
else
buzzer <=1'b1;
end
//每个音符持续一段时间
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1<=0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1<=0;
else
cnt1<=cnt1+1;
end
end
assign add_cnt1=end_cnt0;
assign end_cnt1=add_cnt1 && cnt1==150-1;
//计32个音符(两只老虎共32音节)
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2<=0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2<=0;
else
cnt2<=cnt2+1;
end
end
assign add_cnt2=end_cnt1;
assign end_cnt2=add_cnt2 && cnt2==32-1;
//存放歌曲的简谱
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
pre_set<=0;
end
else begin
case(cnt2)
0:pre_set<=M1;
1:pre_set<=M2;
2:pre_set<=M3;
3:pre_set<=M1;
4:pre_set<=M1;
5:pre_set<=M2;
6:pre_set<=M3;
7:pre_set<=M1;
8:pre_set<=M3;
9:pre_set<=M4;
10:pre_set<=M5;
11:pre_set<=M3;
12:pre_set<=M4;
13:pre_set<=M5;
14:pre_set<=M5;
15:pre_set<=M6;
16:pre_set<=M5;
17:pre_set<=M4;
18:pre_set<=M3;
19:pre_set<=M1;
20:pre_set<=M5;
21:pre_set<=M6;
22:pre_set<=M5;
23:pre_set<=M4;
24:pre_set<=M3;
25:pre_set<=M1;
26:pre_set<=M2;
27:pre_set<=D5;
28:pre_set<=M1;
29:pre_set<=M2;
30:pre_set<=D5;
31:pre_set<=M1;
default:pre_set<=0;
endcase
end
end
*/
endmodule
对于以上各个模块内,进行了非常清晰的层次化设计,大部分都有了较为详细的标注,并通过了基础测试验证。从顶层文件开始,对照各个参数寄存器等依次往下进行理解比较,最后得出自己的设计结果。
对于各个引脚配置如下:
对于引脚的配置,可直接将如下配置直接复制到EDA_clock.qsf文件中,然后进行综合即可直接配置完成。
set_location_assignment PIN_E1 -to sys_clk
set_location_assignment PIN_N13 -to rst_n
set_location_assignment PIN_R14 -to seg[0]
set_location_assignment PIN_N9 -to sel[0]
set_location_assignment PIN_P9 -to sel[1]
set_location_assignment PIN_M10 -to sel[2]
set_location_assignment PIN_N11 -to sel[3]
set_location_assignment PIN_P11 -to sel[4]
set_location_assignment PIN_M11 -to sel[5]
set_location_assignment PIN_N16 -to seg[1]
set_location_assignment PIN_P16 -to seg[2]
set_location_assignment PIN_T15 -to seg[3]
set_location_assignment PIN_P15 -to seg[4]
set_location_assignment PIN_N12 -to seg[5]
set_location_assignment PIN_N15 -to seg[6]
set_location_assignment PIN_R16 -to seg[7]
set_location_assignment PIN_B5 -to key_in_y[3]
set_location_assignment PIN_A4 -to key_in_y[2]
set_location_assignment PIN_B4 -to key_in_y[1]
set_location_assignment PIN_A3 -to key_in_y[0]
set_location_assignment PIN_B3 -to key_out_x[3]
set_location_assignment PIN_A2 -to key_out_x[2]
set_location_assignment PIN_B1 -to key_out_x[1]
set_location_assignment PIN_C2 -to key_out_x[0]
set_location_assignment PIN_C11 -to buzzer
运行成功:文章来源:https://www.toymoban.com/news/detail-761852.html
文章来源地址https://www.toymoban.com/news/detail-761852.html
到了这里,关于基于FPGA的电子时钟设计与实现 (在EDA开发板上实现电子时钟功能)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!