freertos 系統 運行流程 源碼分析

鏈表插入末尾函數 vListItemEnd

//插入到最後
void vListInsertEnd( xList * const pxList, xListItem * const pxNewListItem )
{
xListItem * pxIndex;
 
pxIndex = pxList->pxIndex;  
 
pxNewListItem->pxNext = pxIndex;                     //指向pxList中鏈表
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
 
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList;
 
( pxList->uxNumberOfItems )++;
}

系統調度

freertos 主要文件函數

croutine.c 和task.c 是兩種任務的組織實現this

croutine.c 各任務共享同一個堆棧,使RAM 的需求進一步縮小,但也正因如此,他的使用受到相對嚴格的限制。spa

通常task.c和task.h完成了全部有關建立,調度,和維護任務的繁重工做。操作系統

list.c 任務鏈表,TCB鏈表等定義.net

queue.c和queue.h是負責處理FreeRTOS的通信的。任務和中斷使用隊列互相發送數據,而且使用信號燈和互斥來發送臨界資源的使用狀況。指針

timers 任務定時器等時間管理code

heap_2.c 注意是內存分配blog

port.c 包含了全部實際硬件相關的代碼接口

FreeRTOSConfig.h文件裏選擇。時鐘速度,堆大小,互斥,和API子集,連同其餘許多選項隊列

int main( void )				 
{
	Sys_InitTask(); //系統初始化 包含時鐘、管腳、系統定時器配置

	xTaskCreate( Task1, ( signed portCHAR *) "Task1", configMINIMAL_STACK_SIZE, NULL, (tskIDLE_PRIORITY+1), NULL); //建立一個任務

	vTaskStartScheduler();  //任務開始運行
	
	return 0;
}

過程:先建立任務,源碼見http://my.oschina.net/u/274829/blog/277855

1,給NewTCB 分配內存 堆棧分配 初始化TCB 初始化任務鏈表事件鏈表 初始化CPU寄存器 任務數量加1,任務添加到任務隊列中,若是調度能夠運行,且任務優先級高就進行調度

開始任務調度

