//插入到最后 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 是两种任务的组织实现
croutine.c 各任务共享同一个堆栈,使RAM 的需求进一步缩小,但也正因如此,他的使用受到相对严格的限制。
一般task.c和task.h完成了所有有关创建,调度,和维护任务的繁重工作。
list.c 任务链表,TCB链表等定义
queue.c和queue.h是负责处理FreeRTOS的通讯的。任务和中断使用队列互相发送数据,并且使用信号灯和互斥来发送临界资源的使用情况。
timers 任务定时器等时间管理
heap_2.c 注意是内存分配
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 )--; }