iis服务器助手广告广告
返回顶部
首页 > 资讯 > 操作系统 >FreeRTOS实时操作系统的任务创建与任务切换
  • 334
分享到

FreeRTOS实时操作系统的任务创建与任务切换

2024-04-02 19:04:59 334人浏览 薄情痞子
摘要

目录任务控制块数据结构任务创建函数定义就绪表就绪表初始化启动调度器任务切换    任务控制块数据结构 任务控制块数据结构在task.c声明 typedef str

 

 

任务控制块数据结构

任务控制块数据结构在task.c声明

typedef struct tskTaskControlBlock
{
	volatile StackType_t * pxTopOfStack; //栈顶指针
	ListItem_t xStateListItem; //任务节点
	StackType_t * pxStack; //任务栈起始地址
	char pcTaskName[configMAX_TASK_NAME_LEN];//任务名称
}tskTCB;

任务创建函数

下面是用静态方式创建任务的函数

static void prvInitialiseNewTask(TaskFunction_t pxTaskCode,
                           const char * const pcName,
						   const uint32_t ulStackDepth,
						   void * const pvParameters, 
						   TaskHandle_t* const pxCreatedTask,
						   TCB_t * pxNewTCB) 
{
	StackType_t * pxTopOfStack;
	UBaseType_t x;
	//栈顶地址
	pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t) 1);
	//项下做8字节对齐
	pxTopOfStack = (StackType_t*) (  (uint32_t)pxTopOfStack & (~(uint32_t)0x0007) );
	//存储名字
	for(x=(UBaseType_t)0;x<(UBaseType_t)configMAX_TASK_NAME_LEN;x++)
	{
		pxNewTCB->pcTaskName[x] = pcName[x];
		if(pcName[x]=='\0')
			break;
	}
	pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN-1] = '\0';
	//初始化TCB中的xStateListItem列表项
	vListInitialiseItem(& (pxNewTCB->xStateListItem));
	//设置xStateListItem列表项的拥有者即为传入的TCB
	listSET_LIST_ITEM_OWNER( &(pxNewTCB->xStateListItem), pxNewTCB );
	//初始化任务栈,并返回更新的栈顶指针
	pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack,pxTaskCode,pvParameters);
	//返回任务句柄
	if((void*)pxCreatedTask !=NULL )
	{
			*pxCreatedTask = (TaskHandle_t) pxNewTCB;
	}
}

下面看pxPortInitialiseStack是怎么初始化任务栈的
因为是向下生长的满栈,所以是--操作

#define portINITIAL_XPSR (0x01000000)
#define portSTART_ADDRESS_MASK ((StackType_t)0xfffffffeUL)
StackType_t * pxPortInitialiseStack(StackType_t* pxTopOfStack, TaskFunction_t pxCode, void*pvParameters)
{
    //-------------设置内核会自动加载的寄存器,且顺序不能变
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_XPSR;//xPSR bit24
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;//R15 PC
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError;//R14 LR
	pxTopOfStack -= 5; //R12 R3 R2 R1 
	*pxTopOfStack = ( StackType_t ) pvParameters;//R0
	
	//--------------下面8个需要手动保存
	pxTopOfStack-=8;
	//返回更新后的栈顶指针
	return pxTopOfStack;
}

如下图

在这里插入图片描述

定义就绪表

task.c中,就绪表就是列表类型的数组,元素个数是configMAX_PRIORITIES,就绪表把同一优先级的任务插入到同一优先级的链表中,数组下标表示优先级,如下图

#define configMAX_PRIORITIES 5
List_t pxReadyTasksLists[configMAX_PRIORITIES];

就绪表初始化

就绪表初始化就是把数组每个元素(即列表)初始化(即调用vListInitialise),结果如下

void prvInitialiseTaskLists(void)
{
	UBaseType_t uxPriority;
	for(uxPriority = (UBaseType_t)0 ;
	    uxPriority < (UBaseType_t) configMAX_PRIORITIES;
	    uxPriority ++)
	{
			vListInitialise(&(pxReadyTasksLists[uxPriority]));
	}
}

