460 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			460 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * Copyright (c) 2013-2019 Arm Limited. All rights reserved. | ||
|  |  * | ||
|  |  * SPDX-License-Identifier: Apache-2.0 | ||
|  |  * | ||
|  |  * 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 | ||
|  |  * | ||
|  |  * 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. | ||
|  |  * | ||
|  |  * ----------------------------------------------------------------------------- | ||
|  |  * | ||
|  |  * Project:     CMSIS-RTOS RTX | ||
|  |  * Title:       Timer functions | ||
|  |  * | ||
|  |  * ----------------------------------------------------------------------------- | ||
|  |  */ | ||
|  | 
 | ||
|  | #include "rtx_lib.h"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //  OS Runtime Object Memory Usage
 | ||
|  | #if ((defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0)))
 | ||
|  | osRtxObjectMemUsage_t osRtxTimerMemUsage \ | ||
|  | __attribute__((section(".data.os.timer.obj"))) = | ||
|  | { 0U, 0U, 0U }; | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //  ==== Helper functions ====
 | ||
|  | 
 | ||
|  | /// Insert Timer into the Timer List sorted by Time.
 | ||
|  | /// \param[in]  timer           timer object.
 | ||
|  | /// \param[in]  tick            timer tick.
 | ||
|  | static void TimerInsert (os_timer_t *timer, uint32_t tick) { | ||
|  |   os_timer_t *prev, *next; | ||
|  | 
 | ||
|  |   prev = NULL; | ||
|  |   next = osRtxInfo.timer.list; | ||
|  |   while ((next != NULL) && (next->tick <= tick)) { | ||
|  |     tick -= next->tick; | ||
|  |     prev  = next; | ||
|  |     next  = next->next; | ||
|  |   } | ||
|  |   timer->tick = tick; | ||
|  |   timer->prev = prev; | ||
|  |   timer->next = next; | ||
|  |   if (next != NULL) { | ||
|  |     next->tick -= timer->tick; | ||
|  |     next->prev  = timer; | ||
|  |   } | ||
|  |   if (prev != NULL) { | ||
|  |     prev->next = timer; | ||
|  |   } else { | ||
|  |     osRtxInfo.timer.list = timer; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | /// Remove Timer from the Timer List.
 | ||
|  | /// \param[in]  timer           timer object.
 | ||
|  | static void TimerRemove (const os_timer_t *timer) { | ||
|  | 
 | ||
|  |   if (timer->next != NULL) { | ||
|  |     timer->next->tick += timer->tick; | ||
|  |     timer->next->prev  = timer->prev; | ||
|  |   } | ||
|  |   if (timer->prev != NULL) { | ||
|  |     timer->prev->next  = timer->next; | ||
|  |   } else { | ||
|  |     osRtxInfo.timer.list = timer->next; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | /// Unlink Timer from the Timer List Head.
 | ||
|  | /// \param[in]  timer           timer object.
 | ||
|  | static void TimerUnlink (const os_timer_t *timer) { | ||
|  | 
 | ||
|  |   if (timer->next != NULL) { | ||
|  |     timer->next->prev = timer->prev; | ||
|  |   } | ||
|  |   osRtxInfo.timer.list = timer->next; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | //  ==== Library functions ====
 | ||
|  | 
 | ||
|  | /// Timer Tick (called each SysTick).
 | ||
|  | static void osRtxTimerTick (void) { | ||
|  |   os_timer_t *timer; | ||
|  |   osStatus_t  status; | ||
|  | 
 | ||
|  |   timer = osRtxInfo.timer.list; | ||
|  |   if (timer == NULL) { | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   timer->tick--; | ||
|  |   while ((timer != NULL) && (timer->tick == 0U)) { | ||
|  |     TimerUnlink(timer); | ||
|  |     status = osMessageQueuePut(osRtxInfo.timer.mq, &timer->finfo, 0U, 0U); | ||
|  |     if (status != osOK) { | ||
|  |       (void)osRtxErrorNotify(osRtxErrorTimerQueueOverflow, timer); | ||
|  |     } | ||
|  |     if (timer->type == osRtxTimerPeriodic) { | ||
|  |       TimerInsert(timer, timer->load); | ||
|  |     } else { | ||
|  |       timer->state = osRtxTimerStopped; | ||
|  |     } | ||
|  |     timer = osRtxInfo.timer.list; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | /// Timer Thread
 | ||
|  | __WEAK __NO_RETURN void osRtxTimerThread (void *argument) { | ||
|  |   os_timer_finfo_t finfo; | ||
|  |   osStatus_t       status; | ||
|  |   (void)           argument; | ||
|  | 
 | ||
|  |   osRtxInfo.timer.mq = osRtxMessageQueueId( | ||
|  |     osMessageQueueNew(osRtxConfig.timer_mq_mcnt, sizeof(os_timer_finfo_t), osRtxConfig.timer_mq_attr) | ||
|  |   ); | ||
|  |   osRtxInfo.timer.tick = osRtxTimerTick; | ||
|  | 
 | ||
|  |   for (;;) { | ||
|  |     //lint -e{934} "Taking address of near auto variable"
 | ||
|  |     status = osMessageQueueGet(osRtxInfo.timer.mq, &finfo, NULL, osWaitForever); | ||
|  |     if (status == osOK) { | ||
|  |       EvrRtxTimerCallback(finfo.func, finfo.arg); | ||
|  |       (finfo.func)(finfo.arg); | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | //  ==== Service Calls ====
 | ||
|  | 
 | ||
|  | /// Create and Initialize a timer.
 | ||
|  | /// \note API identical to osTimerNew
 | ||
|  | static osTimerId_t svcRtxTimerNew (osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr) { | ||
|  |   os_timer_t *timer; | ||
|  |   uint8_t     flags; | ||
|  |   const char *name; | ||
|  | 
 | ||
|  |   // Check parameters
 | ||
|  |   if ((func == NULL) || ((type != osTimerOnce) && (type != osTimerPeriodic))) { | ||
|  |     EvrRtxTimerError(NULL, (int32_t)osErrorParameter); | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return NULL; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Process attributes
 | ||
|  |   if (attr != NULL) { | ||
|  |     name  = attr->name; | ||
|  |     //lint -e{9079} "conversion from pointer to void to pointer to other type" [MISRA Note 6]
 | ||
|  |     timer = attr->cb_mem; | ||
|  |     if (timer != NULL) { | ||
|  |       //lint -e(923) -e(9078) "cast from pointer to unsigned int" [MISRA Note 7]
 | ||
|  |       if ((((uint32_t)timer & 3U) != 0U) || (attr->cb_size < sizeof(os_timer_t))) { | ||
|  |         EvrRtxTimerError(NULL, osRtxErrorInvalidControlBlock); | ||
|  |         //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |         return NULL; | ||
|  |       } | ||
|  |     } else { | ||
|  |       if (attr->cb_size != 0U) { | ||
|  |         EvrRtxTimerError(NULL, osRtxErrorInvalidControlBlock); | ||
|  |         //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |         return NULL; | ||
|  |       } | ||
|  |     } | ||
|  |   } else { | ||
|  |     name  = NULL; | ||
|  |     timer = NULL; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Allocate object memory if not provided
 | ||
|  |   if (timer == NULL) { | ||
|  |     if (osRtxInfo.mpi.timer != NULL) { | ||
|  |       //lint -e{9079} "conversion from pointer to void to pointer to other type" [MISRA Note 5]
 | ||
|  |       timer = osRtxMemoryPoolAlloc(osRtxInfo.mpi.timer); | ||
|  |     } else { | ||
|  |       //lint -e{9079} "conversion from pointer to void to pointer to other type" [MISRA Note 5]
 | ||
|  |       timer = osRtxMemoryAlloc(osRtxInfo.mem.common, sizeof(os_timer_t), 1U); | ||
|  |     } | ||
|  | #if (defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0))
 | ||
|  |     if (timer != NULL) { | ||
|  |       uint32_t used; | ||
|  |       osRtxTimerMemUsage.cnt_alloc++; | ||
|  |       used = osRtxTimerMemUsage.cnt_alloc - osRtxTimerMemUsage.cnt_free; | ||
|  |       if (osRtxTimerMemUsage.max_used < used) { | ||
|  |         osRtxTimerMemUsage.max_used = used; | ||
|  |       } | ||
|  |     } | ||
|  | #endif
 | ||
|  |     flags = osRtxFlagSystemObject; | ||
|  |   } else { | ||
|  |     flags = 0U; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (timer != NULL) { | ||
|  |     // Initialize control block
 | ||
|  |     timer->id         = osRtxIdTimer; | ||
|  |     timer->state      = osRtxTimerStopped; | ||
|  |     timer->flags      = flags; | ||
|  |     timer->type       = (uint8_t)type; | ||
|  |     timer->name       = name; | ||
|  |     timer->prev       = NULL; | ||
|  |     timer->next       = NULL; | ||
|  |     timer->tick       = 0U; | ||
|  |     timer->load       = 0U; | ||
|  |     timer->finfo.func = func; | ||
|  |     timer->finfo.arg  = argument; | ||
|  | 
 | ||
|  |     EvrRtxTimerCreated(timer, timer->name); | ||
|  |   } else { | ||
|  |     EvrRtxTimerError(NULL, (int32_t)osErrorNoMemory); | ||
|  |   } | ||
|  | 
 | ||
|  |   return timer; | ||
|  | } | ||
|  | 
 | ||
|  | /// Get name of a timer.
 | ||
|  | /// \note API identical to osTimerGetName
 | ||
|  | static const char *svcRtxTimerGetName (osTimerId_t timer_id) { | ||
|  |   os_timer_t *timer = osRtxTimerId(timer_id); | ||
|  | 
 | ||
|  |   // Check parameters
 | ||
|  |   if ((timer == NULL) || (timer->id != osRtxIdTimer)) { | ||
|  |     EvrRtxTimerGetName(timer, NULL); | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return NULL; | ||
|  |   } | ||
|  | 
 | ||
|  |   EvrRtxTimerGetName(timer, timer->name); | ||
|  | 
 | ||
|  |   return timer->name; | ||
|  | } | ||
|  | 
 | ||
|  | /// Start or restart a timer.
 | ||
|  | /// \note API identical to osTimerStart
 | ||
|  | static osStatus_t svcRtxTimerStart (osTimerId_t timer_id, uint32_t ticks) { | ||
|  |   os_timer_t *timer = osRtxTimerId(timer_id); | ||
|  | 
 | ||
|  |   // Check parameters
 | ||
|  |   if ((timer == NULL) || (timer->id != osRtxIdTimer) || (ticks == 0U)) { | ||
|  |     EvrRtxTimerError(timer, (int32_t)osErrorParameter); | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return osErrorParameter; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (timer->state == osRtxTimerRunning) { | ||
|  |     TimerRemove(timer); | ||
|  |   } else { | ||
|  |     if (osRtxInfo.timer.tick == NULL) { | ||
|  |       EvrRtxTimerError(timer, (int32_t)osErrorResource); | ||
|  |       //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |       return osErrorResource; | ||
|  |     } else { | ||
|  |       timer->state = osRtxTimerRunning; | ||
|  |       timer->load  = ticks; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   TimerInsert(timer, ticks); | ||
|  | 
 | ||
|  |   EvrRtxTimerStarted(timer); | ||
|  | 
 | ||
|  |   return osOK; | ||
|  | } | ||
|  | 
 | ||
|  | /// Stop a timer.
 | ||
|  | /// \note API identical to osTimerStop
 | ||
|  | static osStatus_t svcRtxTimerStop (osTimerId_t timer_id) { | ||
|  |   os_timer_t *timer = osRtxTimerId(timer_id); | ||
|  | 
 | ||
|  |   // Check parameters
 | ||
|  |   if ((timer == NULL) || (timer->id != osRtxIdTimer)) { | ||
|  |     EvrRtxTimerError(timer, (int32_t)osErrorParameter); | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return osErrorParameter; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Check object state
 | ||
|  |   if (timer->state != osRtxTimerRunning) { | ||
|  |     EvrRtxTimerError(timer, (int32_t)osErrorResource); | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return osErrorResource; | ||
|  |   } | ||
|  | 
 | ||
|  |   timer->state = osRtxTimerStopped; | ||
|  | 
 | ||
|  |   TimerRemove(timer); | ||
|  | 
 | ||
|  |   EvrRtxTimerStopped(timer); | ||
|  | 
 | ||
|  |   return osOK; | ||
|  | } | ||
|  | 
 | ||
|  | /// Check if a timer is running.
 | ||
|  | /// \note API identical to osTimerIsRunning
 | ||
|  | static uint32_t svcRtxTimerIsRunning (osTimerId_t timer_id) { | ||
|  |   os_timer_t *timer = osRtxTimerId(timer_id); | ||
|  |   uint32_t    is_running; | ||
|  | 
 | ||
|  |   // Check parameters
 | ||
|  |   if ((timer == NULL) || (timer->id != osRtxIdTimer)) { | ||
|  |     EvrRtxTimerIsRunning(timer, 0U); | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return 0U; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (timer->state == osRtxTimerRunning) { | ||
|  |     EvrRtxTimerIsRunning(timer, 1U); | ||
|  |     is_running = 1U; | ||
|  |   } else { | ||
|  |     EvrRtxTimerIsRunning(timer, 0U); | ||
|  |     is_running = 0; | ||
|  |   } | ||
|  | 
 | ||
|  |   return is_running; | ||
|  | } | ||
|  | 
 | ||
|  | /// Delete a timer.
 | ||
|  | /// \note API identical to osTimerDelete
 | ||
|  | static osStatus_t svcRtxTimerDelete (osTimerId_t timer_id) { | ||
|  |   os_timer_t *timer = osRtxTimerId(timer_id); | ||
|  | 
 | ||
|  |   // Check parameters
 | ||
|  |   if ((timer == NULL) || (timer->id != osRtxIdTimer)) { | ||
|  |     EvrRtxTimerError(timer, (int32_t)osErrorParameter); | ||
|  |     //lint -e{904} "Return statement before end of function" [MISRA Note 1]
 | ||
|  |     return osErrorParameter; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (timer->state == osRtxTimerRunning) { | ||
|  |     TimerRemove(timer); | ||
|  |   } | ||
|  | 
 | ||
|  |   // Mark object as inactive and invalid
 | ||
|  |   timer->state = osRtxTimerInactive; | ||
|  |   timer->id    = osRtxIdInvalid; | ||
|  | 
 | ||
|  |   // Free object memory
 | ||
|  |   if ((timer->flags & osRtxFlagSystemObject) != 0U) { | ||
|  |     if (osRtxInfo.mpi.timer != NULL) { | ||
|  |       (void)osRtxMemoryPoolFree(osRtxInfo.mpi.timer, timer); | ||
|  |     } else { | ||
|  |       (void)osRtxMemoryFree(osRtxInfo.mem.common, timer); | ||
|  |     } | ||
|  | #if (defined(OS_OBJ_MEM_USAGE) && (OS_OBJ_MEM_USAGE != 0))
 | ||
|  |     osRtxTimerMemUsage.cnt_free++; | ||
|  | #endif
 | ||
|  |   } | ||
|  | 
 | ||
|  |   EvrRtxTimerDestroyed(timer); | ||
|  | 
 | ||
|  |   return osOK; | ||
|  | } | ||
|  | 
 | ||
|  | //  Service Calls definitions
 | ||
|  | //lint ++flb "Library Begin" [MISRA Note 11]
 | ||
|  | SVC0_4(TimerNew,       osTimerId_t,  osTimerFunc_t, osTimerType_t, void *, const osTimerAttr_t *) | ||
|  | SVC0_1(TimerGetName,   const char *, osTimerId_t) | ||
|  | SVC0_2(TimerStart,     osStatus_t,   osTimerId_t, uint32_t) | ||
|  | SVC0_1(TimerStop,      osStatus_t,   osTimerId_t) | ||
|  | SVC0_1(TimerIsRunning, uint32_t,     osTimerId_t) | ||
|  | SVC0_1(TimerDelete,    osStatus_t,   osTimerId_t) | ||
|  | //lint --flb "Library End"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | //  ==== Public API ====
 | ||
|  | 
 | ||
|  | /// Create and Initialize a timer.
 | ||
|  | osTimerId_t osTimerNew (osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr) { | ||
|  |   osTimerId_t timer_id; | ||
|  | 
 | ||
|  |   EvrRtxTimerNew(func, type, argument, attr); | ||
|  |   if (IsIrqMode() || IsIrqMasked()) { | ||
|  |     EvrRtxTimerError(NULL, (int32_t)osErrorISR); | ||
|  |     timer_id = NULL; | ||
|  |   } else { | ||
|  |     timer_id = __svcTimerNew(func, type, argument, attr); | ||
|  |   } | ||
|  |   return timer_id; | ||
|  | } | ||
|  | 
 | ||
|  | /// Get name of a timer.
 | ||
|  | const char *osTimerGetName (osTimerId_t timer_id) { | ||
|  |   const char *name; | ||
|  | 
 | ||
|  |   if (IsIrqMode() || IsIrqMasked()) { | ||
|  |     EvrRtxTimerGetName(timer_id, NULL); | ||
|  |     name = NULL; | ||
|  |   } else { | ||
|  |     name = __svcTimerGetName(timer_id); | ||
|  |   } | ||
|  |   return name; | ||
|  | } | ||
|  | 
 | ||
|  | /// Start or restart a timer.
 | ||
|  | osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks) { | ||
|  |   osStatus_t status; | ||
|  | 
 | ||
|  |   EvrRtxTimerStart(timer_id, ticks); | ||
|  |   if (IsIrqMode() || IsIrqMasked()) { | ||
|  |     EvrRtxTimerError(timer_id, (int32_t)osErrorISR); | ||
|  |     status = osErrorISR; | ||
|  |   } else { | ||
|  |     status = __svcTimerStart(timer_id, ticks); | ||
|  |   } | ||
|  |   return status; | ||
|  | } | ||
|  | 
 | ||
|  | /// Stop a timer.
 | ||
|  | osStatus_t osTimerStop (osTimerId_t timer_id) { | ||
|  |   osStatus_t status; | ||
|  | 
 | ||
|  |   EvrRtxTimerStop(timer_id); | ||
|  |   if (IsIrqMode() || IsIrqMasked()) { | ||
|  |     EvrRtxTimerError(timer_id, (int32_t)osErrorISR); | ||
|  |     status = osErrorISR; | ||
|  |   } else { | ||
|  |     status = __svcTimerStop(timer_id); | ||
|  |   } | ||
|  |   return status; | ||
|  | } | ||
|  | 
 | ||
|  | /// Check if a timer is running.
 | ||
|  | uint32_t osTimerIsRunning (osTimerId_t timer_id) { | ||
|  |   uint32_t is_running; | ||
|  | 
 | ||
|  |   if (IsIrqMode() || IsIrqMasked()) { | ||
|  |     EvrRtxTimerIsRunning(timer_id, 0U); | ||
|  |     is_running = 0U; | ||
|  |   } else { | ||
|  |     is_running = __svcTimerIsRunning(timer_id); | ||
|  |   } | ||
|  |   return is_running; | ||
|  | } | ||
|  | 
 | ||
|  | /// Delete a timer.
 | ||
|  | osStatus_t osTimerDelete (osTimerId_t timer_id) { | ||
|  |   osStatus_t status; | ||
|  | 
 | ||
|  |   EvrRtxTimerDelete(timer_id); | ||
|  |   if (IsIrqMode() || IsIrqMasked()) { | ||
|  |     EvrRtxTimerError(timer_id, (int32_t)osErrorISR); | ||
|  |     status = osErrorISR; | ||
|  |   } else { | ||
|  |     status = __svcTimerDelete(timer_id); | ||
|  |   } | ||
|  |   return status; | ||
|  | } |