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 是两种任务的组织实现

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;
}

启动时间片 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 )--;
}


转载于:https://my.oschina.net/u/274829/blog/278551

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值