一天一个设计实例-实时时钟芯片PCF8563的接口电路、时序及程序设计
实时时钟芯片PCF8563的接口电路、时序及程序设计
1.1.1PCF8563 的主要技术性能与特点
PCF8563 是低功耗的 CMOS 实时时钟/日历芯片,它提供一个可编程时钟输出,一个中断输出和掉电检测器,所有的地址和数据通过 I2C 总线接口串行传递。最大总线速度为400Kbits/s,每次读写数据后,内嵌的字地址寄存器会自动产生增量。
(1)低工作电流:典型值为 0.25μ A( VDD=3.0V, Tamb=25℃时)。
(2)世纪标志
(3)大工作电压范围:1.0~5.5
(4)低休眠电流;典型值为 0.25μA(VDD=3.0V,Tamb=25℃)
(5) 400KHz 的 I2C 总线接口( VDD=1.8~5.5V 时)。
(6)可编程时钟输出频率为:32.768KHz, 1024Hz, 32Hz, 1Hz。
(7)报警和定时器。
(8)掉电检测器。
(9)内部集成的振荡器电容。
(10)片内电源复位功能。
(11) I2C 总线从地址:读,0A3H;写,0A2H。
(12)开漏中断引脚。
1.1.2PCF8563 的引脚功能与封装形式
图4‑22 管脚配置 |
图4‑23 二极管保护图 |
在PCF8563的引脚排列,其中VDD输入范围1.0~5.5V电源。OSCI和OSCO是振荡源,外接32.768kHz晶振。管脚 CLKOUT 可以输出可编程的方波。CLKOUT 频率寄存器决定方波的频率, CLKOUT 可以输出 32.768KHz( 缺省值), 1024, 32, 1Hz 的方波。CLKOUT为开漏输出管脚,通电时有效,无效时为高阻抗。/INT中断输出(开漏;低电平有效)。
表4‑10 管脚描述
符号 |
管脚号 |
描 述 |
OSCI |
1 |
振荡器输入 |
OSCO |
2 |
振荡器输出 |
/INT |
3 |
中断输出(开漏;低电平有效) |
VSS |
4 |
地 |
SDA |
5 |
串行数据 I/O |
SCL |
6 |
串行时钟输入 |
CLKOUT |
7 |
时钟输出 (开漏) |
VDD |
8 |
正电源 |
PCF8563 有 16 个8位寄存器:一个可自动增量的地址寄存器,一个内置 32.768KHz 的振荡器(带有一个内部集成的电容),一个分频器(用于给实时时钟 RTC 提供源时钟),一个可编程时钟输出,一个定时器,一个报警器,一个掉电检测器和一个 400KHz I2C总线接口。所有 16 个寄存器设计成可寻址的 8 位并行寄存器,但不是所有位都有用。前两个寄存器(内存地址 00H, 01H)用于控制寄存器和状态寄存器,内存地址 02H~08H 用于时钟计数器(秒~年计数器),地址 09H~0CH 用于报警寄存器(定义报警条件),地址 0DH 控制CLKOUT 管脚的输出频率,地址 0EH 和 0FH 分别用于定时器控制寄存器和定时器寄存器。秒、分钟、小时、日、月、年、分钟报警、小时报警、日报警寄存器,编码格式为 BCD,星期和星期报警寄存器不以 BCD 格式编码。当一个 RTC 寄存器被读时,所有计数器的内容被锁存,因此,在传送条件下, 可以禁止对时钟/日历芯片的错读。
报警功能模式
一个或多个报警寄存器 MSB( AE=Alarm Enable 报警使能位)清0时,相应的报警条件有效,这样,一个报警将在每分钟至每星期范围内产生一次。设置报警标志位 AF(控制/状态寄存器2的位3)用于产生中断, AF 只可以用软件清除。
定时器
8位的倒计数器(地址 0FH)由定时器控制寄存器(地址 0EH,参见表 25)控制,定
时器控制寄存器用于设定定时器的频率( 4096, 64, 1,或 1/60Hz),以及设定定时器有效或无效。定时器从软件设置的 8 位二进制数倒计数,每次倒计数结束,定时器设置标志位TF,定时器标志位 TF 只可以用软件清除, TF 用于产生一个中断( /INT ),每个倒计数周期产生一个脉冲作为中断信号。TI/TP(参见表 7)控制中断产生的条件。当读定时器时,返回当前倒计数的数值。
CLKOUT 输出
管脚 CLKOUT 可以输出可编程的方波。CLKOUT 频率寄存器(地址 0DH;)决定方波的频率, CLKOUT 可以输出 32.768KHz( 缺省值), 1024, 32, 1Hz 的方波。CLKOUT为开漏输出管脚,通电时有效,无效时为高阻抗。
复位
PCF8563 包含一个片内复位电路,当振荡器停止工作时,复位电路开始工作。在复位状态下, I2C 总线初始化,寄存器 TF、 VL、 TD1、 TD0、 TESTC、 AE 被置逻辑1,其它的寄存器和地址指针被清0。
掉电检测器和时钟监控
PCF8563 内嵌掉电检测器,当 VDD 低于 Vlow 时,位 VL( Voltage Low,秒寄存器的位 7)被置1,用于指明可能产生不准确的时钟/日历信息, VL 标志位只可以用软件清除.当VDD 慢速降低(例如以电池供电)达到 Vlow 时,标志位 VL 被设置,这时可能会产生中断。
图4‑24 掉电检测
1.1.3PCF8563 的内部结构
图4‑25 PCF8563 的内部结构
1.1.4PCF8563 的寄存器
寄存器概况
标明“-”的位无效,标明“0”的位应置逻辑0。
表4‑11 寄存器概况
地址 |
寄存器名称 |
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
00H |
控制/状态寄存器 1 |
TEST |
0 |
STOP |
0 |
TESTC |
0 |
0 |
0 |
01H |
控制/状态寄存器 2 |
0 |
0 |
0 |
TI/TP |
AF |
TF |
AIE |
TIE |
0DH |
CLKOUT 频率寄存器 |
FE |
- |
- |
- |
- |
- |
FD1 |
FD0 |
0EH |
定时器控制寄存器 |
TE |
- |
- |
- |
- |
- |
TD1 |
TD0 |
0FH |
定时器倒计数数值寄存器 |
定时器倒计数数值 |
BCD 格式寄存器概况
表4‑12 BCD 格式寄存器概况
地址 |
寄存器名称 |
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
02h |
秒 |
VL |
00~59BCD 码格式数 |
||||||
03h |
分钟 |
- |
00~59BCD 码格式数 |
||||||
04h |
小时 |
- |
- |
00~59BCD 码格式数 |
|||||
05h |
日 |
- |
- |
01~31BCD 码格式数 |
|||||
06h |
星期 |
- |
- |
- |
- |
- |
0~6 |
||
07h |
月/世纪 |
C |
- |
- |
01~12 BCD 码格式数 |
||||
08h |
年 |
00~99 BCD 码格式数 |
|||||||
09h |
分钟报警 |
AE |
00~59 BCD 码格式数 |
||||||
0Ah |
小时报警 |
AE |
- |
00~23 BCD 码格式数 |
|||||
0BH |
日报警 |
AE |
- |
01~31 BCD 码格式数 |
|||||
0CH |
星期报警 |
AE |
- |
- |
- |
- |
0~6 |
控制/状态寄存器1
表4‑13 控制/状态寄存器1位描述(地址 00H)
Bit |
符号 |
描 述 |
7 |
TEST1 |
TEST1=0;普通模式TEST1=1;EXT_CLK 测试模式 |
5 |
STOP |
STOP=0;芯片时钟运行STOP=1;所有芯片分频器异步置逻辑 0;芯片时钟停止运行,(CLKOUT 在 32.768kHz 时可用) |
3 |
TESTC |
TESTC=0;电源复位功能失效(普通模式时置逻辑 0);TESTC=1;电源复位功能有效 |
6,4,2,1,0 |
0 |
缺省值置逻辑 0 |
控制/状态寄存器2
表4‑14 控制/状态寄存器2位描述(地址 01H)
Bit |
符号 |
描述 |
7,6,5 |
0 |
缺省值置逻辑 0 |
4 |
TI/TF |
TI/TP=0:当 TF 有效时 INT 有效 (取决于 TIE 的状态) |
3 |
AF |
当报警发生时, AF 被置逻辑 1;在定时器倒计数结束时, |
2 |
TF |
|
1 |
AIE |
标志位 AIE 和 TIE 决定一个中断的请求有效或无效,当 |
0 |
TIE |
表4‑15 /INT 操作( bit TI/TP=1)
源时钟(Hz) |
/INT 周期 |
|
n=1 |
n>1 |
|
4096 |
1/8192 |
1/4096 |
64 |
1/128 |
1/64 |
1 |
1/64 |
1/64 |
1/60 |
1/64 |
1/64 |
注 1. TF 和/INT 同时有效
注 2. n 为倒计数定时器的数值,当 n= 0 时定时器停止工作。
表4‑16 AF 和 TF 值描述
R/W |
Bit:AF |
Bit:TF |
||
值 |
描述 |
值 |
描述 |
|
Read 读 |
0 1 |
报警标志无效 报警标志有效 |
0 1 |
定时器标志无效 定时器标志有效 |
Write 写 |
0 1 |
报警标志被清除 报警标志保持不变 |
0 1 |
定时器标志被清除 定时器标志保持不变 |
秒、分钟和小时寄存器
表4‑17 秒/VL 寄存器位描述(地址 02H)
Bit |
符号 |
描 述 |
7 |
VL |
VL=0:保证准确的时钟/日历数据 |
6~0 |
<秒> |
代表 BCD 格式的当前秒数值,值为 00~99 |
表4‑18 分钟寄存器位描述(地址 03H)
Bit |
符号 |
描 述 |
7 |
- |
无效 |
6~0 |
<分钟> |
代表 BCD 格式的当前分钟 |
表4‑19 小时寄存器位描述(地址 04H)
Bit |
符 号 |
描 述 |
7~6 |
- |
无效 |
5~0 |
<小时> |
代表 BCD 格式的当前小 |
日、星期、月/世纪和年寄存器
表4‑20 日寄存器位描述(地址 05H)
Bit |
符号 |
描 述 |
7~6 |
- |
无效 |
5~0 |
<日> |
代表 BCD 格式的当前日数值,值 |
表4‑21 星期寄存器位描述(地址 06H)
Bit |
符号 |
描 述 |
7~3 |
- |
无效 |
2~0 |
<星期> |
代表当前星期数值 0~6,参见表 15, |
表4‑22 星期分配表
日( Day) |
Bit2 |
Bit1 |
Bit0 |
星期日 |
0 |
0 |
0 |
星期一 |
0 |
0 |
1 |
星期二 |
0 |
1 |
0 |
星期三 |
0 |
1 |
1 |
星期四 |
1 |
0 |
0 |
星期五 |
1 |
0 |
1 |
星期六 |
1 |
1 |
0 |
表4‑23 月/世纪寄存器位描述(地址 07H)
Bit |
符号 |
描 述 |
7 |
C |
世纪位;C=0 指定世纪数为 20××, C=1 |
6~5 |
- |
无用 |
4~0 |
<月> |
代表 BCD 格式的当前月份,值为 01~12; |
表4‑24 月分配表
月份 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
一月 |
0 |
0 |
0 |
0 |
1 |
二月 |
0 |
0 |
0 |
1 |
0 |
三月 |
0 |
0 |
0 |
1 |
1 |
四月 |
0 |
0 |
1 |
0 |
0 |
五月 |
0 |
0 |
1 |
0 |
1 |
六月 |
0 |
0 |
1 |
1 |
0 |
七月 |
0 |
0 |
1 |
1 |
1 |
八月 |
0 |
1 |
0 |
0 |
0 |
九月 |
0 |
1 |
0 |
0 |
1 |
十月 |
1 |
0 |
0 |
0 |
0 |
十一月 |
1 |
0 |
0 |
0 |
1 |
十二月 |
1 |
0 |
0 |
1 |
0 |
表4‑25 年寄存器位描述(地址 08H)
Bit |
符号 |
描 述 |
7~0 |
<年> |
代表 BCD 格式的当前年数值,值为 00~99。 |
报警寄存器
当一个或多个报警寄存器写入合法的分钟、小时、日或星期数值并且它们相应的 AE( Alarm Enable )位为逻辑0,以及这些数值与当前的分钟、小时、日或星期数值相等,标志位 AF( Alarm Flag)被设置, AF 保存设置值直到被软件清除为止, AF 被清除后,只有在时间增量与报警条件再次相匹配时才可再被设置。报警寄存器在它们相应位 AE 置为逻辑1时将被忽略。
表4‑26 分钟报警寄存器位描述(地址 09H)
Bit |
符号 |
描 述 |
7 |
AE |
AE=0,分钟报警有效;AE=1,分钟报警无效 |
6~0 |
<分钟报警> |
代表 BCD 格式的分钟报警数值,值为 00~59 |
表4‑27 小时报警寄存器位描述(地址 0AH)
Bit |
符号 |
描 述 |
7AE |
AE=0; |
小时报警有效;AE=1;小时报警无效 |
6~0 |
<小时报警> |
代表 BCD 格式的小时报警数值,值为 00~23 |
表4‑28 日报警寄存器位描述(地址 0BH)
Bit |
符号 |
描 述 |
7 |
AE |
AE=0;日报警有效。AE=1;日报警无效。 |
6~0 |
<日报警> |
代表 BCD 格式的日报警数值,值为 00~31 |
表4‑29 星期报警寄存器位描述(地址 0CH)
Bit |
符号 |
描 述 |
7 |
AE |
AE=0;星期报警有效。AE=1;星期报警无效 |
6~0 |
<星期报警> |
代表 BCD 格式的星期报警数值,值为 0~6 |
CLKOUT 频率寄存器
表4‑30 CLKOUT 频率寄存器位描述(地址 0DH)
Bit |
符号 |
描 述 |
7 |
FE |
FE=0;CLKOUT 输出被禁止并设成高阻抗。 |
6~2 |
- |
无效 |
1 |
FD1 |
用于控制 CLKOUT 的频率输出管脚(fCLKOUT ), |
表4‑31 CLKOUT 频率选择表
FD1 |
FD0 |
fCLKOUT |
0 |
0 |
32.768kHz |
0 |
1 |
1024Hz |
1 |
0 |
32Hz |
1 |
1 |
1Hz |
倒计数定时器寄存器
定时器寄存器是一个8位字节的倒计数定时器,它由定时器控制器中位 TE 决定有效或无效,定时器的时钟也可以由定时器控制器选择,其它定时器功能,如中断产生,由控制/状态寄存器2控制。为了能精确读回倒计数的数值, I2C 总线时钟 SCL 的频率应至少为所选定定时器时钟频率的两倍。
表4‑32 定时器控制器寄存器位描述(地址 OEH)
Bit |
符号 |
描 述 |
7 |
TE |
TE=0;定时器无效。TE=1;定时器有效。 |
6~2 |
- |
无用 |
1 |
TD1 |
定时器时钟频率选择位,决定倒计数定时 |
0 |
TD0 |
表4‑33 定时器时钟频率选择
TD1 |
TD0 |
定时器时钟频率(Hz) |
0 |
0 |
4096 |
0 |
1 |
64 |
1 |
0 |
1 |
1 |
1 |
1/60 |
表4‑34 定时器倒计数数值寄存器位描述(地址 OFH)
Bit |
符 号 |
描 述 |
7~0 |
<定时器倒计数数值> |
倒计数数值“n”, |
1.1.5PCF8563 的常见应用原理图
原理图中典型应用如下:
图4‑26 PCF8563原理图中典型应用
FPGA中的应用:
SCL、SDA引脚只需要链接到FPGA的普通I/O引脚就可以,常见电路如下:
图4‑27 PCF8563 FPGA中的应用
CPU中的应用:
在CPU中应用时,首先要确定下CPU型号是什么。关键这个CPU有没有IIC接口,如果有,将PCF8563接IIC接口。注意IIC接口要有上拉电阻。倘若使用的CPU没有IIC接口,比如STC单片机除最新的STC8以外的其他系列,则任意选择通用IO口均可。
图4‑28 PCF8563 CPU中的应用
其他电路:
其他电路同上。
1.1.6PCF8563和DS1302的区别
pcf8563是i2c接口,有倒计时,分频输出,中断等功能。ds1302功能比较单一,也不是标准接口,但价格较pcf8563便宜。
pcf8563和ds1302都是时钟芯片,不过PCF8563要精确一些,要pcf8563和ds1302哪个比较好,根据自己的需求来选择。
1.1.7PCF8563的功能分析
时序主要参考IIC时序,在此不再赘述,主要分析下功能操作。
实时时钟芯片,一旦初始化后,它就会随着现实的时钟一直计数。要明白 PCF8563 芯片最主要的关键,就是“传输时序”和“芯片本身的寄存器分配
下面看下PCF8563寄存器的内容,详见芯片Datasheet。
调用过程大致如下:
普通模式:
发送写器件从地址
发送器件子地址,控制/状态寄存器地址
预置秒
预置分
预置时
写控制寄存器数据
初始化:
发送 8‘hA2/8'b10100010访问字节,发送写器件从地址。
查询应答信号;
发送 8’h00/ 8'b00000000 访问字节,设置控制/状态寄存器1;
查询应答信号
发送8'b 00000010访问字节,PCF8563 的秒寄存器的地址。
查询应答信号
发送8'b xxxxxxxx访问字节,PCF8563 的预置秒。
查询应答信号,秒预置指示灯
写控制寄存器数据
发送8'b 00000011访问字节,PCF8563 的分寄存器的地址。
查询应答信号
发送8'b xxxxxxxx访问字节,PCF8563 的预置分。
查询应答信号
发送8'b 00000100访问字节,PCF8563 的时寄存器的地址。
查询应答信号
发送8'b xxxxxxxx访问字节,PCF8563 的预置时。
查询应答信号
调用:
发送 8‘hA2/8'b10100010访问字节,发送读器件从地址。
查询应答信号
发送8'b 00000010访问字节,PCF8563 的秒寄存器的地址。查询应答信号
读秒钟数据
发送8'b 00000011访问字节,PCF8563 的分寄存器的地址。查询应答信号
读分钟数据
发送8'b 00000100访问字节,PCF8563 的时寄存器的地址。查询应答信号
读小时数据
重复上述内容
EXT_CLK 测试模式:
测试模式用于在线测试、建立测试模式和控制 RTC 的操作。测试模式由控制/状态寄存器1的位 TEST1 设定,这时 CLKOUT 管脚成为输入管脚。在测试模式状态下,通过 CLKOUT 管脚输入的频率信号代替片内的 64Hz 频率信号,每 64个上升沿将产生1秒的时间增量。
注意:进入 EXT_CLK 测试模式时时钟不与片内 64Hz 始终时钟同步,也确定不出预分频的状态。
1.进入 EXT_CLK 测试模式;设置控制/状态寄存器1的位7(TEST=1)。
2.设置控制/状态寄存器1的位5(STOP=1)。
3.清除控制/状态寄存器1的位5(STOP=0)。
4.设置时间寄存器(秒、分钟、小时、日、星期、月/世纪和年)为期望值。
5.提供 32 个时钟脉冲给 CLKOUT。
6.读时间寄存器观察第一次变化。
7.提供 64 个时钟脉冲给 CLKOUT。
8.读时间寄存器观察第二次变化; 需要读时间寄存器的附加增量时, 重复步骤7和8。
1.1.8 PCF8563的功能实现
根据以上分析,建立如下基础模块的结构图
图4‑29 PCF8563基础模块(PCF8563_basemod.v)的建模图
图4‑29是 PCF8563基础模块的建模图,内容包含命令控制模块与IIC IP模块,功能函数模块负责最基础的读写操作,命令控制模块则负责功能调度,准备访问字节等任务。换之,函数功能模块的右边是驱动硬件资源的顶层信号。
表4‑35 PCF8563 (pcf8563_basemod.v)模块间信号定义
模块间信号 |
||
管脚 |
传输方向 |
作用/说明 |
iCall |
信号为命令控制模块发送给IIC IP模块。 |
iCall为2位,其中 [1]为写操作, [0]为读操作。 |
oDone |
oDone为IIC IP模块发送给命令控制模块。 |
oDone为操作完成信号。 |
iAddr |
命令控制模块发送给IIC IP模块 |
读/写操作的地址 |
iData |
命令控制模块发送给IIC IP模块 |
写数据内容 |
oData |
功能函数模块发送给命令控制模块 |
读数据内容 |
时序 |
见上述分析 |
|
表4‑36 PCF8563 (pcf8563_basemod.v)顶层模块信号定义
顶层模块 |
|||
输入管脚 |
作用/说明 |
输出管脚 |
作用/说明 |
iCall |
iCall位宽为 8,作用是调用时给的操作模式。 |
oDone |
操作完成信号 |
iData |
写数据内容 |
SCL |
传感器SCL引脚 |
oData |
读数据内容 |
||
SDA |
传感器读取/写入数据 |
||
时序 |
见上述时序分析 |
||
下面分析下iic.v模块
图4‑30 PCF8563 IIC ip模块(iic.v)的结构图
相关的介绍详见代码说明。
代码4‑5 PCF8563 IIC ip模块(iic.v)
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-05-23 22:05:43 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-01 19:44:49 7.//# Description: 8.//# @Modification History: 2019-05-29 21:12:34 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-29 21:12:34 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.`timescale 1 ns/ 1 ns 17.module iic 18.( 19. input CLOCK, RESET, 20. output SCL, 21. inout SDA, 22. input [1:0]iCall, // Call/Done 有两位,即表示该模块有读功能还有写功能 23. output oDone, // 产生完成信号 24. input [7:0]iAddr, // 写入地址 iAddr 25. input [7:0]iData, // 写入数据 iData 26. output [7:0]oData // 读出数据 oData 27.); 28. parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31; //(1/400E+3)/(1/50E+6) 29. parameter THIGH = 10'd30, TLOW = 10'd65, TR = 10'd15, TF = 10'd15; 30. parameter THD_STA = 10'd30, TSU_STA = 10'd30, TSU_STO = 10'd30; 31. parameter WRFUNC1 = 5'd7; 32. parameter WRFUNC2 = 5'd9, RDFUNC = 5'd19; 33. 34. parameter slave_address={4'b1010, 3'b001, 1'b0}; //器件IIC地址 35. parameter slave_address1={4'b1010, 3'b001, 1'b0}; //写操作地址 36. parameter slave_address2={4'b1010, 3'b001, 1'b1}; //读操作地址 37. 38. 39. reg [4:0]i; 40. reg [4:0]Go; 41. reg [9:0]C1; 42. reg [7:0]D1; 43. reg rSCL,rSDA; 44. reg isAck, isDone, isQ; 45. 46. always @ ( posedge CLOCK or negedge RESET ) 47. if( !RESET ) 48. begin 49. { i,Go } <= { 5'd0,5'd0 }; 50. C1 <= 10'd0; 51. D1 <= 8'd0; 52. { rSCL,rSDA,isAck,isDone,isQ } <= 5'b11101; 53. end 54. else if( iCall[1] ) 55. case( i ) 56. 57. 0: // Start 58. begin 59. isQ = 1; 60. rSCL <= 1'b1; 61. 62. if( C1 == 0 ) rSDA <= 1'b1; 63. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0; 64. 65. if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end 66. else C1 <= C1 + 1'b1; 67. end 68. 69. 1: // Write Device Addr 70. begin D1 <= slave_address; i <= 5'd7; Go <= i + 1'b1; end 71. 72. 2: // Wirte Word Addr 73. begin D1 <= iAddr; i <= WRFUNC1; Go <= i + 1'b1; end 74. 75. 3: // Write Data 76. begin D1 <= iData; i <= WRFUNC1; Go <= i + 1'b1; end 77. 78. /*************************/ 79. 80. 4: // Stop 81. begin 82. isQ = 1'b1; 83. 84. if( C1 == 0 ) rSCL <= 1'b0; 85. else if( C1 == FQUARTER ) rSCL <= 1'b1; 86. 87. if( C1 == 0 ) rSDA <= 1'b0; 88. else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 1'b1; 89. 90. if( C1 == (FQUARTER + FCLK) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 91. else C1 <= C1 + 1'b1; 92. end 93. 94. 5: 95. begin isDone <= 1'b1; i <= i + 1'b1; end 96. 97. 6: 98. begin isDone <= 1'b0; i <= 5'd0; end 99. 100. /*******************************/ //function 101. 102. 7,8,9,10,11,12,13,14: 103. begin 104. isQ = 1'b1; 105. rSDA <= D1[14-i]; 106. 107. if( C1 == 0 ) rSCL <= 1'b0; 108. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 109. 110. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 111. else C1 <= C1 + 1'b1; 112. end 113. 114. 15: // waiting for acknowledge 115. begin 116. isQ = 1'b0; 117. if( C1 == FHALF ) isAck <= SDA; 118. 119. if( C1 == 0 ) rSCL <= 1'b0; 120. else if( C1 == FHALF ) rSCL <= 1'b1; 121. 122. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 123. else C1 <= C1 + 1'b1; 124. end 125. 126. 16: 127. if( isAck != 0 ) i <= 5'd0; 128. else i <= Go; 129. 130. /*******************************/ // end function 131. 132. endcase 133. 134. else if( iCall[0] ) 135. case( i ) 136. 137. 0: // Start 138. begin 139. isQ = 1; 140. rSCL <= 1'b1; 141. 142. if( C1 == 0 ) rSDA <= 1'b1; 143. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0; 144. 145. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 146. else C1 <= C1 + 1'b1; 147. end 148. 149. 1: // Write Device Addr (Write) 150. begin D1 <= slave_address1; i <= 5'd9; Go <= i + 1'b1; end 151. 152. 2: // Wirte Word Addr 153. begin D1 <= iAddr; i <= WRFUNC2; Go <= i + 1'b1; end 154. 155. 3: // Start again 156. begin 157. isQ = 1'b1; 158. 159. if( C1 == 0 ) rSCL <= 1'b0; 160. else if( C1 == FQUARTER ) rSCL <= 1'b1; 161. else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF) ) rSCL <= 1'b0; 162. 163. if( C1 == 0 ) rSDA <= 1'b0; 164. else if( C1 == FQUARTER ) rSDA <= 1'b1; 165. else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0; 166. 167. if( C1 == (FQUARTER + FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 168. else C1 <= C1 + 1'b1; 169. end 170. 171. 4: // Write Device Addr ( Read ) 172. begin D1 <= slave_address2; i <= 5'd9; Go <= i + 1'b1; end 173. 174. 5: // Read Data 175. begin D1 <= 8'd0; i <= RDFUNC; Go <= i + 1'b1; end 176. 177. 6: // Stop 178. begin 179. isQ = 1'b1; 180. 181. if( C1 == 0 ) rSCL <= 1'b0; 182. else if( C1 == FQUARTER ) rSCL <= 1'b1; 183. 184. if( C1 == 0 ) rSDA <= 1'b0; 185. else if( C1 == (FQUARTER + TR + TSU_STO) ) rSDA <= 1'b1; 186. 187. if( C1 == (FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 188. else C1 <= C1 + 1'b1; 189. end 190. 191. 7: 192. begin isDone <= 1'b1; i <= i + 1'b1; end 193. 194. 8: 195. begin isDone <= 1'b0; i <= 5'd0; end 196. 197. /*******************************/ //function 198. 199. 9,10,11,12,13,14,15,16: 200. begin 201. isQ = 1'b1; 202. 203. rSDA <= D1[16-i]; 204. 205. if( C1 == 0 ) rSCL <= 1'b0; 206. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 207. 208. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 209. else C1 <= C1 + 1'b1; 210. end 211. 212. 17: // waiting for acknowledge 213. begin 214. isQ = 1'b0; 215. 216. if( C1 == FHALF ) isAck <= SDA; 217. 218. if( C1 == 0 ) rSCL <= 1'b0; 219. else if( C1 == FHALF ) rSCL <= 1'b1; 220. 221. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 222. else C1 <= C1 + 1'b1; 223. end 224. 225. 18: 226. if( isAck != 0 ) i <= 5'd0; 227. else i <= Go; 228. 229. /*****************************/ 230. 231. 19,20,21,22,23,24,25,26: // Read 232. begin 233. isQ = 1'b0; 234. if( C1 == FHALF ) D1[26-i] <= SDA; 235. 236. if( C1 == 0 ) rSCL <= 1'b0; 237. else if( C1 == FHALF ) rSCL <= 1'b1; 238. 239. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 240. else C1 <= C1 + 1'b1; 241. end 242. 243. 27: // no acknowledge 244. begin 245. isQ = 1'b1; 246. //if( C1 == 100 ) isAck <= SDA; 247. 248. if( C1 == 0 ) rSCL <= 1'b0; 249. else if( C1 == FHALF ) rSCL <= 1'b1; 250. 251. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= Go; end 252. else C1 <= C1 + 1'b1; 253. end 254. 255. /*************************************/ // end fucntion 256. 257. endcase 258. 259. /***************************************/ 260. 261. assign SCL = rSCL; 262. assign SDA = isQ ? rSDA : 1'bz; 263. assign oDone = isDone; 264. assign oData = D1; 265. 266. /***************************************/ 267. 268.endmodule |
注意以上IIC模块使用的时候,要修改写操作地址 /读操作地址。
图4‑31 PCF8563 功能函数模块(pcf8563_ ctrlmod.v)的建模图
表4‑37 Call/Done 的位宽内容
位 |
内容 |
[7] |
控制/状态寄存器1 |
[6] |
写入秒钟 |
[5] |
写入分钟 |
[4] |
写入时钟 |
[3] |
预留 |
[2] |
读取秒钟 |
[1] |
读取分钟 |
[0] |
读取时钟 |
代码4‑6 PCF8563功能函数模块(pcf8563_ ctrlmod.v)
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-05-19 20:55:44 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-05-29 20:56:25 7.//# Description: 8.//# @Modification History: 2019-05-19 20:58:05 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-19 20:58:05 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module pcf8563_ctrlmod 17.( 18. input CLOCK, RST_n, 19. input [7:0]iCall, 20. output oDone, 21. input [7:0]iData, 22. output [1:0]oCall, 23. input iDone, 24. output [7:0]oAddr, oData 25.); 26. 27.parameter psub_address=8'b0000_0000; //PCF8563 第一个控制寄存器的地址 28.parameter psub_address1=8'b0000_0010; //PCF8563 的秒寄存器的地址 29.parameter psub_address2=8'b0000_0011; //PCF8563 的分寄存器的地址 30.parameter psub_address3=8'b0000_0100; //PCF8563 的时寄存器的地址 31. 32. 33. reg [7:0]D1,D2; 34. 35. always @ ( posedge CLOCK or negedge RST_n ) 36. if( !RST_n ) 37. begin 38. D1 <= 8'd0; 39. D2 <= 8'd0; 40. end 41. else 42. case( iCall[7:0] ) 43. 44. 8'b1000_0000 : // psub_address & control_data 45. begin D1 = psub_address; D2 <= iData; end //D2 <= 8'b0000_0000; 46. 47. 8'b0100_0000 : // Write second 48. begin D1 <= psub_address1; D2 <= iData; end //8'b00100100 49. 50. 8'b0010_0000 : // Write minit 51. begin D1 <= psub_address2; D2 <= iData; end//8'b01010111 52. 53. 8'b0001_0000 : // Write hour 54. begin D1 <= psub_address3; D2 <= iData; end//8'b00000111 55. 56. 8'b0000_0100 : // Read second 57. begin D1 = psub_address1; end 58. 59. 8'b0000_0010 : // Read minit 60. begin D1 = psub_address2; end 61. 62. 8'b0000_0001 : // Read hour 63. begin D1 = psub_address3; end 64. 65. 66. endcase 67. 68. reg [1:0]i; 69. reg [1:0]isCall; 70. reg isDone; 71. 72. always @ ( posedge CLOCK or negedge RST_n ) 73. if( !RST_n ) 74. begin 75. i <= 2'd0; 76. isCall <= 2'b00; 77. isDone <= 1'b0; 78. end 79. else if( iCall[7:4] ) // Write action 80. case( i ) 81. 82. 0 : 83. if( iDone ) begin isCall <= 2'b00; i <= i + 1'b1; end 84. else begin isCall <= 2'b10; end 85. 86. 1 : 87. begin isDone <= 1'b1; i <= i + 1'b1; end 88. 89. 2 : 90. begin isDone <= 1'b0; i <= 2'd0; end 91. 92. endcase 93. else if( iCall[2:0] ) // Read action 94. case( i ) 95. 96. 0 : 97. if( iDone ) begin isCall <= 2'b00; i <= i + 1'b1; end 98. else begin isCall <= 2'b01; end 99. 100. 1 : 101. begin isDone <= 1'b1; i <= i + 1'b1; end 102. 103. 2 : 104. begin isDone <= 1'b0; i <= 2'd0; end 105. 106. endcase 107. 108. assign oDone = isDone; 109. assign oCall = isCall; 110. assign oAddr = D1; 111. assign oData = D2; 112. 113.endmodule |
按照图4‑29将PCF8563模块进行封装,代码如下:
代码4‑7 PCF8563模块封装
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-05-19 20:56:01 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-05-26 17:17:11 7.//# Description: 8.//# @Modification History: 2019-05-19 20:57:52 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-19 20:57:52 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module pcf8563_basemod 17.( 18. input CLOCK, RST_n, 19. output RTC_SCL, 20. inout RTC_SDA, 21. input [7:0]iCall, 22. output oDone, 23. input [7:0]iData, 24. output [7:0]oData 25.); 26. wire [7:0]AddrU1; 27. wire [7:0]DataU1; 28. wire [1:0]CallU1; 29. 30. pcf8563_ctrlmod U1 31. ( 32. .CLOCK( CLOCK ), 33. .RST_n( RST_n ), 34. .iCall( iCall ), // < top 35. .oDone( oDone ), // > top 36. .iData( iData ), // > top 37. .oCall( CallU1 ), // > U2 38. .iDone( DoneU2 ), // < U2 39. .oAddr( AddrU1 ), // > U2 40. .oData( DataU1 ) // > U2 41. ); 42. 43. 44. iic U2 45. ( 46. .CLOCK( CLOCK ), 47. .RESET( RST_n ), 48. .SCL( RTC_SCL ), // > top 49. .SDA( RTC_SDA ), // <> top 50. .iCall( CallU1 ), // < U1 51. .oDone( DoneU2 ), // > U1 52. .iAddr( AddrU1 ), // > U1 53. .iData( DataU1 ), // > U1 54. .oData( oData ) // > top 55. ); 56. 57.endmodule |
完成后的RTL电路如图4‑32所示,和图4‑29一样。
图4‑32 PCF8563模块RTL
图4‑33 pcf8563_demo结构框图
图4‑33是pcf8563_demo结构框图。核心操作先初始化 PCF8563基础模块,然后无间断从哪里读取时钟,分钟还有秒钟,最后驱动至 SMG 基础模块。具体内容让我们来看代码。
代码4‑8 pcf8563调用示例
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-05-19 01:42:42 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-05-29 21:00:12 7.//# Description: 8.//# @Modification History: 2019-05-19 01:52:19 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-05-19 01:52:19 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module pcf8563_demo 17.( 18. input CLOCK, RST_n, 19. output RTC_SCL, 20. inout RTC_SDA, 21. output [7:0]SMG_Data, 22. output [5:0]Scan_Sig 23.); 24. wire DoneU1; 25. wire [7:0]DataU1; 26. 27.// [3:0]LED; 28. 29. parameter pre_second= 8'b00100100; //预置秒 , 24 秒 30. parameter pre_minute= 8'b01010111; //预置分 , 57 分 31. parameter pre_hour = 8'b00000111; //预置时, 7 时 32. parameter control_data=8'b00000000; //PCF8563 第一个控制寄存器的控制字 33. pcf8563_basemod U1 34. ( 35. .CLOCK( CLOCK ), 36. .RST_n( RST_n ), 37. .RTC_SCL( RTC_SCL ), 38. .RTC_SDA( RTC_SDA ), 39. .iCall( isCall ), 40. .oDone( DoneU1 ), 41. .iData( D1 ), 42. .oData( DataU1 ) 43. ); 44. 45. smg_interface U2 46. ( 47. .CLOCK( CLOCK ), 48. .RST_n( RST_n ), 49. .SMG_Data( SMG_Data ), // > top 50. .Scan_Sig( Scan_Sig ), // > top 51. .Number_Sig( Number_Sig ) // < core 52. ); 53. 54. reg [3:0]i; 55. reg [13:0]isCall; 56. reg [7:0]D1,alarm_registies; 57. reg [23:0]Number_Sig; 58. 59. always @ ( posedge CLOCK or negedge RST_n ) 60. if( !RST_n ) 61. begin 62. i <= 4'd0; 63. isCall <= 8'd0; 64. D1 <= 8'd0; 65. Number_Sig <= 24'd0; 66. end 67. else 68. case( i ) 69./*********************************************************************** 70.以下内容为核心操作的部分内容,步骤 0 设置pcf8563,步骤 1 初始化时钟,步骤 2 初始 71.化分钟,步骤 3 初始化秒钟并且开启计时。 72.***********************************************************************/ 73. 0: 74. if( DoneU1 ) begin isCall[7] <= 1'b0;i <= i + 1'b1; end 75. else begin isCall[7] <= 1'b1;D1 <= control_data ; end 76. 77. 1: 78. if( DoneU1 ) begin isCall[6] <= 1'b0; i <= i + 1'b1;end 79. else begin isCall[6] <= 1'b1; D1 <= pre_second; end 80. 81. 2: 82. if( DoneU1 ) begin isCall[5] <= 1'b0; i <= i + 1'b1; end 83. else begin isCall[5] <= 1'b1; D1 <= pre_minute; end 84. 85. 3: 86. if( DoneU1 ) begin isCall[4] <= 1'b0; i <= i + 1'b1; end 87. else begin isCall[4] <= 1'b1; D1 <= pre_hour; end 88. 89. 4: 90. i <= i + 1'b1; 91. 92./*********************************************************************** 93.步骤 4 读取秒钟然后暂存至 D2[7:0],步骤 5 读取分钟然后暂存至 D2[15:8],步骤 6 读 94.取时钟然后暂存至 D2[23:16]。 95.***********************************************************************/ 96. 5: 97. if( DoneU1 ) begin Number_Sig[7:0] <= DataU1; isCall[2] <= 1'b0; i <= i + 1'b1;end 98. else begin isCall[2] <= 1'b1; end 99. 100. 6: 101. if( DoneU1 ) begin Number_Sig[15:8] <= DataU1; isCall[1] <= 1'b0; i <= i + 1'b1; end 102. else begin isCall[1] <= 1'b1; end 103. 104. 7: 105. if( DoneU1 ) begin Number_Sig[23:16] <= DataU1; isCall[0] <= 1'b0; i <= i + 1'b1; end 106. else begin isCall[0] <= 1'b1; end 107. 108. 8: 109. i <= 4'd5; 110. 111. endcase 112. 113.endmodule |
综合完毕后下载程序,如果数码管从 07-58-24 开始起跑,表示实验成功。
上述代码使用过程中需要修改IIC IP里的读写操作地址,然后直接修改时分秒的数值就可以了,其他操作只需要理清操作顺序就可以了。