ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PCF8563 RTC驱动设计:I2C时序与Verilog状态机实战解析

PCF8563 RTC驱动设计:I2C时序与Verilog状态机实战解析 简介面向FPGA开发者的I2C接口RTC实时时钟PCF8563读写Verilog驱动工程基于Quartus 18.0设计适用于Cyclone IV E系列EP4CE10F17C8器件。工程通过I2C总线协议控制PCF8563完成实时时钟的初始化、读取与显示适合学习I2C时序、RTC寄存器配置以及FPGA模块化设计的开发者。压缩包共124个文件核心为Verilog源码包含i2c_dri、pcf8563、rtc、seg_bcd_dri、key_debounce等模块此外还有Quartus工程约束文件、编译配置与数据文件等包体仅3.78MB目录结构直观。目前已有820人学习或下载过该工程代码中通过参数定义了器件从机地址、I2C时钟频率和初始时间使用户可以方便地调整设置以匹配不同硬件环境。整个工程从零开始展示了PCF8563初始化、时间写入、时间读取和显示的完整流程对想在FPGA上实现I2C外设驱动或数字时钟的开发者具有直接参考价值。1. 拿到PCF8563的Verilog驱动先别急着上板I2C时序才是真正的考场如果你做过几个带RTC的项目会知道PCF8563这类实时时钟芯片本身不复杂真正让新手翻车的永远是I2C这根两根线的时序。标题里这个“I2C接口RTC实时时钟pcf8563读写Verilog驱动源码Quartus工程文件.zip”本质上是把三件事捆在了一起I2C主机控制器、PCF8563寄存器读写逻辑、以及Quartus工程下的仿真与上板验证路径。适合谁适合已经会Verilog基础语法、想直接拿一个能用的I2C主机核去驱动RTC、但又不想从零调时序的工程师。我见过太多人把PCF8563当“黑匣子”用上电读秒寄存器发现数据不动或者读出来全是0xFF就开始怀疑芯片坏了。实际上十有八九是I2C起始/停止条件没满足、ACK位采样窗口偏了、或者时钟频率太快导致从机跟不上。这个工程文件给你的不是“点一下就能跑”的魔法而是一个可以对着波形逐拍检查的参考实现。你要做的第一件事不是综合下载而是先搞清楚这套代码里的I2C状态机长什么样以及它和你之前用过的软件I2C、STM32硬件I2C在时序上有哪些必须对齐的细节。接下来的内容我把这套方案按照“为什么这么设计→怎么搭工程→参数怎么调→踩了什么坑→还能怎么扩展”的顺序拆开讲。全程用我习惯的工程做法来讲不会假装我见过你手上那个zip包里的每一行代码但你可以拿着这篇文章去对照你自己的工程一个模块一个模块地验证。2. I2C主机控制器设计从协议时序到Verilog状态机的映射2.1 I2C协议里最容易写错的四个时刻起始、停止、ACK、数据翻转I2C总线只有SCL和SDA两根线但协议细节比UART多得多。PCF8563作为从机工作速率最高100kHz标准模式这意味着你的I2C主机时钟分频必须保证SCL不超过这个上限。很多人在FPGA里直接拿50MHz系统时钟去产生SCL结果分频数算错SCL跑到200kHz以上PCF8563虽然偶尔能响应但长时间跑必然出错。先看起始条件SCL为高时SDA发生高→低跳变。停止条件反过来SCL为高时SDA发生低→高跳变。这两个条件在Verilog里实现时最容易犯的错是把SDA的翻转和SCL的拉低放在同一个时钟周期里做。正确做法是先让SDA在SCL低电平期间准备好数据再拉高SCL等SCL高电平稳定后再改变SDA。写成代码就是一组带时序延迟的状态转移。ACK位是第二个重灾区。主机发送完8位数据后第9个时钟周期要释放SDA置为高阻然后采样SDA的电平。如果从机拉低说明ACK如果保持高说明NACK。很多初学者在这里直接把SDA当输出驱动永远输出高电平导致从机的ACK信号根本没法被读到。还有一个细节采样ACK的时机应该在SCL高电平的中间不能一拉高SCL就立刻采样否则可能采到SDA还没稳定的瞬间。数据翻转规则是第三个坑。I2C规定SDA只能在SCL低电平时改变SCL高电平时SDA必须保持稳定。这就意味着你的状态机里必须有明确的“SCL低→SDA变化→SCL拉高→保持→SCL拉低”这样至少四拍的节奏。如果为了省状态直接把SDA和SCL同时赋值波形上就会出现毛刺或者建立时间不足。第四个时刻是重复起始条件Restart。读PCF8563时先写地址寄存器然后要发一个Restart再发读命令不能直接发停止再发起始。虽然分开写也能工作但总线上就多了一个“主机释放总线”的时刻一旦有别的设备抢占总线就会出问题。规范的写法是在状态机里增加一个Restart状态复用起始条件的SDA翻转逻辑。2.2 I2C主机状态机用有限状态机把协议变成可综合逻辑把这四个时刻做成状态机常见的做法是定义IDLE、START、SHIFT_OUT、SHIFT_IN、ACK_IN、ACK_OUT、STOP这几个状态。核心思路是一个SCL周期拆成高半周期和低半周期两个阶段SDA的变化只允许发生在低半周期。localparam IDLE 4d0; localparam START 4d1; localparam SHIFT 4d2; // 数据移位每次移1位 localparam ACK_IN 4d3; // 主机读取从机ACK localparam ACK_OUT 4d4; // 主机发送ACK给从机 localparam STOP 4d5; reg [3:0] state; reg [3:0] bit_cnt; // 已发送/接收的位数 0~7 reg [7:0] tx_shift; // 发送移位寄存器 reg [7:0] rx_shift; // 接收移位寄存器 always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; scl_out 1b1; sda_out 1b1; end else begin case (state) IDLE: begin scl_out 1b1; sda_out 1b1; if (i2c_start) state START; end START: begin // SCL高时SDA拉低产生起始条件 sda_out 1b0; if (scl_high_done) state SHIFT; end SHIFT: begin // 数据在SCL低电平变化 if (scl_low_half) begin sda_out tx_shift[7]; tx_shift {tx_shift[6:0], 1b0}; bit_cnt bit_cnt 1b1; end if (bit_cnt 4d7 scl_high_done) state ACK_IN; end ACK_IN: begin // 释放SDA采样从机应答 sda_out 1bz; if (scl_high_mid) ack_flag sda_in; if (scl_high_done) begin if (ack_flag) state STOP; // NACK则停止 else if (op_rw) state SHIFT_IN; // 读操作 else state SHIFT; end end // ... 其余状态 endcase end end这段代码的关键逻辑说明sda_out 1bz表示释放SDA线改为输入方向这在FPGA的I/O引脚上需要配置为inout类型。实际工程里会在顶层模块里把sda_out和sda_in合并成一根sda线用三态门控制方向。scl_high_done和scl_low_half是分频时钟产生的窗口信号用于确定SCL高低电平的维持时间。参数说明bit_cnt计数到7后进入ACK状态不要计数到8因为第9个时钟周期是ACK专用。op_rw信号在发起传输前由上层模块设置1表示读0表示写。ack_flag在ACK_IN状态的scL_high_mid时刻采样这个时刻点是整个I2C时序里最需要精确对准的地方建议用示波器或者ModelSim波形对比验证。如果你不想写这么细的状态机也可以找现成的I2C主机IP核。但是自己写一遍的好处是你能清楚知道每个时钟沿上SDA和SCL的状态排查问题时不用把IP核当黑匣子。对于学习目的手写状态机的价值远比调通一个IP核大。2.3 时钟分频设计为什么100kHz的SCL需要至少500kHz的内部节拍PCF8563的标准模式最高100kHzSCL的低电平时间和高电平时间分别要求不低于4.7μs和4.0μs具体数值见数据手册接近这个量级。这意味着你产生SCL的时钟分频周期不能粗略地“一高一低算一个周期”必须考虑占空比和边沿建立时间。parameter CLK_FREQ 50_000_000; // 系统时钟50MHz parameter SCL_FREQ 100_000; // 目标SCL 100kHz parameter DIV_WIDTH 16; reg [DIV_WIDTH-1:0] div_cnt; reg [DIV_WIDTH-1:0] scl_high_cnt; reg [DIV_WIDTH-1:0] scl_low_cnt; always (posedge clk) begin if (div_cnt (CLK_FREQ / SCL_FREQ / 2 - 1)) div_cnt div_cnt 1b1; else begin div_cnt 0; scl_pulse ~scl_pulse; // 产生SCL翻转脉冲 end end分频参数说明CLK_FREQ / SCL_FREQ / 2是半个SCL周期对应的系统时钟数。50MHz除以100kHz等于500再除2得250所以div_cnt计数到249就翻转一次。注意分频得到的scL_pulse还不能直接作为SCL输出你还需要在状态机里打一拍确保SDA的变化发生在SCL低半周期。如果直接把scL_pulse接到引脚上数据和时钟边沿可能同时变化违反I2C的数据保持时间。实际中很多人把SCL频率降到10kHz甚至1kHz来调试这不是坏事反而能减少信号完整性干扰。PCF8563没有最小频率限制慢速跑完全没问题。等你把寄存器的读写都调通了再把分频系数改回去上到100kHz跑稳定性测试。2.4 三态门与上拉电阻inout引脚的Verilog描述方法I2C的SDA是双向口必须在FPGA内部实现一个三态缓冲器。常见写法是把SDA引脚声明为inout内部通过一个dir信号决定是输出还是高阻输入。inout sda; reg sda_out; reg sda_dir; wire sda_in; assign sda sda_dir ? sda_out : 1bz; assign sda_in sda;这个写法的含义是当sda_dir1时FPGA驱动SDA引脚输出sda_out的值当sda_dir0时SDA引脚呈高阻态由外部上拉电阻拉高同时sda_in可以读到从机拉低的电平。注意sda_in是wire类型不能放在always块里赋值。SCL线一般只由主机驱动不需要三态门但PCF8563支持时钟同步clock stretching即从机可能在需要时拉低SCL来延长时钟周期。为了兼容这个特性最好也把SCL做成三态门。不过很多工程直接忽略clock stretching因为PCF8563在标准速率下极少使用这个机制。如果你追求稳妥就统一用inout方式处理SCL。上拉电阻的选择也有讲究。PCF8563的I2C引脚是开漏输出必须外接上拉电阻到VCC。常见的取值是4.7kΩ配合3.3V电源和2.2kΩ配合5V或高总线电容场景。电阻太大会让信号上升沿变缓导致在SCL高电平采样时SDA还没稳定到有效电平电阻太小会增加灌电流长期运行可能损坏IO。之前遇到一个“I2C上拉电阻小了不通信”的案例就是用了1kΩ电阻结果SDA低电平时的电流超过了芯片规格通信时好时坏。如果你在调试中遇到偶发通信失败先检查上拉电阻和总线电容。3. PCF8563寄存器读写从数据手册到Verilog模块的转换3.1 PCF8563的寄存器地图哪些寄存器需要初始化哪些必须逐位控制PCF8563内部有16个寄存器。控制/状态寄存器1地址0x00的bit5是STOP位置1时时钟停止置0时运行。控制/状态寄存器2地址0x01的bit2是TI_TP位控制定时器中断输出。这两个寄存器在上电复位后的默认值通常是不确定的所以第一步操作应该是先把它们配置为确定值再开始读写时间寄存器。秒寄存器地址0x02的bit7是VL位voltage low上电时如果内部振荡器没起来VL会被置1。读取秒寄存器时如果发现bit7为1说明时钟电路有问题——可能是32.768kHz晶振没起振也可能是电池没接好。这是调试时最常遇到的“寄存器读出来但时间不走”的原因。时间寄存器全部是BCD码格式秒、分、时、日、月、年分别占一个字节。BCD码转二进制或者反向转换是Verilog代码里最繁琐的部分。常见做法是写两个函数模块bcd_to_bin和bin_to_bcd分别用组合逻辑实现。3.2 写寄存器模块起始→设备地址→寄存器地址→数据→停止写PCF8563的流程是发送设备地址0xA2PCF8563的7位地址是0x51左移一位后是0xA2bit0为0表示写然后发送要写的寄存器地址再连续发送数据。task write_reg; input [7:0] reg_addr; input [7:0] reg_data; begin i2c_start 1; i2c_data 8hA2; // 设备写地址 i2c_rw 0; i2c_done 0; // 等待I2C状态机完成发送 (posedge i2c_done); i2c_start 0; i2c_data reg_addr; i2c_done 0; (posedge i2c_done); i2c_data reg_data; i2c_done 0; (posedge i2c_done); i2c_stop 1; i2c_done 0; (posedge i2c_done); i2c_stop 0; end endtask用task来封装I2C传输序列的好处是上层逻辑代码会非常干净。每个(posedge i2c_done)都在等待一次完整的字节传输结束。这种写法在仿真中非常直观但在综合时task会被展开成组合逻辑和状态寄存器所以注意task里不能有不可综合的语句比如延时。上面的示例更适合做仿真测试平台如果要在板上运行需要改写成自动状态机。如果你拿到的zip包里看到类似task的写法先确认它是在tb文件里还是RTL源码里。在RTL里用task配合延时是不可综合的只能用于仿真。真正的可综合写法应该是用一个“传输请求→完成”的握手信号配合一个状态机来控制多个字节的传输顺序。3.3 读寄存器模块伪读与真读的区别以及为什么不能直接连续读读操作比写操作多了一个环节写完寄存器地址后要发Restart然后发设备读地址0xA3再接收数据。很多芯片支持“伪读”即先指定地址再重复起始再读PCF8563也支持标准I2C读流程。task read_reg; input [7:0] reg_addr; output [7:0] reg_data; begin i2c_start 1; i2c_data 8hA2; i2c_rw 0; i2c_done 0; (posedge i2c_done); i2c_start 0; i2c_data reg_addr; i2c_done 0; (posedge i2c_done); // 重复起始 i2c_start 1; i2c_data 8hA3; // 读地址 i2c_rw 1; i2c_done 0; (posedge i2c_done); i2c_start 0; // 读取数据主机发NACK结束 i2c_rw 1; i2c_ack 0; // 最后一个字节回NACK i2c_done 0; (posedge i2c_done); reg_data i2c_rx_data; i2c_stop 1; i2c_done 0; (posedge i2c_done); i2c_stop 0; end endtask参数说明i2c_ack0表示主机在第9个时钟周期发送NACK告诉从机“这是最后一个字节不要再发了”。如果读多个字节前几个字节要发ACK最后一个字节发NACK。很多初学I2C的人在读操作时把ACK位方向搞反主机一直发NACK从机在发送第一个字节后就不再继续了导致读到的数据永远是第一字节后面的数据全部丢失。PCF8563的时间寄存器在连续读模式下会自动递增地址所以你可以一次读出秒、分、时、日、月、年六个字节。但注意在读取过程中如果你从0x02开始连续读6个字节不要在0x07处停因为0x08是闹钟寄存器组0x09之后是定时器和控制寄存器。PCF8563的地址递增到0x0F后会回卷到0x00不是继续0x10。如果你要连续读务必确认读的地址范围不跨过0x0F。3.4 BCD码与二进制互转组合逻辑实现要点避免用除法器BCD转二进制最直接的思路是8421码按位加权求和。比如秒寄存器读回0x59高4位是5低4位是9结果就是5*10959。Verilog里用移位相加就能实现不需要除法器。function [7:0] bcd_to_bin; input [7:0] bcd; begin bcd_to_bin (bcd[7:4] * 10) bcd[3:0]; end endfunction function [7:0] bin_to_bcd; input [7:0] bin; reg [7:0] temp; begin temp bin; bin_to_bcd[3:0] temp % 10; temp temp / 10; bin_to_bcd[7:4] temp % 10; end endfunction这个写法用到了*和%运算符在FPGA里综合后会用乘法器和除法器资源对于简单的时间转换够用。如果你在意资源占用可以用查表法——直接生成一个256字节的ROM把BCD和二进制对应关系存进去。查表法组合逻辑路径更短时序收敛更容易。不过对于RTC这种低频操作乘法器完全够用不必过度优化。这里有个细节PCF8563的世纪寄存器0x13用bit7表示世纪需要手动切换。如果你的应用跨度超过100年就要额外处理这个bit如果只是做日常计时忽略也没关系。多数产品用不到这个功能。4. Quartus工程搭建与验证从源码到板子上跑通4.1 Quartus工程文件结构哪些文件是必须的哪些只是辅助一个完整的Quartus工程至少包含顶层Verilog文件、I2C控制器模块、PCF8563驱动模块、约束文件.qsf或.sdc、以及可选的Testbench文件。zip包里如果还有.do脚本或者.tcl脚本通常是用来自动化仿真流程的不是必须。我习惯的工程组织方式是这样的文件作用是否必须top.v顶层模块例化所有子模块分配引脚是i2c_master.vI2C主机状态机是rtc_pcf8563.vPCF8563读写逻辑是clk_div.v时钟分频是pcf8563_tb.v仿真测试平台建议有pcf8563.qsfQuartus工程设置与引脚分配是pcf8563.sdc时序约束文件建议有引脚分配这一步最容易出错。FPGA的SDA引脚必须支持inout不是所有GPIO都能做三态口。查一下你的FPGA芯片数据手册确认分配的引脚是否有内部上拉使能寄存器。如果引脚有内部弱上拉可以省掉外部上拉电阻但我不建议这样做——内部上拉阻值通常在几十kΩ驱动能力太弱总线电容稍大就会让边沿变缓。4.2 用ModelSim跑仿真Testbench里怎么模拟PCF8563的ACK行为没有实物芯片时仿真验证全靠Testbench里模拟的I2C从机行为。这只“虚拟PCF8563”需要能回应ACK、能存储写入的数据、能按地址回读数据。// 虚拟PCF8563从机模型 module pcf8563_model ( inout scl, inout sda ); reg [7:0] mem [0:15]; reg [7:0] bit_cnt; reg [7:0] addr; reg [7:0] data_in; reg sda_drive; // 检测起始条件SCL高时SDA下降沿 always (negedge sda) begin if (scl 1) $display(START detected); end // 在SCL高电平中间采样SDA always (posedge scl) begin if (bit_cnt 8) begin data_in {data_in[6:0], sda}; bit_cnt bit_cnt 1; end else begin if (data_in[0] 0) // 写地址 mem[addr] data_in; else // 读地址 sda_drive mem[addr]; bit_cnt 0; end end endmodule这个模型简化了很多细节比如没有处理ACK相位但对于验证I2C主机的数据输出帧格式已经够了。关键是你需要在ModelSim里观察SCL和SDA的联合波形确认起始条件、数据位、停止条件的边沿间隔是否符合PCF8563的数据手册。运行仿真时要注意如果Testbench里用了和RTL时钟相同的周期波形会很难看所有跳变都挤在一起。建议把系统时钟设为10MHz100ns周期SCL分频到10kHz这样每个SCL周期在波形图上占10格看起来非常清楚。代码逻辑跑通后再改回50MHz和100kHz。4.3 SignalTap II在线调试上板后看不到波形怎么办仿真通过不代表上板一定能跑。最有效的调试手段是SignalTap II逻辑分析仪它利用FPGA内部的Block RAM存储采样数据通过JTAG传回电脑。你可以把SCL、SDA、状态机当前状态、以及读写寄存器地址和数值都加到采样列表里。设置SignalTap时注意三个参数采样时钟、采样深度、触发条件。采样时钟一般用系统时钟50MHz采样深度256个点就够看一次完整的事务最多够用512。触发条件可以设成“起始条件后的第一个SCL下降沿”这样抓到的波形正好是一次完整的I2C事务。上板调试最常见的现象是SignalTap里看到SDA波形正常但PCF8563就是无响应。此时优先排查硬件连接——SDA和SCL有没有接反上拉电阻有没有焊上芯片电源引脚有没有退耦电容。还有一个经典问题FPGA引脚配置成开漏输出时如果忘了使能输出使能信号引脚就一直处于高阻态波形看起来SDA全是高电平但实际上总线根本没有被驱动。4.4 上板验证顺序先读ID再读状态最后才读时间拿到板子后不要一上来就读取秒寄存器显示时间。按照我习惯的排查顺序应该一步步确认链路正常第一步初始化控制寄存器。向0x00写入0x00启动时钟向0x01写入0x00关闭中断输出。读回验证。第二步读取0x02秒寄存器。检查bit7VL位是否为0。如果VL1说明内部振荡器没工作先查晶振和电容。32.768kHz晶振的两个负载电容常见12.5pF必须焊接可靠虚焊是VL位置1的最常见原因。第三步隔一秒钟再读一次秒寄存器确认数值有变化。如果两次读到的值完全一样说明时钟根本没走问题大概率在晶振电路或STOP位没有清零。第四步设置闹钟或定时器用LED指示中断引脚状态验证PCF8563的中断输出是否正常。这一步对低功耗唤醒类应用很重要。如果前面的步骤都通过了时间读写基本就是水到渠成的事情。万一时间写入后读出来不对检查BCD码转换逻辑——用十六进制直接看寄存器原始值如果写0x12后读回0x12说明写入没问题如果读回0x18说明你的二进制转BCD逻辑写错了。5. 常见问题排查驱动上电不通、时间不走、读写错乱的三类现场现象一上电后I2C状态机跑飞SCL没有波形输出信号量里状态乱跳。原因大概率是复位电路不稳定系统时钟还没锁相环锁定就开始了第一次传输。解决方法是延时至少10ms再发起第一次I2C操作或者等锁相环locked信号拉高后再启动状态机。我常用的做法是在顶层模块加一个上电复位计数器计满1ms后才释放复位信号。现象二PCF8563能响应写操作但读回来的时间不更新。先检查0x00寄存器的bit5STOP位如果置1就清零。STOP位通常在首次上电时是随机的有些批次芯片出厂时这一位为1导致时钟停止。此外0x02的bit7VL位如果为1秒值可能被锁存在0不会递增。解决办法是先把秒寄存器写一次0x00清掉VL位再启动时钟。现象三写入某个寄存器后影响到了其他寄存器。这是地址边界问题。PCF8563的寄存器地址范围是0x00~0x0F如果你写入时用的是7位设备地址而不是8位写地址0xA2或者把寄存器地址错发成0x50、0x51就可能落在非法地址空间。检查一下你的设备地址定义。0x51是芯片的7位地址1字节的写地址是0xA2读地址是0xA3。如果把0xA2当成寄存器地址发给芯片芯片会当成设备地址处理返回NACK。现象四SCL频率太低每秒钟只能完成一次读写但系统感觉像卡死。这多半是分频系数算错了。如果你写的是13位分频计数器最高只能分到8191那么在50MHz下SCL最低也只能到约3kHz不至于太慢。反而是分频系数写成500对应100kHz但实际计数到500才翻转一次实际SCL就是50kHz减半再减半变成50kHz。拿示波器量一下SCL脚确认实际频率和目标一致。现象五SignalTap里看到数据正确但板子重新上电后第一次读写失败后面恢复正常。这是“上电首次访问失败”的经典问题。原因是PCF8563内部上电复位需要时间通常需要几十毫秒而FPGA可能在上电后几毫秒就开始访问。解决办法是在初始化序列前加一个等待定时器等待至少100ms再发起第一次I2C通信。如果你用的是电池供电的RTC电池电压如果低于2V芯片也可能处于不确定状态测量一下电池电压。现象六I2C总线死锁SDA一直为低。从机的SDA和SCL在开漏驱动下一股由主机或从机拉低。如果主机状态机在发送过程中掉电总线可能停在SDA为低的中间状态。解决方法是在软件里实现总线恢复功能——翻转SCL最多9次同时监测SDA是否被释放。如果你的Verilog状态机里有超时保护可以在超时后自动做这个恢复动作。这个功能对嵌入式产品很重要因为总有各种原因导致I2C事务被打断。6. 进阶扩展从单向读写到多字节突发、时钟校准与低功耗唤醒读写时间和日期只是PCF8563的基本功能。实际项目里还有三个需求很常见连续读取多个寄存器避免数据错乱、校准32.768kHz晶振的频率偏差、以及利用闹钟中断实现系统定时唤醒。这三个功能共享同一个I2C总线如果你之前写的状态机不支持多字节突发和中断处理现在就得改。多字节突发读的核心是主机的NACK时序。前N-1个字节主机回ACK最后一个字节回NACK。这时你的状态机需要增加一个last_byte标志。常见做法是在发起读操作时传入要读的字节数状态机在收到倒数第二个字节后把ACK位反转为NACK。我在前面给的代码示例里i2c_ack0这一个信号的时序就决定了突发读是否成功不要把它写成固定值。时钟校准方面PCF8563的偏移寄存器0x0F可以微调振荡器的负载电容每步约4ppm。如果你测量的时钟一天偏快/偏慢几十秒可以通过该寄存器做补偿。校准的流程是先用精确的参考时钟比如GPS秒脉冲测量PCF8563的实际偏差计算偏移量然后写入偏移寄存器再测量验证。注意偏移寄存器的更新需要重新设置0x00寄存器才会生效具体位定义查数据手册的“FREQUENCY OFFSET”章节。手头没有频率计的话可以用逻辑分析仪抓取秒寄存器的变化间隔用FPGA内部计数器数一下1秒内系统时钟的个数偏差一目了然。低功耗唤醒是RTC最值钱的应用。PCF8563的INT引脚不是I2C引脚在闹钟匹配时会产生中断脉冲可以接MCU的外部中断唤醒。要启用这个功能你的驱动模块需要额外提供两个功能设置闹钟寄存器0x09~0x0C、配置控制/状态寄存器2的中断使能位0x01的bit1。闹钟寄存器和时间寄存器格式相同也是BCD码每个字节的bit7是“使能有效”标志比如你只想在每天特定小时触发闹钟就把分钟闹钟的使能位清零。做产品时这是最强的低功耗策略——MCU整个睡眠只在闹钟匹配时被叫醒其它时间系统功耗只有RTC的微安级。最后说一个我自己的习惯拿到任何RTC驱动第一件事不是测时间而是把秒寄存器连续读100次看每次读到的值是否满足单调递增、没有跳变。这比任何功能测试都能更快暴露I2C时序问题。如果100次中有一次读回0x00或者0xFF说明你的采样窗口还有问题检查SCL高电平中点的时序裕量。这个习惯帮我排除过很多“看起来正常但磁场上偶尔失灵”的板子希望也能帮到你。如果你要在这个方向继续深挖下一步建议把I2C控制器改成参数化的支持标准模式、快速模式400kHz和高速模式再用同一个状态机去驱动EEPROM、温度传感器和PCF8563。能通吃三种从机的I2C主机控制器基本就彻底掌握I2C协议了。Verilog代码没有想象中那么玄学时序逻辑的每个沿都看得见调通一次以后所有I2C设备在你眼里都是浮云。希望帮到你。本文还有配套的精品资源点击获取
返回列表