freertos源码解析-1启动过程分析

2023-05-16

freertos启动过程分析

启动过程流程图

在这里插入图片描述

启动过程完成的事情

1.创建必要的任务:空闲任务和软件定时器任务(后者可选)
2.初始化系统的全局变量
3.初始化systick中断 PendSV中断 SVC中断
4.启动调度器
5.触发SVC中断, 启动第一个任务

代码分析

/*-----------------------------------------------------------*/

void vTaskStartScheduler( void )
{
BaseType_t xReturn;

	/* Add the idle task at the lowest priority. */
	#if( configSUPPORT_STATIC_ALLOCATION == 1 )
	{
		StaticTask_t *pxIdleTaskTCBBuffer = NULL;
		StackType_t *pxIdleTaskStackBuffer = NULL;
		uint32_t ulIdleTaskStackSize;

		/* The Idle task is created using user provided RAM - obtain the
		address of the RAM then create the idle task. */
		vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
		xIdleTaskHandle = xTaskCreateStatic(	prvIdleTask,
												configIDLE_TASK_NAME,
												ulIdleTaskStackSize,
												( void * ) NULL, /*lint !e961.  The cast is not redundant for all compilers. */
												portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
												pxIdleTaskStackBuffer,
												pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

		if( xIdleTaskHandle != NULL )
		{
			xReturn = pdPASS;
		}
		else
		{
			xReturn = pdFAIL;
		}
	}
	#else
	{
		/* The Idle task is being created using dynamically allocated RAM. */
		xReturn = xTaskCreate(	prvIdleTask,
								configIDLE_TASK_NAME,
								configMINIMAL_STACK_SIZE,
								( void * ) NULL,
								portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
								&xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
	}
	#endif /* configSUPPORT_STATIC_ALLOCATION */

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

	if( xReturn == pdPASS )
	{
		/* freertos_tasks_c_additions_init() should only be called if the user
		definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
		the only macro called by the function. */
		#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
		{
			freertos_tasks_c_additions_init();
		}
		#endif

		/* Interrupts are turned off here, to ensure a tick does not occur
		before or during the call to xPortStartScheduler().  The stacks of
		the created tasks contain a status word with interrupts switched on
		so interrupts will automatically get re-enabled when the first task
		starts to run. */
		portDISABLE_INTERRUPTS();

		#if ( configUSE_NEWLIB_REENTRANT == 1 )
		{
			/* Switch Newlib's _impure_ptr variable to point to the _reent
			structure specific to the task that will run first. */
			_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
		}
		#endif /* configUSE_NEWLIB_REENTRANT */

		xNextTaskUnblockTime = portMAX_DELAY;
		xSchedulerRunning = pdTRUE;
		xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

		/* If configGENERATE_RUN_TIME_STATS is defined then the following
		macro must be defined to configure the timer/counter used to generate
		the run time counter time base.   NOTE:  If configGENERATE_RUN_TIME_STATS
		is set to 0 and the following line fails to build then ensure you do not
		have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
		FreeRTOSConfig.h file. */
		portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

		traceTASK_SWITCHED_IN();

		/* Setting up the timer tick is hardware specific and thus in the
		portable interface. */
		if( xPortStartScheduler() != pdFALSE )
		{
			/* Should not reach here as if the scheduler is running the
			function will not return. */
		}
		else
		{
			/* Should only reach here if a task calls xTaskEndScheduler(). */
		}
	}
	else
	{
		/* This line will only be reached if the kernel could not be started,
		because there was not enough FreeRTOS heap to create the idle task
		or the timer task. */
		configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
	}

	/* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
	meaning xIdleTaskHandle is not used anywhere else. */
	( void ) xIdleTaskHandle;
}

/*-----------------------------------------------------------*/

/*
 * See header file for description.
 */
BaseType_t xPortStartScheduler( void )
{
	/* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.
	See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
	configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY );

	#if( configASSERT_DEFINED == 1 )
	{
		volatile uint32_t ulOriginalPriority;
		volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
		volatile uint8_t ucMaxPriorityValue;

		/* Determine the maximum priority from which ISR safe FreeRTOS API
		functions can be called.  ISR safe functions are those that end in
		"FromISR".  FreeRTOS maintains separate thread and ISR API functions to
		ensure interrupt entry is as fast and simple as possible.

		Save the interrupt priority value that is about to be clobbered. */
		ulOriginalPriority = *pucFirstUserPriorityRegister;

		/* Determine the number of priority bits available.  First write to all
		possible bits. */
		*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;

		/* Read the value back to see how many bits stuck. */
		ucMaxPriorityValue = *pucFirstUserPriorityRegister;

		/* Use the same mask on the maximum system call priority. */
		ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;

		/* Calculate the maximum acceptable priority group value for the number
		of bits read back. */
		ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
		while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
		{
			ulMaxPRIGROUPValue--;
			ucMaxPriorityValue <<= ( uint8_t ) 0x01;
		}

		#ifdef __NVIC_PRIO_BITS
		{
			/* Check the CMSIS configuration that defines the number of
			priority bits matches the number of priority bits actually queried
			from the hardware. */
			configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS );
		}
		#endif

		#ifdef configPRIO_BITS
		{
			/* Check the FreeRTOS configuration that defines the number of
			priority bits matches the number of priority bits actually queried
			from the hardware. */
			configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS );
		}
		#endif

		/* Shift the priority group value back to its position within the AIRCR
		register. */
		ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
		ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;

		/* Restore the clobbered interrupt priority register to its original
		value. */
		*pucFirstUserPriorityRegister = ulOriginalPriority;
	}
	#endif /* conifgASSERT_DEFINED */

	/* Make PendSV and SysTick the lowest priority interrupts. */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

	/* Start the timer that generates the tick ISR.  Interrupts are disabled
	here already. */
	vPortSetupTimerInterrupt();

	/* Initialise the critical nesting count ready for the first task. */
	uxCriticalNesting = 0;

	/* Start the first task. */
	vPortStartFirstTask();

	/* Should not get here! */
	return 0;
}

