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