Use RTC Allarms

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
Post Reply
BOB63
Posts: 58
Joined: Sat Jul 25, 2015 8:24 pm
Location: Monza , Italy

Use RTC Allarms

Post by BOB63 » Fri Nov 06, 2015 10:04 pm

Hi ,
I'm looking to use the RTC allarms to activate at a certain HR:MM two relays and after a certain time (20-40 minutes ) deactivate.
I've started reading the AN3371 documents about the usage of RTC in STM32F4 MCU and then the RM0090 Datasheet to understand which register were involved and whic sequence use to set the allarms.

Looking page 11 of AN3371 I found this clear table that explain how proceed to set the allarms

and in the below part of code I think to have implemented what required to set the Allarm A every day at 23:00

Code: Select all

class BkpRAM(object):
    BKPSRAM = 0x40024000
    def __init__(self):
      stm.mem32[stm.RCC + stm.RCC_APB1ENR] |= 0x10000000 # PWREN bit
      stm.mem32[stm.PWR + stm.PWR_CR] |= 0x100 # Set the DBP bit in the PWR power control register
      stm.mem32[stm.RCC +stm.RCC_AHB1ENR]|= 0x40000 # enable BKPSRAMEN
      stm.mem32[stm.PWR + stm.PWR_CSR] |= 0x200 # BRE backup register enable bit
    def __getitem__(self, idx):
        assert idx >= 0 and idx <= 0x3ff, "Index must be between 0 and 1023"
        return stm.mem32[self.BKPSRAM + idx * 4]
    def __setitem__(self, idx, val):
        assert idx >= 0 and idx <= 0x3ff, "Index must be between 0 and 1023"
        stm.mem32[self.BKPSRAM + idx * 4] = val


class RTC_SETALA(object):  #VEDI PDF USO RTC E DATASHEET CPU PER DEFINIRE COME USARE ALLARMI RTC INTEGRATO NELLA CPU. 
    def __init__(self):
      stm.mem32[stm.RTC + stm.RTC_WPR] |= 0x000000CA     #pag 11 an3371 
      stm.mem32[stm.RTC + stm.RTC_WPR] |= 0x00000053
      stm.mem32[stm.RTC + stm.RTC_CR] = 0x00000000
      pyb.delay(5)
      if  stm.mem32[stm.RTC + stm.RTC_ISR] &0x0001 == 0x0001 :   #test ALRAWF IN RTC_ISR
          stm.mem32[stm.RTC + stm.RTC_ALRMAR] |= 0x86230000      #set allarm A @ 23:00 of every day     
          stm.mem32[stm.RTC+stm.RTC_CR] |= 0x00000010
          stm.mem32[stm.RTC + stm.RTC_WPR] = 0x000000FF 
The problem is that wen I run the code i got this error that apparently is related to the fact that i try to set in the register a value grater than max value , but looking the register bit setting on descriptions RM0090 page 815 I think to have done in the right way.

Where I'm wrong this time ? :roll:
Attachments
Cattura2.JPG
Cattura2.JPG (28.46 KiB) Viewed 9843 times
Cattura.JPG
Cattura.JPG (56.25 KiB) Viewed 9843 times
Last edited by BOB63 on Fri Nov 06, 2015 10:21 pm, edited 1 time in total.
Thanks. Roberto

BOB63
Posts: 58
Joined: Sat Jul 25, 2015 8:24 pm
Location: Monza , Italy

Re: Use RTC Allarms

Post by BOB63 » Fri Nov 06, 2015 10:13 pm

Sorry I forgot to cancel part of code , anyway the line of code involved into the problem is in Class RTC_SETALA.
Thanks. Roberto

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Use RTC Allarms

Post by pythoncoder » Sat Nov 07, 2015 7:57 am

The error is in this line

Code: Select all

stm.mem32[stm.RTC + stm.RTC_ALRMAR] |= 0x86230000
It occurs because of the way MicroPython stores integers. To conserve RAM it stores small integers in a single 32 bit word, whereas Python arbitrary precision integers are stored in an object. To differentiate between the data types which are stored in a single word, there is a constraint in that small integers must have the two most significant bits equal. Usually this is transparent, but you've hit a case where it is not. The value 0x86230000 does not meet the constraint and hence is stored as a long integer so a type conversion error occurs when you try to assign it to a 32 bit register. The same error can be provoked as follows:

Code: Select all

from array import array
a = array('i', (0,))
a[0] = 0x86230000
I'm sure someone will come up with a more elegant solution, but here is my suggestion. The first argument is the top 16 bits, the second is the bottom 16 bits, and the third the address where the result is to be assigned.

Code: Select all

from array import array
a = array('i', (0,))
@micropython.asm_thumb
def set_register(r0, r1, r2):
    mov(r3, 16)
    lsl(r0, r3)
    orr(r0, r1)
    str(r0, [r2, 0])
set_register(0x8620, 0, a)
print(hex(a[0] & 0xffffffff)) # coerce into a long integer
The print statement produces 0x86200000.
So this should do the trick

