2.2 创建Daemon任务
软件定时器任务(Daemon任务)的创建是通过xTaskCreate方法来创建,在创建守护任务之前,还要先通过prvCheckForValidListAndQueue函数创建两个列表和一个消息队列:
/* 创建列表与消息队列 */ prvCheckForValidListAndQueue();
if( xTimerQueue != NULL ){ #if( configSUPPORT_STATIC_ALLOCATION == 1 ) ...略去部分代码 #else { /* 创建软件定时器任务(守护任务) */ xReturn = xTaskCreate( prvTimerTask, "Tmr Svc", configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, &xTimerTaskHandle ); } #endif /* configSUPPORT_STATIC_ALLOCATION */} else{ mtCOVERAGE_TEST_MARKER();}
configASSERT( xReturn ); return xReturn;}
创建列表与消息队列的具体函数内容如下:
2.3 创建列表与消息队列由于系统节拍采用32位变量进行计数,总有一天会溢出,所以软件定时器使用了两个列表:
当前定时器列表pxCurrentTimerList :系统新创建并激活的定时器都会以超时时间升序的方式插入到pxCurrentTimerList列表中。系统在定时器任务中扫描pxCurrentTimerList中的第一个定时器,看是否已超时,若已经超时了则调用软件定时器回调函数,否则将定时器任务挂起。
溢出定时器列表pxOverflowTimerList:在软件定时器溢出的时候使用,作用与pxCurrentTimerList一致。
定时器列表会按照唤醒时间从早到晚挂接在当前定时器列表中,唤醒时间如果溢出了就挂接在溢出定时器列表中。当系统节拍溢出之后,两个列表的功能会进行交换,即当前列表变为溢出列表,溢出列表变为当前列表。
此外,FreeRTOS的软件定时器还使用了一个消息队列xTimerQueue,利用“定时器命令队列”向软件定时器任务发送一些命令,任务在接收到命令就会去处理命令对应的程序,比如启动定时器,停止定时器,复位、删除、改变周期等。
假如定时器任务处于阻塞状态,我们又需要马上再添加一个软件定时器的话,就是采用这种消息队列命令的方式进行添加,才能唤醒处于等待状态的定时器任务,并且在任务中将新添加的软件定时器添加到软件定时器列表中
(注:事件标志组在中断中设置事件标志,实际也是通过队列发送消息给软件定时器任务来执行)
#if( configSUPPORT_STATIC_ALLOCATION == 1 ) ...略去部分代码 #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();}
既然消息队列是用来处理软件定时器的一些操作指令的,那这些在哪里呢?其实就是软件定时器的一些API函数,如下。