zl程序教程

您现在的位置是:首页 >  硬件

当前栏目

FPGA-使用RTC时钟芯片进行时钟读取(数码管显示)

芯片 进行 显示 读取 FPGA 时钟 rtc 数码管
2023-09-14 09:13:03 时间
做时钟显示如果用单片机做,会产生很大的累积误差,本程序采用时钟芯片PCF8563

一:芯片主要电气特性:
1)大工作电压范围:1.0~5.5V
2)400KHz 的 I2C 总线接口(VDD=1.8~5.5V 时)。 


时钟频率最大400KH

3)i2C 总线从地址:读,0A3H;写,0A2H .

二:管脚

 

电路接线图:

top.v:

module top(ext_clk_25m,ext_rst_n,dtube_cs_n,dtube_data,rtc_iic_sck,rtc_iic_sda
    );
	input ext_clk_25m;
	input ext_rst_n;
	output [3:0] dtube_cs_n;
	output [6:0] dtube_data;
	output rtc_iic_sck;
	inout rtc_iic_sda;
	
	wire [7:0]rtc_secd,rtc_mini,rtc_hour;
	wire iicrd_req;
	wire iicwr_req;
	wire [7:0] iic_addr;
	wire [7:0] iic_rddb;
	wire [7:0] iic_wrdb;
	wire iic_ack;
	
	rtc_controller uut_rtc(
	.clk(ext_clk_25m),
	.rst_n(ext_rst_n),
	.iicwr_req(iicwr_req),
	.iicrd_req(iicrd_req),
	.iic_addr(iic_addr),
	.iic_wrdb(iic_wrdb),
	.iic_rddb(iic_rddb),
	.iic_ack(iic_ack),
	.rtc_hour(rtc_hour),
	.rtc_mini(rtc_mini),
	.rtc_secd(rtc_secd)
    );
	
	iic_controller uut_iic(
	.clk(ext_clk_25m),
	.rst_n(ext_rst_n),
	.iicwr_req(iicwr_req),
	.iicrd_req(iicrd_req),
	.iic_addr(iic_addr),
	.iic_wrdb(iic_wrdb),
	.iic_rddb(iic_rddb),
	.iic_ack(iic_ack),
	.scl(rtc_iic_sck),
	.sda(rtc_iic_sda)
    );
	seg_4 uut_seg(
	.clk(ext_clk_25m),
	.rst_n(ext_rst_n),
	.dis_data({rtc_mini,rtc_secd}),
	.dtube_cs_n(dtube_cs_n),
	.dtube_data(dtube_data)
		);
endmodule

iic.v:

