PCF8563 是低功耗的 CMOS 实时时钟/日历芯片
本文用到ESP32 WROOM模块,文中假定你熟悉用 Micropython开发该模块的方式。
应用中需要知道当前的年份是哪一年,想了多种方案。最后,还是决定用一款低功耗的实时时钟/日历芯片来实现。选型过程中,看到有人说,达拉斯DS1302等产品,对电源电压有特别的要求,要十分注意,否则可能读不到数据。故而,本人避开达拉斯产品,选用了PCF8653这款芯片(模块)。
程序设计和应用过程
我的主控芯片是EPS32 WROOM,用Micropython当作开发平台。 EPS32 WROOM是一个紧凑的电子电路板,在裸机上运行MicroPython,就有了一个低级Python操作系统, MicroPython旨在尽可能与普通Python兼容,所以编程很方便。
-
下载pcf8653.py源程序
PCF8653这款芯片是IIC接口,虽然MicroPython有现成的IIC machine函数,但要编程序代码,还是要充分理解IIC的工作原理才行,不然,会出现意想不到的情况来。偷懒的我就在网上搜,看有没有现成的程序代码。还果真有,感谢这位老兄的奉献。链接如下:
https://www.micropython.org.cn/wiki/doku.php?id=micropython:mpy-lib:misc:pcf8563%E5%AE%9E%E6%97%B6%E6%97%B6%E9%92%9F
输入以上网址,点击在上图红箭头处,就可下载源程序了。紧接着应是测试代码能不能用了。 -
把该程序传ESP32模块中
-
编制测试程序
主程序main.py 代码如下
from machine import UART,Pin,Timer,WDT,I2C,SoftI2C
import time
BoardLed=Pin(2,Pin.OUT) #create LED object
#---------------------------------------------------------
#定时器中断服务函数
def tim0_handle_callback(tim0):
BoardLed.value(not BoardLed.value())
#开启定时器0中断,每隔on_ms触发一次
def SetLedStatus(on_ms):
tim0 = Timer(0) #创建定时器对象 tim0~3 共四个类
#周期性模式 最小单位为ms
tim0.init(period=on_ms, mode=Timer.PERIODIC, callback = tim0_handle_callback)
try:
#i2c0 = SoftI2C(sda=Pin(5),scl=Pin(4),freq=400000)
i2c0 = I2C(0) # 默认的i2c0:scl:18 sda:19 默认的i2c1:scl:25 sda:26
addr0 = i2c0.scan() #扫描I2C地址,以列表的形式返回设备地址
print("address:", addr0) #打印I2C设备地址
SetLedStatus(1000) #调用,指示程序在运行
Rtc8563 = PCF8563(i2c = i2c0) #实例化
#设定PCF8653的日期、时间
Rtc8563.year(year = 2023)
Rtc8563.month(month = 12)
Rtc8563.day(day = 9)
Rtc8563.weekday(weekday=6)
Rtc8563.hour(hour = 19)
Rtc8563.minute(minute = 11)
#看读取情况
while True:
print("time: ",Rtc8563.datetime())
time.sleep(1) #延时1秒后,再读RTC
except:
print("mainprog err,exit")
- 当然还有一个boot.py程序如下:
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
#import webrepl
#webrepl.start()
exec(open('pcf8563.py').read(),globals())
exec(open('main.py').read(),globals())
- 运行情况
每延时1秒,读到的内容
硬件连接
调试经验:遇到的问题
本示例基于Thonny平台开发。
本想用软件I2C总线,自定义相关引脚,这样的话,设计电路图方便一些。有资料也介绍说:软件 I2C(使用 bit-banging)适用于所有具有输出输入功能的引脚,并通过machine.SoftI2C类访问:
例如,引用方式如下:
from machine import SoftI2C
i2c = SoftI2C(scl=Pin(22),sda=Pin(23),freq=100000)
但这样不行,不能读到RTC数据
我改用默认I2C0的引脚来调试,ESP32有两个标识符为0和1的默认I2C硬件外设。下面给出了默认值。
I2C(0) I2C(1)
SCL IO18 IO25
SDA IO19 IO26
默认的I2C引脚引用方法:
from machine import Pin, I2C
i2c = I2C(0) # 默认的i2c0:scl:18 sda:19
i2c = I2C(1) # 默认的i2c1:scl:25 sda:26
等价于:
from machine import Pin, I2C
i2c = I2C(scl=Pin(18), sda=Pin(19),freq=400000)
i2c = I2C(scl=Pin(25), sda=Pin(26),freq=400000)
待调试出RTC数据后,再来尝试一下其他的引脚,即也可以自定义切换指定引脚
from machine import I2C,
i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000)
再次感谢提供pcf8653.py源程序的网友,这个程序完全可用
pcf8653.py代码我复制如下,以便引用
"""
PCF8563 RTC drive
Author: shaoziyang
Date: 2021.1
http://www.micropython.org.cn
"""
from micropython import const
PCF8563_I2C_ADDRESS = const(81)
PCF8563_REG_CTRL1 = const(0)
PCF8563_REG_CTRL2 = const(1)
PCF8563_REG_SECOND = const(2)
PCF8563_REG_MINUTE = const(3)
PCF8563_REG_HOUR = const(4)
PCF8563_REG_WEEKDAY = const(6)
PCF8563_REG_DAY = const(5)
PCF8563_REG_MONTH = const(7)
PCF8563_REG_YEAR = const(8)
class PCF8563():
def __init__(self, i2c):
self.i2c = i2c
self.tb = bytearray(1)
self.rb = bytearray(1)
self.buf = bytearray(7)
self.DT = [0] * 8
# set reg
def setReg(self, reg, dat):
self.tb[0] = dat
self.i2c.writeto_mem(PCF8563_I2C_ADDRESS, reg, self.tb)
# get reg
def getReg(self, reg):
self.i2c.readfrom_mem_into(PCF8563_I2C_ADDRESS, reg, self.rb)
return self.rb[0]
def DecToHex(self, dat):
return (dat//10) * 16 + (dat%10)
def HexToDec(self, dat):
return (dat//16) * 10 + (dat%16)
def year(self, year = None):
if year == None:
return self.HexToDec(self.getReg(PCF8563_REG_YEAR)) + 2000
else:
self.setReg(PCF8563_REG_YEAR, self.DecToHex(year%100))
def month(self, month = None):
if month == None:
return self.HexToDec(self.getReg(PCF8563_REG_MONTH)%32)
else:
self.setReg(PCF8563_REG_MONTH, self.DecToHex(month%13))
def day(self, day = None):
if day == None:
return self.HexToDec(self.getReg(PCF8563_REG_DAY)%64)
else:
self.setReg(PCF8563_REG_DAY, self.DecToHex(day%32))
def weekday(self, weekday = None):
if weekday == None:
return self.HexToDec(self.getReg(PCF8563_REG_WEEKDAY)%8)
else:
self.setReg(PCF8563_REG_WEEKDAY, self.DecToHex(weekday%8))
def hour(self, hour = None):
if hour == None:
return self.HexToDec(self.getReg(PCF8563_REG_HOUR)%64)
else:
self.setReg(PCF8563_REG_HOUR, self.DecToHex(hour%24))
def minute(self, minute = None):
if minute == None:
return self.HexToDec(self.getReg(PCF8563_REG_MINUTE)%128)
else:
self.setReg(PCF8563_REG_MINUTE, self.DecToHex(minute%60))
def second(self, second = None):
if second == None:
return self.HexToDec(self.getReg(PCF8563_REG_SECOND)%128)
else:
self.setReg(PCF8563_REG_SECOND, self.DecToHex(second%60))
def datetime(self, DT=None):
if DT == None:
self.i2c.readfrom_mem_into(PCF8563_I2C_ADDRESS, PCF8563_REG_SECOND, self.buf)
self.DT[0] = self.HexToDec(self.buf[6]) + 2000
self.DT[1] = self.HexToDec(self.buf[5]%32)
self.DT[2] = self.HexToDec(self.buf[3]%64)
self.DT[3] = self.HexToDec(self.buf[4]%8)
self.DT[4] = self.HexToDec(self.buf[2]%64)
self.DT[5] = self.HexToDec(self.buf[1]%128)
self.DT[6] = self.HexToDec(self.buf[0]%128)
self.DT[7] = 0
return self.DT
else:
self.buf[0] = self.DecToHex(DT[6]%60) # second
self.buf[1] = self.DecToHex(DT[5]%60) # minute
self.buf[2] = self.DecToHex(DT[4]%24) # hour
self.buf[3] = self.DecToHex(DT[2]%32) # date
self.buf[4] = self.DecToHex(DT[3]%8) # week da
self.buf[5] = self.DecToHex(DT[1]%13) # month
self.buf[6] = self.DecToHex(DT[0]%100) # year
self.i2c.writeto_mem(PCF8563_I2C_ADDRESS, PCF8563_REG_SECOND, self.buf)