pcf8563-dd 0.3.0

A driver for the PCF8563/BM8563 real-time clock (uses device-driver crate)
Documentation

PCF8563/BM8563 Real-Time Clock Rust Driver (pcf8563-dd)

Crates.io Docs.rs License: MIT OR Apache-2.0

A no_std Rust driver for the NXP PCF8563 and compatible BM8563 real-time clock ICs. This driver leverages the device-driver crate with a declarative YAML manifest (device.yaml) for a robust, type-safe register map definition. The low-level API covers 100% of the PCF8563's registers, with device.yaml providing a comprehensive and accurate description of all registers and their fields verified against the official datasheet.

Overview

The pcf8563-dd driver offers:

  • Declarative Configuration: The PCF8563 register map is defined in device.yaml, enabling device-driver to generate a type-safe, low-level register access API.
  • Unified Async/Blocking API: Uses the bisync crate to provide both asynchronous (Pcf8563Async) and blocking (Pcf8563) drivers from the same codebase, with no feature flags required.
  • High-Level and Low-Level APIs:
    • High-level methods simplify tasks like reading/setting date and time, configuring alarms, and managing the timer.
    • Low-level API (via the ll field) offers direct, type-safe access to all registers defined in device.yaml.
  • no_std and no-alloc: Optimized for bare-metal and RTOS environments.
  • Optional Logging: Supports defmt and the log facade for debugging.
  • Optional rtcc Traits (blocking): Enable the rtcc feature to get rtcc::DateTimeAccess and rtcc::Rtcc implementations for the blocking driver.

Features

  • Date and Time: Read and set year (2000-2099), month, day, weekday, hours, minutes, and seconds.
  • Clock Integrity Detection: Voltage-low (VL) flag indicates if clock data may be invalid due to power loss.
  • Century Flag: Track century rollover for extended date range.
  • Alarm Function: Programmable alarm with minute, hour, day, and weekday matching.
  • Countdown Timer: 8-bit countdown timer with selectable clock frequencies (4096Hz, 64Hz, 1Hz, 1/60Hz).
  • Clock Output (CLKOUT): Programmable square wave output (32.768kHz, 1024Hz, 32Hz, 1Hz).
  • Interrupt Support: Configurable interrupts for alarm and timer events.
  • Low Power: Designed for battery-backed operation.

Supported Devices

  • PCF8563 - NXP real-time clock (original)
  • BM8563 - Compatible clone found in M5Stack devices

Getting Started

  1. Add pcf8563-dd to Cargo.toml:

    [dependencies]
    pcf8563-dd = "0.2.0"
    # For blocking usage (Pcf8563):
    embedded-hal = "1.0.0"
    # For async usage (Pcf8563Async):
    embedded-hal-async = "1.0.0"
    
  2. Instantiate the driver with your I2C bus:

    • Blocking:

      use pcf8563_dd::{Pcf8563, DateTime, Alarm};
      
      let i2c_bus = /* your I2C bus */;
      let mut rtc = Pcf8563::new(i2c_bus);
      
      // Initialize the RTC
      rtc.init()?;
      
      // Check if clock data is valid
      let valid = rtc.is_clock_valid()?;
      
      // Set the date and time
      let dt = DateTime {
          year: 24,       // 2024
          month: 12,      // December
          day: 1,         // 1st
          weekday: 0,     // Sunday
          hours: 12,      // 12:00:00
          minutes: 0,
          seconds: 0,
      };
      rtc.set_datetime(&dt)?;
      
      // Read current date and time
      let current = rtc.get_datetime()?;
      
    • Async:

      use pcf8563_dd::{Pcf8563Async, DateTime};
      
      let i2c_bus = /* your async I2C bus */;
      let mut rtc = Pcf8563Async::new(i2c_bus);
      
      // Initialize the RTC
      rtc.init().await?;
      
      // Read current date and time
      let current = rtc.get_datetime().await?;
      

High-Level API

Date and Time

// Get current date/time
let dt = rtc.get_datetime()?;

// Set date/time
rtc.set_datetime(&DateTime {
    year: 24, month: 12, day: 25,
    weekday: 3, hours: 10, minutes: 30, seconds: 0,
})?;

// Set only time (preserves date)
rtc.set_time(&Time { hours: 14, minutes: 30, seconds: 0 })?;

Clock Control

// Check clock validity (false if power was lost)
let valid = rtc.is_clock_valid()?;

// Clear the voltage-low flag after setting time
rtc.clear_voltage_low_flag()?;

// Stop/start the clock
rtc.set_clock_running(false)?; // Stop
rtc.set_clock_running(true)?;  // Start

Alarm

use pcf8563_dd::Alarm;

// Set alarm for 12:30 on any day
let alarm = Alarm {
    minute: Some(30),
    hour: Some(12),
    day: None,      // Any day
    weekday: None,  // Any weekday
};
rtc.set_alarm(&alarm)?;
rtc.set_alarm_interrupt(true)?;

