天天看点

3. 软件定时器组

定时器组

// configTIMER_TASK_PRIORITY 	定时器任务的任务优先级,可以为 0~( configMAX_PRIORITIES-1),越高越及时响应
// configTIMER_QUEUE_LENGTH		用来设置定时器命令队列的队列长度。
// configTIMER_TASK_STACK_DEPTH	用来设置定时器任务堆栈大小,单位为字,不是字节!
           

定时器组的时基基于系统时钟节拍实现的,

定时器的实现不基于任何硬件的定时器(不需要使能读寄存器),并且定时器可以创建很多个

硬件定时器是在定时器中断中实现需要的功能

软件定时器,在创建定时器时指定定时器的回调函数,在回调函数中实现对应的功能(回调函数类似中断处理函数)

定时器创建后,到达时间触发回调函数

定时器的回调函数是在定时器服务函数中执行,所以回调函数中一定不可以调用阻塞任务的函数

RTOS通过 prvTimerTask(也叫守护任务Daemon) 任务管理定时器

启动调度器后自动创建该任务,执行期间检查用户启动的 时间周期溢出的定时器,然后调用回调函数

单次模式:用户创建了定时器并启动后,到达定时时间定时器将不再继续执行

周期模式:即使到达了定时时间,会按照设置的时间周期重复执行

运作机制

在创建定时器时会分配一块内存空间

创建并启动软件定时器时,RTOS根据当前系统时间及用户设置的定时时间确定定时器唤醒时间,将定时器控制块挂入定时器列表

RTOS使用两个定时器列表维护定时器,

pxCurrentTimerList和pxOverflowTimerList两个指针在初始化时分别指向xActiveTimerList1和xActiveTimerList2

PRIVILEGED_DATA static List_t xActiveTimerList1;
PRIVILEGED_DATA static List_t xActiveTimerList2;
PRIVILEGED_DATA static List_t *pxCurrentTimerList;	// 系统时间没有溢出,用于管理定时器
PRIVILEGED_DATA static List_t *pxOverflowTimerList;	// 系统时间溢出后,用于管理定时器
           

pxCurrentTimerList

系统新创建并激活的定时器以超时时间升序的方式插入pxCurrentTimerList列表中。

系统在定时器任务中扫描pxCurrentTimerList中的第一个定时器,查看是否超时,超时则调用回调函数。

如果没有超时,则将定时器任务挂起(升序排列,第一个最先溢出,没有溢出,剩余的肯定没有溢出)

定时器可使用消息队列进行通信,利用定时器命令序列,向软件定时器任务发送命令,

任务在接收到命令后处理命令对应的程序(重启定时器,停止定时器)

如果定时器任务处于阻塞态,需要添加软件定时器,使用消息队列命令的方式进行添加(向定时器任务发送命令)

唤醒处于等待状态的定时器任务,将新添加的定时器添加到软件定时器列表中

在定时器启动函数中,RTOS采用队列的方式发送消息给软定时器任务,任务被唤醒从而执行接收的命令

xTimerNow用于记录当前时间片

xTicksToWait用于记录定时器溢出时间

Timer1:xTimerNow 0创建,定时200,溢出时间200

Timer2:xTimerNow 20创建,定时100,溢出时间120

Timer3:xTimerNow 40创建,定时50,溢出时间90

升序排列,最先溢出的排在列表第一个,第一个不溢出则其余的定时器都不会溢出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-beepyHbz-1590904170891)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1587812226091.png)]

每一次硬件中断,xTimeNow都会自增

定时器任务运行时自动获取下一个要唤醒的定时器,比较当前系统时间xTimeNow与xTicksToWait,出现超时情况就会调用回调函数,否则将定时器任务挂起,直到下一个要唤醒的定时器时间到来,或者接收到命令消息

注意

回调函数务必快进快出,不允许使用导致定时器任务挂起或阻塞的api函数,也不允许出现死循环

定时器使用系统的一个队列和任务资源,优先级默认为 configTIMER_TASK_PRIORITY,为更好响应任务优先级应是任务中最高

对于单次定时器,定时器超时执行回调函数后,系统自动删除软件定时器并回收资源

定时器是在定时器任务,或者说 定时器守护任务 中实现

关于定时器的API函数都是通过 定时器命令队列 的队列给定时器守护任务发送命令

应用程序通过API函数,向定时器任务发送命令序列,定时器接收命令序列后悔处理命令