Code: Select all

set_register(0x8600, 0, stm.RTC + stm.RTC_ALRMAR)
Peter Hinch
Index to my micropython libraries.

BOB63
Posts: 58
Joined: Sat Jul 25, 2015 8:24 pm
Location: Monza , Italy

Re: Use RTC Allarms

Post by BOB63 » Sat Nov 07, 2015 12:19 pm

Hi pythoncoder ,
I was expecting my usual stupid finger error as source of the problem but I see that is more complex.
Anyway ,let me see if I'm able to implement this solution in my project.

Thanks you so much!

P.S: Sometime I ask to my self why I'm not satisfied enogh to write just a program to see a blinking LED instead than enter into these brain teaser ! :roll: :shock:
Thanks. Roberto

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Use RTC Allarms

Post by pythoncoder » Sat Nov 07, 2015 3:08 pm

Unfortunately my solution still isn't correct as the address of the RTC itself falls foul of the limitation: the RTC registers have a base address of 0x4000000. The following code lets you write arbitrary values to an RTC register, demonstrated here with backup register 0. Note that we clear down the top two bits of the address before passing it to the assembler, which then sets bit 30.

Code: Select all

@micropython.asm_thumb
def set_register(r0, r1, r2):
    mov(r3, 16)
    lsl(r0, r3)
    orr(r0, r1)
    mov(r3, 0)
    movt(r3, 0x4000)
    orr(r2, r3)
    str(r0, [r2, 0])
The following illustrates that it works

Code: Select all

>>> set_register(0x8012, 0x23, (stm.RTC +stm.RTC_BKP0R) & 0x3fffffff)
1179683
>>> hex(stm.mem32[stm.RTC +stm.RTC_BKP0R]& 0xffffffff)
'0x80120023'
>>> 
I'd be interested to hear how you get on with the alarms as I haven't yet studied these. I would have thought that to disable the alarms you'd need

Code: Select all

stm.mem32[stm.RTC + stm.RTC_CR] &= 0xfffcff
and to re-enable alarm 0

Code: Select all

stm.mem32[stm.RTC+stm.RTC_CR] |= 0x100
But that's on a pretty cursory reading of the data. Good luck ;)
Peter Hinch
Index to my micropython libraries.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Use RTC Allarms

Post by pythoncoder » Sun Nov 08, 2015 9:10 am

OK, the following hack may form the basis of a solution. On power up the red LED flashes after about ten seconds, thereafter at one minute intervals (when the seconds value is 9). It uses my upower module to determine the reason for the startup: code is here https://github.com/peterhinch/micropyth ... opower.git

Code: Select all

import stm, pyb, upower

led = pyb.LED(1)
led.on()

# Enables values with top bits set to be written to RTC registers
@micropython.asm_thumb
def set_rtc_register(r0, r1, r2):               # MS word, LS word, offset address of register
    mov(r3, 16)
    lsl(r0, r3)
    orr(r0, r1)
    movw(r3, 0x2800)
    movt(r3, 0x4000)                            # Base address of RTC 0x40002800
    orr(r2, r3)
    str(r0, [r2, 0])

def init_alarm_a():
    BIT17 = 1 << 17                                 # Alarm mask bit
    stm.mem32[stm.EXTI + stm.EXTI_IMR] |= BIT17     # Set up ext interrupt
    stm.mem32[stm.EXTI + stm.EXTI_RTSR] |= BIT17    # Rising edge
    stm.mem32[stm.EXTI + stm.EXTI_PR] |= BIT17      # Clear pending bit
    stm.mem32[stm.RTC + stm.RTC_WPR] |= 0xCA        # enable write
    stm.mem32[stm.RTC + stm.RTC_WPR] |= 0x53
    stm.mem32[stm.RTC + stm.RTC_CR] &= 0xffeeff     # Clear ALRAE in RTC_CR to disable Alarm A
    pyb.delay(5)
    if stm.mem32[stm.RTC + stm.RTC_ISR] & 0x1 :     # test ALRAWF IN RTC_ISR
        set_rtc_register(0x8080, 0x8009, stm.RTC_ALRMAR)
        stm.mem32[stm.RTC + stm.RTC_ISR] &= 0x1feff # Clear the RTC alarm ALRAF flag
        stm.mem32[stm.PWR + stm.PWR_CR] |= 4        # Clear the PWR Wakeup (WUF) flag
        stm.mem32[stm.RTC+stm.RTC_CR] |= 0x1100     # Enable the RTC alarm A interrupt
        stm.mem32[stm.RTC + stm.RTC_WPR] = 0xff
    else:
        raise OSError("Can't access alarm A")

reason = upower.why()                           # Why have we woken?
if reason == 'BOOT':                            # first boot or reset
    upower.rtc.datetime((2015, 8, 6, 4, 13, 0, 0, 0))  # Code to run on 1st boot only
    init_alarm_a()

