| | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \file wsf_timer.c |
| | | * |
| | | * \brief Timer service. |
| | | * |
| | | * Copyright (c) 2009-2019 Arm Ltd. All Rights Reserved. |
| | | * |
| | | * Copyright (c) 2019-2020 Packetcraft, Inc. |
| | | * |
| | | * Licensed under the Apache License, Version 2.0 (the "License"); |
| | | * you may not use this file except in compliance with the License. |
| | | * You may obtain a copy of the License at |
| | | * |
| | | * http://www.apache.org/licenses/LICENSE-2.0 |
| | | * |
| | | * Unless required by applicable law or agreed to in writing, software |
| | | * distributed under the License is distributed on an "AS IS" BASIS, |
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | | * See the License for the specific language governing permissions and |
| | | * limitations under the License. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | |
| | | #include "wsf_types.h" |
| | | #include "wsf_queue.h" |
| | | #include "wsf_timer.h" |
| | | |
| | | #include "wsf_assert.h" |
| | | #include "wsf_cs.h" |
| | | #include "wsf_trace.h" |
| | | #include "pal_sys.h" |
| | | |
| | | /************************************************************************************************** |
| | | Macros |
| | | **************************************************************************************************/ |
| | | |
| | | #if (WSF_MS_PER_TICK == 10) |
| | | |
| | | /*! \brief WSF timer ticks per second. */ |
| | | #define WSF_TIMER_TICKS_PER_SEC (1000 / WSF_MS_PER_TICK) |
| | | |
| | | /* convert seconds to timer ticks */ |
| | | #define WSF_TIMER_SEC_TO_TICKS(sec) (WSF_TIMER_TICKS_PER_SEC * (sec)) |
| | | |
| | | /* convert milliseconds to timer ticks */ |
| | | #define WSF_TIMER_MS_TO_TICKS(ms) ((ms) / WSF_MS_PER_TICK) |
| | | |
| | | #else |
| | | #error "WSF_TIMER_MS_TO_TICKS and WSF_TIMER_SEC_TO_TICKS not defined for WSF_MS_PER_TICK" |
| | | #endif |
| | | |
| | | /*! \brief Minimum RTC ticks required to go into sleep. */ |
| | | #define WSF_TIMER_MIN_MS_FOR_POWERDOWN (2) |
| | | #define WSF_TIMER_MAX_MS_FOR_POWERDOWN (10000) |
| | | #include "mk_common.h" |
| | | #include "mk_misc.h" |
| | | // #include "mk_trace.h" |
| | | |
| | | /************************************************************************************************** |
| | | Global Variables |
| | | **************************************************************************************************/ |
| | | |
| | | static wsfQueue_t wsfTimerTimerQueue; /*!< Timer queue */ |
| | | |
| | | /*! \brief Last SysTick value read. */ |
| | | static uint32_t wsfTimerSysTickcLastTick = 0; |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | |
| | | pElem = pElem->pNext; |
| | | } |
| | | |
| | | /* if timer found remove from queue */ |
| | | /* if timer found, remove from queue */ |
| | | if (pElem != NULL) |
| | | { |
| | | WsfQueueRemove(&wsfTimerTimerQueue, pTimer, pPrev); |
| | |
| | | * \param mode Timer work mode. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | static void wsfTimerInsert(wsfTimer_t *pTimer, wsfTimerTicks_t ticks, uint8_t mode) |
| | | static void wsfTimerInsert(wsfTimer_t *pTimer, uint32_t ticks, uint8_t mode) |
| | | { |
| | | wsfTimer_t *pElem; |
| | | wsfTimer_t *pPrev = NULL; |
| | | |
| | | /* task schedule lock */ |
| | | uint32_t lock = WsfTaskLock(); |
| | | uint32_t lock = int_lock(); |
| | | |
| | | /* if timer is already running stop it first */ |
| | | if (pTimer->isStarted) |
| | |
| | | pElem = pElem->pNext; |
| | | } |
| | | |
| | | /* insert timer into queue */ |
| | | WsfQueueInsert(&wsfTimerTimerQueue, pTimer, pPrev); |
| | | |
| | | /* task schedule unlock */ |
| | | WsfTaskUnlock(lock); |
| | | int_unlock(lock); |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | |
| | | * function can return zero even if a timer is running, indicating a timer |
| | | * has expired but has not yet been serviced. |
| | | * |
| | | * \param pTimerRunning Returns TRUE if a timer is running, FALSE if no timers running. |
| | | * |
| | | * \return The number of ticks until the next timer expiration. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | static wsfTimerTicks_t WsfTimerNextExpiration(bool_t *pTimerRunning) |
| | | uint32_t WsfTimerNextExpiration(void) |
| | | { |
| | | wsfTimerTicks_t ticks; |
| | | |
| | | /* task schedule lock */ |
| | | uint32_t lock = WsfTaskLock(); |
| | | uint32_t ticks; |
| | | uint32_t lock = int_lock(); |
| | | |
| | | if (wsfTimerTimerQueue.pHead == NULL) |
| | | { |
| | | *pTimerRunning = FALSE; |
| | | ticks = 0; |
| | | ticks = UINT32_MAX; |
| | | } |
| | | else |
| | | { |
| | | *pTimerRunning = TRUE; |
| | | ticks = ((wsfTimer_t *)wsfTimerTimerQueue.pHead)->ticks; |
| | | } |
| | | |
| | | /* task schedule unlock */ |
| | | WsfTaskUnlock(lock); |
| | | |
| | | int_unlock(lock); |
| | | return ticks; |
| | | } |
| | | |
| | |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Start a timer in units of seconds. |
| | | * \brief Start a timer in units of 32768Hz ticks. |
| | | * |
| | | * \param pTimer Pointer to timer. |
| | | * \param sec Seconds until expiration. |
| | | * \param ticks 32768Hz ticks until expiration. |
| | | * \param mode Timer work mode. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | void WsfTimerStartSec(wsfTimer_t *pTimer, wsfTimerTicks_t sec, uint8_t mode) |
| | | void WsfTimerStartTick(wsfTimer_t *pTimer, uint32_t ticks, uint8_t mode) |
| | | { |
| | | WSF_TRACE_INFO2("WsfTimerStartSec pTimer:0x%x ticks:%u", (uint32_t)pTimer, WSF_TIMER_SEC_TO_TICKS(sec)); |
| | | |
| | | /* insert timer into queue */ |
| | | wsfTimerInsert(pTimer, WSF_TIMER_SEC_TO_TICKS(sec), mode); |
| | | wsfTimerInsert(pTimer, ticks, mode); |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | |
| | | * \param mode Timer work mode. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | void WsfTimerStartMs(wsfTimer_t *pTimer, wsfTimerTicks_t ms, uint8_t mode) |
| | | void WsfTimerStartMs(wsfTimer_t *pTimer, uint32_t ms, uint8_t mode) |
| | | { |
| | | WSF_TRACE_INFO2("WsfTimerStartMs pTimer:0x%x ticks:%u", (uint32_t)pTimer, WSF_TIMER_MS_TO_TICKS(ms)); |
| | | |
| | | /* insert timer into queue */ |
| | | wsfTimerInsert(pTimer, WSF_TIMER_MS_TO_TICKS(ms), mode); |
| | | wsfTimerInsert(pTimer, __MS_TO_32K_CNT(ms), mode); |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | |
| | | /*************************************************************************************************/ |
| | | void WsfTimerStop(wsfTimer_t *pTimer) |
| | | { |
| | | WSF_TRACE_INFO1("WsfTimerStop pTimer:0x%x", pTimer); |
| | | |
| | | /* task schedule lock */ |
| | | uint32_t lock = WsfTaskLock(); |
| | | uint32_t lock = int_lock(); |
| | | |
| | | wsfTimerRemove(pTimer); |
| | | |
| | | /* task schedule unlock */ |
| | | WsfTaskUnlock(lock); |
| | | int_unlock(lock); |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | |
| | | * \param ticks Number of ticks since last update. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | void WsfTimerUpdate(wsfTimerTicks_t ticks) |
| | | void WsfTimerUpdate(uint32_t ticks) |
| | | { |
| | | wsfTimer_t *pElem; |
| | | |
| | | /* task schedule lock */ |
| | | uint32_t lock = WsfTaskLock(); |
| | | uint32_t lock = int_lock(); |
| | | |
| | | pElem = (wsfTimer_t *)wsfTimerTimerQueue.pHead; |
| | | |
| | |
| | | else |
| | | { |
| | | pElem->ticks = 0; |
| | | } |
| | | |
| | | if (pElem->ticks < POWER_DOWN_TIME_TICK_MIN) |
| | | { |
| | | pElem->ticks = 0; |
| | | /* timer expired; set task for this timer as ready */ |
| | | WsfTaskSetReady(pElem->handlerId, WSF_TIMER_EVENT); |
| | | } |
| | | |
| | | // LOG_INFO(TRACE_MODULE_OS, "WsfTimerUpdate %u %u %u %u\r\n", pElem->handlerId, pElem->msg.event, pElem->ticks, ticks); |
| | | |
| | | pElem = pElem->pNext; |
| | | } |
| | | |
| | | /* task schedule unlock */ |
| | | WsfTaskUnlock(lock); |
| | | int_unlock(lock); |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Service expired timers for the given task. |
| | | * |
| | | * \param taskId Task ID. |
| | | * |
| | | * \return Pointer to timer or NULL. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | wsfTimer_t *WsfTimerServiceExpired(wsfTaskId_t taskId) |
| | | wsfTimer_t *WsfTimerServiceExpired(void) |
| | | { |
| | | wsfTimer_t *pElem; |
| | | wsfTimer_t *pPrev = NULL; |
| | | |
| | | /* Unused parameters */ |
| | | (void)taskId; |
| | | |
| | | /* task schedule lock */ |
| | | uint32_t lock = WsfTaskLock(); |
| | | uint32_t lock = int_lock(); |
| | | |
| | | /* find expired timers in queue */ |
| | | pElem = (wsfTimer_t *)wsfTimerTimerQueue.pHead; |
| | |
| | | } |
| | | else |
| | | { |
| | | /* remove timer from queue */ |
| | | WsfQueueRemove(&wsfTimerTimerQueue, pElem, pPrev); |
| | | |
| | | pElem->isStarted = FALSE; |
| | | } |
| | | |
| | | /* task schedule unlock */ |
| | | WsfTaskUnlock(lock); |
| | | |
| | | WSF_TRACE_INFO1("Timer expired pTimer:0x%x", pElem); |
| | | |
| | | /* return timer */ |
| | | int_unlock(lock); |
| | | return pElem; |
| | | } |
| | | |
| | | /* task schedule unlock */ |
| | | WsfTaskUnlock(lock); |
| | | |
| | | int_unlock(lock); |
| | | return NULL; |
| | | } |
| | | |
| | | /*************************************************************************************************/ |
| | | /*! |
| | | * \brief Function for checking if there is an active timer and if there is enough time to |
| | | * go to sleep and going to sleep. |
| | | */ |
| | | /*************************************************************************************************/ |
| | | uint8_t WsfTimerSleepCheck(uint32_t *sleep_ms) |
| | | { |
| | | wsfTimerTicks_t nextExpiration; |
| | | |
| | | /* If PAL system is busy, no need to sleep. */ |
| | | if (PalSysIsBusy()) |
| | | { |
| | | // active |
| | | return 0; |
| | | } |
| | | |
| | | bool_t running; |
| | | nextExpiration = WsfTimerNextExpiration(&running); |
| | | |
| | | if (running) |
| | | { |
| | | uint32_t awake = WSF_MS_PER_TICK * nextExpiration; |
| | | uint32_t elapsed = PalSysTickElapse(); |
| | | |
| | | /* if we have time to sleep before timer expiration */ |
| | | if ((awake - elapsed) > WSF_TIMER_MIN_MS_FOR_POWERDOWN) |
| | | { |
| | | *sleep_ms = awake - elapsed; |
| | | // Power down |
| | | return 2; |
| | | } |
| | | else |
| | | { |
| | | /* Not enough time to go to powerdown. Let the system sleep. */ |
| | | // Sleep |
| | | return 1; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | *sleep_ms = WSF_TIMER_MAX_MS_FOR_POWERDOWN; |
| | | // Power down |
| | | return 2; |
| | | } |
| | | } |
| | | |
| | | //***************************************************************************** |
| | | // |
| | | // Calculate the elapsed time, and update the WSF software timers. |
| | | // |
| | | //***************************************************************************** |
| | | void WsfTimerUpdateTicks(void) |
| | | { |
| | | uint32_t ui32CurrentTick, ui32ElapsedTicks; |
| | | |
| | | // |
| | | // Read the continuous systick. |
| | | // |
| | | ui32CurrentTick = PalSysTickGet(); |
| | | |
| | | // |
| | | // Figure out how long it has been since the last time we've read the |
| | | // continuous systick. |
| | | // |
| | | if (ui32CurrentTick > wsfTimerSysTickcLastTick) |
| | | { |
| | | ui32ElapsedTicks = ui32CurrentTick - wsfTimerSysTickcLastTick; |
| | | } |
| | | else |
| | | { |
| | | ui32ElapsedTicks = 0xffffffff - wsfTimerSysTickcLastTick + ui32CurrentTick; |
| | | } |
| | | |
| | | WsfTimerUpdate(ui32ElapsedTicks); |
| | | wsfTimerSysTickcLastTick = ui32CurrentTick; |
| | | } |