RV‑3028 Timer Registers 0x18–0x1A Do Not Retain Writes

RV‑3028 Timer Registers 0x18–0x1A Do Not Retain Writes (PiicoDev RV‑3028)

Hi all,

I’m working with the PiicoDev RV‑3028 module and doing some register‑level testing to enable the countdown timer and INT functionality. I’ve run into behaviour that doesn’t match the RV‑3028‑C7 datasheet, and I’d like an engineer familiar with the PiicoDev implementation to review it.

Summary of the issue

  • Normal control registers (e.g. 0x00, 0x0F) accept writes and read back correctly.

  • Timer registers (0x18, 0x19, 0x1A) never retain writes, always reading back 0x00.

  • This behaviour is consistent across multiple tests and power cycles.

  • I²C communication is otherwise working normally (timekeeping, alarms, etc.).

This suggests the issue is specific to the timer block, not the bus or the register map.

Test 1 — Control register vs timer registers

Code used

python

from machine import Pin, I2C
import time

i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)
addr = 0x52

def w(reg, val):
    i2c.writeto(addr, bytes([reg, val]))
    time.sleep_us(200)

def r(reg):
    i2c.writeto(addr, bytes([reg]))
    time.sleep_us(200)
    return i2c.readfrom(addr, 1)[0]

print("=== BEFORE ===")
for reg in (0x00, 0x01, 0x0E, 0x0F, 0x18, 0x19, 0x1A):
    print(f"0x{reg:02X} =", hex(r(reg)))

print("\nModifying some control bits...")

# Toggle bit 2 of CTRL1 (0x00)
orig_00 = r(0x00)
w(0x00, orig_00 ^ 0b00000100)

# Attempt to start timer
w(0x18, 0x00)
w(0x19, 0x05)
w(0x1A, 0x04)

print("\n=== AFTER ===")
for reg in (0x00, 0x01, 0x0E, 0x0F, 0x18, 0x19, 0x1A):
    print(f"0x{reg:02X} =", hex(r(reg)))

Output

Code

=== BEFORE ===
0x00 = 0x5
0x01 = 0x4
0x0E = 0x11
0x0F = 0x1
0x18 = 0x0
0x19 = 0x0
0x1A = 0x0

=== AFTER ===
0x00 = 0x1
0x01 = 0x4
0x0E = 0x11
0x0F = 0x1
0x18 = 0x0
0x19 = 0x0
0x1A = 0x0

Interpretation:

  • 0x00 changed exactly as expected → control register writes work.

  • 0x18–0x1A remained 0x00 → timer registers did not retain writes.

Test 2 — CE‑recommended “0x55 write” test

Code

python

print("=== TIMER WRITE TEST (0x55) ===")
for reg in (0x18, 0x19, 0x1A):
    w(reg, 0x55)
    print(f"Reg 0x{reg:02X} =", hex(r(reg)))

print("\n=== CONTROL REGISTER TEST (toggle bit 1 of 0x0F) ===")
orig = r(0x0F)
w(0x0F, orig ^ 0x02)
print("0x0F =", hex(r(0x0F)))

Output

Code

=== TIMER WRITE TEST (0x55) ===
Reg 0x18 = 0x0
Reg 0x19 = 0x0
Reg 0x1A = 0x0

=== CONTROL REGISTER TEST (toggle bit 1 of 0x0F) ===
0x0F = 0x3

Interpretation:

  • 0x0F toggled correctly → normal register writes work.

  • Timer registers still read back 0x00 → behaviour isolated to timer block.

What I’m asking

Given the above:

  • Are registers 0x18–0x1A expected to be writable on the PiicoDev RV‑3028 module?

  • Is there any PiicoDev‑specific configuration required before the timer block becomes active?

  • Is the PiicoDev RV‑3028 known to support (or not support) the countdown timer and INT functionality described in the RV‑3028‑C7 datasheet?

Any guidance from someone familiar with the PiicoDev implementation would be appreciated.

Thanks!

Further to the above Additional Technical Findings (New Information)