在这里插入图片描述

启动调度器

调用关系,这里手动指定第一个运行的任务

vTaskStartScheduler->xPortStartScheduler->prvStartFirstTask

void vTaskStartScheduler(void)
{
	//手动指定一个和要运行的任务
	pxCurrentTCB = &Task1TCB;
	//启动调度器
	if(xPortStartScheduler()!=pdfALSE)
	{
		//启动成功不会跑到这里
	}
}
//STM32 用4bit表示优先级,所以最低优先级则是15
#define configKERNEL_INTERRUPT_PRIORITY 15 
#define portNVIC_SYSPRI2_REG (*((volatile uint32_t*) 0xe000ed20))
#define portNVIC_PENDSV_PRI  (((uint32_t)configKERNEL_INTERRUPT_PRIORITY)<<16UL)
#define portNVIC_SYSTICK_PRI  (((uint32_t)configKERNEL_INTERRUPT_PRIORITY)<<24UL)
BaseType_t xPortStartScheduler(void)
{
	//把pendsv systick中断优先级设置为最低
	portNVIC_SYSPRI2_REG |=portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |=portNVIC_SYSTICK_PRI;
	
	//启动第一个任务,该函数不会返回
	prvStartFirstTask();
	//不会运行到这里
	return 0;
}

在CM3中,0xE000ED08存放的是SCB_VTOR寄存器地址,SCB_VTOR是向量表的起始地址,即MSP的地址

__asm void prvStartFirstTask(void)
{
	PRESERVE8
	ldr r0, =0xE000ED08 //r0=0xE000ED08,相当于r0=&SCB_VTOR
	ldr r0, [r0]        //r0=*r0,即r0=*((uint32_t*)0xE000ED08),即r0=0x0
	ldr r0, [r0]        //r0=*r0,即r0=*((uint32_t*)0x0),即r0=msp指针值
	msr msp, r0         //msp=r0,msp指针获得msp地址
	cpsie i   //开中断
	cpsie f   //开中断
	dsb
	isb
	svc 0    //触发SVC系统调用,此后会进入SVCHandler
	nop      //下面这2个nop不会执行
	nop
}
__asm void vPortSVCHandler(void)
{
	extern pxCurrentTCB;
	PRESERVE8
	//r3=&pxCurrentTCB,注意pxCurrentTCB本身就是一个指针,是指向一个任务的TCB指针
	ldr r3, =pxCurrentTCB  
	//r1=*r3,即r1=pxCurrentTCB
	ldr r1, [r3]           
	//r0=*r1,此时r1=pxCurrentTCB,
	//又结构体第一个成员(栈顶指针)的地址和结构体地址数值上是相同的,
	//所以r0=*(&(pxCurrentTCB.pxTopOfStack)),即r0=pxCurrentTCB.pxTopOfStack,
	//即r0此时指向当前任务空闲栈顶位置
	ldr r0, [r1]           
	//以r0为开始把r4-r11依次保存到当前任务的psp栈
	ldmia r0!,{r4-r11}
	//更新当前任务psp栈指针
	msr psp,r0
	isb
	mov r0, #0
	msr basepri,r0 //开中断
    //此时r14是0xFFFF_FFF9,表示返回后进入Thread mode,使用MSP,返回Thmub状态,
    //这里其实是把bit2置1,要求返回后使用PSP
	orr r14,#0x0d  
	//返回,这里会自动加载初始化任务栈填写的xPSR、R15、R14、R12、R3-R0值到内核对应寄存器,
	//所以返回后就直接到pxCurrentTCB指向的任务
	bx r14  
}

任务切换

这里手动触发任务切换,其实就是向中断控制及状态寄存器ICSR(地址0xE000_ED04)PENDSVSET位(bit28)写1悬起pendsv中断,在pendsv_handler中寻找下一个要运行的任务并做任务切换

