嵌入式FreeRTOS学习八,xTaskCreate创建任务的细节以及恢复中断任务实现

news2025/7/13 22:54:11

一.创建任务函数xTaskCreate

任务也不是很复杂的东西,任务也就是一个函数xTaskCreate。简单得说,创建一个任务,你得提供它的执行函数,你得提供它的栈的大小,函数的执行空间,函数的优先级等重要的条件。因为任务在运行中,任务函数有调用关系,有局部变量,这些都保存在任务的栈里面;任务有可能被切换,有可能被暂停,这时候CPU寄存器中断现场数据都保存在栈里面。

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                        const char * const pcName, 
/*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                        const configSTACK_DEPTH_TYPE usStackDepth,
                        void * const pvParameters,
                        UBaseType_t uxPriority,
                        TaskHandle_t * const pxCreatedTask )

参数说明:
(1)TaskFunction_t   : typedef   void   (*TaskFunction_t)( void * );    函数指针
(2)const  char  *  const  pcName  :  任务名字
(3)configSTACK_DEPTH_TYPE   :   #define  configSTACK_DEPTH_TYPE   uint16_t   是无符号的2字节数值,表示栈的深度大小,实际由malloc函数分配大小
(4)void  *  const  pvParameters  :是要传入的参数
(5)UBaseType_t   uxPriority   :    typedef  unsigned   short   UBaseType_t;  是一个无符号的整形数,表示优先级的大小,数值越大优先级越大
(6)TaskHandle_t  *  const   pxCreatedTask :这里面有一个TCB结构体指针,传出去的参数

 

TCB_t的全称为Task Control Block,也就是任务控制块,这个结构体包含了一个任务所有的信息,但是源代码中存在大量的条件配置选项,以下屏蔽掉的都是可以通过条件来配置的选项,通过条件来决定哪些定义使用或者不用,暂时不需要用到这些,对条件配置项进行屏蔽,TCB最主要的参数在上面它的定义以及相关变量的解释如下

typedef struct tskTaskControlBlock             
    {
        // 这里栈顶指针必须位于TCB第一项是为了便于上下文切换操作,详见xPortPendSVHandler中任务切换的操作。
        volatile StackType_t    *pxTopOfStack;    

       
        // 表示任务状态,不同的状态会挂接在不同的状态链表下
        ListItem_t            xStateListItem;    
        // 事件链表项,会挂接到不同事件链表下
        ListItem_t            xEventListItem;        
        // 任务优先级,数值越大优先级越高
        UBaseType_t            uxPriority;            
        // 指向堆栈起始位置,这只是单纯的一个分配空间的地址,可以用来检测堆栈是否溢出
        StackType_t            *pxStack;            
        // 任务名
        char                pcTaskName[ configMAX_TASK_NAME_LEN ];

/*
//以下屏蔽掉的都是可以通过条件来配置的选项,通过条件来决定哪些定义使用或者不用,暂时不需要用到这 
//些,屏蔽掉,TCB最主要的参数在上面
//#####################################################################################

        // MPU相关暂时不讨论
        #if ( portUSING_MPU_WRAPPERS == 1 )
            xMPU_SETTINGS    xMPUSettings;        
        #endif
        // 指向栈尾,可以用来检测堆栈是否溢出
        #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
            StackType_t        *pxEndOfStack;        
        #endif

        // 记录临界段的嵌套层数
        #if ( portCRITICAL_NESTING_IN_TCB == 1 )
            UBaseType_t        uxCriticalNesting;    
        #endif
        // 跟踪调试用的变量
        #if ( configUSE_TRACE_FACILITY == 1 )
            UBaseType_t        uxTCBNumber;        
            UBaseType_t        uxTaskNumber;        
        #endif
        // 任务优先级被临时提高时,保存任务原本的优先级
        #if ( configUSE_MUTEXES == 1 )
            UBaseType_t        uxBasePriority;        
            UBaseType_t        uxMutexesHeld;
        #endif
        // 任务的一个标签值,可以由用户自定义它的意义,例如可以传入一个函数指针可以用来做Hook    函数调用
        #if ( configUSE_APPLICATION_TASK_TAG == 1 )
            TaskHookFunction_t pxTaskTag;
        #endif

        // 任务的线程本地存储指针,可以理解为这个任务私有的存储空间
        #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
            void            *pvThreadLocalStoragePointers[     configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
        #endif

        // 运行时间变量
        #if( configGENERATE_RUN_TIME_STATS == 1 )
            uint32_t        ulRunTimeCounter;    
        #endif

        // 支持NEWLIB的一个变量
        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            struct    _reent xNewLib_reent;
        #endif
        // 任务通知功能需要用到的变量
        #if( configUSE_TASK_NOTIFICATIONS == 1 )
            // 任务通知的值 
            volatile uint32_t ulNotifiedValue;
            // 任务通知的状态
            volatile uint8_t ucNotifyState;
        #endif
        // 用来标记这个任务的栈是不是静态分配的
        #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
            uint8_t    ucStaticallyAllocated;         
        #endif

        // 延时是否被打断
        #if( INCLUDE_xTaskAbortDelay == 1 )
            uint8_t ucDelayAborted;
        #endif
        // 错误标识
        #if( configUSE_POSIX_ERRNO == 1 )
            int iTaskErrno;
        #endif
//###################################################################################
*/
    } tskTCB;
    typedef tskTCB TCB_t;

