172 lines
5.8 KiB
C
172 lines
5.8 KiB
C
/** @file hal/micro/cortexm3/system-timer.c
|
|
* @brief STM32W108 System Timer HAL functions.
|
|
*
|
|
* \b NOTE: The Sleep Timer count and compare registers are only 16 bits, but
|
|
* the counter and comparators themselves are actually 32bits. To deal with
|
|
* this, there are High ("H") and Low ("L") registers. The High register is
|
|
* the "action" register. When working with SLEEPTMR_CNT, reading the "H"
|
|
* register will return the upper 16 bits and simultaneously trigger the
|
|
* capture of the lower 16bits in the "L" register. The "L" register may then
|
|
* be read. When working with the SLEEPTMR_CMP registers, writing "L" will
|
|
* set a shadow register. Writing "H" will cause the value from the "H" write
|
|
* and the "L" shadow register to be combined and simultaneously loaded into
|
|
* the true 32bit comparator.
|
|
*
|
|
* <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
|
|
*/
|
|
#include PLATFORM_HEADER
|
|
#include "error.h"
|
|
#include "hal/micro/micro-common.h"
|
|
#include "hal/micro/cortexm3/micro-common.h"
|
|
#include "micro/system-timer.h"
|
|
|
|
|
|
//A simple flag used by internalSleepForQs to check that it has exited
|
|
//from sleep mode at the request of the expected timer interrupt.
|
|
static boolean sleepTimerInterruptOccurred = FALSE;
|
|
|
|
// halInternalStartSystemTimer() was moved to micro.c
|
|
|
|
/**
|
|
* Return a 16 bit real time clock value. With 1024 clock ticks per second,
|
|
* a single clock tick occurs every 0.9769625 milliseconds, and a rollover
|
|
* of the 16-bit timer occurs every 64 seconds.
|
|
*/
|
|
int16u halCommonGetInt16uMillisecondTick(void)
|
|
{
|
|
return (int16u)halCommonGetInt32uMillisecondTick();
|
|
}
|
|
|
|
int16u halCommonGetInt16uQuarterSecondTick(void)
|
|
{
|
|
return (int16u)(halCommonGetInt32uMillisecondTick() >> 8);
|
|
}
|
|
|
|
/**
|
|
* Return a 32 bit real time clock value. With 1024 clock ticks per second,
|
|
* a single clock tick occurs every 0.9769625 milliseconds, and a rollover
|
|
* of the 32-bit timer occurs approximately every 48.5 days.
|
|
*/
|
|
int32u halCommonGetInt32uMillisecondTick(void)
|
|
{
|
|
int32u time;
|
|
|
|
time = SLEEPTMR_CNTH<<16;
|
|
time |= SLEEPTMR_CNTL;
|
|
|
|
return time;
|
|
}
|
|
|
|
|
|
void halSleepTimerIsr(void)
|
|
{
|
|
//clear the second level interrupts
|
|
INT_SLEEPTMRFLAG = INT_SLEEPTMRWRAP | INT_SLEEPTMRCMPA | INT_SLEEPTMRCMPB;
|
|
|
|
//mark a sleep timer interrupt as having occurred
|
|
sleepTimerInterruptOccurred = TRUE;
|
|
}
|
|
|
|
#define CONVERT_QS_TO_TICKS(x) ((x) << 8)
|
|
#define CONVERT_TICKS_TO_QS(x) ((x) >> 8)
|
|
#define TIMER_MAX_QS 0x1000000 // = 4194304 seconds * 4 = 16777216
|
|
static StStatus internalSleepForQs(boolean useGpioWakeMask,
|
|
int32u *duration,
|
|
int32u gpioWakeBitMask)
|
|
{
|
|
StStatus status = ST_SUCCESS;
|
|
int32u sleepOverflowCount;
|
|
int32u remainder;
|
|
int32u startCount;
|
|
|
|
//There is really no reason to bother with a duration of 0qs
|
|
if(*duration==0) {
|
|
INTERRUPTS_ON();
|
|
return status;
|
|
}
|
|
|
|
ATOMIC(
|
|
//disable top-level interrupt while configuring
|
|
INT_CFGCLR = INT_SLEEPTMR;
|
|
|
|
//Our tick is calibrated to 1024Hz, giving a tick of 976.6us and an
|
|
//overflow of 4194304.0 seconds. Calculate the number of sleep overflows
|
|
//in the duration
|
|
sleepOverflowCount = (*duration)/TIMER_MAX_QS;
|
|
//calculate the remaining ticks
|
|
remainder = CONVERT_QS_TO_TICKS((*duration)%TIMER_MAX_QS);
|
|
//grab the starting sleep count
|
|
startCount = halCommonGetInt32uMillisecondTick();
|
|
|
|
sleepTimerInterruptOccurred = FALSE;
|
|
|
|
if(remainder) {
|
|
//set CMPA value
|
|
SLEEPTMR_CMPAL = (startCount+remainder)&0xFFFF;
|
|
SLEEPTMR_CMPAH = ((startCount+remainder)>>16)&0xFFFF;
|
|
//clear any stale interrupt flag and set the CMPA interrupt
|
|
INT_SLEEPTMRFLAG = INT_SLEEPTMRCMPA;
|
|
INT_SLEEPTMRCFG = INT_SLEEPTMRCMPA;
|
|
}
|
|
if(sleepOverflowCount) {
|
|
//set CMPB value
|
|
SLEEPTMR_CMPBL = startCount&0xFFFF;
|
|
SLEEPTMR_CMPBH = (startCount>>16)&0xFFFF;
|
|
//clear any stale interrupt flag and set the CMPB interrupt
|
|
//this will also disable the CMPA interrupt, since we only want to wake
|
|
//on overflows, not the remainder yet
|
|
INT_SLEEPTMRFLAG = INT_SLEEPTMRCMPB;
|
|
INT_SLEEPTMRCFG = INT_SLEEPTMRCMPB;
|
|
}
|
|
|
|
//enable top-level interrupt
|
|
INT_CFGSET = INT_SLEEPTMR;
|
|
)
|
|
|
|
while(*duration > 0) {
|
|
{
|
|
halSleepWithOptions(SLEEPMODE_WAKETIMER, gpioWakeBitMask);
|
|
}
|
|
|
|
INT_SLEEPTMRCFG = INT_SLEEPTMRCFG_RESET; //disable all SleepTMR interrupts
|
|
|
|
//If we didn't come out of sleep via a compare or overflow interrupt,
|
|
//it was an abnormal sleep interruption; report the event.
|
|
if(!sleepTimerInterruptOccurred) {
|
|
status = ST_SLEEP_INTERRUPTED;
|
|
//Update duration to account for how long last sleep was. Using the
|
|
//startCount variable is always valid because full timer wraps always
|
|
//return to this value and the remainder is an offset from this value.
|
|
//And since the duration is always reduced after each full timer wrap,
|
|
//we only need to calculate the final duration here.
|
|
*duration -= CONVERT_TICKS_TO_QS(halCommonGetInt32uMillisecondTick() -
|
|
startCount);
|
|
break;
|
|
} else {
|
|
if(sleepOverflowCount) {
|
|
sleepOverflowCount--;
|
|
*duration -= TIMER_MAX_QS;
|
|
} else {
|
|
*duration -= CONVERT_TICKS_TO_QS(remainder);
|
|
}
|
|
sleepTimerInterruptOccurred = FALSE;
|
|
if(sleepOverflowCount) {
|
|
//enable sleeping for a full timer wrap
|
|
INT_SLEEPTMRFLAG = INT_SLEEPTMRCMPB;
|
|
INT_SLEEPTMRCFG = INT_SLEEPTMRCMPB;
|
|
} else if(!sleepOverflowCount && (*duration>0)){
|
|
//enable sleeping for the remainder
|
|
INT_SLEEPTMRFLAG = INT_SLEEPTMRCMPA;
|
|
INT_SLEEPTMRCFG = INT_SLEEPTMRCMPA;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
StStatus halSleepForQsWithOptions(int32u *duration, int32u gpioWakeBitMask)
|
|
{
|
|
return internalSleepForQs(TRUE, duration, gpioWakeBitMask);
|
|
}
|
|
|