vPortStartFirstTask
	/* 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
	/* Call SVC to start the first task, ensuring interrupts are enabled. */
	cpsie i
	cpsie f
	dsb
	isb
	svc 0 /* 使用svc中断 启动第一个任务, SVC中断不允许被延迟响应,必须立即响应 */

	END
vPortSVCHandler:
	/* Get the location of the current TCB. */
	ldr	r3, =pxCurrentTCB  /*找到第一个任务的TCB*/
	ldr r1, [r3]   /*找到保存第一个任务的栈地址的地址,TCB的第一个变量是栈的地址*/
	ldr r0, [r1]   /*找到第一个任务栈的地址*/
	/* Pop the core registers. */
	ldmia r0!, {r4-r11}   /*手动出栈*/
	msr psp, r0       /*设置中断返回后的栈为第一个任务的栈*/
	isb       /*指令屏障*/
	mov r0, #0    
	msr	basepri, r0   /*关闭中断*/
	orr r14, r14, #13    /*《权威手册电子版139页》*/
	bx r14     /*当r14为0xfffffffd时,该条指令代表从中断中返回,并且返回使用进程栈出栈、返回后进入线程模式*/

解释一下最后一个函数:vPortSVCHandler:。【看注释】
这里使用SVC中断来启动第一个任务,正常的任务切换使用的是PendSV中断。具体原因可以查看《权威指南》或者百度。

任务创建的时候,会初始化任务栈,保存任务函数的地址在栈中,以及一些环境变量,所以从任务栈出栈,PC可以准确获取到任务函数的地址,bx r14 可以返回到第一个任务执行。

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	/* Simulate the stack frame as it would be created by a context switch
	interrupt. */
	pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
	*pxTopOfStack = portINITIAL_XPSR;	/* xPSR */
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;	/* PC */
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;	/* LR */
	pxTopOfStack -= 5;	/* R12, R3, R2 and R1. */
	*pxTopOfStack = ( StackType_t ) pvParameters;	/* R0 */
	pxTopOfStack -= 8;	/* R11, R10, R9, R8, R7, R6, R5 and R4. */

	return pxTopOfStack;
}

参考资料

《Mastering the FreeRTOS™ Real Time Kernel》
《cortex M3 权威指南》
《freertos 开发手册》
freertos

公众号:嵌入式软件和硬件
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

freertos源码解析-1启动过程分析 的相关文章