定时器控制块

typedef struct tmrTimerControl 
{
    const char *pcTimerName;                // 定时器名称
    ListItem_t xTimerListItem;              // 定时器列表项,用于插入定时器列表
    TickType_t xTimerPeriodInTicks;         // 定时器周期
    UBaseType_t uxAutoReload;               // 模式,pdFalse为单词模式
    void *pvTimerID;                        // 软件定时器的ID,回调函数中根据ID判定定时器(可多个定时器调一个回调函数)
    TimerCallbackFunction_t pxCallbackFunction; // 回调函数的入口
    #if( configUSE_TRACE_FACILITY == 1 )
    	UBaseType_t uxTimerNumber;
    #endif

    #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
        uint8_t ucStaticallyAllocated;		// 定时器使用内存,删除需要释放
    #endif
} xTIMER;
           

API函数

创建

软件定时器在创建成功后处于休眠状态,需要使用相关API函数将其激活(类似创建任务)

定时器消息队列

typedef struct tmrTimerQueueMessage
{
   BaseType_t xMessageID;								// 发送到定时器的消息ID
   union
   {
   	TimerParameter_t xTimerParameters;

   	#if ( INCLUDE_xTimerPendFunctionCall == 1 )
   		CallbackParameters_t xCallbackParameters;	// 定时器回调函数
   	#endif 
   } u;
} DaemonTaskMessage_t;

typedef struct tmrTimerControl
{
   const char *pcTimerName;		// 内核不使用,仅用于调试;
   ListItem_t xTimerListItem;		// 时间列表项
   TickType_t xTimerPeriodInTicks;	// 
   UBaseType_t uxAutoReload;		//
   void *pvTimerID;				//
   TimerCallbackFunction_t pxCallbackFunction;	/*<< The function that will be called when the timer expires. */
   #if( configUSE_TRACE_FACILITY == 1 )
   	UBaseType_t uxTimerNumber;		/*<< An ID assigned by trace tools such as FreeRTOS+Trace */
   #endif

   #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
   	uint8_t ucStaticallyAllocated; /*<< Set to pdTRUE if the timer was created statically so no attempt is made to free the memory again if the timer is later deleted. */
   #endif
} xTIMER;

typedef xTIMER Timer_t;
typedef struct tmrTimerParameters
{
   TickType_t			xMessageValue;		// 更改周期时用的可选值
   Timer_t *			pxTimer;			//	
} TimerParameter_t;
           
TimerHandle_t xTimerCreate(	const char * const pcTimerName,             // 定时器名称
                           	const TickType_t xTimerPeriodInTicks, 		// 定时器周期
                           	const UBaseType_t uxAutoReload,             // 定时器模式
                           	void * const pvTimerID,                     // 定时器ID
                           	TimerCallbackFunction_t pxCallbackFunction )// 定时器回调函数
{
    Timer_t *pxNewTimer;											// 临时定时器控制块
    
    pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) );	// 为定时器控制块申请一块内存 
    if ( pxNewTimer != NULL ) 										// 内存申请成功则指针不为空
    {
        /* 内存申请成功,进行初始化软件定时器 */
        prvInitialiseNewTimer(	pcTimerName,
                              	xTimerPeriodInTicks,
                              	uxAutoReload,                      /* pdTRUE为周期性软件定时器 */
                              	pvTimerID,
                              	pxCallbackFunction,
                              	pxNewTimer );						// 初始化这个临时控制块的成员,最终返回控制块指针,也就是句柄
        
        #if( configSUPPORT_STATIC_ALLOCATION == 1 )
        {
              pxNewTimer->ucStaticallyAllocated = pdFALSE;
        }
        #endif
   }
   return pxNewTimer;
}
// 用于初始化定时器控制块,定时器 挂载在两个列表下,pxCurrentTimerList 和 pxOverflowTimerList
static void prvInitialiseNewTimer(	const char * const pcTimerName,				// 定时器名称
                                  	const TickType_t xTimerPeriodInTicks,		// 定时器周期
                                  	const UBaseType_t uxAutoReload,				// 定时器模式
                                  	void * const pvTimerID,						// 定时器ID
                                  	TimerCallbackFunction_t pxCallbackFunction,	// 定时器回调函数
                                  	Timer_t *pxNewTimer )						// 定时器控制块
{
    configASSERT( ( xTimerPeriodInTicks > 0 ) );				// 断言,判断定时器的周期是否大于 0
    if ( pxNewTimer != NULL )
    {
        prvCheckForValidListAndQueue();							// 系统初始化软件定时器列表,创建定时器消息队列
        														// 所有的API函数都是消息队列发送命令序列
        /* 初始化软件定时信息,这些信息保存在软件定时器控制块中 */
        pxNewTimer->pcTimerName = pcTimerName;					// 定时器名称
        pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks;	// 定时器周期
        pxNewTimer->uxAutoReload = uxAutoReload;				// 周期模式
        pxNewTimer->pvTimerID = pvTimerID;						// 定时器ID
        pxNewTimer->pxCallbackFunction = pxCallbackFunction;	// 定时器回调函数

        vListInitialiseItem( &( pxNewTimer->xTimerListItem ) );	// pxNewTimer->xTimerListItem 记载定时器挂在哪个列表下,
        traceTIMER_CREATE( pxNewTimer );
   }
}

