天天看点

基于FPGA的数字时钟

基于FPGA的数字时钟

一.数字时钟设计

1.硬件资源:LCD1602液晶屏一块,FPGA开发板一块(A_C8V4);

2. 开发板资源:3颗独立按键,LCD1602接口;

3. 功能设计:三种功能:a.时钟功能;b.闹钟功能;c.校时功能;

4. 按键功能设计:按键1切换数字钟模式,按键2调整数字钟时钟显示(包括闹钟调时),按键3调整数字时钟分钟显示(包括闹钟调分);

二.数字时钟代码

1.数字时钟顶层模块RTL视图

基于FPGA的数字时钟

1) 说明:这个为数字时钟的顶层模块,按键消抖模块debkey,数字钟功能模块digitalclk,LCD1602液晶显示模块lcd_ip;

2) 端口

输入:clk,reset,key;

输出:lcd_e,lcd_rw,lcd_rs,lcd_data,beep;

3) 代码

//数字时钟总模块
module digitalclk_top(clk,reset,key,lcd_rw,lcd_e,lcd_rs,lcd_data,beep);
	input clk;
	input reset;
	input [2:0]key;
	output lcd_rw;
	output lcd_rs;
	output lcd_e;
	output [7:0]lcd_data;
	output beep;
	
	wire [2:0]debkey;
	wire [255:0]disp;
	
	debkey U0
			(.clk(clk),
			.reset(reset),
			.key(key),
			.debkey(debkey));
				
	digitalclk U1
			(.clk(clk),
			.reset(reset),
			.key(debkey),
			.lcd_data_disp(disp),
			.beep(beep));
			
	lcd_ip U3
			(.clk(clk),
			.rst(reset),
			.data_buf(disp),
			.lcd_rw(lcd_rw),
			.lcd_rs(lcd_rs),
			.lcd_e(lcd_e),
			.lcd_data(lcd_data));
			
endmodule
           

2.按键消抖模块

基于FPGA的数字时钟

1) 说明:这个模块为按键消抖模块,三颗按键;

2) 端口

输入:clk,reset,key;

输出:debkey;

3) 代码

请参考按键消抖

3. LCD模块

基于FPGA的数字时钟

1) 说明:这个为LCD1602模块,其中data_buf为256位,每一个格为8位,一共有32个,所以32x8=256;

2) 端口

输入:clk,rst,data_buf;

输出:lcd_e,lcd_rs,lcd_rw,lcd_data;

 3)代码

请参考LCD模块

4.时钟钟功能模块

基于FPGA的数字时钟
基于FPGA的数字时钟

1) 说明:这个模块为数字钟功能模块,包括数字钟模式模块digitalclk_mode,数字钟时钟模块clock,数字钟闹钟模块alarm,数字钟校时模块adj,数字钟数据处理选择模块disp_sel,还有一个不和谐的闹铃铃声模块beep;

2) 端口

输入:clk,reset,key;

输出:beep,lcd_data_disp;

3) 代码

//数字时钟顶层模块
module digitalclk(clk,reset,key,lcd_data_disp,beep);
	input clk;
	input reset;
	input [2:0]key;
	output [255:0]lcd_data_disp;
	output beep;
	
	wire [2:0]mode;
	wire [255:0]clock_disp,alarm_disp,adj_disp;
	wire [255:0]sync_clock,sync_adj;
	wire alarm_en;
	
	digitalclk_mode U4
			(.clk(clk),
			.reset(reset),
			.key(key),
			.mode(mode));
			
	clock U5
			(.clk(clk),
			.reset(reset),
			.mode(mode),
			.sync_clock(sync_clock),
			.lcd_data_disp(clock_disp));
			
	alarm U6
			(.clk(clk),
			.reset(reset),
			.mode(mode),
			.key(key),
			.lcd_data_disp(alarm_disp));
			
	adj U7
			(.clk(clk),
			.reset(reset),
			.mode(mode),
			.key(key),
			.sync_adj(sync_adj),
			.lcd_data_disp(adj_disp));
	
	disp_sel U8
			(.clk(clk),
			.mode(mode),
			.alarm_disp(alarm_disp),
			.clock_disp(clock_disp),
			.adj_disp(adj_disp),
			.sync_clock(sync_clock),
			.sync_adj(sync_adj),
			.lcd_data_disp(lcd_data_disp),
			.alarm_en(alarm_en));
			
	beep U9 
			(.clk(clk),
			.reset(reset),
			.alarm_en(alarm_en),
			.beep(beep));
	
