FreeRTOS调度器启动

 在真正启动调度器之前,先创建空闲任务,初始化一些变量。真正启动调度器的函数是xPortStartScheduler

/* 启动调度器 */
void vTaskStartScheduler( void )
{
	BaseType_t xReturn;

	#if (configSUPPORT_STATIC_ALLOCATION == 1)
	{
		StaticTask_t *pxIdleTaskTCBBuffer = NULL;
		StackType_t *pxIdleTaskStackBuffer = NULL;
		uint32_t ulIdleTaskStackSize;

		vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize);
		xIdleTaskHandle = xTaskCreateStatic(prvIdleTask, configIDLE_TASK_NAME, ulIdleTaskStackSize, 
                                                    (void *)NULL, portPRIVILEGE_BIT, pxIdleTaskStackBuffer, 
                                                    pxIdleTaskTCBBuffer);
		if(xIdleTaskHandle != NULL)
		{
			xReturn = pdPASS;
		}
		else
		{
			xReturn = pdFAIL;
		}
	}
	#else
	{
		/* 创建空闲任务 */
		xReturn = xTaskCreate(prvIdleTask, configIDLE_TASK_NAME, configMINIMAL_STACK_SIZE, 
                                      (void *)NULL, portPRIVILEGE_BIT, &xIdleTaskHandle);
	}
	#endif

	#if (configUSE_TIMERS == 1)
	{
		if(xReturn == pdPASS)
		{
			xReturn = xTimerCreateTimerTask();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif

	if(xReturn == pdPASS)
	{
		#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
		{
			freertos_tasks_c_additions_init();
		}
		#endif

		/* 禁止中断 */
		portDISABLE_INTERRUPTS();

		#if (configUSE_NEWLIB_REENTRANT == 1)
		{
			_impure_ptr = &(pxCurrentTCB->xNewLib_reent);
		}
		#endif

		/* 下一次解除阻塞的时间初始化为最大 */
		xNextTaskUnblockTime = portMAX_DELAY;
		
		/* 调度器运行状态初始化为正在运行 */
		xSchedulerRunning = pdTRUE;
		
		/* 初始化当前节拍数为0 */
		xTickCount = (TickType_t)configINITIAL_TICK_COUNT;

		portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

		traceTASK_SWITCHED_IN();

		/* 启动调度器 */
		if(xPortStartScheduler() != pdFALSE)
		{
		}
		else
		{
		}
	}
	else
	{
		configASSERT(xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY);
	}

	(void)xIdleTaskHandle;
}

启动调度器的本质是启动系统节拍器并切换到第一个任务

/* 启动调度器 */
BaseType_t xPortStartScheduler(void)
{
	#if (configASSERT_DEFINED == 1)
	{
		volatile uint32_t ulOriginalPriority;
		volatile uint8_t *const pucFirstUserPriorityRegister = (uint8_t *)(portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER);
		volatile uint8_t ucMaxPriorityValue;

		ulOriginalPriority = *pucFirstUserPriorityRegister;

		*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;

		ucMaxPriorityValue = *pucFirstUserPriorityRegister;

		configASSERT(ucMaxPriorityValue == (configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue));

		ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;

		ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
		while((ucMaxPriorityValue & portTOP_BIT_OF_BYTE) == portTOP_BIT_OF_BYTE)
		{
			ulMaxPRIGROUPValue--;
			ucMaxPriorityValue <<= (uint8_t)0x01;
		}

		#ifdef __NVIC_PRIO_BITS
		{
			configASSERT((portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue) == __NVIC_PRIO_BITS);
		}
		#endif

		#ifdef configPRIO_BITS
		{
			configASSERT((portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue) == configPRIO_BITS);
		}
		#endif

		ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
		ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;

		*pucFirstUserPriorityRegister = ulOriginalPriority;
	}
	#endif

	/* 设置PendSV优先级为255(最低) */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	/* 设置SysTick优先级为255(最低) */
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

	/* 初始化定时器中断 */
	vPortSetupTimerInterrupt();

	/* 临界层数初始化为0 */
	uxCriticalNesting = 0;

	/* 启动第一个任务 */
	prvStartFirstTask();

	return 0;
}

启动第一个任务,主要工作是请求系统调用(系统调用属于异常,可以获取权限将堆栈指针从MSP切换到PSP)

/* 启动第一个任务 */
__asm void prvStartFirstTask(void)
{
	PRESERVE8

	/* 向量表偏移寄存器地址 */
	ldr r0, =0xE000ED08
	/* 向量表起始地址 */
	ldr r0, [r0]
	/* MSP初始值 */
	ldr r0, [r0]
	/* 初始化MSP */
	msr msp, r0
	
	/* 打开IRQ */
	cpsie i
	/* 打开FIQ */
	cpsie f
	
	dsb
	isb
	
	/* 产生系统调用请求 */
	svc 0

	nop
	nop
}

在看系统调用异常处理源代码之前,先了解一下基础知识,参考《Cortex-M3 权威指南》的第九章(中断的具体行为)

发生异常之后会自动进行三个步骤:

1.入栈    2.取向量    3.选择堆栈指针MSP/PSP,更新堆栈指针SP,更新链接寄存器LR,更新程序计数器PC

异常返回之后会自动进行两个步骤:

1.出栈    2.更新NVIC寄存器

在进入异常服务程序后,LR的值被自动更新为特殊的EX_RETURN

系统调用异常处理入口步骤:

1.出栈和入栈寄存器是相对应的不会将r4-r11出栈,因此要手动出栈

2.将当前任务栈设置为PSP

3.通过改变LR的值,返回线程模式并使用线程堆栈

4.跳转到任务

/* 系统调用异常处理入口 */
__asm void vPortSVCHandler(void)
{
	PRESERVE8

	/* r3=&pxCurrentTCB */
	ldr	r3, =pxCurrentTCB
	/* r1=pxCurrentTCB */
	ldr r1, [r3]
	/* r0=pxTopOfStack */
	ldr r0, [r1]
	/* 将任务栈中的r4-r11弹出 */
	ldmia r0!, {r4-r11}
	/* 将任务栈设置为psp(进程堆栈) */
	msr psp, r0
	isb
	/* 打开中断 */
	mov r0, #0
	msr	basepri, r0
	
	/* 在异常中LR的值被重新解释 */
	/* bit31-4:EXC_RETURN bit3:模式 bit2:栈 bit0:arm/thumb */
	/* 0xd=1101b 线程模式+PSP+thumb */
	/* 根据异常响应序列,异常返回执行自动出栈(PSP),并跳转到PC(原压入PSP中的) */
	orr r14, #0xd
	bx r14
}

猜你喜欢

转载自blog.csdn.net/lushoumin/article/details/87978482