FreeRTOS 原理 --- 软件定时器
简介
有一个定时器任务,任务内读队列。启动定时器,会向队列发送消息,定时器任务读到消息后把定时器回调函数等信息作为一个链表项插入链表。当链表有链表项,算出还剩多长时间执行定时器回调函数,这个时间作为定时器任务阻塞时间。所以定时器任务重新运行要么是时间到准备运行定时器回调函数,要么是队列接收到消息。
相关的API
xTimerCreate()
xTimerStart()
xTimerPendFunctionCallFromISR()
流程
创建定时器,调用 xTimerCreate(),如果没有创建队列 xTimerQueue,则创建;
启动定时器,调用 xTimerStart(),把xMessage写入队列 xTimerQueue
xMessage.xMessageID = tmrCOMMAND_START; xMessage.u.xTimerParameters.xMessageValue = xTaskGetTickCount(); xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer; // 定时器描述符
启动调度器时,如果有使用FreeRTOS提供的定时器功能,则创建一个任务给定时器用
void vTaskStartScheduler( void ) { ... #if ( configUSE_TIMERS == 1 ) { if( xReturn == pdPASS ) { xReturn = xTimerCreateTimerTask(); } else { mtCOVERAGE_TEST_MARKER(); } } ... }
任务对应的执行函数是 prvTimerTask()
static void prvTimerTask( void *pvParameters ) { TickType_t xNextExpireTime; BaseType_t xListWasEmpty; /* Just to avoid compiler warnings. */ ( void ) pvParameters; for( ;; ) { /* Query the timers list to see if it contains any timers, and if so, obtain the time at which the next timer will expire. */ xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); // 刚开始,定时器链表为空,返回值为0,xListWasEmpty为true /* If a timer has expired, process it. Otherwise, block this task until either a timer does expire, or a command is received. */ prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); // 定时器回调函数在这里执行,算出阻塞时间(链表有链表项,即有要处理的定时器回调函数,阻塞到执行这个定时器回调函数;链表无链表项,无限期阻塞,除非队列收到消息),阻塞在这里 /* Empty the command queue. */ prvProcessReceivedCommands(); // 读队列消息,没消息不阻塞;启动定时器,会发送一个消息,这个函数内处理消息添加到定时器链表 } }
static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ) { TickType_t xTimeNow; BaseType_t xTimerListsWereSwitched; vTaskSuspendAll(); { xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); if( xTimerListsWereSwitched == pdFALSE ) { /* The tick count has not overflowed, has the timer expired? */ if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) // 有要处理的定时器回调函数,并且时间已到 { ( void ) xTaskResumeAll(); prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); // 如果是周期性回调函数,把定时器再加到链表;执行定时器回调函数 } else { /* The tick count has not overflowed, and the next expire time has not been reached yet. This task should therefore block to wait for the next expire time or a command to be received - whichever comes first. The following line cannot be reached unless xNextExpireTime > xTimeNow, except in the case when the current timer list is empty. */ if( xListWasEmpty != pdFALSE ) { /* The current timer list is empty - is the overflow list also empty? */ xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList ); } vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty ); // 没要处理的定时器回到函数,或者时间还没到,阻塞在此 if( xTaskResumeAll() == pdFALSE ) { /* Yield to wait for either a command to arrive, or the block time to expire. If a command arrived between the critical section being exited and this yield then the yield will not cause the task to block. */ portYIELD_WITHIN_API(); } else { mtCOVERAGE_TEST_MARKER(); } } } else { ( void ) xTaskResumeAll(); } } }
void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) // xWaitIndefinitely 为true表示链表空 { Queue_t * const pxQueue = ( Queue_t * ) xQueue; prvLockQueue( pxQueue ); if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ) // 队列空 { /* There is nothing in the queue, block for the specified period. */ vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely ); } else // 队列非空,不阻塞,在后面会读出队列消息处理 { mtCOVERAGE_TEST_MARKER(); } prvUnlockQueue( pxQueue ); }
void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) { configASSERT( pxEventList ); vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); if( xWaitIndefinitely != pdFALSE ) // 链表空,无限期阻塞;否则根据下一个定时器回调函数运行时刻算出阻塞时间 { xTicksToWait = portMAX_DELAY; } traceTASK_DELAY_UNTIL( ( xTickCount + xTicksToWait ) ); prvAddCurrentTaskToDelayedList( xTicksToWait, xWaitIndefinitely ); }