RTOS随笔之FreeRTOS启动与同步方法

2023-05-16

RTOS启动与同步机制

  • RTOS启动
  • 任务切换场景
  • 任务同步机制
    • 队列
    • 信号量
    • 事件组
    • 任务通知
  • 任务延时

RTOS启动

FreeRTOS在任务创建完成后调用函数vTaskStartScheduler()启动任务调度器。
vTaskStartScheduler()任务启动函数详解

void vTaskStartScheduler( void )
{
     BaseType_t xReturn;
	xReturn = xTaskCreate(	prvIdleTask,
							"IDLE", configMINIMAL_STACK_SIZE,
							( void * ) NULL,
							( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
							&xIdleTaskHandle );//创建空闲任务,空闲任务优先级最低
	#if ( configUSE_TIMERS == 1 )//是否使用软件定时器
	{
		if( xReturn == pdPASS )
		{
			xReturn = xTimerCreateTimerTask();//创建软件定时器任务
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif 
	if( xReturn == pdPASS )
	{
		portDISABLE_INTERRUPTS();//关闭中断
		#if ( configUSE_NEWLIB_REENTRANT == 1 )
		{
			_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
		}
		#endif 
		xNextTaskUnblockTime = portMAX_DELAY;
		xSchedulerRunning = pdTRUE;//调度器开始运行
		xTickCount = ( TickType_t ) 0U;
		portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();//实现任务统计功能,具体由用户实现
		if( xPortStartScheduler() != pdFALSE )//初始化任务硬件相关的功能,Systick/PendSV/FPU
		{
			//如果调度器启动成功,xPortStartScheduler()没有返回值,不会运行到这里
		}
		else
		{
		 //不会运行到这里,除非调用xTaskEndScheduler()
		}
	}
	else
	{
		//创建空闲任务或定时器任务失败,内存不足
		configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
	}
	//防止编译器报错,INCLUDE_xTaskGetIdleTaskHandle定义为0,编译器会提示
	( void ) xIdleTaskHandle;
}

xPortStartScheduler()任务硬件相关函数详解

BaseType_t xPortStartScheduler( void )
{
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;//设置PendSV中断为最低优先级
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;//设置Systick中断为最低优先级
	vPortSetupTimerInterrupt();//设置Systick周期
	uxCriticalNesting = 0;//初始化临界区嵌套计数
	prvEnableVFP();//使能FPU浮点运算
	*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;//采用惰性压栈的特性自动保存寄存器
	/*
	  惰性压栈猜测:惰性压栈指的是异常产生时只将通用寄存器的值进行压栈,浮点寄存器值不压栈,
	  除非异常处理函数中存在浮点运算才会将浮点寄存器压栈。
	  具有浮点运算的处理器,总线错误和MemManage错误等异常可能会在惰性压栈期间产生。惰性压栈
	  特性推迟了浮点寄存器的压栈,只有当异常处理中存在浮点运算时才会将浮点寄存器的值先压栈,
	  后执行异常处理中的浮点运算。
    */
	prvStartFirstTask();//在SVC异常中启动第一个任务
	return 0;
}

prvStartFirstTask()启动第一个任务函数详解

__asm void prvStartFirstTask( void )
{
	PRESERVE8  
	ldr r0, =0xE000ED08  //向量表偏移寄存器VTOR的地址,存储栈顶地址,对该寄存器VTOR的值进行解引用获得栈顶地址
	ldr r0, [r0] //取R0地址处的值赋给R0,R0地址处的值是一个地址
	ldr r0, [r0] //取R0地址处的值赋给R0, R0地址处的值是栈顶
	msr msp, r0  //复位栈顶MSP
	cpsie i      //使能中断(清除PRIMASK)
	cpsie f      //使能中断(清除FAULTMASK)
	dsb          //数据同步屏障
	isb          //指令同步屏障
	svc 0        //触发SVC异常
	nop
	nop
}

vPortSVCHandler()SVC异常处理函数详解

__asm void vPortSVCHandler( void )
{
	PRESERVE8
	ldr	r3, =pxCurrentTCB   //获取pxCurrentTCB指针的地址赋值R3
	ldr r1, [r3]            //获取pxCurrentTCB指针指向的值赋值R1
	ldr r0, [r1]            //获取R1地址处的值赋值给R0,获取任务栈栈顶指针,即任务控制块的第一个数据就是任务栈顶指针
	ldmia r0!, {r4-r11, r14}//R4~R11,R14寄存器出栈,栈顶移动36=4x9个字节,用户手动出栈
	msr psp, r0             //设置进程栈指针,PSP=R0
	isb                     //指令同步屏障
	mov r0, #0              //R0=0
	msr	basepri, r0         //basepri寄存器=0,开中断
	bx r14                  //硬件自动恢复R0~R3,R12,LR,PC,xPSP值,PC指向下一条指令
}

任务切换场景

任务切换场景:
1.执行系统调用接口(含有taskYIELD()的API)
2.Systick定时器中断

任务在哪里切换:任务在PendSV的异常中切换

__asm void xPortPendSVHandler( void )
{
	extern uxCriticalNesting;
	extern pxCurrentTCB;
	extern vTaskSwitchContext;
	PRESERVE8
	mrs r0, psp //读取进程栈指针,保存在R0寄存器中
	isb  
	ldr	r3, =pxCurrentTCB //获取当前任务控制块的指针
	ldr	r2, [r3] //将当前任务控制块的地址保存在R2寄存器中
	tst r14, #0x10
	it eq    //判断是否使用FPU,使用的话在进行任务切换的时候要手动保存FPU寄存器到任务栈中
	vstmdbeq r0!, {s16-s31}//保存S16~S31这16个FPU寄存器
	stmdb r0!, {r4-r11, r14}//保存R4~R11,R14寄存器
	str r0, [r2]//将R0的值保存到R2所保存的地址中去,R2保存当前任务控制块的地址,等于更新任务块栈顶指针
	stmdb sp!, {r3}//将R3的值入栈
	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
	msr basepri, r0//关闭中断进入临界区
	dsb
	isb
	bl vTaskSwitchContext//调用vTaskSwitchContext()获取下一个运行的任务
	mov r0, #0
	msr basepri, r0//打开中断,退出临界区
	ldmia sp!, {r3}//R3的值出栈,新的任务控制块
	ldr r1, [r3]
	ldr r0, [r1]//获取新的任务的栈顶指针,保存到R0中
	ldmia r0!, {r4-r11, r14}//R4~R11,R14出栈
	tst r14, #0x10
	it eq
	vldmiaeq r0!, {s16-s31}//判断当前任务是否使用FPU,是的话手动恢复FPU寄存器
	msr psp, r0//跟新进程栈指针
	isb
	#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */
		#if WORKAROUND_PMU_CM001 == 1
			push { r14 }
			pop { pc }
			nop
		#endif
	#endif
	bx r14
	/*
	硬件自动恢复R0~R3,R12,LR,PC,xPSP值,确定异常返回后进入处理器模式还是进程模式,
	使用主栈指针MSP还是进程栈指针PSP。后续进入进程模式,使用进程栈指针,PC的值指向
	新任务的函数,新任务开始运行
	*/
}

任务同步机制

  • 队列
  • 信号量(二值/计数/互斥)
  • 事件组
  • 任务通知(消息邮箱)
    在这里插入图片描述

队列

队列传输数据的两种方式:

  • 拷贝:把数据复制进队列
  • 引用:把数据地址复制进队列,数据要做保护

多任务发送队列,怎么区别数据来源:

  • 结构体:ID+数据形式
  • 位拼接:高位表示ID,低位表示数据
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
BaseType_t xQueueReset( QueueHandle_t pxQueue);
void vQueueDelete( QueueHandle_t xQueue );
BaseType_t xQueueSend(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait);
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait );
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue,void *pvBuffer,BaseType_t *pxTaskWoken);
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );//返回队列已有数据空间个数
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );//返回队列剩余数据空间个数
BaseType_t xQueueOverwrite(QueueHandle_t xQueue,const void * pvItemToQueue);//队列覆盖写
BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t*pxHigherPriorityTaskWoken);
BaseType_t xQueuePeek(QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait);//复制队列中的数据,不删除数据,队列中无数据时阻塞
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void *pvBuffer,);