endmodule
           

5.数字钟digitalclk_mode模块

基于FPGA的数字时钟

1) 说明:这个为数字钟的模式切换模块

2) 端口

输入:clk,reset,key;

输出;mode;

3) 代码

//数字时钟功能模块
module digitalclk_mode(clk,reset,key,mode);
	input clk;
	input reset;
	input [2:0]key;
	output [2:0]mode;
	//---------------------------------------------------------------	
	parameter clock=2'd0,alarm=2'd1,adj=2'd2;
	//---------------------------------------------------------------	
	reg [2:0]func;
	reg [2:0]mode;
	always @(posedge key[0] or negedge reset)
		if(!reset)
			func <= 1'b0;
		else
			begin
			func <= func + 1'b1;		//产生不同的mode码
				if(func == 2'd2)
					func <= 1'b0;
			end		
	//---------------------------------------------------------------				
	always @(posedge clk)
		case(func)
			clock : mode = 3'b001;		//时钟模式
			alarm : mode = 3'b010;		//闹钟模式
			adj   : mode = 3'b100;		//校时模式
			default : mode = 3'b001;
		endcase

endmodule
           

6.时钟模块

基于FPGA的数字时钟

1) 说明:这个是数字钟的时钟模块,其中sync_clock是用来后台同步校时功能的时钟和分钟

2) 端口

输入:clk,reset,mode,sync_clock;

输出:lcd_data_displ;

3) 代码