=========================================================================

二.创建任务的具体内部细节

以简单的任务创建函数为例,这里分别创建了三个简单任务vTask1,vTask2,vTask3

void vTask1( void *pvParameters )
{    /* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 1;
		flagTask2run = 0;
		flagTask3run = 0;
		/* 打印任务的信息 */
		printf("T1\r\n");				
    }
}
void vTask2( void *pvParameters )
{	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 0;
		flagTask2run = 1;
		flagTask3run = 0;
		/* 打印任务的信息 */
		printf("T2\r\n");				
	}
}

void vTask3( void *pvParameters )
{	
	const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );		
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flagIdleTaskrun = 0;
		flagTask1run = 0;
		flagTask2run = 0;
		flagTask3run = 1;
		/* 打印任务的信息 */
		printf("T3\r\n");				
		// 如果不休眠的话, 其他任务无法得到执行
		vTaskDelay( xDelay5ms );
	}
}
//主函数的实现
int main( void )
{
	prvSetupHardware();	
	xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
	xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
	xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
    /* 启动调度器 */
	vTaskStartScheduler();
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

三.TCB任务结构体在RAM内存中的存在

下面的图中,将一整个tsTaskControlBlock结构体代码数据放入RAM内存中,表示出在内存中分配一个TCB结构体的效果(只画图表示效果,实际上可能不完全一样);可以看到,在RAM内存中分配的栈空间保存的数据有:

栈顶指针pxTopOfStack;  指向划分出来的内存空间的最后一个数据保存的位置
状态链表xStateListItem;
事件链表 xEventListItem;    
任务优先级 uxPriority;         
指向堆栈起始位置指针 pxStack;指向划分出来的内存空间的起始地址位置          
任务名 pcTaskName[ configMAX_TASK_NAME_LEN ];

 =========================================================================

四.任务函数创建时栈空间分配和大小的问题

//任务创建函数
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);

在创建这个任务的时候,传进来的参数是保存在哪里呢?首先看传进来的栈的大小这个参数1000,这时需要弄明白两个问题。第一是栈是从哪里分配?第二是栈的大小怎么确定?

第一个问题栈是从哪里分配?栈其实就是一块空闲的内存,FreeRTOS的heap2.cpp文件中关于是定义了一个巨大的全局数组ucHeap,这个数组没人使用,作为一块空闲的内存,在以后的栈的空间的分配就从这个巨大的数组里面划分可用的内存,来给某个任务当做栈来使用,如上图所示;由宏定义可知数组的大小为17*1024个字节。

第二个问题,例子中栈的大小怎么确定?可以根据程序员出入的值的大小进行分配,例子中划分出1000*4字节的内存,然后内存的起始空间保存在pxStack指针中。

 =========================================================================