static void prvCheckForValidListAndQueue( void )				// 初始化定时器列表,创建定时器命令队列
{
	taskENTER_CRITICAL();
	{
		if( xTimerQueue == NULL )
		{
			vListInitialise( &xActiveTimerList1 );				// 初始化 pxCurrentTimerList 列表
			vListInitialise( &xActiveTimerList2 );				// 初始化 pxOverflowTimerList 列表
			pxCurrentTimerList = &xActiveTimerList1;
			pxOverflowTimerList = &xActiveTimerList2;

			#if( configSUPPORT_STATIC_ALLOCATION == 1 )
			{
				static StaticQueue_t xStaticTimerQueue; 		// 静态定时器命令队列分配空间
				static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]																			// 定义数组,大小为 命令队列深度 * 单个命令大小
				xTimerQueue = xQueueCreateStatic(	( UBaseType_t ) configTIMER_QUEUE_LENGTH, 
                                                 	( UBaseType_t ) sizeof( DaemonTaskMessage_t ), 
                                                 	&( ucStaticTimerQueueStorage[ 0 ] ), 
                                                 	&xStaticTimerQueue );	// 静态 创建命令队列
			}
			#else
			{
				xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, 
                                           	sizeof( DaemonTaskMessage_t ) );// 动态创建 定时器命令队列
			}
			#endif

			#if ( configQUEUE_REGISTRY_SIZE > 0 )
			{
				if( xTimerQueue != NULL )
				{
					vQueueAddToRegistry( xTimerQueue, "TmrQ" );
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			#endif /* configQUEUE_REGISTRY_SIZE */
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	taskEXIT_CRITICAL();
}
           
static TimerHandle_t Swtmr1_Handle =NULL; /* 软件定时器句柄 */
/* 周期模式的软件定时器 1,定时器周期 1000(tick),创建定时器*/
Swtmr1_Handle=xTimerCreate(	(const char*)"AutoReloadTimer",	/* 定时器名称 */
							(TickType_t)1000,				/* 定时器周期 1000(tick) */
							(UBaseType_t)pdTRUE,			/* 周期模式 */
							(void* )1,						/* 为每个计时器分配一个索引的唯一 ID */
							(TimerCallbackFunction_t)Swtmr1_Callback); /* 回调函数 */
static void Swtmr1_Callback(void* parameter)
{
	/* 软件定时器的回调函数,用户自己实现 */
}
           

启动

在系统开始运行的时候,系统会自动创建定时器任务,如果没有运行中的定时器,则定时器任务进入阻塞状态。

定时器创建后默认处于休眠状态,需要API函数启动定时器

所谓启动函数是通过 定时器命令队列 向定时器任务发送启动命令,定时器任务获得命令后解除阻塞,执行启动软件定时器命令

定时器任务因为没有没有收到消息而一直阻塞,直到消息队列中有消息,定时器任务被唤醒(最高优先级任务)

唤醒后定时器接收命令并处理命令,处理完毕后再次因为没有消息而阻塞

#define xTimerStart( xTimer, xTicksToWait ) \
   	xTimerGenericCommand( (xTimer),tmrCOMMAND_START,( xTaskGetTickCount() ),NULL,( xTicksToWait ) )
#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken )\
   	xTimerGenericCommand( (xTimer), tmrCOMMAND_START_FROM_ISR,( xTaskGetTickCountFromISR() ),( pxHigherPriorityTaskWoken ), 0U )
// xTimer 为定时器句柄
// tmrCOMMAND_START 为定时器启动命令序列
// xTaskGetTickCount 用于获取当前系统时间
// pxHigherPriorityTaskWoken 仅在用于中断函数中才不为null
// xTimerGenericCommand 类似队列,可指定发送的阻塞时间
           
BaseType_t xTimerGenericCommand(TimerHandle_t xTimer,							// 定时器控制块
   							const BaseType_t xCommandID,					// 定时器命令
   							const TickType_t xOptionalValue,				// 当前系统时间
   							BaseType_t * const pxHigherPriorityTaskWoken,	// 仅用于中断
   							const TickType_t xTicksToWait )					// 最大阻塞时间
{	// 向定时器任务发送命令
   BaseType_t xReturn = pdFAIL;
   DaemonTaskMessage_t xMessage;
   
   configASSERT( xTimer );
   
   /* 发送命令给定时器任务 */
   if ( xTimerQueue != NULL )				// 定时器 命令队列 不为空,为定时器命令队列分配空间成功
   {
   	xMessage.xMessageID = xCommandID;	// 定时器命令 结构体初始化
   	xMessage.u.xTimerParameters.xMessageValue = xOptionalValue;	// 记录当前系统时间
   	xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer;	// 获取定时器控制块
   	
   	if ( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) 				// 命令是任务而不是中断发送
   	{
   		/* 如果调度器已经运行了,就根据用户指定超时时间发送 */
   		if ( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) 
           {
   			xReturn = xQueueSendToBack( xTimerQueue,&xMessage,xTicksToWait );	// 向消息队列发送消息
   		}
   		else
   		{
   			/* 如果调度器还未运行,发送就行了,不需要阻塞 */
   			xReturn = xQueueSendToBack( xTimerQueue,&xMessage,tmrNO_DELAY );	// 未运行无需阻塞的向消息队列发送消息(超时无效)
   		}
   	}
   	/* 命令是在中断中发出的 */
   	else 
   	{
   		/* 调用从中断向 命令队列 发送消息的函数 */
   		xReturn = xQueueSendToBackFromISR( xTimerQueue,&xMessage,pxHigherPriorityTaskWoken );
   	}
   	
   	traceTIMER_COMMAND_SEND( xTimer,xCommandID,xOptionalValue,xReturn );
   } 
   else 
   {
   	mtCOVERAGE_TEST_MARKER();
   }
   
   return xReturn;
}
           

停止

本质是任务向定时器任务发送停止命令,唤醒软件定时器任务将定时器停止

BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xBlockTime );
           

定时器任务

定时器任务是在系统开始调度,也就是调度器开始的时候被创建的

void vTaskStartScheduler( void )
{
	#if ( configUSE_TIMERS == 1 )
	{
		if ( xReturn == pdPASS )
		{
			xReturn = xTimerCreateTimerTask();		// 创建定时器任务
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_TIMERS */
}
// 创建定时器任务实现
BaseType_t xTimerCreateTimerTask( void )
{
	BaseType_t xReturn = pdFAIL;
	
	prvCheckForValidListAndQueue();		// 初始化定时器列表,创建定时器 命令队列
	
	if ( xTimerQueue != NULL ) 			// 定时器 命令队列 创建成功
	{
		#if( configSUPPORT_STATIC_ALLOCATION == 1 ) 						// 静态创建任务
		{
			StaticTask_t *pxTimerTaskTCBBuffer = NULL;
			StackType_t *pxTimerTaskStackBuffer = NULL;
			uint32_t ulTimerTaskStackSize;
			
			vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer,			// 设置定时器任务的堆栈大小,任务控制块
											&pxTimerTaskStackBuffer,		// 静态分配空间
											&ulTimerTaskStackSize );		// 堆栈单位,字
			// 静态创建 定时器任务
			xTimerTaskHandle = xTaskCreateStatic(prvTimerTask,				// 任务函数名称
												 "Tmr Svc",					// 任务名
												 ulTimerTaskStackSize,		// 任务堆栈单位
												 NULL,						// 任务函数参数
												 ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,	// 任务优先级
												 pxTimerTaskStackBuffer,	// 分配给任务的静态空间,是个数组
												 pxTimerTaskTCBBuffer );	// 定时器任务 控制块,静态创建任务
			if ( xTimerTaskHandle != NULL )									// 定时器任务 创建失败
			{
				xReturn = pdPASS;
			}
		}
		#else /* 动态创建 定时器任务 */
		{
			xReturn = xTaskCreate( prvTimerTask,							// 任务函数名称
								   "Tmr Svc",								// 任务名称,用于调试
								   configTIMER_TASK_STACK_DEPTH,			// 任务堆栈大小
								   NULL,									// 任务函数参数
								   ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,	// 任务优先级
								   &xTimerTaskHandle );						// 定时器任务 句柄
		}
		#endif
	}
	else 
	{
		mtCOVERAGE_TEST_MARKER();
	}
	
	configASSERT( xReturn );
	return xReturn;
}

static void prvTimerTask( void *pvParameters )						// 定时器任务 函数实现
{
	TickType_t xNextExpireTime;
	BaseType_t xListWasEmpty;
	( void ) pvParameters;
	for ( ;; ) 
	{
		xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );	// 获取下一溢出定时器时间,Timer由俩列表维护,升序,第一个先溢出
		prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );// 立即处理定时器溢出任务,或任务阻塞到下一定时器溢出时间处理
		prvProcessReceivedCommands();							// 定时器命令队列中读取命令,任务通过命令唤醒定时器任务,或者溢出唤醒
	}
}