在这里插入图片描述

#define taskYIELD() portYIELD()
#define portNVIC_INT_CTRL_REG (*((volatile uint32_t*)0xe000ed04))
#define portNVIC_PENDSVSET_BIT (1<<28UL)
#define portSY_FULL_READ_WRITE (15)
#define portYIELD() \
{ \
   //触发一次pendsv中断
  	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
	  __dsb(portSY_FULL_READ_WRITE); \
	  __isb(portSY_FULL_READ_WRITE); \
}
__asm void xPortPendSVHandler(void)
{
    //进入中断前会自动保存xPSR、R15、R14、R12、R3-R0到当前任务P栈中,此时任务栈顶指针指向要保存R11的位置
	extern pxCurrentTCB;
	extern vTaskSwitchContext;//这个函数是用来寻找下一个要运行的任务
	PRESERVE8
	mrs r0,psp  //当前任务栈psp指针存入r0,
	isb
	ldr r3,=pxCurrentTCB //r3=&pxCurrentTCB 
	ldr r2,[r3]          //r2=*r3, 即r2=pxCurrentTCB
	//以r0开始递减的手动保存r4-r11
	stmdb r0!,{r4-r11}
	//r0=*r2,此时r2=pxCurrentTCB,
	//又结构体第一个成员(栈顶指针)的地址和结构体地址数值上是相同的,
	//所以r0=*(&(pxCurrentTCB.pxTopOfStack)),即r0=pxCurrentTCB.pxTopOfStack,
	//即r0此时指向当前任务空闲栈顶位置
	str r0,[r2]
	//到这里上文就保存完成
	//将r3、r14压入栈保存起来(此时sp使用的是msp)
	//r14要保存是因为等下要调用函数,避免r14被覆盖无法从pendsv中断正常返回
	//r3要保存是因为r3保存的是当前正在运行任务控制块的二级指针&pxCurrentTCB,后面要通过r3来操作pxCurrentTCB来切到下文
	stmdb sp!,{r3,r14}
	//关中断,阈值是configMAX_SYSCALL_INTERRUPT_PRIORITY
	mov r0,#configMAX_SYSCALL_INTERRUPT_PRIORITY
	msr basepri,r0
	dsb
	isb
    //调用vTaskSwitchContext,功能是找到优先级最高的任务,然后让pxCurrentTCB = &优先级最高任务TCB,我们这里手动指定
	bl vTaskSwitchContext
    //开中断
	mov r0,#0
	msr basepri,r0
    //从msp中恢复r3、r14
	ldmia sp!,{r3,r14}
	//r1=*r3,此时r3=&pxCurrentTCB
	//即r1=pxCurrentTCB
	ldr r1,[r3] 
    //r0=*r1则r0=pxCurrentTCB.pxTopOfStack 理由前面讲过
	ldr r0,[r1]
    //这里把pxCurrentTCB保存的需要手动加载的值加载到内核的r4-r11
	ldmia r0!,{r4-r11}
    //更新加载了r4-r11的任务栈到psp
	msr psp,r0 
	isb
	//返回,这里会以psp自动加载保存了xPSR、R15、R14、R12、R3-R0值到内核的xPSR、R15、R14、R12、R3-R0寄存器,所以返回的是pxCurrentTCB任务
	bx r14
	nop
}

这里为了简单手动指定TCB

extern TCB_t Task1TCB;
extern TCB_t Task2TCB;
void vTaskSwitchContext(void)
{
  if(pxCurrentTCB == &Task1TCB)
	{
		pxCurrentTCB = &Task2TCB;
	}
	else
	{
		pxCurrentTCB = &Task1TCB;
	}
}

至此任务切换原理讲完

main.c