// Check and clear alarm flag
if rtc.get_alarm_flag()? {
    rtc.clear_alarm_flag()?;
}

// Disable alarm
rtc.disable_alarm()?;

Timer

use pcf8563_dd::TimerFrequency;

// Configure 10-second countdown timer
rtc.set_timer_frequency(TimerFrequency::Freq1Hz)?;
rtc.set_timer_value(10)?;
rtc.set_timer_enabled(true)?;
rtc.set_timer_interrupt(true)?;

// Check timer status
let value = rtc.get_timer_value()?;
if rtc.get_timer_flag()? {
    rtc.clear_timer_flag()?;
}

Clock Output

use pcf8563_dd::ClkoutFrequency;

// Enable 1Hz square wave output on CLKOUT pin
rtc.set_clkout_frequency(ClkoutFrequency::Freq1Hz)?;
rtc.set_clkout_enabled(true)?;

Low-Level API Usage

The driver provides direct access to all PCF8563 registers through the low-level API via rtc.ll. This API is automatically generated from device.yaml and provides type-safe access to all register fields.

Reading Registers

// Read control status registers
let ctrl1 = rtc.ll.control_status_1().read()?;
let is_stopped = ctrl1.stop();

let ctrl2 = rtc.ll.control_status_2().read()?;
let alarm_flag = ctrl2.af();
let timer_flag = ctrl2.tf();

// Read seconds register with voltage-low flag
let seconds = rtc.ll.seconds().read()?;
let vl = seconds.vl(); // Clock integrity flag
let sec_value = seconds.seconds_ten() * 10 + seconds.seconds_unit();

Writing Registers

// Configure control status 2
rtc.ll.control_status_2().write(|w| {
    w.set_aie(true);  // Enable alarm interrupt
    w.set_tie(false); // Disable timer interrupt
    w.set_af(false);  // Clear alarm flag
    w.set_tf(false);  // Clear timer flag
})?;

// Set timer value
rtc.ll.timer().write(|w| {
    w.set_timer_value(60); // 60 counts
})?;

Modifying Registers

Use .modify() to read-modify-write, preserving other fields:

// Enable timer without changing frequency
rtc.ll.timer_control().modify(|w| {
    w.set_te(true);
})?;

Async Low-Level API

Append _async to method names for async usage:

let ctrl1 = rtc.ll.control_status_1().read_async().await?;

rtc.ll.timer_control().modify_async(|w| {
    w.set_te(true);
}).await?;

Register Map

The complete PCF8563 register map is defined in device.yaml:

Address Register Description
0x00 Control Status 1 TEST1, STOP, TESTC flags
0x01 Control Status 2 Interrupt enables and flags (TI_TP, AF, TF, AIE, TIE)
0x02 Seconds VL flag, seconds (0-59)
0x03 Minutes Minutes (0-59)
0x04 Hours Hours (0-23)
0x05 Days Day of month (1-31)
0x06 Weekdays Day of week (0-6)
0x07 Century/Months Century flag, month (1-12)
0x08 Years Year (0-99)
0x09 Minute Alarm AE_M flag, minute alarm value
0x0A Hour Alarm AE_H flag, hour alarm value
0x0B Day Alarm AE_D flag, day alarm value
0x0C Weekday Alarm AE_W flag, weekday alarm value
0x0D CLKOUT Control FE (enable), FD (frequency)
0x0E Timer Control TE (enable), TD (frequency)
0x0F Timer Countdown timer value (0-255)

Examples

Examples for ESP32 using esp-hal are included. Both examples demonstrate high-level convenience methods and low-level register API usage.

Feature Flags

  • default = []: No default features; async and blocking drivers are always available.
  • std: Enables std features for thiserror.
  • log: Enables log facade logging.
  • defmt: Enables defmt logging for embedded debugging.
  • rtcc: Implements rtcc::DateTimeAccess and rtcc::Rtcc for the blocking driver.

Timer Frequencies

The countdown timer supports four clock frequencies:

Frequency Period Max Duration
4096 Hz ~244 µs ~62 ms
64 Hz ~15.6 ms ~4 seconds
1 Hz 1 second ~4.25 minutes
1/60 Hz 1 minute ~4.25 hours

CLKOUT Frequencies

The clock output pin can generate square waves at:

  • 32.768 kHz (default)
  • 1024 Hz
  • 32 Hz
  • 1 Hz

Contributing

Contributions are welcome! You can contribute by:

  • Adding high-level convenience methods for additional features.
  • Enhancing documentation with examples or clarifications.
  • Reporting issues or suggesting improvements.
  • Testing on different hardware platforms.

Please submit issues, fork the repository, and create pull requests.

License

This project is dual-licensed under the MIT License or Apache License 2.0, at your option.