One limitation of SysTick is that its speed is tied to the main clock. If you slow down the main clock to reduce power consumption, SysTick will slow down correspondingly. Many (all?) STM32 parts have some sort of "real-time clock" function which can run independently from the main clock; in some cases, it can even run independently from the main power supply. The real-time clock functions can be programmed to wake up the CPU at specified times, though their precision is rather limited (e.g. some restrict wake-ups to one-second increments unless one uses some rather icky tricks). Note also that some chips, for some reason that completely baffles me, rather annoyingly store the time as (0-9) seconds + (0-5) tens of seconds + (0-9) minutes + (0-5) tens of minutes + (0-9) hours up to 23, plus (0-2) tens of hours up to 24, etc. If you won't need to go more than an hour without polling the clock on the STM32LF151, I would suggest that you ignore the upper portions of the real-time clock and simply do something like:
uint32_t present_time;
uint32_t last_clock_reading;
uint32_t last_raw_reading;
uint32_t get_time(void)
{
// Capture seconds and minutes 0-9, in BCD format
uint32_t this_clock_reading = (RTC->TR) & 0x0FFF;
// Early-exit if no change
if (this_clock_reading == last_raw_reading)
return present_time;
last_raw_reading = this_clock_reading;
// Each ten minutes should be 600 seconds, not 4096
this_clock_reading -= (4096-600)*(this_clock_reading & 0x0F00)
// Each minute should be 60 seconds, not 256
this_clock_reading -= (256-60)*(this_clock_reading & 0x0F00)
// Each ten seconds should be 10 seconds, not 16
this_clock_reading -= (16-10)*(this_clock_reading & 0x00F0);
// Update present time
if (last_clock_reading > this_clock_reading)
present_time += 3600 + this_clock_reading - last_clock_reading;
else
present_time += this_clock_reading - last_clock_reading;
last_clock_reading = this_clock_reading;
return present_time;
}
int set_alarm(uint32_t alarm_time) // Returns -1 if alarm time already passed
{
int32_t temp;
// How far in future (if at all) is alarm time?
temp = (alarm_time - get_time());
if (temp 3000) // Set alarm a max of 50 minutes in future
temp = 3000;
// Compute clock reading when we should have our alarm
temp += last_clock_reading;
if (temp > 3600)
temp -= 3600;
// Convert to BCD by undoing corrections which would be done on reading
temp += (16-10)*(temp / 10) + (256-60)*(temp / 60) + (4096-60)*(temp / 600);
// Now store the alarm value (first write-enable the registers)
RTC->WPR = 0xCA;
RTC->WPR = 0x53;
RTC->ALARMAR = temp | 0x80800000; // Set alarm (only match minutes and seconds)
}
Using routines like the above, the time used by the application will be kept in a nice uniformly-incrementing 32-bit value. One can set an alarm up to 50 minutes in advance, and then have the chip go to sleep; the chip will wake up when the alarm time arrives. Setting an alarm more than 50 minutes in advance will cause it to be set 50 minutes in advance; when the CPU wakes up, it can go "back to bed" if the alarm time hasn't arrived yet.
On the STM32L151, one may if desired set the clock to run at 2x, 4x, 16x, or various other multiples of normal speed. The above code would not change when this is done; the only effects would be (1) the values in present_time and the parameter to set_alarm would represent a faster time unit than seconds, and (2) the maximum time before the next alarm would be scaled accordingly. It's somewhat ugly working with BCD units that represent e.g. 37.5 seconds, 3.75 seconds, 0.625 seconds, and 0.0625 seconds (which is what the "minutes" and "seconds" would represent at 16x speed) but such is life.