05 Software Timer Management
Software timers are used to schedule the execution of a function at a set time in the future, or periodically with a fixed frequency. The function executed by the software timer is called the software timer’s callback function.
Software timers are implemented by, and are under the control of, the FreeRTOS kernel. They do not require hardware support, and are not related to hardware timers or hardware counters.
Note that, in line with the FreeRTOS philosophy of using innovative design to ensure maximum efficiency, software timers do not use any processing time unless a software timer callback function is actually executing.
To include software timer functionality:
- Build the FreeRTOS source file FreeRTOS/Source/timers.c as part of your project.
- Set configUSE_TIMERS to 1 in FreeRTOSConfig.h.
Software Timer Callback Functions
void ATimerCallback( TimerHandle_t xTimer );
Software timer callback functions execute from start to finish, and exit in the normal way. They should be kept short, and must not enter the Blocked state.
Note: As will be seen, software timer callback functions execute in the context of a task that is created automatically when the FreeRTOS scheduler is started. Therefore, it is essential that software timer callback functions never call FreeRTOS API functions that will result in the calling task entering the Blocked state. It is ok to call functions such as xQueueReceive(), but only if the function’s xTicksToWait parameter (which specifies the function’s block time) is set to 0. It is not ok to call functions such as vTaskDelay(), as calling vTaskDelay() will always place the calling task into the Blocked state.
One-shot and Auto-reload Timers
- One-shot timers: Once started, a one-shot timer will execute its callback function once only. A one-shot timer can be restarted manually, but will not restart itself.
- Auto-reload timers: Once started, an auto-reload timer will re-start itself each time it expires, resulting in periodic execution of its callback function.
Software Timer States
- Dormant A Dormant software timer exists, and can be referenced by its handle, but is not running, so its callback functions will not execute.
- Running A Running software timer will execute its callback function after a time equal to its period has elapsed since the software timer entered the Running state, or since the software timer was last reset.
The xTimerDelete() API function deletes a timer. A timer can be deleted at any time.
The Context of a Software Timer
All software timer callback functions execute in the context of the same RTOS daemon (or ‘timer service’) task.
The daemon task is a standard FreeRTOS task that is created automatically when the scheduler is started. Its priority and stack size are set by the configTIMER_TASK_PRIORITY and configTIMER_TASK_STACK_DEPTH compile time configuration constants respectively. Both constants are defined within FreeRTOSConfig.h.
Software timer callback functions must not call FreeRTOS API functions that will result in the calling task entering the Blocked state, as to do so will result in the daemon task entering the Blocked state.
The Timer Command Queue
Software timer API functions send commands from the calling task to the daemon task on a queue called the ‘timer command queue’.
The timer command queue is a standard FreeRTOS queue that is created automatically when the scheduler is started. The length of the timer command queue is set by the configTIMER_QUEUE_LENGTH compile time configuration constant in FreeRTOSConfig.h.
Daemon Task Scheduling
The daemon task is scheduled like any other FreeRTOS task; it will only process commands, or execute timer callback functions, when it is the highest priority task that is able to run.
In the scenario shown by Figure 42, time passed between Task 1 sending a command to the timer command queue, and the daemon task receiving and processing the command. In the scenario shown by Figure 43, the daemon task had received and processed the command sent to it by Task 1 before Task 1 returned from the function that sent the command.
Commands sent to the timer command queue contain a time stamp. The time stamp is used to account for any time that passes between a command being sent by an application task, and the same command being processed by the daemon task. For example, if a ‘start a timer’ command is sent to start a timer that has a period of 10 ticks, the time stamp is used to ensure the timer being started expires 10 ticks after the command was sent, not 10 ticks after the command was processed by the daemon task.
Creating and Starting a Software Timer
The xTimerCreate() API Function
FreeRTOS V9.0.0 also includes the xTimerCreateStatic() function, which allocates the memory required to create a timer statically at compile time: A software timer must be explicitly created before it can be used.
Software timers are referenced by variables of type TimerHandle_t. xTimerCreate() is used to create a software timer and returns a TimerHandle_t to reference the software timer it creates. Software timers are created in the Dormant state.
Software timers can be created before the scheduler is running, or from a task after the scheduler has been started.
TimerHandle_t xTimerCreate( const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
UBaseType_t uxAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
pcTimerName
A descriptive name for the timer. This is not used by FreeRTOS in any way. It is included purely as a debugging aid. Identifying a timer by a human readable name is much simpler than attempting to identify it by its handle.
xTimerPeriodInTicks
The timer’s period specified in ticks. The pdMS_TO_TICKS() macro can be used to convert a time specified in milliseconds into a time specified in ticks.
uxAutoReload
Set uxAutoReload to pdTRUE to create an auto-reload timer. Set uxAutoReload to pdFALSE to create a one-shot timer.
pvTimerID
Each software timer has an ID value. The ID is a void pointer, and can be used by the application writer for any purpose. The ID is particularly useful when the same callback function is used by more than one software timer, as it can be used to provide timer specific storage. Use of a timer’s ID is demonstrated in an example within this chapter.
pvTimerID sets an initial value for the ID of the task being created
pxCallbackFunction
Software timer callback functions are simply C functions that conform to the prototype shown in Listing 72. The pxCallbackFunction parameter is a pointer to the function (in effect, just the function name) to use as the callback function for the software timer being created.
Returned value
If NULL is returned, then the software timer cannot be created because there is insufficient heap memory available for FreeRTOS to allocate the necessary data structure.
A non-NULL value being returned indicates that the software timer has been created successfully. The returned value is the handle of the created timer.
The xTimerStart() API Function
xTimerStart() is used to start a software timer that is in the Dormant state, or reset (re-start) a software timer that is in the Running state. xTimerStop() is used to stop a software timer that is in the Running state. Stopping a software timer is the same as transitioning the timer into the Dormant state.
xTimerStart() can be called before the scheduler is started, but when this is done, the software timer will not actually start until the time at which the scheduler starts.
Note: Never call xTimerStart() from an interrupt service routine. The interrupt-safe version xTimerStartFromISR() should be used in its place.
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
xTimer
The handle of the software timer being started or reset. The handle will have been returned from the call to xTimerCreate() used to create the software timer.
xTicksToWait
TimerStart() uses the timer command queue to send the ‘start a timer’ command to the daemon task. xTicksToWait specifies the maximum amount of time the calling task should remain in the Blocked state to wait for space to become available on the timer command queue, should the queue already be full.
xTimerStart() will return immediately if xTicksToWait is zero and the timer command queue is already full.
The block time is specified in tick periods, so the absolute time it represents is dependent on the tick frequency. The macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into a time specified in ticks.
If INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h then setting xTicksToWait to portMAX_DELAY will result in the calling task remaining in the Blocked state indefinitely (without a timeout) to wait for space to become available in the timer command queue.
If xTimerStart() is called before the scheduler has been started then the value of xTicksToWait is ignored, and xTimerStart() behaves as if xTicksToWait had been set to zero.
Returned value
There are two possible return values:
- pdPASS pdPASS will be returned only if the ‘start a timer’ command was successfully sent to the timer command queue. If the priority of the daemon task is above the priority of the task that called xTimerStart(), then the scheduler will ensure the start command is processed before xTimerStart() returns. This is because the daemon task will pre-empt the task that called xTimerStart() as soon as there is data in the timer command queue. If a block time was specified (xTicksToWait was not zero), then it is possible the calling task was placed into the Blocked state to wait for space to become available in the timer command queue before the function returned, but data was successfully written to the timer command queue before the block time expired.
- pdFALSE pdFALSE will be returned if the ‘start a timer’ command could not be written to the timer command queue because the queue was already full. If a block time was specified (xTicksToWait was not zero) then the calling task will have been placed into the Blocked state to wait for the daemon task to make room in the timer command queue, but the specified block time expired before that happened.
Example1
The Timer ID
Each software timer has an ID, which is a tag value that can be used by the application writer for any purpose. The ID is stored in a void pointer (void *), so can store an integer value directly, point to any other object, or be used as a function pointer.
An initial value is assigned to the ID when the software timer is created—after which the ID can be updated using the vTimerSetTimerID() API function, and queried using the pvTimerGetTimerID() API function.
Unlike other software timer API functions, vTimerSetTimerID() and pvTimerGetTimerID() access the software timer directly they do not send a command to the timer command queue.
The vTimerSetTimerID() API Function
void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID );
xTimer
The handle of the software timer being updated with a new ID value. The handle will have been returned from the call to xTimerCreate() used to create the software timer.
pvNewID
The value to which the software timer’s ID will be set.
The pvTimerGetTimerID() API Function
void *pvTimerGetTimerID( TimerHandle_t xTimer );
xTimer
The handle of the software timer being queried. The handle will have been returned from the call to xTimerCreate() used to create the software timer.
Returned value
The ID of the software timer being queried.
Example2
example 介紹
這個範例使用函示傳入值來判斷是one-shot timer 還是auto-reload timer的呼叫,接著使用timer ID來記錄timer執行的次數
Changing the Period of a Timer
The xTimerChangePeriod() API Function
If xTimerChangePeriod() is used to change the period of a timer that is already running, then the timer will use the new period value to recalculate its expiry time. The recalculated expiry time is relative to when xTimerChangePeriod() was called, not relative to when the timer was originally started.
If xTimerChangePeriod() is used to change the period of a timer that is in the Dormant state (a timer that is not running), then the timer will calculate an expiry time, and transition to the Running state (the timer will start running).
Note: Never call xTimerChangePeriod() from an interrupt service routine. The interrupt-safe version xTimerChangePeriodFromISR() should be used in its place.
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
TickType_t xNewTimerPeriodInTicks,
TickType_t xTicksToWait );
xTimer
The handle of the software timer being updated with a new period value. The handle will have been returned from the call to xTimerCreate() used to create the software timer.
xTimerPeriodInTicks
The new period for the software timer, specified in ticks. The pdMS_TO_TICKS() macro can be used to convert a time specified in milliseconds into a time specified in ticks.
xTicksToWait
xTimerChangePeriod() uses the timer command queue to send the ‘change period’ command to the daemon task. xTicksToWait specifies the maximum amount of time the calling task should remain in the Blocked state to wait for space to become available on the timer command queue, should the queue already be full.
xTimerChangePeriod() will return immediately if xTicksToWait is zero and the timer command queue is already full.
The macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into a time specified in ticks.
If INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h, then setting xTicksToWait to portMAX_DELAY will result in the calling task remaining in the Blocked state indefinitely (without a timeout) to wait for space to become available in the timer command queue.
If xTimerChangePeriod() is called before the scheduler has been started, then the value of xTicksToWait is ignored, and xTimerChangePeriod() behaves as if xTicksToWait had been set to zero.
Returned value
- pdPASS pdPASS will be returned only if data was successfully sent to the timer command queue. If a block time was specified (xTicksToWait was not zero), then it is possible the calling task was placed into the Blocked state to wait for space to become available in the timer command queue before the function returned, but data was successfully written to the timer command queue before the block time expired.
- pdFALSE pdFALSE will be returned if the ‘change period’ command could not be written to the timer command queue because the queue was already full. If a block time was specified (xTicksToWait was not zero) then the calling task will have been placed into the Blocked state to wait for the daemon task to make room in the queue, but the specified block time expired before that happened.
Resetting a Software Timer
Resetting a software timer means to re-start the timer; the timer’s expiry time is recalculated to be relative to when the timer was reset.
The xTimerReset() API Function
xTimerReset() can also be used to start a timer that is in the Dormant state.
Note: Never call xTimerReset() from an interrupt service routine. The interrupt-safe version xTimerResetFromISR() should be used in its place.
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
xTimer
The handle of the software timer being reset or started. The handle will have been returned from the call to xTimerCreat () used to create the software timer.
xTicksToWait
xTimerChangePeriod() uses the timer command queue to send the ‘reset’ command to the daemon task. xTicksToWait specifies the maximum amount of time the calling task should remain in the Blocked state to wait for space to become available on the timer command queue, should the queue already be full.
xTimerReset() will return immediately if xTicksToWait is zero and the timer command queue is already full.
If INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h then setting xTicksToWait to portMAX_DELAY will result in the calling task remaining in the Blocked state indefinitely (without a timeout) to wait for space to become available in the timer command queue.
Returned value
- pdPASS pdPASS will be returned only if data was successfully sent to the timer command queue. If a block time was specified (xTicksToWait was not zero), then it is possible the calling task was placed into the Blocked state to wait for space to become available in the timer command queue before the function returned, but data was successfully written to the timer command queue before the block time expired.
- pdFALSE pdFALSE will be returned if the ‘reset’ command could not be written to the timer command queue because the queue was already full. If a block time was specified (xTicksToWait was not zero) then the calling task will have been placed into the Blocked state to wait for the daemon task to make room in the queue, but the specified block time expired before that happened.
Example3
example3 介紹
此程式使用Task監控按鈕的狀態,每當按鈕被按下時則使Timer重置並且LED的狀態為ON,Timer的功能為使LED狀態OFF
留言
張貼留言