extern List_t pxReadyTasksLists[configMAX_PRIORITIES];
portCHAR flag1;
portCHAR flag2;
TaskHandle_t Task1_Handle;
StackType_t Task1Stack[128];
TCB_t Task1TCB;
TaskHandle_t Task2_Handle;
StackType_t Task2Stack[128];
TCB_t Task2TCB;
void delay(uint32_t x)
{
	for(;x!=0;x--);
}
void Task1_Fntry(void *arg)
{
	while(1)
	{
	  flag1=1;
	  delay(100);
	  flag1=0;
	  delay(100);
      taskYIELD();//手动触发切换任务
	}
}
void Task2_Fntry(void *arg)
{
	while(1)
	{
	 flag2=1;
	 delay(100);
	 flag2=0;
	 delay(100);
     taskYIELD();//手动触发切换任务
	}
}
int main(void)
{
    //初始化就绪列表
    prvInitialiseTaskLists();
    //创建任务1
	Task1_Handle = xTaskCreateStatic(Task1_Fntry,"task1",128,NULL,Task1Stack,&Task1TCB);
	//将任务添加到就绪列表
	vListInsertEnd(&pxReadyTasksLists[1],&((&Task1TCB)->xStateListItem));
	//创建任务2
	Task2_Handle = xTaskCreateStatic(Task2_Fntry,"task2",128,NULL,Task2Stack,&Task2TCB);
	//将任务添加到就绪列表
	vListInsertEnd(&pxReadyTasksLists[2],&((&Task2TCB)->xStateListItem));
	//启动调度器
	vTaskStartScheduler();
	for(;;);
}

以上就是FreeRTOS实时操作系统的任务创建与任务切换的详细内容,更多关于FreeRTOS任务创建与切换的资料请关注编程网其它相关文章!

--结束END--

本文标题: FreeRTOS实时操作系统的任务创建与任务切换

