基于PCF8563的数字钟FPGA设计与实现
2020-06-29
(中国电子科技集团公司 第二十七研究所, 郑州 450047)
0 引言
现场可编程门阵列(field-programmable gate array,FPGA)的飞速发展,在工业控制、电子计量、便携仪器、数字通信、人工智能等各个领域得到广泛应用。Verilog HDL(Verilog hard design language)作为目前主流的硬件描述语言得到了FPGA工程师的青睐。目前数字钟多应用于仪器仪表、电子计量、实时控制系统中[1-2]。实时时钟芯片PCF8563在数字钟设计中应用也非常广泛,实质是一个对标准频率计数的计数电路,通常由晶体振荡电路、分频电路、时间计数电路、译码驱动电路等组成[3]。本文提出了一种基于实时时钟芯片PCF8563的数字钟的FPGA设计实现思路,采用Altera公司的Cyclone IV E系列FPGA芯片EP4CE10F17C8作为主控芯片,通过Inter-Integrated Circuit(I2C)总线实现对实时时钟芯片PCF8563的读写控制,通过4个按键分别完成时钟/日历切换、实时显示和手动调校切换、数据移位设置和数据累加设置功能,完成实时和调校模式下的六位数码管显示,并在调校模式下实现数码管闪烁指示功能。通过SignalTap逻辑分析仪,验证了系统功能实现的技术可行性,并在正点原子开拓者EP4CE10开发板上完成调试。
1 时钟芯片PCF8563简介
PCF8563是飞利浦公司的一款具有极低功耗的工业级多功能CMOS实时时钟/日历芯片,工作电流为0.25 μA,具有时钟输出、报警、定时器以及中断输出功能,能完成各种复杂的定时服务,其内部功能如图1所示[4]。
图1 PCF8563内部功能框图
PCF8563内部包括16个8位寄存器、内置32.768 Hz的振荡器、时钟监控、电源复位、分频器、可编程时钟输出、中断输出、看门狗电路以及I2C总线接口。分频器给实时时钟RTC提供源时钟,内存地址00 H、01 H用作控制和状态寄存器,地址02 H~08 H用作TIME计时器,地址09 H~0 CH用于报警寄存器,地址0DH控制CLKOUT管脚的输出频率,地址0EH和0FH分别用于定时器控制寄存器和定时器寄存器,其中年月日时分秒寄存器均为BCD编码[5]。
当一个RTC寄存器被写入或读取时,所有时间计数器的内容将被冻结,可避免在进位条件下错误地写或读时钟和日历,因此所有访问必须在1 s内完成。开机复位(POR)覆盖,POR持续时间与晶体振荡器的启动时间直接相关,在上电复位后,应持续大于8 ms,再进行总线操作[6]。PCF8563的所有数据和地址均通过I2C总线接口串行传递,最大总线速度为400 kbits/s,每次读写数据后,内嵌的地址寄存器会自动产生增量。
PCF8563寄存器写时序过程如下:先发送器件地址+写操作0,随后发8位寄存器地址,最后发8位寄存器值,S为起始信号,A为应答信号,P为停止信号。读时序过程如下:先发7位地址+写操作0,然后发送寄存器地址,重新发送起始信号Sr,再次发送7位地址+读操作1,读取寄存器值。
2 数字钟系统设计
数字钟系统由晶振、电源模块、实时时钟芯片PCF8563、FPGA电路模块、按键模块以及数码管显示模块6部分组成。系统采用3.3 V供电,晶振选择32.768 kHz和50 MHz两种,分别用于实时时钟芯片PCF8563工作的外部晶振和FPGA工作的基准时钟,FPGA采用Cyclone IV E系列EP4CE10F17C8芯片[7],时钟芯片PCF8563与FPGA通过I2C总线实现双向通信,系统框图如图2所示。
图2 数字时钟系统框图
数字钟系统具体实现功能如下:1)实时时钟功能,能够完成常规时钟/日历的实时计数;2)手动调校功能,能够手动设置年月日时分秒,数据按照相应约束条件进行循环累加和移位操作,完成时钟/日历的手动调校;3)数码管动态显示功能,能够在实时和调校模式下完成时钟/日历的数码管切换显示,并在调校模式下实现数码管在当前调整位置的闪烁指示功能。
3 FPGA设计实现
FPGA作为本设计的核心部件,按照自顶向下的设计流程,各模块功能通过Verilog硬件描述语言实现,主要电路由按键消抖模块、键控时钟设置模块、实时时钟模块、I2C驱动模块、数码管驱动模块5部分构成。
3.1 按键消抖模块设计
设计采用4个机械弹性按键,按下为低电平,弹起为高电平。由于机械按键开关在闭合和断开时均不会马上稳定,存在瞬间抖动,为解决不利影响,故采用按键消抖的方法,即当检测到按键状态改变且按键状态稳定后,计数器从20 ms开始倒计时,当计数器递减到1时,说明按键稳定状态维持了20 ms,此时按键消抖过程结束,通过寄存器寄存4个按键的状态值key_value[3:0],分别表示按键0、1、2、3状态,并送入键控时钟设置模块。
3.2 键控时钟设置模块设计
键控时钟设置模块,主要完成边沿检测、模式切换和调校控制功能。
1)边沿检测功能是当一个按键按下时,按键状态值key_value[3:0]中的某一位为低电平,经过下降沿的边沿检测,得到对应的一个周期CLK的高电平信号,边沿检测后的信号表示为neg_sgn[3:0],即按键标志信号。
2)模式切换功能是完成按键标志信号neg_sgn[3:0]产生的相应动作。
当按键0按下,按键标志信号neg_sgn[0]检测到一个周期的高电平,则通过状态模式切换信号switch_0[1:0]完成状态循环跳转,其中0表示实时时钟模式,1表示手动调校模式,2表示调校完成并跳转至实时时钟模式;
当按键1按下,按键标志信号neg_sgn[1]检测到一个周期的高电平,则通过显示模式切换信号switch_1完成状态翻转,其中0表示时钟显示,1表示日历显示;
3)调校控制功能完成时钟和日历设置。当按键2按下,按键标志信号neg_sgn[2]检测到一个周期高电平,当前调整数据值移位一次;当按键3按下,按键标志信号neg_sgn[3]检测到一个周期高电平,当前调整数据值累加一次。时钟设置代码如下,日历设置代码类似,不再赘述。
//检测到移位按键2标志信号有效,且在手动调校模式、时钟显示模式
else if(neg_sgn[2]=1’b1 &&swtich_0=2’d1 &&switch_1=1’b1)
begin
if(disp_drive_t< 3’b101)
disp_drive_t<= disp_drive_t +1’b1;
else
disp_drive_t<= 3’d0;
end
//检测到移位按键3标志信号有效,当前调整数据值累加一次。
elseif(neg_sgn[2]=1 &&swtich_0=2’d 1&&switch_1=1’b1)begin
//时间驱动信号
case(disp_drive_t)
3’d0:begin
//时计数
if(hour_set< 4’b0010)
hour_set<= hour_set +1’b1;
else
hour_set<= 4’d0;
3’d1://分计数
…
default:…
end
else
//数据保持
begin
disp_drive_t<= disp_drive_t;
hour_set<= hour_set;
…
end
最后,将寄存的时钟和日历数据分别拼接为调校时间输出值num_timeset和调校日期输出值num_dataset,通过显示模式切换信号switch_1实现时钟和日历的切换输出,具体代码如:assignnum_set_xs = switch_1 ? num_dataset : num_timeset,将得到的调校输出值num_set_xs[23:0]送入到数码管显示模块进行调校模式的数据显示,同时将调校时间输出值num_timeset和调校日期输出值num_dataset进行拼接,得到设置值num_set[47:0]送入实时时钟模块作为实时时钟的初值,即通过I2C总线要写入PCF8563实时时钟芯片中的数据,并将状态模式切换信号switch_0、显示模式切换信号switch_1、时间驱动信号disp_drive_t、日期驱动信号disp_drive_d一并送出至后级模块。
3.3 实时时钟模块设计
实时时钟模块通过调用I2C驱动模块来实现对PCF8563实时时钟数据的读写,实时时钟模块与I2C驱动模块的操作时钟一致,频率选为250 kHz[8]。送至I2C驱动模块的信号包括I2C读写控制信号I2C_r_w、I2C触发执行信号I2C_exe、I2C器件内地址I2C_addr、I2C要写的数据I2C_data_w,以及从I2C驱动模块得到的信号包括I2C一次操作完成标识信号I2C_done、从I2C读的数据I2C_data_r。通过状态机实现I2C数据的读写操作,实时时钟模块状态流程如图3所示,主体循环状态为12个。写状态时,将设置初值num_set[47:0]数据分段写入I2C_data_w寄存器;读状态是将I2C读的数据I2C_data_r,分别存入秒寄存器sec[7:0]、分寄存器min[7:0]、时寄存器hour[7:0]、日寄存器day[7:0]、月寄存器month[7:0]、年寄存器year[7:0],最后将年月日时分秒寄存器值拼接为实时日期显示值num_data[23:0]和实时时间显示值num_time[23:0]的数据,通过显示模式切换信号switch_1实现实时显示下的时钟/日历切换,具体代码如:assignnum = switch_1 ? num_data : num_time,最后将得到的实时显示输出值num[23:0]传递至数码管驱动模块进行显示。
图3 实时时钟模块状态流程图
状态机流程描述如下:上电初始化后,延时8 ms,按照顺序执行Q1~Q12状态。进入Q1写秒状态时,I2C触发执行信号I2C_exe设为1,器件内地址I2C_addr设为0x02,将设置初值中的低8位num_set[8:0]存入I2C要写的数据I2C_data_w寄存器;进入Q2读秒状态时,当I2C一次操作完成标识信号I2C_done=1,将从I2C读的数据I2C_data_r存入秒寄存器sec[7:0]寄存器;其他读写过程类似,不同之处是器件内地址、写入和读出的数据值不同。顺序执行到Q12状态时,I2C读写控制信号I2C_r_w拉高。
特别指出的是,当进入循环状态后,写入数据完成,I2C一次操作完成标识信号I2C_done拉高后,一直在执行读操作。设计要求完成调校设置后,需要重新写入调校后的值。这时我们在循环体外,加入判断条件。当状态模式切换信号switch_0设置为2时,即调校设置成功后,模块中状态和数据寄存器清零,否则进入Q0状态重新开始执行循环体,实现了调校设置后的数据值成功写入I2C总线,完成了时钟/日历初值调整。
3.4 I2C驱动模块设计
I2C驱动模块实现底层的I2C读写时序控制[9],读写状态转移如图4所示。
图4 I2C读写状态转移图
当I2C触发执行信号I2C_exe=1时,从空闲W0跳转至W1状态, Bit_ctrl=1表示16位字地址,Bit_ctrl=0表示8位字地址,本系统采用8位字地址,故跳转至W3状态。当I2C读写控制标识信号rw_flag=0时,表示写数据,跳转至W6状态,当状态结束信号done=1时,结束I2C操作W7状态,表示一次I2C写操作完成,然后转至空闲;当I2C读写控制标识信号rw_flag=1时,表示读数据,跳转至W4状态,写地址读后,进入W5完成读数据,检测到done=1时结束I2C操作W7状态,表示一次I2C读操作完成,转至空闲。
具体各状态的输出,按照I2C总线协议要求设计[10-11],SDA和SCL两条信号线同时为高电平时,总线处于空闲状态,即释放总线。当SCL为高电平且SDA信号由高电平跳变为低电平,即为开始信号S;当SCL为高电平且SDA信号由低电平跳变为高电平,即为停止信号P,开始信号和停止信号均有主控制器(FPGA)产生。SDA数据在SCL为高电平期间必须保持稳定,只有在SCL为低电平期间,SDA数据线上的电平状态才可以改变。输出在SDA线上的每个字节均为8位,每个字节后需要有一个应答信号ACK,即FPGA在时钟9期间释放数据线SDA,由从器件(PCF8563)反馈应答信号,确保在SCL时钟9高电平期间为稳定的低电平,则表示接收器(PCF8563)成功接收到该字节。当FPGA为接收器时,即从PCF8563中读取数据,当FPGA接收完最后一个字节后,需要发送一个非应答信号NACK,通知PCF8563停止数据发送,并释放数据线SDA,以便FPGA发送停止信号P。
3.5 数码管驱动模块
数码管采用动态扫描显示,通过段选和位选信号进行分时点亮控制。为实现在手动调校模式下,进行数据移位设置时,具有当前数码管闪烁指示功能,首先通过显示模式切换信号switch_1对时间驱动信号disp_drive_t和日期驱动信号disp_drive_d进行选择,得到位选闪烁标志信号disp_drive,在0~5范围内变化;通过一个延时计数器得到一个0.5 s电平翻转一次的信号sel_reg,当进入调校模式后,case语句进行0~5状态之间的常规动态扫描,在0状态情况下,位选信号sel[5:1] <= 5’b11111;如果位选闪烁标志信号disp_drive = 0,则sel[0] <= sel_reg,即数码管0闪烁显示;否则sel[0] <= 0,即数码管位选0点亮;在1状态情况下,位选信号sel[5:2] <= 4‘b1111;sel[0] <= 1’b1;如果位选闪烁标志信号disp_drive = 1,则sel[1]<= sel_reg,即数码管1闪烁显示;否则sel[1]<= 0,即数码管位选1点亮;同理实现其他位选闪烁显示。进入显示模式后,进行常规动态扫描,从而完成数码管数据实时显示和调校模式的闪烁指示。
具体实现代码如下:
//得到位选闪烁标志信号disp_drive
assigndisp_drive = switch_1 ? disp_drive_t :disp_drive_d;
//得到0.5 s电平翻转一次的信号sel_reg
if(!rst_n)
begin
sel_reg<= 1’b0;
cnt_ss<= 26’d0;
end
else if(cnt_ss 26’d24999999)
begin
sel_reg<= ~sel_reg;
cnt_ss<= cnt_ss + 1’b1;
end
else if(cnt_ss 26’d49999999)
begin
sel_reg<= ~sel_reg;
cnt_ss<= 26’d0;
end
else
begin
sel_reg<= sel_reg;
cnt_ss<= cnt_ss + 1’b1;
end
//当进入调校模式,数码管闪烁指示
else if(switch_0 2’d1)
begin
//动态扫描
case(cnt)
//秒低位或日低位显示
3’d0:begin
sel[5:1] <= 5’b11111; //数码管1~5灭
num1 <= num[3:0]; //传入秒低位或日低位
point1 <= point[0]; //小数点显示
if(disp_drive 0)
sel[0] <= sel_reg; //0.5 s翻转,数码管0闪烁
else
sel[0] <= 1’b0; //数码管0点亮
end
//秒高位或日高位显示
3’d1:begin
sel[5:2] <= 4‘b1111; //数码管2~5灭
sel[0] <= 1’b1; //数码管0灭
num1 <= num[7:4]; //传入秒高位或日高位
point1 <= point[1]; //小数点显示
if(disp_drive 1)
sel[1]<= sel_reg; //0.5 s翻转,数码管1闪烁
else
图5 SignalTap逻辑分析结果
sel[1]<= 1’b0; //数码管1点亮
end
//分低位或月低位显示
3’d2:…
…
default:…
end
//当进入显示模式
else
begin
//动态扫描数码管0~5
case(cnt)
3’d0:begin
sel<= 6’b111110; //数码管1~5灭,0亮
num1 <= num[3:0]; //传入秒低位或日低位
point1 <= point[0]; //小数点显示
… : …
end
3’d5:begin
sel<= 6’b011111; //数码管0~4灭,5亮
num1 <= num[23:20]; //传入时高位或年高位
point1 <= point[0]; //小数点显示
end
default:…
end
4 功能验证
通过SignalTap逻辑分析仪进行功能验证,模式切换按键1设置为边沿触发,在开发板上将模式切换为调校模式,设置时间为21∶11∶10,移位按键调整至位选信号sel[3]控制的数码管上,点击按键1后,模式切换至实时显示,SignalTap输出逻辑分析结果如图5所示。
实测结果为:键控时钟设置模块输出num_set[47∶0]=0x000000211110h,实时时钟模块显示输出num_xs[23∶0]=0x211110h,时钟显示驱动信号disp_drive_t=0x4h,结果符合期望要求,验证了该设计的时钟数据写入和读取以及位选闪烁指示功能的正确性,日历数据不再赘述。
5 结束语
通过FPGA对实时芯片PCF8563进行读写控制,实现了时钟/日历切换显示,对时钟/日历手动调校。在调校状态下,实现了数据移位时数码管闪烁指示功能。通过SignalTap逻辑分析仪,验证了系统功能实现的技术可行性,并在开发板上完成调试,可供FPGA开发和数字钟感兴趣的人员参考,为后续实现报警、秒表、星期的显示设置提供借鉴。PCF8563是一款功能完备的实时时钟芯片,其可靠性高、功耗和成本低,可用于便携式仪器、电子计量等场合或实际产品中[12]。