static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty )	// 返回值下一到期定时器时间,挂载列表pxCurrentTimerList否
{
	TickType_t xNextExpireTime;
	*pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList );				// pxCurrentTimerList 列表是否为空,
	if( *pxListWasEmpty == pdFALSE )										// pxCurrentTimerList 不为空,获取第一个定时器控制块
	{	// 系统时间未溢出,定时器都在 pxCurrentTimerList 下;系统时间溢出,定时器都在 pxOverflowTimerList 下
		xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );	// 第一个定时器时间最先到达
	}
	else																 	// pxCurrentTimerList 中没有,都在 pxOverflowTimerList
	{
		xNextExpireTime = ( TickType_t ) 0U;								// 设置为0则用不溢出
	}
	return xNextExpireTime;
}

// 下一到期时间,pxCurrentTimerList 中是否有活动定时器
static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty )	
{
	TickType_t xTimeNow;
	BaseType_t xTimerListsWereSwitched;

	vTaskSuspendAll();												// 挂起所有任务,避免定时器列表被影响
	{
		xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );	// 获取当前时钟节拍,判定系统时间是否溢出(溢出要使用溢出列表)
		if( xTimerListsWereSwitched == pdFALSE )					// 当前系统时钟节拍没有溢出(溢出使用溢出列表)
		{
			if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) )	// 溢出列表无定时器,下一溢出时间比当前时间小
			{															// 当前定时器在 pxCurrentTimerList
				( void ) xTaskResumeAll();								// 对定时器列表操作完毕,恢复调度器
				prvProcessExpiredTimer( xNextExpireTime, xTimeNow );	// 对于周期模式的定时器,更新下一次溢出时间,插入列表
			}
			else
			{
				if( xListWasEmpty != pdFALSE )							// pxCurrentTimerList为空,系统时间溢出了
				{
					xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList );	// pxOverflowTimerList是否有溢出定时器
				}
				// 定时器时间还没有到,当前任务挂起,阻塞直到定时器到期或定时器收到命令(接收消息命令),阻塞时间为到溢出的时间
				vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty );

				if( xTaskResumeAll() == pdFALSE )						// 恢复调度器
				{
					portYIELD_WITHIN_API();								// 任务切换
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		else
		{
			( void ) xTaskResumeAll();
		}
	}
}
// 获取当前系统时间,判定定时器在哪个列表中
static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched )
{
	TickType_t xTimeNow;
	PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; 

	xTimeNow = xTaskGetTickCount();		// 获取当前系统时间

	if( xTimeNow < xLastTime )			// 判定系统时间是否溢出,当前节拍比上一节拍小,则溢出
	{
		prvSwitchTimerLists();			// 发生溢出系统要处理未溢出列表上所有定时器,切换所有定时器到定时器溢出列表,并且标记定时器列表切换
										// 也就是pxTimerListsWereSwitched标识为pdTRUE
		*pxTimerListsWereSwitched = pdTRUE;
	}
	else
	{
		*pxTimerListsWereSwitched = pdFALSE;	// 系统时间没有溢出则不作处理
	}

	xLastTime = xTimeNow;				// 更新xLastTime的值

	return xTimeNow;					// 返回当前系统时间,查询 pxCurrentTimerList 列表,使用 pxCurrentTimerList 为pdFALSE
}