void vTaskStartScheduler( void )
{
portBASE_TYPE xReturn;
 
/* Add the 空閒任務 at the lowest priority. */
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
{
/* 建立空閒任務 須要返回任務句柄的idle任務建立*/
    xReturn = xTaskCreate( prvIdleTask, ( signed char * ) "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle );
}
#else
{
/*不返回idle任務句柄/
    xReturn = xTaskCreate( prvIdleTask, ( signed char * ) "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), NULL ); 
}
#endif /* INCLUDE_xTaskGetIdleTaskHandle */
 
#if ( configUSE_TIMERS == 1 )
{
    if( xReturn == pdPASS )
    {
        xReturn = xTimerCreateTimerTask();  /*調用建立定時器任務的函數,注意這個   不是建立的任務*/ 
    }
}
#endif /* configUSE_TIMERS */
 
if( xReturn == pdPASS )  /*任務建立成功*/ 
{

    portDISABLE_INTERRUPTS();  /*禁止全部中斷*/
 
    xSchedulerRunning = pdTRUE;  /*調度器能夠開始調度*/
    xTickCount = ( portTickType ) 0U; /*運行操做系統的時間滴答數初始化爲0*/
 
    /* 若是configGENERATE_RUN_TIME_STATS定義,下面的宏必須定義配置的定時器/計數器,用來產生運行時間計數器    的時間基準。 */
    portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
 
    /* 特定於硬件,於是在設置計時器滴答便攜式接口。 */
    if( xPortStartScheduler() != pdFALSE )  /*開始實施調度,這個須要在Port.c中移植*/ 
    {
        /* 應該不會到達這裏,若是調度 運行函數不會返回。 */
    }
    else
    {
        /* 若是任務 calls xTaskEndScheduler(). 會到這裏*/
    }
}
else
{
    /* 若是內核不能啓動會到這裏由於沒有足夠的 FreeRTOS heap to create the idle task
    or the timer task. */
    configASSERT( xReturn );
}
}

任務調度

portBASE_TYPE xPortStartScheduler( void )
{
	/* Make PendSV, CallSV and SysTick the same priroity as the kernel. */
	*(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI;
	*(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI;

	/* 啓動時鐘產生時間片  */
	prvSetupTimerInterrupt();
	
	/*初始化 中斷計數值 */
	uxCriticalNesting = 0;

	/* 啓動第一個任務 */
	vPortStartFirstTask();

	/* Should not get here! */
	return 0;
}

啓動時間片 prvSetupTimerInterrupt()

void prvSetupTimerInterrupt( void )
{
	/* Configure SysTick to interrupt at the requested rate. */
	*(portNVIC_SYSTICK_LOAD) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
	*(portNVIC_SYSTICK_CTRL) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE;
}

關中斷源碼

#define portDISABLE_INTERRUPTS()				vPortSetInterruptMask()
__asm void vPortSetInterruptMask( void )
{
	PRESERVE8

	push { r0 }
	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
	msr basepri, r0
	pop { r0 }
	bx r14
}

vPortStartFirstTask() 啓動第一個任務

__asm void vPortStartFirstTask( void )
{
	PRESERVE8

	/* Use the NVIC offset register to locate the stack. */
	ldr r0, =0xE000ED08
	ldr r0, [r0]
	ldr r0, [r0]
	/* Set the msp back to the start of the stack. */
	msr msp, r0
	/* Globally enable interrupts. */
	cpsie i
	/* Call SVC to start the first task. */
	svc 0
	nop
}

任務阻塞掛起

void vTaskDelay( portTickType xTicksToDelay )
{
portTickType xTimeToWake;
signed portBASE_TYPE xAlreadyYielded = pdFALSE;
 
if( xTicksToDelay > ( portTickType ) 0U ) /*延時時間參數不爲0,表示須要延時.不然只是但願作一次調度*/
{
    vTaskSuspendAll(); /*掛起調度器,來建立臨界區*/
    {
    traceTASK_DELAY();
 
/* 從事件列表中移除一個任務,而調度暫停將不會被放置在準備列表或阻止列表中刪除,直到調度被恢復。此任務不能在事件列表中,由於它是目前執行的任務。*/
 

    xTimeToWake = xTickCount + xTicksToDelay; /*轉換成絕對時間:xTimeToWake 喚醒時間*/  /*xTicksToDelay:延時的節拍數*/ 
 
   /*由於調度器在就緒運行隊列鏈表中,再也找不本task的信息,進而調度器也就不會調度本task了 */
   /*把任務從當前運行鏈表中移除出去,而後把它添加到阻塞鏈表裏面*/
    if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( unsigned portBASE_TYPE ) 0 )
       {
            portRESET_READY_PRIORITY(pxCurrentTCB->uxPriority, uxTopReadyPriority );
        }
        prvAddCurrentTaskToDelayedList( xTimeToWake );
    }
    xAlreadyYielded = xTaskResumeAll();
    /*在運行上面臨界區的程序時,可能有任務須要調度,但由於調度器的掛起而沒有被調度,只是給出了登記,而這個xTaskResumeAll函數就是要把放進xPendingReadyList鏈表中的任務節點轉移到真正的就緒鏈表pxReadyTasksLists裏面,若是任務是由於tick缺失或者由於在恢復實際走過的滴答數時有任務須要搶佔CPU,則 xAlreadyYielded 都爲真,從而致使下面不會運行,若是沒有被搶佔也就是說當前仍是處於最高級任務,可是上面的延時已經使其阻塞,從而在下面發生搶佔*/
}
 
    if( xAlreadyYielded == pdFALSE ) /*強制本身交出CPU,使自身進入等待延時*/
    {
        portYIELD_WITHIN_API();   /*任務切換*/  
    }
}

時間片 

void xPortSysTickHandler( void )
{
unsigned long ulDummy;

	/* If using preemption, also force a context switch. */
	#if configUSE_PREEMPTION == 1
		*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;	
	#endif

	ulDummy = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		vTaskIncrementTick();
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy );
}

時間片中任務

void vTaskIncrementTick( void )
{
tskTCB * pxTCB;

	/* 每一個時間片調用一次 任務調度器沒有被掛起*/
	if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
	{
		++xTickCount;
		if( xTickCount == ( portTickType ) 0U )
		{
			xList *pxTemp;

			/* 切換到就緒隊列 判斷阻塞隊列是否爲空 若是空就報錯 */
			configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );
			由於xTickCount溢出,因此咱們須要交換任務延時鏈表,系統定義了兩個鏈表指針pxDelayedTas                        kList和pxOverflowDelayedTaskList,其中pxDelayedTaskList始終指向當前正在使用的那個任                        務延時鏈表,而pxOverflowDelayedTaskList指向的則老是備用的那個任務鏈表,在這裏咱們讓p                        xDelayedTaskList指向原先由pxOverflowDelayedTaskList指向的那個鏈表,作個交換
			 /*交換延遲鏈表,把當前的鏈表設置爲pxOverflowDelayedTaskList*/
			pxTemp = pxDelayedTaskList;
			pxDelayedTaskList = pxOverflowDelayedTaskList;
			pxOverflowDelayedTaskList = pxTemp;
			xNumOfOverflows++;
	
			if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
			{
			       //由於任務鏈表爲空,空鏈表,只有一個尾節點,也就是pxList->xListEnd
			       // pxList->xListEnd.xItemValue = portMAX_DELAY,
				xNextTaskUnblockTime = portMAX_DELAY;  //因此,下個任務時間
			}
			else
			{
				/*隊列不爲空找到起始的任務而後移出隊列*/
				pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
				xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) );
			}
		}

		/*找出時間已經到的任務,並把他們從阻塞態轉換爲就緒態,是一個宏定義 */
		prvCheckDelayedTasks();
	}
	else
	{
	        //若是調度器被禁止,則咱們把丟失的時鐘節拍記錄在全局變量uxMissedTicks中
		++uxMissedTicks;

		#if ( configUSE_TICK_HOOK == 1 )
		{
			vApplicationTickHook();
		}
		#endif
	}

	#if ( configUSE_TICK_HOOK == 1 )
	{
		/* Guard against the tick hook being called when the missed tick
		count is being unwound (when the scheduler is being unlocked. */
		if( uxMissedTicks == ( unsigned portBASE_TYPE ) 0U )
		{
			vApplicationTickHook();
		}
	}
	#endif

	traceTASK_INCREMENT_TICK( xTickCount );
}