本文链接: https://www.lsjlt.com/news/145197.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • FreeRTOS实时操作系统的任务创建与任务切换
    目录任务控制块数据结构任务创建函数定义就绪表就绪表初始化启动调度器任务切换    任务控制块数据结构 任务控制块数据结构在task.c声明 typedef str...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务创建和删除
    目录前言 1.任务创建1.1函数描述1.2参数描述1.3返回值1.4用法举例2.任务删除2.1任务描述2.2参数描述前言  在FreeRTOS移植到Cortex-...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务怎么创建和删除
    本文小编为大家详细介绍“FreeRTOS实时操作系统的任务怎么创建和删除”,内容详细,步骤清晰,细节处理妥当,希望这篇“FreeRTOS实时操作系统的任务怎么创建和删除”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧...
    99+
    2023-06-29
  • FreeRTOS实时操作系统的任务是什么
    这篇文章主要介绍了FreeRTOS实时操作系统的任务是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇FreeRTOS实时操作系统的任务是什么文章都会有所收获,下面我们一起来看看吧。1. 任务和协程(Co-r...
    99+
    2023-06-29
  • FreeRTOS实时操作系统的任务通知方法
    目录前言1.发送通知-方法11.1函数描述1.2参数描述1.3返回值2.发送通知-方法22.1函数描述2.2参数描述2.3用法举例3.获取通知3.1函数描述3.2参数描述3.3返回值...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务概要讲解
    目录1. 任务和协程(Co-routines)1.1任务的特性1.2任务概要2. 任务状态3.任务优先级4.实现一个任务5.空闲任务和空闲任务钩子(idle task和Idle Ta...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务通知怎么实现
    这篇文章主要介绍“FreeRTOS实时操作系统的任务通知怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“FreeRTOS实时操作系统的任务通知怎么实现”文章能帮助大家解决问题。前言注:本文介绍...
    99+
    2023-06-29
  • FreeRTOS实时操作系统空闲任务的阻塞延时实现
    目录什么是阻塞延时、为什么需要空闲任务空闲任务的实现阻塞延时的实现xTicksToDelay 递减SysTick初始化仿真什么是阻塞延时、为什么需要空闲任务 RTOS中的延时叫阻塞延...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务应用函数详解
    目录1.获取任务系统状态1.1函数描述1.2参数描述1.3返回值1.4用法举例2.获取当前任务句柄2.1函数描述2.2返回值3.获取空闲任务句柄3.1函数描述3.2返回值4.获取任务...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务应用函数是什么
    本文小编为大家详细介绍“FreeRTOS实时操作系统的任务应用函数是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“FreeRTOS实时操作系统的任务应用函数是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧...
    99+
    2023-06-29
  • FreeRTOS实时操作系统多任务管理基础知识
    目录什么是多任务系统?FreeRTOS  任务与协程1.任务(Task) 的特性2.协程(Co-routine)的特性任务状态运行态就绪态阻塞态挂起态任务优先级任务实现任务...
    99+
    2024-04-02
  • FreeRTOS实时操作系统空闲任务的阻塞延时怎么实现
    这篇文章主要介绍“FreeRTOS实时操作系统空闲任务的阻塞延时怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“FreeRTOS实时操作系统空闲任务的阻塞延时怎么实现”文章能帮助大家解决问题。...
    99+
    2023-06-29
  • FreeRTOS任务创建的方法
    本篇内容介绍了“FreeRTOS任务创建的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!我们这里先回顾一下这个函数的声明: &...
    99+
    2023-06-29
  • freertos实时操作系统空闲任务阻塞延时示例解析
    目录前言空闲任务阻塞延时SysTick实验现象前言 阻塞态:如果一个任务当前正在等待某个外部事件,则称它处于阻塞态。 rtos中的延时叫阻塞延时,即任务需要延时的时候,会放弃CPU的...
    99+
    2024-04-02
  • FreeRTOS实时操作系统多任务管理基础知识有哪些
    本篇内容主要讲解“FreeRTOS实时操作系统多任务管理基础知识有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“FreeRTOS实时操作系统多任务管理基础知识有哪些”吧!RTOS 系统的核心...
    99+
    2023-06-29
  • 实时操作系统:深入剖析实时任务的调度与管理
    一、实时任务的调度 实时任务是指具有严格时限要求的任务,必须在规定的时间内完成,否则会导致系统故障或不可预知的行为。实时任务的调度是RTOS的核心功能之一,其主要目标是确保所有实时任务都能按时完成。 1、调度算法 RTOS中常用的调度算...
    99+
    2024-02-03
    实时操作系统 调度算法 任务管理 优先级调度 时间片轮转调度 速率单调调度
  • 操作系统的天堂:实时操作系统在关键任务中的应用
    实时性的重要性 在关键任务中,实时性至关重要,因为即使很小的延迟也可能导致灾难性后果。例如,在医疗设备中,及时响应患者生命体征的变化至关重要。在交通系统中,信号灯必须在适当的时间切换,以避免事故。 RTOS 的优势 RTOS 为关键任务提...
    99+
    2024-04-02
  • 实时操作系统:探索关键任务系统的坚实后盾
    1. 实时操作系统的定义和特点 实时操作系统(RTOS)是一种专门为满足实时任务需求设计的操作系统,具备低延迟、可靠性和准确性等关键特性。实时任务是指对时间有着严格要求的任务,诸如航空控制、医疗监控、工业自动化等,其正确性和可靠性对于系...
    99+
    2024-02-03
    实时操作系统 关键任务系统 任务调度 中断处理 资源管理
  • 分时操作系统:多任务处理的基石
    分时操作系统简介 分时操作系统是一种计算机操作系统,它通过将 CPU 时间划分为小块的时间片来实现多任务处理。每个时间片分配给一个正在运行的程序,称为进程。当一个进程用完其时间片时,操作系统就会切换到另一个进程,从而在用户看来,多个进程...
    99+
    2024-03-05
    分时操作系统 多任务处理 内存管理 进程管理 调度算法
  • FreeRTOS实时操作系统的列表与列表项操作示例
    目录前言列表项数据结构列表项初始化列表数据结构将列表项按照升序排列插入到列表将列表项从列表删除    前言 FreeRTOS列表与列表项其实就是链表和节点,在li...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作