随机推荐

  • Ubuntu18.04扩容系统分区/根目录,以扩容分区“/“为例(亲测)

    Ubuntu18 04扩容系统分区 根目录 xff0c 以扩容分区 34 34 为例 xff08 亲测 xff09 本人因为一些有关三维重建的工作 xff0c 安装了win10和ubuntu18 04双系统 刚开始时没有特别的注意 xff0
  • Linux内核目录结构介绍(超详细)

    已经写了一篇文章介绍Linux系统目录的 xff1a https blog csdn net weixin 38715577 article details 101033419 现在介绍一下Linux内核 xff08 kernel xff0
  • unscented kalman filter

    UKF 原理及仿真 UKF的优势UKF的问题UKF原理UKF 步骤stepstwo problemsHow to choose the sigma points How to set the weights 其他参数的选择 UKF 算法 U
  • 深度学习系列之Anchor based 和 Anchor free 目标检测方法

    深度学习系列之Anchor based 和 Anchor free 的目标检测方法 致敬各路网络无名大神 持续更新中 文章目录 深度学习系列之Anchor based 和 Anchor free 的目标检测方法一 Anchor 概述二 基于
  • linux下多个usb设备固定名称方法

    当我们接入两个或两个以上的同类型usb设备时 xff0c 内核会在 dev目录下生成对应的设备文件例如 dev ttyUSB0 dev ttyUSB1 这样就会导致我们在访问设备时不知道相应的设备文件对应的是哪一个usb设备 下面讲一下几种
  • jetson-tx2平台命令行连接wifi

    软件环境如下 xff1a TX2软件包版本 xff1a R28 2 1 内核版本 xff1a 4 4 38 文件系统 xff1a ubuntu16 04 命令行连接wifi的步骤如下 xff1a 连接 xff1a wpa passphras
  • 下拉筛选时el-select在部分ios系统上软键盘不能弹出软键盘

    el select下拉筛选框在android上软键盘可以正常弹出 xff0c 部分ios系统上不能正常弹出 xff0c 查找资料 xff0c 发现是readonly属性导致 xff0c 按照以下方式解决 xff1a
  • 网络编程面试题总结

    网络编程知识 tcp udp http https 等常用协议 tcp协议 xff1a 传输控制协议 xff08 TCP xff0c Transmission Control Protocol xff09 是为了在不可靠的互联网络上提供可靠
  • vscode git插件提交代码变慢

    最近两天用vscode的git插件提交代码巨慢 xff0c 具体现象是修改代码后 xff0c git可以正常展示改动的地方 xff0c 也可以将代码提到暂存区 xff0c 但提交到本地仓库时就很慢很慢 xff0c 十分钟都结束不了 xff0
  • 5分钟用C#实现串口助手

    嵌入式开发中 xff0c 由于产品的绑定 验证等逻辑限制比较严重 xff0c 需要自己做一个上位机工具 xff0c 来实现USB BT通讯工具 xff0c 实现如串口通讯 OTA升级等功能 开发之初 xff0c 比较了下C 和QT的环境 x
  • c++实现图的操作(增删改查、遍历、最小生成树)

    如果嫌复制粘贴麻烦 xff0c 可以到我的资源下载 xff1a http download csdn net detail dancheng1 9708649 这个下下来直接可以使用 这些代码直接就可以复制粘贴进行操作 xff08 编译器
  • 移植和使用ucOSII

    目录 1 首先打开STM32CubeMx xff0c 选择New Project xff0c 选择芯片型号 2 其次 xff0c 选择RCC时钟配置 3 第三 xff0c 选择对应的debug接口 4 配置主频时钟 5 不生成PendSV
  • STM32CubeMx+freeRTOS的使用

    目录 第一步 xff0c 利用CubeMx创建工程并修改一些必要设置 第二步 xff0c 测试LED灯和HAL库延时 第三步 xff0c 选择和配置freeRTOS选项 3 1 选择RTOSV1版本 3 2 配置内核参数 3 3 设置Tas
  • 无人驾驶--实时定位与地图构建(SLAM)仿真与实战(附源码)

    无人驾驶 实时定位与地图构建 xff08 SLAM xff09 仿真与实战 xff08 附源码 xff09 一个SLAM的技术小结 xff0c 供自己回顾也为后人学习提供参考 另外建了一个无人驾驶方面的微信交流群 xff0c 有兴趣的同行或
  • AD20使用之 怎么使用第三方PCB文件或封装库文件

    怎么使用第三方PCB文件或封装库文件 1 复制第三方封装库中封装1 打开文件2 复制封装3 粘贴封装到自己的封装库中4 完成 2 将第三方封装库导入到自己项目中3 从第三方PCB文件中生成封装库 1 复制第三方封装库中封装 1 打开文件 2
  • 超详讲解图像拼接/全景图原理和应用 | 附源码

    点击上方 AI算法与图像处理 xff0c 选择加 34 星标 34 或 置顶 重磅干货 xff0c 第一时间送达 研究好玩又有用的技术第 008 期 在学习中发现快乐 xff0c 在应用找到价值 这是我第八期分享图像技术应用的文章 前七期欢
  • git push origin master报错: src refspec master does not match any error: failed to push some refs to *

    git push origin master 改为 xff1a git push origin main
  • Openstack_ocata部署

    1 1 云计算概述 1 行业比较 传统行业 传统的应用正在变得越来越复杂 xff0c 需要支持更多的用户 xff0c 需要更强的计算能力 xff0c 需要更加稳定安全等等 xff0c 而为了支撑这些不断增长的需求 xff0c 企业不得不去购
  • 最新WinIO驱动测试GPIO口

    目录 前言实验环境第一步 xff1a 准备实验环境第二步 xff1a 代码实现初始化写值到具体的地址中从具体的地址中读值 第三步 xff1a 程序的打包总结 前言 首先 xff0c 对于WinIo这个驱动的介绍你可以参考这个博客利用winI
  • freertos源码解析-1启动过程分析

    freertos启动过程分析 启动过程流程图 启动过程完成的事情 1 创建必要的任务 xff1a 空闲任务和软件定时器任务 xff08 后者可选 xff09 2 初始化系统的全局变量 3 初始化systick中断 PendSV中断 SVC中