信号量

  • 通知作用
  • 计数型信号,信号初值自定义,发送加1,接收减1
  • 二进制信号,信号初值是0,发送加1,接收减1
  • 互斥信号,防止同时访问共享资源(共享资源:全局变量,静态变量,公共函数)
  • 互斥信号禁止中断中使用
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
SemaphoreHandle_t xSemaphoreCreateBinary( void );
SemaphoreHandle_t xSemaphoreCreateMutex( void );//互斥锁,他人也能解锁
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );//递归锁,自己上锁自己解锁,他人无法解锁
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait);
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);

事件组

  • 事件发生,广播所有任务
  • 事件可以与或组合
  • 多任务可以同步
EventGroupHandle_t xEventGroupCreate( void );
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet );
EventBits_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet ,BaseType_t * pxHigherPriorityTaskWoken);
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToWaitFor,const BaseType_t xClearOnExit,const BaseType_t xWaitForAllBits,TickType_t xTicksToWait );
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait );

任务通知

  • 明确通知的任务,数据任务独享
  • 不额外开辟内存
  • 效率更高
  • 中断中只能发送数据给任务
/*任务通知计数功能函数*/
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
/*任务通知复杂功能函数*/
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyActioneAction );
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t *pxHigherPriorityTaskWoken );
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );

