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

news2024/11/18 16:55:09

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349683.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目管理工具dhtmlxGantt甘特图入门教程(九):支持哪些数据格式(下篇)

这篇文章给大家讲解 dhtmlxGantt可以加载或支持哪些数据格式。 dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表,可满足应用程序的所有需求,是最完善的甘特图图表库 DhtmlxGantt正版试用下载(qun:764148812&am…

虚拟机NAT模式无法连外网

虚拟机使用NAT模式连接外网设置时要注意两点 虚拟机的网段要与物理机保持一致各个节点的IP中的GATEWAY要与虚拟机的网关保持一致 1、虚拟机的网段要与物理机保持一致 1.1首先查看物理机的ip,看虚拟机和物理机ip是否在同一网段 winR cmd计入控制台,然…

uni-app前端H5页面底部内容被tabbar遮挡

如果你想在原生 tabbar 上方悬浮一个菜单&#xff0c;之前写 bottom:0。这样的写法编译到 h5 后&#xff0c;这个菜单会和 tabbar 重叠&#xff0c;位于屏幕底部。 原码&#xff1a; <view style"position: fixed;bottom:0;left: 0;background-color: #007AFF;right: …

PythonWeb Django PostgreSQL创建Web项目(二)

安装数据库PostgreSQL并创建数据库 我第一次尝试使用PostgreSQL数据库&#xff0c;why&#xff1f;我喜欢它提供的丰富的数据类型&#xff0c;例如货币类型、枚举类型、几何类型(点、直线、线段、矩形等等)、网络地址类型、文本搜索类型、XML类型JSON类型等等&#xff0c;非常…

Web3中文|聊聊这个让Opensea头疼的新对手Blur ($BLUR)

2022年10月19日&#xff0c;NFT市场迎来一个新的平台。 这个被精心设计的NFT交易市场和聚合器被命名为Blur。 与其他NFT平台不同&#xff0c;Blur旨在提升专业交易者的NFT交易体验。它的开发团队认为其他交易平台存在界面混杂、无法获取分析数据和工具、处理速度缓慢等问题。…

postman使用简介

1、介绍 postman是一款功能强大的网页调试和模拟发送HTTP请求的Chrome插件&#xff0c;支持几乎所有类型的HTTP请求 2、下载及安装 官方文档&#xff1a;https://www.getpostman.com/docs/v6/ chrome插件&#xff1a;chrome浏览器应用商店直接搜索添加即可&#xff08;需墙&…

在魔改PLUS-F5280开发板上使用合封qsp iflash

文章目录引言硬件调整软件调整总结引言 由于目前灵动官网暂未发布正式版的PLUS-F5280开发板&#xff0c;可以使用现有的PLUS-F5270 v1.2开发板&#xff08;下文简称PLUS-F5270开发版&#xff09;替换为MM32F5280微控制器芯片&#xff0c;改装为PLUS-F5280开发板。本文记录了使…

【Mybatis源码解析】mapper实例化及执行流程源码分析

文章目录简介环境搭建源码解析基础环境&#xff1a;JDK17、SpringBoot3.0、mysql5.7 储备知识&#xff1a;《【Spring6源码・AOP】AOP源码解析》、《JDBC详细全解》 简介 基于SpringBoot的Mybatis源码解析&#xff1a; 1.如何对mapper实例化bean 在加载BeanDefinition时&a…

哈希表题目:矩阵置零

文章目录题目标题和出处难度题目描述要求示例数据范围进阶解法一思路和算法代码复杂度分析解法二思路和算法代码复杂度分析解法三思路和算法代码复杂度分析题目 标题和出处 标题&#xff1a;矩阵置零 出处&#xff1a;73. 矩阵置零 难度 3 级 题目描述 要求 给定一个 m…

oracle单库重建undo表空间步骤

前言&#xff1a;undo表空间不足可直接增加空间&#xff1b; alter tablespace UNDOTBS1 add datafile /data/oradata/datafile/UNDOTBS102.dbf size 30g autoextend off; 注&#xff1a;单次增加最大不得超过32G 回收空间 缩小表空间直接可以resize命令缩小&#xff1b…

webpack(高级)--创建自己的loader 同步loader 异步loader loader参数校验

webpack 创建自己的loader loader是用于对模块的源代码进行转换&#xff08;处理&#xff09; 我们使用过很多loader 比如css-loader style-loader babel-loader 我么如果想要自己创建一个loader 首先创建webpack环境 pnpm add webpack webpack-cli -D 之后创建loader模块…

Hadoop初步理解

产生原因 在之前&#xff0c;数据量小&#xff0c;增长速度慢&#xff0c;且数据基本都是文件。储存和处理这些数据并不麻烦&#xff0c;单个存储单元和处理器组合就可以。 之后随着互联网发展&#xff0c;产生了大量多种形式的数据。 非结构化数据&#xff1a;邮件、图像、音…

盘点3个.Net开发的WMS仓库管理系统

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 仓库管理系统在企业中&#xff0c;重要性越来越高&#xff0c;不仅可以提高效率&#xff0c;还能降低企业的压力&#xff0c;企业通过协调和优化资源使用和物料流动&#xff0c;能极大程度地提升了管理效率&…

中国500强|长虹控股集团携手契约锁,推动采购-人事业务电子签

四川长虹电子控股集团&#xff08;以下简称“长虹控股集团”&#xff09;是国内知名的电器制造商之一&#xff0c;拥有六家上市公司、一家新三板的公众公司&#xff0c;入选世界品牌500强、中国企业家协会发布的中国500强企业榜单。此次&#xff0c;长虹控股集团携手契约锁打造…

数据分析与SAS学习笔记3

SAS在最新的展示图&#xff0c;表现力比较丰富。 SAS的处理流程&#xff1a; 数据步 过程步&#xff1a; ETL是数据分析非常重要的步骤。70%-90%花在收集数据以及整理数据&#xff0c;数据分析数据的时间不是很多的。 一个完整的数据步和过程步&#xff1a; 数据步基本语句总…

新手学习node.js基础,node.js安装过程,node.js运行环境及javascript运行环境.

学习node.js1.什么是node.js?2.node.js中的javaScript运行环境3.node.js可以做什么&#xff1f;4. node.js学习思路5.node.js环境的安装6.如何在node.js中执行JavaScript代码1.什么是node.js? node.js是一个基于Chrome v8 引擎的JavaScript运行环境(后端) node.js官网 &…

Flutter for Android

将 Flutter 添加到现有应用程序 在 Flutter 中一次重写整个应用程序是不切实际的。 对于这些情况&#xff0c;Flutter 可以作为库或模块逐步集成到您现有的应用程序中。 然后可以将该模块导入到您的 Android 或 iOS&#xff08;当前支持的平台&#xff09;应用程序中&#xff…

GEE学习笔记 八十六:分类中的特征重要性分析

之前在GEE中做随机森林分类时候&#xff0c;很多人都在问如何做特征重要性分析&#xff1f;但是在GEE之前并没有相关API可以做特征重要性分析&#xff0c;最新的API更新后GEE也可以做特征重要性分析了。 1、目前常用的包含特征重要信息分析的分类方法包括&#xff1a; &#…

基础篇:03-SpringCloud工程部署启动

目录 1.工程搭建部署 方案一&#xff1a;完整工程导入 方案二&#xff1a;从零开始搭建 1.工程与module创建 2.数据库导入 3.项目启动 3.1 启动并访问user-service 3.2 启动并访问order-service 4.服务远程调用 时序图说明 服务远程调用实现 注入RestTemplate Res…

自学web前端觉得好难,可能你遇到了这些困境

好多人跟我说上学的时候也学过前端&#xff0c;毕业了想从事web前端开发的工作&#xff0c;但自学起来好难&#xff0c;快要放弃了&#xff0c;所以我总结了一些大家遇到的困境&#xff0c;希望对你会有所帮助。 目录 1. 意志是否坚定 2. 没有找到合适自己的老师 3. 为了找…