五.任务函数创建时函数指针和参数保存问题和作用

//任务创建函数
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);

第一个参数是函数指针xTaskCreat其实就是函数的地址addrF,当我们想要去启动这个任务,或者调用这个函数的时候,我们可以让CPU中的R15寄存器,也就是PC寄存器的值等于函数的地址。addrF即可。

第四个参数是随着函数传入的参数值,通常保存在R0寄存器中。

=========================================================================

六.任务处于暂停的状态恢复运行

系统从定义的全局数组ucHeap中分配可用的空间给任务函数xTaskCreate使用,例子中分配了1000*4的内存空间,pxStack指向内存开始的地址位置,pxTopOfStack指向内存中最后一个数据保存的位置。

我们在创建任务的时候,任务函数xTaskCreate已经创建了这部分内存,帮我们修改了RAM内存里面的内容,在TCB结构体里面,我们没有看到传入的参数和函数指针,其实任务函数已经把这些值写入了CPU内存中的R15和R0等寄存器,以便恢复任务时使用。

刚创建任务的时候,任务还没有运行,属于某种暂停状态,当任务被中断,系统将当前任务的寄存器入栈进行现场保护,其中包括R15函数返回地址,R0参数等;然后开始运行中断任务,中断任务结束,则开始恢复RAM内存中栈里面的各种寄存器现场,从R15函数返回地址开始执行,跳转回上一个被中断的任务,继续执行。如上图所示。

 

 

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

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

相关文章

IPWorks EDI Translator Delphi Edition

IPWorks EDI Translator Delphi Edition 一套轻量级可编程EDI解析和翻译组件。 IPWorks EDI转换器包括便于电子数据交换(EDI)解析、翻译和验证的软件组件。这些组件包括灵活的模式支持,使开发人员能够使用各种模式格式,从而更容易与现有EDI处理应用程序集…

Maven项目属性与版本管理

本次将介绍两个内容,分别是: 属性版本管理 1. 属性 1.1 问题分析 我们先来分析一下问题: 前面在父工程中的dependencyManagement标签中对项目中所使用的jar包版本进行了统一的管理,但是如果在标签中有如下的内容:…

React源码分析4-深度理解diff算法

上一章中 react 的 render 阶段,其中 begin 时会调用 reconcileChildren 函数, reconcileChildren 中做的事情就是 react 知名的 diff 过程,本章会对 diff 算法进行讲解。 diff 算法介绍 react 的每次更新,都会将新的 ReactElem…

[安卓逆向]IDA Pro的认识及使用

[安卓逆向]IDA Pro的认识及使用 软件介绍 IDA Pro全称是交互式反汇编器专业版,人们其简称为IDA,IDA pro 是业界最成熟、先进的反汇编工具之一,是目前最棒的一个静态反编译软件,为众多0day世界的成员和ShellCode安全分析人士不可…

指纹浏览器是什么?可以用来解决流量套利的什么问题?

套利是一个永远不会过期的形式,由于信息差永远存在,有信息差就有套利空间。流量套利是购买和转售流量的过程。套利专家通常通过购买廉价流量并以更好的价格出售来赚取收入。他们把流量导流到广告商的网站上,满足广告商希望客户访问自己的网站…

理解Linux权限(一)

理解Linux文件权限 Permission Groups(权限组) 根据权限组划分:每个文件和目录都有3种使用者(用户) ower(所有者) - 所有者的权限仅适用于文件和目录的所有者,不会影响其他用户的操作;group(所属组) - 所属组的权限仅适用于已分配的文件和…

Transwarp Inceptor介绍

Transwarp Inceptor是星环科技推出的用于数据仓库和交互式分析的大数据平台软件,它基于Hadoop和Spark技术平台打造,加上自主开发的创新功能组件,有效的解决了企业级大数据数据处理和分析的各种技术难题,帮助企业快速的构建和推广数…

进化吧,MySQL锁!无锁->偏向锁->轻量级锁->重量级锁(请自动脑补数码宝贝进化音)