任务延时

vTaskDelay(n):进入vTaskDelay到退出间隔至少n个Tick中断
xTaskDelayUntil(&Pre, n):两次退出 xTaskDelayUntil 的时间至少是n个Tick中断,也就是任务运行时的Tick+Delay的Tick

在这里插入图片描述

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

RTOS随笔之FreeRTOS启动与同步方法 的相关文章

随机推荐

  • 中国用户如何免费用chatgpt

    ChatGPT是一种自然语言生成模型 xff0c 它可以根据输入的文本自动生成文本 如果你是中国用户 xff0c 想要免费使用ChatGPT xff0c 你可以通过以下方式获得 xff1a 在GitHub上找到ChatGPT的开源代码 xf
  • SDN控制平台开源代码

    开源代码是指源代码在开放的许可下公开发布 xff0c 任何人都可以从源代码免费获得 复制 修改和分发 在SDN控制平台领域 xff0c 有许多开源代码可供选择 xff0c 如 xff1a OpenDaylight ONOS Ryu等 这些项
  • 两两比较统计学方法Tukey的优点、缺点,以及适用和不适用的情况

    Tukey的优点是它可以使用不同的统计检验来检测抽样数据中的异常值 xff0c 并能够计算出一个可以比较不同数据组之间的平均数的统计量 它的缺点是它只能处理少量的数据 xff0c 而且不能用于比较非正态分布的数据 Tukey适用于有限的数据
  • 浅谈现代无人机技术

    摘要 xff1a 在物联网技术 电池能源技术 传感器技术不断发展的今天 xff0c 无人机技术也变得日趋成熟起来 xff0c 成为一大热门技术 笔者主要对当下的无人机技术做出简单的分析 xff0c 并且简单实践复现该项技术 囊括 xff1a
  • c++数组初始化

    静态数组 span class token keyword int span dp span class token punctuation span span class token number 1 span span class to
  • PID控制器主要针对线性系统还是非线性系统

    PID控制器可以用于线性系统和部分非线性系统 PID控制器最初是为线性系统设计的 xff0c 可以有效控制具有稳定线性动态特性的系统 xff0c 如电机控制 温度控制等 但是 xff0c PID控制器也可以应用于一些非线性系统中 xff0c
  • VNC远程登录服务器(Ubuntu14.04)

    使用服务器多用户登录 xff0c 使用命令行没有图像化界面 xff0c 难免有所不便 xff0c 就来搞下VNC远程登录Ubuntu14 04 1 使用命令行登录进行vnc安装 sudo apt get install vnc4server
  • 树莓派4b使用记录(一):在树莓派4b使用python-opencv打开海康工业相机及遇到的问题与解决方法

    树莓派4b使用记录 一 xff1a 在树莓派4b使用python opencv打开海康工业相机及遇到的问题与解决方法 一 在树莓派上安装海康工业机器人的MVS软件 xff08 Linux版本 xff09 海康工业机器人软件下载地址 xff1
  • react 的性能优化

    一 性能永远是第一需求 xff0c 时刻考虑性能问题 如何避免应用出现性能问题 xff0c 如下所示 xff1a 了解常见的性能问题场景时刻注意代码的潜在性能问题注重可重构的代码了解如何使用工具定位性能问题 二 网络性能优化 xff0c 自
  • STM32应用之485通信

    我们先看看普通的收发电路 普通的485电路 xff0c 除了 用RXD连接485芯片的RO引脚 用TXD连接485芯片的DI引脚 xff0c 还会用一个单片机的普通IO引脚连接到RE DE引脚上 当单片机要发送数据的时候 xff0c 控制P
  • AD铺铜技巧总结

    原文链接 xff1a https blog csdn net snaking616 article details 78643046 目录 1 铜皮操作分类 2 铺铜技巧 2 1 过孔处理 2 1 1 过孔与绿油 2 1 2 过孔的十字连接
  • 十大滤波算法

    一 限幅滤波 1 xff09 方法 根据经验判断两次采样允许的最大偏差值A 每次采新值时判断 xff1a 若本次值与上次值之差 lt 61 A xff0c 则本次有效 xff1b 若本次值与上次值之差 gt A xff0c 本次无效 xff
  • 初识GD32

    什么是GD32 xff1f GD32是由北京兆易创新开发的国产32位MCU xff0c 基于Arm Cortex M3 M23 M4内核的32位通用微控制器 目前已经推出GD32F1xx xff0c GD32F2xx xff0c GD32F
  • GD32 新建工程模板

    本文以GD32F450MCU为核心 xff0c 新建工程模板 准备资料 xff1a GD32F4xx Firmware Library V2 1 0 GD32F4Pack包 http www gd32mcu com cn download
  • VirtualBox安装Win10系统

    VirtualBox是一款免费的开源虚拟机 xff0c 它简单易用 xff0c 支持Windows Linux和Mac系统等 最重要的是安装简单 xff0c 操作方便 装机准备 Windows10镜像文件 VirtualBox软件 Virt
  • SMT32H7系列DMA和DMAMUX的一点理解

    DMA和DMAMUX DMA xff1a 无CPU参与下直接进行数据搬运的控制器 DMAMUX xff1a 建立DMA请求和DMA通道之间的映射关系 xff0c 类似于路由的功能 无DMAMUX的MCU xff0c DMA请求和DMA通道的
  • 基于STM32的UVC设备枚举解析

  • ERP实施顾问与项目经理的区别

    来到青岛出差 xff0c 再次见识了青岛的美丽 xff0c 特别是在这个秋天时节 xff0c 天气宜人 xff0c 看来是一个放假休养的好时节 可惜过来是工作的 xff0c 不然倒是可以在这里呆上一段时间 xff0c 在海边看看书 xff0
  • RTOS随笔之FreeRTOS

    RTOS几点思考 为什么使用RTOS 1 先考虑OS有什么特点 xff1f 2 再考虑什么情况下使用OS FreeRTOS调试技巧任务管理1 任务调度流程2 任务栈大小 xff0c 栈溢出检测3 中断管理4 内存管理 FreeRTOS AP
  • RTOS随笔之FreeRTOS启动与同步方法

    RTOS启动与同步机制 RTOS启动任务切换场景任务同步机制队列信号量事件组任务通知 任务延时 RTOS启动 FreeRTOS在任务创建完成后调用函数vTaskStartScheduler 启动任务调度器 vTaskStartSchedul