//插入到最後 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; }
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 }
__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 )--; }