找到時間已到的任務,轉到就緒列表

#define prvCheckDelayedTasks()															\
{																						\
portTickType xItemValue;																\
																						\
	/* 判斷時間是否到了 若是到了*/				
	if( xTickCount >= xNextTaskUnblockTime )											\
	{																					\
		for( ;; )																		\
		{																				\
			if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )						\
			{																			\
				/* 與上面同樣 判斷是否爲空 */														\
				xNextTaskUnblockTime = portMAX_DELAY;									\
				break;																	\
			}																			\
			else																		\
			{																			\											\
				pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );	\
				xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) );	\
																						\
				if( xTickCount < xItemValue )											\
				{																		\
					/* It is not time to unblock this item yet, but the item			\
					value is the time at which the task at the head of the				\
					blocked list should be removed from the Blocked state -				\
					so record the item value in xNextTaskUnblockTime. */				\
					xNextTaskUnblockTime = xItemValue;									\
					break;																\
				}																		\
																						\
				/* It is time to remove the item from the Blocked state. */				\
				vListRemove( &( pxTCB->xGenericListItem ) );							\
																						\
				/* Is the task waiting on an event also? */								\
				if( pxTCB->xEventListItem.pvContainer )									\
				{																		\
					vListRemove( &( pxTCB->xEventListItem ) );							\
				}																		\
				prvAddTaskToReadyQueue( pxTCB );										\
			}																			\
		}																				\
	}																					\
}

移除鏈表

void vListRemove( xListItem *pxItemToRemove )
{
xList * pxList;

	pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
	pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
	
	pxList = ( xList * ) pxItemToRemove->pvContainer;

	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious;
	}

	pxItemToRemove->pvContainer = NULL;
	( pxList->uxNumberOfItems )--;
}
相關文章
相關標籤/搜索