写在前边 走到哪都有各种琐事,在MySQL中咱已经聊透了各种琐事 ->MySQL锁机制&&事务,今天来看看Java里边的锁升级过程,以及各种锁之间的比较,悲观乐观,粗化消除~ 四种锁的Markword 优先程度 偏向锁->轻量…

【FPGA】FPGA实现IIC协议读写EEPROM(三) ----- 汇总篇

IIC协议读写EEPROM一、功能分析/模块划分二、状态转移图1、EEPROM读写控制状态转移图2、IIC接口驱动状态转移图三、工程代码实现1、顶层模块2、EEPROM读写控制模块3、IIC接口驱动模块4、参数配置5、其他模块四、仿真测试五、上板验证写在前面 FPGA实现IIC协议读写EEPROM相关文章…

【附源码】计算机毕业设计JAVA教学辅助系统

项目运行 环境配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: Springboot mybatis Maven Vue 等等组成,B/…

【Qt】控件探幽——QWidget

注1:本系列文章使用的Qt版本为Qt 6.3.1 注2:本系列文章常规情况下不会直接贴出源码供复制,都以图片形式展示。所有代码,自己动手写一写,记忆更深刻。 本文目录探索QWidget1、ui文件最后会变成什么?2、如何改…

在 OpenHarmony 轻量设备开发应用

本文档旨在讲解新建 Helloworld 项目步骤、固件包烧录到 BES2600WM 开发板、实现 js 和 C 代码的通讯。该 Demo 重点体现的是 OpenAtom OpenHarmony(以下简称“OpenHarmony”) 3.1 Beta 系统轻量设备 js 和 C 的交互能力, 效果如图 &#xf…

dpdk PMD

PMD是Poll Mode Driver的缩写,即基于用户态的轮询机制的驱动 在不考虑vfio的情况下,PMD的结构图如下 虽然PMD是在用户态实现设备驱动,但还是依赖于内核提供的策略。其中uio模块,是内核提供的用户态驱动框架,而igb_uio…

深度探讨react-hooks实现原理

react hooks 实现 Hooks 解决了什么问题 在 React 的设计哲学中,简单的来说可以用下面这条公式来表示: UI f(data)等号的左边时 UI 代表的最终画出来的界面;等号的右边是一个函数,也就是我们写的 React 相关的代码&#xff1b…

最新最全面的Spring详解(一)——Spring概述与IOC容器

前言 本文为 【Spring】Spring概述与IOC容器 相关知识,下边将对Spring概述,IOC容器(包括:IOC概述、配置元数据、容器实例化与使用、Bean的概述、依赖注入 Dependency Injection、Bean 作用范围(作用域)、更…

计算机网络(二)

三、数据链路层 3.1 数据链路层概述 数据链路层在物理层提供的服务的基础上向网络层提供服务,其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层。数据链路层在不可靠的物理介质上提供可靠的传输。 该层的作用包括:物理地址寻址…

安装Redis

一、Windows安装 1、下载安装包 2、下载完毕得到压缩包 3、解压到自己电脑上的环境目录 4、开启redis,双击运行服务 5、使用redis客户端来连接redis 注意:Window下使用确实简单,但是Redis推荐我们使用Linux去开发使用! 二、Linux安装 1、官网下载…

everything常用搜索命令

参考:玩转Everything(三) https://baijiahao.baidu.com/s?id1735662355311796969&wfrspider&forpc 可右键菜单显示要显示的内容 指定目录搜索 例:e: 文件名 (注意加空格) 多目录内搜索 例&#x…

ModStartCMS v5.2.0 字段扩展支持,SiteMap增强

系统介绍 ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。 系统完全开源,基于 Apache 2.0 开源协议,免费且不限制商业使用。 功能特性 丰…

多肽914910-73-9:血管紧张素Angiotensin(1-12)(mouse, rat)

血管紧张素 (1-12) 是局部生成血管紧张素的潜在前体。它在广泛的器官和组织中表达,包括小肠、脾脏、肝脏、肾脏和心脏。卡托普利和 CV-11974(一种血管紧张素 II I 型受体拮抗剂)可消除对静脉输注血管紧张素 (1-12) 的血管收缩和升压反应。编号…