pyb.delay(1000)
led.off()
pyb.standby()
Manual P137 and 796 provide some background.
[edit]
I've had a first pass at supporting the two timers: see the above URL.
Peter Hinch
Index to my micropython libraries.

BOB63
Posts: 58
Joined: Sat Jul 25, 2015 8:24 pm
Location: Monza , Italy

Re: Use RTC Allarms

Post by BOB63 » Sat Nov 14, 2015 9:12 pm

Hi pythoncoder,
I've spent some time study your micropower project and I've found really many things useful to better understand how use also the RTC.

I've tryed to modify the allarm.py as follow to set the allarm based on combination of hours+minutes but i doesn't work as I want , infact it continue to wakeup about every 30 sec also if I 've set Hour and minutes , and also if allarm B is not set le yellow led blink.

The first time that I've tested your allarm.py has worked as you describe in the documentation.

What coul be the reason ?

Code: Select all

import stm, pyb, upower

led = pyb.LED(1)
led.on()
reason = upower.why()                           # Why have we woken?
if reason == 'BOOT':                            # first boot or reset: yellow LED
    upower.rtc.datetime((2015, 8, 6, 4, 13, 0, 0, 0))  # Code to run on 1st boot only
    aa = upower.alarm('a')
    aa.timeset(hour=13,minute=3)
    ab = upower.alarm('b')
    ab.timeset()
elif reason == 'ALARM_A':
    lg = pyb.LED(2)
    lg.on()
elif reason == 'ALARM_B':
    ly = pyb.LED(3)
    ly.on()

pyb.delay(1000)     # Let LED's be seen!
pyb.standby()
For the project on which I'm working as case study , a simple control to switch on/off two pumps used to irrigate the garden ,I don't think that I can put the upython in standby mode , because I must be able at any time to receive from BT or Xbee ,some data sent from a Processing application on the PC , used to change start/stop time , days or keep in consideration the data coming from two moisture sensors to avoid a garden is already wet.

The circuit will be supplied with AC/DC power supply so is not a must the low power operation, while is important that at power on I can reload the setting stored in the eeprom .

Anyway , in case of an application that need a low power usage , Is there any way to keep the UART operative and wake up the CPU if data are received un UART ?

Thanks.
Thanks. Roberto

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Use RTC Allarms

Post by pythoncoder » Sun Nov 15, 2015 7:49 am

Odd. It works fine here. However your program doesn't actually use alarm B as you call timeset() without arguments: this causes my driver to ignore alarm B. To simplify:

Code: Select all

import pyb, upower

led = pyb.LED(1)
led.on()
reason = upower.why()                           # Why have we woken?
if reason == 'BOOT':                            # first boot or reset: yellow LED
    upower.rtc.datetime((2015, 8, 6, 4, 13, 0, 0, 0))  # Code to run on 1st boot only
    aa = upower.alarm('a')
    aa.timeset(hour=13,minute=3)
elif reason == 'ALARM_A':
    lg = pyb.LED(2)
    lg.on()

pyb.delay(1000)     # Let LED's be seen!
pyb.standby()
Both versions work with the LED flashing once, 3 minutes after boot. I didn't wait 24 hours for the next flash ;).

On your other points, if you need to wait on a UART you can't use standby: my document lists the only events capable of waking the Pyboard (unless someone more knowledgeable corrects me). If you're running on mains power, you might want to install an RTC battery to enable the RTC to maintain time when the power is down.

Running from mains power, the easiest way to schedule a regular operation is to periodically check the time rather than try to use the alarms. Try something along these lines:

Code: Select all

import time
has_run = False
while True:
    if has_run and time.localtime()[2] == 1 # 1am
        has_run = False
    if not has_run and time.localtime()[2] == 13 # 13.00 hours
        has_run = true
        irrigate()
    # Check UART etc
Peter Hinch
Index to my micropython libraries.

BOB63
Posts: 58
Joined: Sat Jul 25, 2015 8:24 pm
Location: Monza , Italy

Re: Use RTC Allarms

Post by BOB63 » Sun Nov 15, 2015 9:23 am

Now I've tested the same program an another upython board and is working fine while on the original one still failing.
Both boards are uptated to pybv10-2015-11-13-v1.5-117-gf882d53 . :?: :(
Thanks. Roberto

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Use RTC Allarms

Post by pythoncoder » Sun Nov 15, 2015 5:03 pm

My guess is that on the failing board you are inadvertently running the original version of the program rather than the modified one. If you have an SD card fitted the Pyboard will run the one there in preference to an identically named file on the flash. This also applies to main.py and boot.py: the SD card always takes precedence.

To resolve any confusion I suggest saving the new version under a new name and editing main.py to import that renamed version. Ensure that both files are on the SD card. You should then be able to plug the card into any Pyboard and see identical results.

To put this another way: I can see no way in which the modified version I posted can flash the LED's alternately at 30 second intervals!
Peter Hinch
Index to my micropython libraries.

Post Reply