static void	prvProcessReceivedCommands( void )	// 从命令队列 接收消息
{
	DaemonTaskMessage_t xMessage;
	Timer_t *pxTimer;
	BaseType_t xTimerListsWereSwitched, xResult;
	TickType_t xTimeNow;
	// 从消息队列中接收消息,
	while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL )		// 接收消息,成功接收到消息
	{
		if( xMessage.xMessageID >= ( BaseType_t ) 0 )							// 命令有效
		{
			pxTimer = xMessage.u.xTimerParameters.pxTimer;						// 
																				// 定时器是否在列表中
			if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) 
			{
				( void ) uxListRemove( &( pxTimer->xTimerListItem ) );			// 将定时器从列表中移出
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();										// 定时器不在列表中
			}

			traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue );

			xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );			// 获取当前系统时间,查询系统时间是否溢出

			switch( xMessage.xMessageID )										// 根据命令ID进行处理
			{
				case tmrCOMMAND_START :
			    case tmrCOMMAND_START_FROM_ISR :
			    case tmrCOMMAND_RESET :
			    case tmrCOMMAND_RESET_FROM_ISR :
				case tmrCOMMAND_START_DONT_TRACE :								// 开始或重启命令,求溢出时间插入定时器列表
					if( prvInsertTimerInActiveList( pxTimer,  xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE )
					{
						pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );// 定时器已溢出,调用回调函数
						traceTIMER_EXPIRED( pxTimer );

						if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE )	// 如果是周期模式
						{														// 发送命令重新启动定时器
							xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY );
							configASSERT( xResult );
							( void ) xResult;
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
					break;

				case tmrCOMMAND_STOP :
				case tmrCOMMAND_STOP_FROM_ISR :
					// 停止命令则将定时器从列表中移除,之前已进行移除操作
					break;
					// 更新定时器配置,就是改变周期
				case tmrCOMMAND_CHANGE_PERIOD :
				case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR :
					// 更新定时器周期消息,伴随跟新周期的时间
					pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue;
					configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) );
					( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow );		// 从命令发出,到处理命令,
					break;

				case tmrCOMMAND_DELETE :		// 如果是删除命令,从当前列表中删除,前期已有这个操作
					#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
					{
						vPortFree( pxTimer );	// 动态分配释放内存
					}
					#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
					{
						if( pxTimer->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
						{
							vPortFree( pxTimer );
						}
						else					// 静态分配
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif
					break;

				default	:
					/* Don't expect to get here. */
					break;
			}
		}
	}
}
           