After further register‑level diagnostics, I’ve gathered more evidence that the timer subsystem on my PiicoDev RV‑3028 module is not behaving like a genuine MicroCrystal RV‑3028‑C7.

1. Extension Register (0x0D) is not writable

This register is documented as R/W in the RV‑3028‑C7 Application Manual.
It contains timer clock frequency bits (TD), CLKOUT control, and other extension features.

Test:

python

Before write: 0x0D = 0x00
After write:  0x0D = 0x00

Even forcing 0xFF into 0x0D:

python

0x0D = 0x00

A genuine RV‑3028‑C7 must reflect the written value.
This strongly suggests the extension register block is disabled or not implemented.

2. Timer Registers (0x18–0x1A) remain unwritable

As previously shown:

Code

0x18 = 0x00
0x19 = 0x00
0x1A = 0x00

Even after writing patterns like 0x55, 0xAA, 0xFF, the values always read back as 0x00.

These registers are also documented as R/W in the official datasheet.

3. Chip ID Register (0x3F) returns 0x00

According to MicroCrystal documentation, 0x3F is a Feature/ID Register and should return a non‑zero value on all genuine RV‑3028‑C7 silicon.

My module returns:

Code

0x3F = 0x00

This is not a valid ID for any RV‑3028‑C7 revision.

4. Summary of observed behaviour

Register Expected (RV‑3028‑C7) Actual (PiicoDev module) Notes
0x00, 0x0F Writable Writable Normal behaviour
0x0D Writable Always 0x00 Extension block disabled
0x18–0x1A Writable Always 0x00 Timer block disabled
0x3F Non‑zero ID 0x00 Not matching genuine silicon

5. Interpretation

Based on these results:

  • The timer subsystem (clock source, preload registers, control/status) appears absent or permanently disabled.

  • The extension register block is also disabled.

  • The ID register does not match any documented RV‑3028‑C7.

This behaviour is not possible on a full‑feature RV‑3028‑C7 as described in the MicroCrystal Application Manual.

6. Request for clarification

Can Core Electronics confirm whether the PiicoDev RV‑3028 module uses:

  • a full‑feature RV‑3028‑C7,

  • a reduced‑feature or custom variant,

  • or a factory‑configured version with the timer subsystem disabled?

The register‑level behaviour suggests the timer engine is not implemented on this module.

Hey there, Henk,

A couple of things to note from this:

  1. 0x00 is not a Ctrl Register. It is the second register.
  2. 0x0D, 0x18-0x1A are all read only only and always return a value of 0x00.

Since you’re using MicroPython, I really recommend avoiding the registers entirely and just use the CE-PiicoDev-RV3028-MicroPython-Module since our team of engineers have already done the job of building a library for you.

However, if you are hoping to make your own library, you should still check in with application documents to check the register values in the application. As you can see,

All the registers you are having issues always returning 0x00 are all Read only.

We are not wanting to do anything the hard way we simply need the INT to fire periodically, and it seems to be very hard to get anything to work,

1 Like

Hey there, Henk,

There’s definitely an option for that in the PiicoDev_RV3028 Library. The alarm code will by default trigger the Pico’s INT pin. Reproduced from our guide on this unit for easy reference:

What we did, step‑by‑step

1. Identified the RV‑3028 on the I²C bus

We scanned the Pico’s I²C bus and found the device at address 0x29.

  • 0x29 is the correct address for the PiicoDev RV‑3028 module.

  • This confirmed the module is powered, wired, and responding to I²C address ACK.

So far, so good.

2. Verified the wiring to GP4 (SDA) and GP5 (SCL)

We explicitly scanned:

  • GP4/GP5 → returned [82] (0x52 decimal → 0x29 hex)

  • GP0/GP1 → returned nothing

This confirmed:

  • SDA is really on GP4

  • SCL is really on GP5

  • The Pico is using I2C(0) on those pins

  • The wiring is correct

  • The PiicoDev board is physically connected properly

No wiring fault.

3. Forced the PiicoDev driver to use GP4/GP5

We tried:

python

rtc = PiicoDev_RV3028(bus=0, sda=4, scl=5)

The unified driver printed:

Code

Using supplied bus, sda, and scl to create machine.I2C()