//mode为0010时为时钟功能
//时钟功能
module clock(clk,reset,mode,sync_clock,lcd_data_disp);
	input clk;
	input reset;
	input [2:0]mode;
	input [255:0]sync_clock;
	output [255:0]lcd_data_disp;
	//---------------------------------------------------------------	
	//分频模块
	reg clk_1Hz;
	reg clk_100Hz;
	integer i,j;
	always @(posedge clk)
		begin
			i <= i + 1'b1;
			if(i==32'd249999)
				begin i <= 1'b0; clk_100Hz <= ~clk_100Hz;end	
			
			j <= j + 1'b1;
			if(j==32'd24999999)
				begin j <= 1'b0; clk_1Hz <= ~clk_1Hz; end	
		end
	//---------------------------------------------------------------	
	//闪烁模块 1秒闪一次
	always @(posedge clk)
		if(mode==3'b001)
			begin 
				clock_disp[23:16] <= clk_1Hz ? " " : ":";
				clock_disp[47:40] <= clk_1Hz ? " " : ":";
				clock_disp[71:64] <= clk_1Hz ? " " : ":";
			end
	//---------------------------------------------------------------	
	//百分秒模块
	reg [255:0]clock_disp;
	always @(posedge clk_100Hz or negedge reset)
		if(!reset)
			clock_disp[15:0] <= 1'b0;
		else
			begin
				clock_disp[7:0] <= clock_disp[7:0] + 1'b1;			//百分秒的个位
				if(clock_disp[7:0]==4'h9)
					begin
						clock_disp[7:0] <= 1'b0;
						clock_disp[15:8] <= clock_disp[15:8] + 1'b1;	//百分秒的十位
						if(clock_disp[15:8]==4'h9)
							clock_disp[15:8] <= 1'b0;
					end
			end
	//---------------------------------------------------------------	
	//计时模块
	always @(posedge clk_1Hz or negedge reset)
		if(!reset)	//重置清零
			begin
				clock_disp[39:24] <= 1'b0;
				clock_disp[63:48] <= 1'b0;
				clock_disp[87:72] <= 1'b0;
			end
			else
			begin
				clock_disp[255:88] = "MODE  H  M  S MSCLOCK";
				//秒计时模块
				clock_disp[31:24] <= clock_disp[31:24] + 1'b1;		//秒的个位
				if(clock_disp[31:24]==8'h9)
					begin
						clock_disp[31:24] <= 1'b0;
						clock_disp[39:32] <= clock_disp[39:32] + 1'b1;	//秒的十位
		//---------------------------------------------------------------						
				//分计时模块
				if(clock_disp[39:32]==8'h5)
					begin
						clock_disp[39:32] <= 1'b0;
						clock_disp[55:48] <= clock_disp[55:48] + 1'b1;		//分钟的个位
						if(clock_disp[55:48]==8'h9)
						begin
							clock_disp[55:48] <= 1'b0;
							clock_disp[63:56] <= clock_disp[63:56] + 1'b1;	//分钟的十位
		//---------------------------------------------------------------							
				//小时计时模块					
				if(clock_disp[63:56]==8'h5)	
					begin
						clock_disp[63:56] <= 1'b0;
						clock_disp[79:72] <= clock_disp[79:72] + 1'b1;		//时钟的个位
						if(clock_disp[79:72]==8'h9)
						begin
							clock_disp[79:72] <= 1'b0;
							clock_disp[87:80] <= clock_disp[87:80] + 1'b1;		//时钟的十位
						end
					end
		//---------------------------------------------------------------				
						end
					end	
					end				
		//---------------------------------------------------------------					
				//如果记到23:59:59 时清零 00:00:00
				else if(clock_disp[87:72]==16'h0204)		//0000 0010 0000 0100
					clock_disp[87:72] <= 1'b0;					
		//----------------------------------------------------------
				//后台同步校准模式的时钟和分钟	
				else if(mode==3'b100)	
					begin
						clock_disp[87:72] = sync_clock[87:72];
						clock_disp[63:48] = sync_clock[63:48];
					end		
			end	
	//----------------------------------------------------------	
	assign lcd_data_disp = clock_disp ;
	
endmodule
           

7.闹钟模块

基于FPGA的数字时钟

    1)说明:这个是数字钟的闹钟模块,其中key是用来调闹钟的时钟和分钟;

    2)端口

       输入:clk,reset,mode,key;

       输出:lcd_data_disp;

3)代码

//mode=3'b010时为闹钟功能
//校时可以长按按键,可以快速加数字
//闹钟功能
module alarm(clk,reset,mode,key,lcd_data_disp);
	input clk;
	input reset;
	input [2:0]key;
	input [2:0]mode;
	output [255:0]lcd_data_disp;	
	//---------------------------------------------------------------	
	//校时分频200ms的频率用来更新时间
	integer i;
	reg clk_alarm;
	always @(posedge clk)
		begin
			i <= i + 1'b1;
			if(i==32'd4999999)
				begin	i <= 1'b0; clk_alarm <= ~clk_alarm; end
		end
	//---------------------------------------------------------------		
	reg [255:0]alarm_disp;	
	always @(posedge clk_alarm or negedge reset)
	if(!reset)	//复位时清零
		alarm_disp[87:72] <= 1'b0;		
		else if(mode==3'b010)
		begin 
			//设置一些常量,没使用到的要把初始值赋为0,可以减少警告
			alarm_disp[255:88] <= "MODE  H  M  S MSALARM";
			alarm_disp[71:64] <= ":";
			alarm_disp[47:40] <= ":";
			alarm_disp[39:24] <= 1'b0;
			alarm_disp[23:16] <= ":";
			alarm_disp[15:0] <= 1'b0;
	//---------------------------------------------------------------			
			//闹钟设置时钟
			if(!key[1])	
			begin
				alarm_disp[79:72] <= alarm_disp[79:72] + 1'b1;	//时钟的个位
				if(alarm_disp[79:72]==4'h9)
					begin
						alarm_disp[79:72] <= 1'b0;
						alarm_disp[87:80] <= alarm_disp[87:80] + 1'b1;	//时钟的十位
						if(alarm_disp[87:80]==4'h5)
							alarm_disp[87:80] <= 1'b0;
					end
				else if(alarm_disp[87:72]==16'h0203)		//时钟计算到23的时候回00
					alarm_disp[87:72] <= 1'b0;
			end
	//---------------------------------------------------------------			
			//闹钟设置分钟	
			if(!key[2])		
				begin
					alarm_disp[55:48] <= alarm_disp[55:48] + 1'b1;		//时钟的个位
					if(alarm_disp[55:48]==4'h9)
						begin
							alarm_disp[55:48] <= 1'b0;
							alarm_disp[63:56] <= alarm_disp[63:56] + 1'b1;		//时钟的十位
							if(alarm_disp[63:56]==4'h5)
								alarm_disp[63:56] <= 1'b0;
						end
				end
		end
	//---------------------------------------------------------------		
	assign lcd_data_disp = alarm_disp ;
				
endmodule
           

8. 校时模块

基于FPGA的数字时钟

1)说明:这个是数字钟的校时模快,其中key是用来调整校时模式下的时钟和分钟,sync_adj是用来后台同步时钟模式下的时钟数据

    2)端口

       输入:clk,reset,mode,key,sync_adj;

       输出:lcd_data_disp;

3)代码

//mode=3'b100时为校时功能
//校时可以长按按键,可以快速加数字
//校时功能
module adj(reset,clk,mode,key,sync_adj,lcd_data_disp);
	input clk;
	input reset;
	input [2:0]key;
	input [2:0]mode;
	input [255:0]sync_adj;
	output [255:0]lcd_data_disp;
	//---------------------------------------------------------------	
	//校时分频100ms的频率用来更新时间,
	integer i,j;
	reg clk_adj,clk_1Hz;
	always @(posedge clk)
		begin
			i <= i + 1'b1;
			if(i==32'd4999999)
				begin i <= 1'b0; clk_adj <= ~clk_adj; end
				
			j <= j + 1'b1;
			if(j==32'd24999999)
				begin j <= 1'b0; clk_1Hz <= ~clk_1Hz; end
		end
	//---------------------------------------------------------------		
	reg [255:0]adj_disp;	
	always @(posedge clk_adj or negedge reset)
	if(!reset)	//复位时清零
		adj_disp[87:72] <= 1'b0;	
		else if(mode==3'b100)
			begin 
				//设置一些常量,没使用到的要把初始值赋为0,可以减少警告
				adj_disp[255:88] <= "MODE  H  M  S MSADJ  ";	
				adj_disp[71:64] <= ":";
				adj_disp[47:40] <= ":";
				adj_disp[23:16] <= ":";
				adj_disp[15:0] <= 1'b0;
		//---------------------------------------------------------------			
				//校时状态下调节时钟
				if(!key[1])
					begin
						adj_disp[79:72] <= adj_disp[79:72] + 1'b1;		//时钟的个位
						if(adj_disp[79:72]==4'h9)
						begin
							adj_disp[79:72] <= 1'b0;
							adj_disp[87:80] <= adj_disp[87:80] + 1'b1;		//时钟的十位
							if(adj_disp[87:80]==4'h5)
								adj_disp[87:80] <= 1'b0;
						end
						else if(adj_disp[87:72]==16'h0203)			//时钟计算到23的时候回00
							adj_disp[87:72] <= 1'b0;
					end
		//---------------------------------------------------------------					
				//校时状态下调节分钟
				if(!key[2])
					begin
						adj_disp[55:48] <= adj_disp[55:48] + 1'b1;		//时钟的个位
						if(adj_disp[55:48]==4'h9)
						begin
							adj_disp[55:48] <= 1'b0;
							adj_disp[63:56] <= adj_disp[63:56] + 1'b1;		//时钟的十位
							if(adj_disp[63:56]==4'h5)
								adj_disp[63:56] <= 1'b0;
						end
					end
			end
		//---------------------------------------------------------------	
		else if(mode==3'b001)	//后台同步时钟
			begin
				adj_disp[87:72] = sync_adj[87:72];
				adj_disp[63:48] = sync_adj[63:48];
			end
	//---------------------------------------------------------------
	//校准模式下分钟显示
	always @(posedge clk_1Hz or negedge reset)
		if(!reset)
			adj_disp[39:24] <= 1'b0;
		else
		begin
			adj_disp[31:24] <= adj_disp[31:24] + 1'b1;			//分钟的个位
			if(adj_disp[31:24]==8'h9)
			begin
				adj_disp[31:24] <= 1'b0;
				adj_disp[39:32] <= adj_disp[39:32] + 1'b1;		//分钟的十位
				if(adj_disp[39:32]==8'h5)	
					adj_disp[39:32] <= 1'b0;
			end
		end
	//---------------------------------------------------------------	
	assign lcd_data_disp = adj_disp ;
			
endmodule
           

9.数据处理选择模块

基于FPGA的数字时钟

1)说明:这个是用来处理时钟,闹钟,校时的数据,同时提供两条信号线sync_clock,sync_adj来给时钟和闹钟同步数据;其中alarm_en是闹钟铃声使能信号;

    2)端口

       输入:clk,mode,alarm_disp,clock_disp,adj_disp;

       输出:alarm_en,sync_clock,sync_a,lcd_data_disp;

3)代码

//LCD数据选择
module disp_sel(clk,mode,alarm_disp,clock_disp,adj_disp,sync_clock,sync_adj,lcd_data_disp,alarm_en);
	input clk;
	input [2:0]mode;
	input [255:0]clock_disp;
	input [255:0]alarm_disp,adj_disp;
	output reg[255:0]sync_clock;
	output reg[255:0]sync_adj;
	output [255:0]lcd_data_disp;
	output alarm_en;
	//---------------------------------------------------------------		
	reg[255:0] result_disp;
	always @(posedge clk)
		begin
			if(mode==3'b001)	//时钟功能时选择时钟的的数据
				begin
					result_disp <= clock_disp ;
					sync_adj <= clock_disp;
				end
			else if(mode==3'b010)	//闹钟功能时选择闹钟的数据
				begin
					result_disp <= alarm_disp;
				end
			else if(mode==3'b100)	//校时功能时选择闹钟的数据
				begin
					result_disp <= adj_disp;
					sync_clock <= adj_disp;
				end
		end
	//---------------------------------------------------------------	
	//闹钟响蜂鸣器模块
	reg alarm_en = 0;
	always @(posedge clk)
		if(mode==3'b001)
			begin
			if((clock_disp[63:48]!==16'b0) || (clock_disp[87:72]!==16'b0))//还没想到好的方法处理半夜0点尖叫,只能采用折中方法 
				begin																		//这类似几年前ios系统的0点不响事件,这是一个bug//
					if(clock_disp[39:24]==alarm_disp[39:24])		//响一分钟模块
					begin
						if((clock_disp[87:72]==alarm_disp[87:72]) & (clock_disp[63:48]==alarm_disp[63:48]))
							alarm_en <= 1;
						else
							alarm_en <= 0;
					end
				end
			end
		else alarm_en <= 0;
	//---------------------------------------------------------------	
	//数据处理
	assign lcd_data_disp[255:88] = result_disp[255:88];
	assign lcd_data_disp[87:80] = result_disp[87:80] + 8'd48;
	assign lcd_data_disp[79:72] = result_disp[79:72] + 8'd48;
	assign lcd_data_disp[71:64] = result_disp[71:64];
	assign lcd_data_disp[63:56] = result_disp[63:56] + 8'd48;
	assign lcd_data_disp[55:48] = result_disp[55:48] + 8'd48;
	assign lcd_data_disp[47:40] = result_disp[47:40];
	assign lcd_data_disp[39:32] = result_disp[39:32] + 8'd48;
	assign lcd_data_disp[31:24] = result_disp[31:24] + 8'd48;
	assign lcd_data_disp[23:16] = result_disp[23:16];
	assign lcd_data_disp[15:8] = result_disp[15:8] + 8'd48;
	assign lcd_data_disp[7:0] = result_disp[7:0] + 8'd48;	

endmodule
           

10.闹铃模块

基于FPGA的数字时钟

    1)说明:这个是闹钟的闹铃模块;

    2)端口

       输入:clk,reset,alarm_en;

       输出:beep;

3)代码

//蜂鸣器模块
module beep(clk,reset,alarm_en,beep);
	input clk;
	input reset;
	input alarm_en;
	output beep;
	//---------------------------------------------------------------	
	parameter  IDLE= 8'b00000001,
					DO = 8'b00000010,
					RE = 8'b00000100,
					MI = 8'b00001000,
					FA = 8'b00010000,
					SO = 8'b00100000,
					LA = 8'b01000000,
					SI = 8'b10000000;
	//---------------------------------------------------------------	
	reg[31:0] cnt_1Hz;
	always @(posedge clk or negedge reset)
		if(!reset)
			cnt_1Hz <= 32'd0;
		else
			begin
				if(cnt_1Hz >=  32'd49_999_99)
					cnt_1Hz <= 32'd0;
				else
					cnt_1Hz <= cnt_1Hz + 32'd1;
			end
	//---------------------------------------------------------------		
	reg[7:0]beep_status;
	always @(posedge clk or negedge reset)
		if(!reset)
			beep_status <= IDLE;
		else
			begin
				if( cnt_1Hz == 32'd49_999_99)
					beep_status[7:0] <= {beep_status[6:0],beep_status[7]}; 
			end
	//---------------------------------------------------------------		
	reg[31:0]cnt;	
	reg beep;
	always @(posedge clk or negedge reset)
		begin
			if(!reset)
				begin
					cnt <= 0;
					beep <= 1;
				end
			else if(alarm_en)
				case(beep_status)
				  IDLE: beep <= 1;
						
					DO : begin if(cnt>=11944) begin cnt <= 0; beep <= ~beep ;end	//1
									else cnt <= cnt + 1; end
						
					RE : begin if(cnt>=10642) begin cnt <= 0; beep <= ~beep ;end	//2
									else cnt <= cnt + 1; end	
						
					MI : begin if(cnt>=9480) begin cnt <= 0; beep <= ~beep ;end		//3
									else cnt <= cnt + 1; end	
									
					FA : begin if(cnt>=8947) begin cnt <= 0; beep <= ~beep ;end		//4
									else cnt <= cnt + 1;	end	
						
					SO : begin if(cnt>=7971) begin cnt <= 0; beep <= ~beep ;end		//5
									else cnt <= cnt + 1; end	
							
					LA : begin if(cnt>=7102) begin cnt <= 0; beep <= ~beep ;end		//6
									else cnt <= cnt + 1; end	
						
					SI	: begin if(cnt>=6327) begin cnt <= 0; beep <= ~beep ;end		//7
									else cnt <= cnt + 1; end		
			endcase
			else beep <= 1;
		end

endmodule
           

11.使用平台Altera CycloneII EP2C8Q208C8 

开发板A_C8V4;

基于FPGA的数字时钟

12.引脚绑定

基于FPGA的数字时钟