static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, 
                                             const TickType_t xNextExpiryTime, 
                                             const TickType_t xTimeNow, 
                                             const TickType_t xCommandTime )
{
	BaseType_t xProcessTimerNow = pdFALSE;

	listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime );
	listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );

	if( xNextExpiryTime <= xTimeNow )	// 下一到期时间 <= 当前时间,定时器到期溢出
	{									// 当前时间 - 更新周期 >= 当前周期
        // ( xTimeNow + pxTimer->xTimerPeriodInTicks ) = xNextExpiryTime
        // pxTimer->xTimerPeriodInTicks = xNextExpiryTime - xTimeNow
        // ( xTimeNow - xCommandTime ) ) >= ( xNextExpiryTime - xTimeNow )
		if( ( ( TickType_t ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks ) 
		{	// 当前时间 >= 上次到期时间 + 命令持续时间,当前已到达时间
			xProcessTimerNow = pdTRUE;	// 立刻执行
		}
		else							// 
		{								// 将定时器添加到系统时间后,定时器管理列表
			vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );
		}
	}
	else								// 下一到期时间 > 当前时间,需要等待
	{									// 当前时间 < 要运行的时间,且 下一到期时间 >= 要运行的时间
		if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) )
		{
			xProcessTimerNow = pdTRUE;	// 立刻运行
		}
		else
		{								// 插入 pxCurrentTimerList 列表
			vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) );
		}
	}

	return xProcessTimerNow;
}
           

删除

#define xTimerDelete( xTimer, xTicksToWait )\
		xTimerGenericCommand( ( xTimer ),tmrCOMMAND_DELETE,0U, NULL, ( xTicksToWait ) )
           

继续阅读