So the driver did use the correct pins.

But then it immediately failed with:

Code

PiicoDev could not communicate with module at address 0x29
OSError: [Errno 5] EIO

This means:

  • The driver can write to the chip

  • The driver cannot read from the chip

This is the critical failure.

:star: What we tried next (all failed)

We attempted every legal I²C read method supported by MicroPython:

:check_mark: Write register pointer

python

i2c.writeto(0x29, b'\x00')

:check_mark: Read 1 byte

python

i2c.readfrom(0x29, 1)

:check_mark: readfrom_mem

python

i2c.readfrom_mem(0x29, 0x00, 1)

:check_mark: repeated‑start read

(not supported by your firmware)

:check_mark: STOP‑then‑read

(not supported by your firmware)

:check_mark: writeto_then_readfrom

(not implemented in your firmware)

:cross_mark: ALL read attempts failed with EIO

This is the key point:

The RV‑3028 module ACKs writes but refuses every read operation.

This is not normal behaviour for a genuine RV‑3028‑C7.

:star: Why the PiicoDev example code fails

The code they gave you:

python

rtc = PiicoDev_RV3028()
rtc.timestamp()
rtc.checkAlarm()

All rely on reading registers from the RTC.

But your module cannot perform any read, so:

  • timestamp() fails

  • alarmSetup() fails

  • checkAlarm() fails

  • The driver throws EIO

  • The example loop never works

The PiicoDev code is correct — your module simply cannot respond to reads.

:star: What is still going on

Here is the exact technical situation:

  • The RV‑3028 appears on the bus at the correct address

  • It ACKs writes

  • It refuses all reads

  • MicroPython cannot force STOP conditions

  • The PiicoDev driver cannot work without read capability

  • The example code cannot work because it requires reading registers

  • The chip behaves like an RV‑3028 variant or a faulty RV‑3028

In short:

The module is visible but unreadable.
No driver can fix unreadable hardware.

I have the Pico2 connected to PiicoDevRV3028 3V3 to 3V3, GND to GND SDA to SDA SCL to SCL and all i want is INT to out put at a set timed interval that I want to program with the Pico and then it needs to output this on the INT pin - but have been bashing my head for weeks trying to go through libraries and code etc, please make it simple.

could this be my issue? Raspberry Pi forums (RP2350 I²C behaviour differences)

Multiple threads discuss that:

  • The RP2350 I²C peripheral is not the same as the RP2040

  • MicroPython support is still incomplete

  • Some devices that work on Pico 1 do not work on Pico 2

  • STOP/START timing is different

  • Some devices require strict I²C timing that Pico 2 MicroPython cannot yet provide

If you’re scanning an i2c bus and its returning decimal 82 than thats hex 0x52, not 0x29, which would match with the PiicoDev Docs and the addresses in the library. All of your I2C scans were for the wrong address.

Errno 5 is a generic input / output error and most commonly indicates a wiring issue. I really think it would be helpful here if you upload photos of your wiring so that we can confirm that nothing is wrong.

Hey there, Henk,

I believe @Sqwawker’s right on both counts. The RTC module has an I2C address of 0x52 not 0x29. If your communicating with 0x29 you will receive an Errno5 Input / Output regardless of the library you use.

That by itself would explain the results you were seeing in your recent posts.

Even so, could you please upload some photos of your setup so we can verify that there is nothing problematic in your wiring.

surely it shouldnt be this hard

We have fixed it with overcoming the below

  • No 500ms delay fix :cross_mark:
  • No 400kHz frequency specified :cross_mark:
  • No INT pin monitoring :cross_mark:
  • No re-arming for next minute :cross_mark:
  • Uses checkAlarm() polling only, not hardware INT :cross_mark:

That official example would have failed on your setup for exactly the reasons we discovered today. Their example assumes the library just works out of the box — which it didn’t because of the boot delay bug.

What we added beyond their example:

  1. time.sleep_ms(500) in the library
  2. freq=400000 explicitly set
  3. Hardware INT pin monitoring on GP15
  4. Re-arming the alarm each time it fires
  5. LED flash confirmation