`timescale 1ns / 1ps
module iic_controller(clk,rst_n,iicwr_req,iicrd_req,iic_addr,iic_wrdb,iic_rddb,iic_ack,scl,sda
    );
	input clk;              //时钟信号
	input rst_n;            //复位信号
	input iicwr_req;        //写请求信号
	input iicrd_req;        //读请求信号
	input [7:0]iic_addr;    //读入地址寄存器
	input [7:0]iic_wrdb;    //写入数据寄存器
	output reg[7:0]iic_rddb;//读入数据寄存器
	output iic_ack;         //读写应答信号
	output reg scl;         //配置时钟信号
	inout sda;              //配置数据信号
	
	parameter   I2C_ILDE       =4'D0,  //起始状态    
				I2C_START      =4'D1,  //起始信号    
				//写信号
				I2C_WR_IDADDR  =4'D2,  //(写指令)    
				I2C_WR_ACK1    =4'D3,  //应答信号    
				I2C_WR_REGADDR =4'D4,  //驱动写地址  
				I2C_WR_ACK2    =4'D5,  //应答信号    
				I2C_WR_DATA    =4'D6,  //写数据      
				I2C_WR_ACK3    =4'D7,  //应答信号    
				//读信号
				I2C_RS_START   =4'D8,  //复位起始信号
				I2C_RD_IDADDR  =4'D9,  //(读信号)    
				I2C_RD_ACK     =4'D10, //应答信号    
				I2C_RD_DATA    =4'D11, //读数据
				I2C_RD_NPACK   =4'D12, //应答信号
				I2C_STOP       =4'D13; //停止传输信号
				
	parameter   DEVICE_WRADD = 8'HA2,  //写地址
				DEVICE_RDADD = 8'HA3;  //读地址
				
	parameter   I2C_FREQ     =250,     //时钟频率
				SEND_BIT     =8;       //发送数据位数
	//-------------------------------------------------
	reg [7:0]cnt;//scl计数寄存器
	reg [2:0]byte_cnt;//数据位寄存器
	reg [3:0]state_c,state_n;//状态寄存器
	reg sdar;         //sda输出寄存器
	reg sdalink;      //sda方向控制器 0-input 1-output
	assign scl_hs =(cnt==1'b0+1'b1);
	assign scl_hc =(cnt==(I2C_FREQ>>2));
	assign scl_ls =(cnt==(I2C_FREQ>>1));
	assign scl_lc =(cnt==(I2C_FREQ>>2)*3);
	
	
	//iic时钟信号产生逻辑
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			cnt<=1'b0;
		end
		else if(cnt==I2C_FREQ-1'b1)begin
			cnt<=1'b0;
		end
		else begin
			cnt<=cnt+1'b1;
		end
	end
	always @(posedge clk or negedge rst_n)begin
		if(rst_n == 1'b0)begin
			scl <= 1'b0;
		end
		else if(cnt >=1'b0 &&cnt <= (I2C_FREQ>>1)-1'b1)begin
			scl <= 1'b1;
		end
		else begin 
			scl <= 1'b0;
		end
	end
	//iic状态机控制信号
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			state_c<=I2C_ILDE;
		end
		else begin
			state_c<=state_n;
		end
	end
	always@(*)begin
		case(state_c)
			I2C_ILDE:
				if((iicwr_req==1'b1||iicrd_req==1'b1)&&scl_hs==1'b1)begin
					state_n<=I2C_START;
				end
				else begin
					state_n<=I2C_ILDE;
				end
			I2C_START:
				if(scl_ls==1'b1)begin
					state_n<=I2C_WR_IDADDR;
				end
				else begin
					state_n<=I2C_START;
				end
			I2C_WR_IDADDR:
				if(scl_lc==1'b1&&byte_cnt==3'd0)begin
					state_n<=I2C_WR_ACK1;
				end
				else begin
					state_n<=I2C_WR_IDADDR;
				end
			I2C_WR_ACK1:
				if(scl_ls==1'b1&&byte_cnt==SEND_BIT-1'b1)begin
					state_n<=I2C_WR_REGADDR;
				end
				else begin
					state_n<=I2C_WR_ACK1;
				end
			I2C_WR_REGADDR:
				if(scl_lc==1'b1&&byte_cnt==3'd0)begin
					state_n<=I2C_WR_ACK2;
				end
				else begin
					state_n<=I2C_WR_REGADDR;
				end
			I2C_WR_ACK2:
				if(scl_ls==1'b1&&(byte_cnt==SEND_BIT-1'b1)&&iicwr_req)begin
					state_n<=I2C_WR_DATA;
				end
				else if(scl_ls==1'b1&&(byte_cnt==SEND_BIT-1'b1)&&iicrd_req)begin
					state_n<=I2C_RS_START;
				end
				else begin
					state_n<=I2C_WR_ACK2;
				end
			I2C_WR_DATA:
				if(scl_lc==1'b1&&(byte_cnt==3'd0))begin
					state_n<=I2C_WR_ACK3;
				end
				else begin
					state_n<=I2C_WR_DATA;
				end
			I2C_WR_ACK3:
				if(scl_ls==1'b1&&byte_cnt==SEND_BIT-1'b1)begin
					state_n<=I2C_STOP;
				end
				else begin
					state_n<=I2C_WR_ACK3;
				end
			I2C_RS_START:
				if(scl_ls==1'b1)begin
					state_n<=I2C_RD_IDADDR;
				end
				else begin
					state_n<=I2C_RS_START;
				end
			I2C_RD_IDADDR:
				if(scl_lc==1'b1&&byte_cnt==3'd0)begin
					state_n<=I2C_RD_ACK;
				end
				else begin
					state_n<=I2C_RD_IDADDR;
				end
			I2C_RD_ACK:
				if(scl_ls==1'b1&&(byte_cnt==SEND_BIT-1'b1))begin
					state_n<=I2C_RD_DATA;
				end
				else begin
					state_n<=I2C_RD_ACK;
				end
			I2C_RD_DATA:
				if(scl_ls==1'b1&&byte_cnt==SEND_BIT-1'b1)begin
					state_n<=I2C_RD_NPACK;
				end
				else begin
					state_n<=I2C_RD_DATA;
				end
			I2C_RD_NPACK:
				if(scl_ls==1'b1&&SEND_BIT-2'd2)begin
					state_n<=I2C_STOP;
				end
				else begin
					state_n<=I2C_RD_NPACK;
				end
			I2C_STOP:
				if(scl_ls==1'b1)begin
					state_n<=I2C_ILDE;
				end
				else begin
					state_n<=I2C_STOP;
				end
			default:state_n<=I2C_ILDE;
		endcase
	end
	//数据位寄存器控制
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			byte_cnt<=3'd0;
		end
		else begin
			case(state_c)
				I2C_ILDE:
					byte_cnt<=SEND_BIT-1'b1;
				I2C_WR_IDADDR,I2C_WR_REGADDR,I2C_WR_DATA,
				I2C_RD_IDADDR,I2C_RD_DATA:
					if(scl_hs==1'b1)begin
						byte_cnt<=byte_cnt-1'b1;
					end
				I2C_WR_ACK1,I2C_WR_ACK2,I2C_WR_ACK3:
					if(scl_lc==1'b1)begin
						byte_cnt<=SEND_BIT-1'b1;
					end
				I2C_RD_ACK:
					if(scl_ls==1'b1)begin
						byte_cnt<=SEND_BIT-1'b1;
					end
				I2C_RD_NPACK:
					if(scl_lc==1'b1)begin
						byte_cnt<=byte_cnt-1'b1;
					end
				default: ;
			endcase
		end
	end
	
	//数据输入输出控制
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			sdar<=1'b1;
			sdalink<=1'b1;
			iic_rddb<=8'd0;
		end
		else begin
			case(state_c)
				I2C_ILDE:
						begin
							sdar<=1'b1;
							sdalink<=1'b1;
						end
				I2C_START:
						begin
							if(scl_hc==1'b1)begin
								sdar<=1'b0;
							end							
						end
				I2C_WR_IDADDR:
						begin
							if(scl_lc==1'b1)begin
								sdar<=DEVICE_WRADD[byte_cnt];
							end
						end
				I2C_WR_ACK1:
						begin
							if(scl_lc==1'b1)begin
								sdar<=1'b1;
								sdalink<=1'b0;
							end
						end
				I2C_WR_REGADDR:
						begin
							if(scl_lc==1'b1)begin
								sdar<=iic_addr[byte_cnt];
								sdalink<=1'b1;
							end
						end
				I2C_WR_ACK2:
						begin
							if(scl_lc==1'b1)begin
								sdar<=1'b1;
								sdalink<=1'b0;
							end
						end
				I2C_WR_DATA:
						begin
							if(scl_lc==1'b1)begin
								sdar<=iic_wrdb[byte_cnt];
								sdalink<=1'b1;
							end
						end
				I2C_WR_ACK3:
						begin
							if(scl_lc==1'b1)begin
								sdar<=1'b1;
								sdalink<=1'b0;
							end
						end
				I2C_RS_START:
						begin
							if(scl_hc==1'b1)begin
								sdar<=1'b0;
							end
							else if(scl_lc==1'b1)begin
								sdar<=1'b1;
								sdalink<=1'b1;
							end
						end
				I2C_RD_IDADDR:
						begin
							if(scl_lc==1'b1)begin
								sdar<=DEVICE_RDADD[byte_cnt];
							end
						end
				I2C_RD_ACK: 
						begin
							if(scl_lc==1'b1&&byte_cnt==SEND_BIT-1'b1)begin
								sdalink<=1'b0;
							end
						end
				I2C_RD_DATA:   
						begin
							if(scl_hc==1'b1)begin
								iic_rddb[byte_cnt+1'b1]<=sda;
								sdar<=1'b1;
							end
						end
				I2C_RD_NPACK:  
						begin
							if(scl_lc==1'b1)begin
								sdalink<=1'b1;
								sdar<=1'b0;
							end
						end
				I2C_STOP:  
						begin
							if(scl_lc==1'b1)begin
								sdalink<=1'b1;
								sdar<=1'b0;
							end
							else if(scl_hc==1'b1)begin
								sdar<=1'b1;
							end
						end
				default: ;
			endcase
		end
	end
	assign sda =sdalink?sdar:1'bz;
	assign iic_ack=(state_n==I2C_STOP)&&scl_hs==1'b1;
endmodule

rtc_controller.v:

`timescale 1ns / 1ps
module rtc_controller(clk,rst_n,iicwr_req,iicrd_req,iic_addr,iic_wrdb,iic_rddb,iic_ack,rtc_hour,rtc_mini,rtc_secd
    );
	input clk;
	input rst_n;
	output reg iicrd_req;
	output reg iicwr_req;
	output reg[7:0]iic_addr;
	output reg[7:0]iic_wrdb;
	input [7:0] iic_rddb;
	input iic_ack;
	output reg[7:0]rtc_hour;
	output reg[7:0]rtc_mini;
	output reg[7:0]rtc_secd;
	
	//--------------------------------------------
	parameter 	RTC_IDLE   =4'D0,
				RTC_RD_SECD=4'D1,
				RTC_WAIT1  =4'D2,
				RTC_RD_MINI=4'D3,
				RTC_WAIT2  =4'D4,
				RTC_RD_HOUR=4'D5;
	
	//--------------------------------------------
	reg [17:0]cnt;
	reg [3:0] state_c,state_n;
	
	//10ms定时器
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			cnt<=1'b0;
		end
		else if(cnt<18'd249_999)begin
			cnt<=cnt+1'b1;
		end
		else begin
			cnt<=1'b0;
		end
	end
	wire timer1_10ms =(cnt==18'd49_999);
	wire timer2_10ms =(cnt==18'd149_999);
	wire timer3_10ms =(cnt==18'd249_999);
	//状态控制
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			state_c<=RTC_IDLE;
		end
		else begin
			state_c<=state_n;
		end
	end
	
	always@(*)begin
		case(state_c)
			RTC_IDLE:
				begin
					if(timer1_10ms==1'b1)begin
						state_n<=RTC_RD_SECD;
					end
					else begin
						state_n<=RTC_IDLE;
					end
				end
			RTC_RD_SECD:
				begin
					if(iic_ack==1'b1)begin
						state_n<=RTC_WAIT1;
					end
					else begin
						state_n<=RTC_RD_SECD;
					end
				end
			RTC_WAIT1:  
				begin
					if(timer2_10ms==1'b1)begin
						state_n<=RTC_RD_MINI;
					end
					else begin
						state_n<=RTC_WAIT1;
					end
				end
			RTC_RD_MINI:
				begin
					if(iic_ack==1'b1)begin
						state_n<=RTC_WAIT2;
					end
					else begin
						state_n<=RTC_RD_MINI;
					end
				end
			RTC_WAIT2:  
				begin
					if(timer3_10ms==1'b1)begin
						state_n<=RTC_RD_HOUR;
					end
					else begin
						state_n<=RTC_WAIT2;
					end
				end
			RTC_RD_HOUR:
				begin
					if(iic_ack==1'b1)begin
						state_n<=RTC_IDLE;
					end
					else begin
						state_n<=RTC_RD_HOUR;
					end
				end
			default:state_n<=RTC_IDLE;
		endcase		
	end
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			iicwr_req<=1'b0;
			iicrd_req<=1'b0;
			iic_addr<=1'b0;
			iic_wrdb<=1'b0;
		end
		else begin
			case(state_c)
				RTC_RD_SECD:
					begin
						iicwr_req<=1'b0;
						iicrd_req<=1'b1;
						iic_addr<=8'd2;
						iic_wrdb<=1'b0;
					end
				RTC_RD_MINI:
					begin
					    iicwr_req<=1'b0;
						iicrd_req<=1'b1;
						iic_addr<=8'd3;
						iic_wrdb<=1'b0;
					end
				RTC_RD_HOUR:
					begin
						iicwr_req<=1'b0;
						iicrd_req<=1'b1;
						iic_addr<=8'd4;
						iic_wrdb<=1'b0;
					end
				default:
					begin
						iicwr_req<=1'b0;
						iicrd_req<=1'b0;
						iic_addr<=1'b0;
						iic_wrdb<=1'b0;
					end
			endcase
		end
	end
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			rtc_hour<=1'b0;
			rtc_mini<=1'b0;
			rtc_secd<=1'b0;
		end
		else begin
			case(state_c)
				RTC_RD_SECD:
					begin
						if(iic_ack==1'b1)begin
							rtc_secd<={1'b0,iic_rddb[6:0]};
						end
						else begin
							rtc_secd<=rtc_secd;
						end
					end
				RTC_RD_MINI:
					begin
						if(iic_ack==1'b1)begin
							rtc_mini<={1'b0,iic_rddb[6:0]};
						end
						else begin
							rtc_mini<=rtc_mini;
						end
					end
				RTC_RD_HOUR:
					begin
						if(iic_ack==1'b1)begin
							rtc_hour<={1'b0,iic_rddb[6:0]};
						end
						else begin
							rtc_hour<=rtc_hour;
						end
					end
			endcase
		end
	end
endmodule

 seg_4.v:

`timescale 1ns / 1ps
module seg_4(clk,rst_n,dis_data,dtube_cs_n,dtube_data	
		);
	input clk;		//时钟信号25MHz
	input rst_n;			//复位信号a
	input [15:0] dis_data;//
	output reg[3:0] dtube_cs_n;	//段选数据位
	output reg[6:0] dtube_data;//位选数据位
	reg [3:0]TimeH;			//两位输入高位  [0]
	reg [3:0]TimeL;			//两位输入低位  [1]
	reg [3:0]TimeH1;		//两位输入高位  [2]
	reg [3:0]TimeL1;		//两位输入低位  [3]	
	reg [3:0] display_num;	//当前显示数据
	reg [16:0] div_cnt;	//延时计数器计数位
	always@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)begin
			TimeH<=1'b0;	
			TimeL<=1'b0;	
			TimeH1<=1'b0;
			TimeL1<=1'b0;
		end
		else begin
			TimeH <=dis_data[7:4];		
			TimeL <=dis_data[3:0];a
			TimeH1<=dis_data[15:12];
			TimeL1<=dis_data[11:8];
		end
	end
initial div_cnt = 0;//赋初值为0
//延时计数器模块
	always@ (posedge clk or negedge rst_n)
	begin
		if(!rst_n) 
			div_cnt <= 8'd0;
		else if(div_cnt==17'd80000)
			div_cnt <= 8'd0;		
		else 
			div_cnt <= div_cnt+1'b1;
	end
	
//显示当前的数据模块
	always @(posedge clk or negedge rst_n)
	begin
	if(!rst_n) 
		display_num <= 4'h0;
	else if(div_cnt < 17'd20000)
		display_num <= TimeL;
	else if((div_cnt>17'd20000)&(div_cnt <17'd40000))
		display_num <= TimeH;
	else if((div_cnt>17'd40000)&(div_cnt < 17'd60000))
		display_num <=TimeL1;
	else
		display_num <=TimeH1;
	end
		
//段选数据译码模块(共阴数码管)
	always @(*)
	begin
		if(!rst_n) 
			dtube_data <= 8'h00;
		else begin
			case(display_num) 
				4'h0: dtube_data <= 8'h3f;
				4'h1: dtube_data <= 8'h06;
				4'h2: dtube_data <= 8'h5b;
				4'h3: dtube_data <= 8'h4f;
				4'h4: dtube_data <= 8'h66;
				4'h5: dtube_data <= 8'h6d;
				4'h6: dtube_data <= 8'h7d;
				4'h7: dtube_data <= 8'h07;
				4'h8: dtube_data <= 8'h7f;
				4'h9: dtube_data <= 8'h6f;
				default:dtube_data <= 8'h00;
			endcase
		end
	end
//位选选译模块
	always @(posedge clk or negedge rst_n)	
	begin
		if(!rst_n) 
			dtube_cs_n <=  4'b1111;
		else if(div_cnt <= 17'd20000)
			dtube_cs_n <= 4'b1110;
		else if((div_cnt>17'd20000)&(div_cnt <=17'd40000))
			dtube_cs_n <= 4'b1101;
		else if((div_cnt>17'd40000)&(div_cnt <=17'd60000))
			dtube_cs_n <= 4'b1011;
		else 
			dtube_cs_n <=4'b0111;
	end
endmodule