Page 1 of 1
Use RTC Allarms
Posted: Fri Nov 06, 2015 10:04 pm
by BOB63
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 ?

Re: Use RTC Allarms
Posted: Fri Nov 06, 2015 10:13 pm
by BOB63
Sorry I forgot to cancel part of code , anyway the line of code involved into the problem is in Class RTC_SETALA.
Re: Use RTC Allarms
Posted: Sat Nov 07, 2015 7:57 am
by pythoncoder
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)
Re: Use RTC Allarms
Posted: Sat Nov 07, 2015 12:19 pm
by BOB63
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 !

Re: Use RTC Allarms
Posted: Sat Nov 07, 2015 3:08 pm
by pythoncoder
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

Re: Use RTC Allarms
Posted: Sun Nov 08, 2015 9:10 am
by pythoncoder
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.
Re: Use RTC Allarms
Posted: Sat Nov 14, 2015 9:12 pm
by BOB63
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.
Re: Use RTC Allarms
Posted: Sun Nov 15, 2015 7:49 am
by pythoncoder
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
Re: Use RTC Allarms
Posted: Sun Nov 15, 2015 9:23 am
by BOB63
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 .

Re: Use RTC Allarms
Posted: Sun Nov 15, 2015 5:03 pm
by pythoncoder
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!