基于FPGA的电子时钟设计与实现 (在EDA开发板上实现电子时钟功能)

这篇具有很好参考价值的文章主要介绍了基于FPGA的电子时钟设计与实现 (在EDA开发板上实现电子时钟功能)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

开发板:此款开发板使用的是 ALTERA 公司的 Cyclone IV 系列 FPGA,型号为 EP4CE6F17C8, 256 个引脚的 FBGA 封装。

26'd25_000_000,fpga开发

 题目:在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

​

        对于以上各个模块内,进行了非常清晰的层次化设计,大部分都有了较为详细的标注,并通过了基础测试验证。从顶层文件开始,对照各个参数寄存器等依次往下进行理解比较,最后得出自己的设计结果。


对于各个引脚配置如下:

26'd25_000_000,fpga开发

       对于引脚的配置,可直接将如下配置直接复制到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

26'd25_000_000,fpga开发

运行成功:

26'd25_000_000,fpga开发文章来源地址https://www.toymoban.com/news/detail-761852.html

到了这里,关于基于FPGA的电子时钟设计与实现 (在EDA开发板上实现电子时钟功能)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于FPGA的数字时钟设计与实现(含源码)

    随着数字电子技术的不断发展,基于FPGA(现场可编程门阵列)的数字时钟设计方案逐渐成为了一种流行的选择。本篇博客将详细介绍如何利用FPGA实现一个简单的数字时钟,涉及到分频器、数码管驱动、时分秒计数、三八译码器和扫描数码管等模块。 1. 系统设计概述 在本设计

    2024年02月05日
    浏览(48)
  • FPGA学习——FPGA实现电子时钟

    Cyclone IV开发板上的数码管一共有6个,我们每次只能选择其中一个显示,怎么解决电子时钟时、分、秒同时显示呢?要实现电子时钟首先要了解什么是余晖效应。   余晖效应一般指视觉暂留。 视觉暂留现象即视觉暂停现象(Persistence of vision,Visual staying phenomenon,duration o

    2024年02月09日
    浏览(73)
  • 基于FPGA的数字电子钟的设计与实现

    系统顶层模块设计   系统功能 基本功能:实现秒、分钟、小时、星期的计数,分频,时分秒检测时钟选择,实现8位数码管显示计数结果。 拓展功能:“6,9”补全,时钟暂停,时钟清零, 时钟加杠,星期,整点报时,秒表        的计时、暂停和清零 ,手动校时, 切换

    2024年02月10日
    浏览(49)
  • 电子技术课程设计基于FPGA的音乐硬件演奏电路的设计与实现

    【ChatGPT】前些天发现了一个巨牛的人工智能学习电子书,通俗易懂,风趣幽默,无广告,忍不住分享一下给大家。(点击查看学习资料) wx供重浩:创享日记 对话框发送:乐曲电路 免费获取完整无水印论文报告(包含电路图) 1、课程设计题目 设计一个乐曲演奏电路,能够

    2024年02月05日
    浏览(62)
  • 基于单片机的电子时钟设计

    收藏和点赞,您的关注是我创作的动力   电子时钟具有长远的发展历史,它的出现使得人们对时间的概念有了进一步的认知和了解,可以说意义十分的重大。在时代的推动,以及市场的需求下,电子时钟的功能以及性能都有着质的突破,而且应用的范围也越来越广,到处都

    2024年02月04日
    浏览(48)
  • FPGA多功能数字时钟 基于Quartus实现设计与仿真 华南师范大学数电综设

    专业: 通信工程 学号:__ 姓名: 龚易乾___指导老师: 电子与信息工程学院 2023年2月 有任何疑问可以联系邮箱:codealan@qq.com 项目仓库地址:https://github.com/CodeAlanqian/e-clock github仓库地址 熟练掌握Quartus等EDA设计与仿真工具,掌握多路选择器、N进制计数器、显示译码电路、开关

    2024年02月12日
    浏览(47)
  • 基于STM32的电子时钟设计,代码开源!!!

    使用STM32+ESP32开发一个电子时钟,拥有时钟显示,报警,自动对时等功能的电子时钟 MCU:STM32F103 通信:串口通信,波特率:115200、数据位:8、停止位:1、校验:None 开发软件:keil mdk 主要硬件连接:stm32与esp32通过串口相连 按键:SLLB510100,显示屏:VFD 硬件原理图如下,仅供

    2024年02月05日
    浏览(38)
  • 电子时钟设计(verilog实现)

    Quartus编程实现时钟,具有基本的时间显示功能(00:00:00~23:59:59),以及其他附加功能:调整时间,设置闹铃,或者其它与时钟相关的功能。Modelsim实现仿真。 设计代码: 仿真代码: 仿真结果: //时钟模块    从仿真图中看出,当使能端口en为低电平时,时钟暂停工作,当en为高

    2024年02月11日
    浏览(46)
  • 基于51单片机、DS1302时钟模块的电子闹钟设计

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 提示:以下是本篇文章正文内容,下面案例可供参考 DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周、时、分、秒进行计时,具有闰年补偿功能,工作

    2024年02月02日
    浏览(63)
  • stm32毕业设计 电子时钟设计与实现

    共用体除非必要,否则学长不推荐使用,枚举的用法比较简单,在本书19章的项目实践中有很好的示例,这节课我们先来练习一下结构体的使用。下边这个程序的功能是一个带日期的电子钟,相当于一个简易万年历了,并且加入了按键调时功能。学有余力的同学看到这里,不

    2024年02月08日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包