Linux和,FreeRTOS 任务调度原理,r0-r15寄存器,以及移植freertos(一)

目录、
1、r0-r15寄存器,保护现场,任务切换的原理
2、freertos移植
3、freertos的任务管理。

一、前言

写这篇文章的目的,是之前面试官,刚好问到我,移植FreeRTOS 到mcu,需要做哪些步骤,当时回答的时候,我愣了一下。想了想
基本上说出来了。移植freertos,是五年前学习过,需要修改哪些文件,但是现在一直做的是,linux方向,早就不记得太底层的细节了
但是根据我对arm架构的了解,我还是大概说了步骤。面试过也比较满意。

操作系统是允许多个任务“同时运行”的,操作系统的这个特性被称为多任务。
然而实际上,一个 CPU 核心在某一时刻只能运行一个任务,而操作系统中任务调度器的责任,
就是决定在某一时刻 CPU 究竟要运行哪一个任务,任务调度器使得 CPU 在各个任务之间来回切换并处理任务

首先,我们回想一下,freertos是不是,实时抢占系统,创建的任务,一般有这些状态。多个任务,按时间片轮询,高优先级优先运行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

有了上面这两张图,是不是,比较清晰了,当发生高优先级事件,中断时,就会发生任务切换,那rtos,要和linux系统一样,可以实现进程,任务的管理,是不是要能获得cpu的管理权,能操控底层的寄存器。

玩过arm架构的mcu还是soc,都知道,对进程管理,任务切换,是需要保存现场的(哪个函数被调用函数被执行到哪句代码,执行过程中,计算的局部变量的值),都是需要保存,才能返回的。

# 发生中断,或者线程上下文,进程上下文,我们需要,保存寄存器状态
pushq %rbx        # 将rbx寄存器的值压入堆栈
pushq %r12        # 将r12寄存器的值压入堆栈
pushq %r13        # 将r13寄存器的值压入堆栈# 执行一些操作,(进程切换,或线程切换,或中断处理)...# 返回,恢复寄存器状态
popq %r13         # 从堆栈中弹出值到r13寄存器
popq %r12         # 从堆栈中弹出值到r12寄存器
popq %rbx         # 从堆栈中弹出值到rbx寄存器# 继续执行,前面未完成的操作。

再来回想下这种图,是不是很熟悉,在学linux还是stm32,应该都见过这种图,cpu的寄存器图。
在这里插入图片描述

R15 PC程序计数器(Program Counter),存储下一条要执行的指令的地址。
R14 LR连接寄存器(Link Register ),保存函数返回地址,当通过BL或BLX指令调用函数时,硬件自动将函数返回地址保存在R14寄存器中。当函数完成时,将LR值传到PC,即可返回到被调用位置。
R13 SP 堆栈指针(Process Stack Pointer),保护现场和恢复现场要用,当发生异常的时候,硬件会把当前状态(使用到寄存器数值)保存在堆栈中,SP保存这个堆栈指针,异常处理完成,通过SP出栈,恢复到异常前的状态,有两种堆栈指针MSP、PSP。(Main_Stack_Pointer 和Process_Stack_Pointer)CPSR 程序状态寄存器(current program status register),CPSR和其他寄存器不一样,其他寄存器是用来存放数据的,都是整个寄存器具有一个含义.而CPSR寄存器是按位起作用的,也就是说,它的每一位都有专门的含义。
函数形参被放在R0-R3中,超过4个参数值传递则放栈里双堆栈指针双堆栈指针对于任务现场保护、恢复现场至关重要。
在这里插入图片描述

可以看下,汇编是如何实现的
在这里插入图片描述

在这里插入图片描述

看下freertos源码,是如何实现的。进入任务切换中断函数,挑选下一个最高优先级的任务(vTaskSwitchContext())去执行

void xPortPendSVHandler( void )
{/* This is a naked function. */__asm volatile("      .syntax unified                               \n""      mrs r0, psp                                    \n""      ldr    r3, pxCurrentTCBConst                   \n" /* Get the location of the current TCB. */"      ldr    r2, [r3]                                \n""      subs r0, r0, #32                              \n" /* Make space for the remaining low registers. */"      str r0, [r2]                                  \n" /* Save the new top of stack. */"      stmia r0!, {r4-r7}                            \n" /* Store the low registers that are not saved automatically. */"      mov r4, r8                                     \n" /* Store the high registers. */"      mov r5, r9                                     \n""      mov r6, r10                                   \n""      mov r7, r11                                    \n""      stmia r0!, {r4-r7}                            \n""      push {r3, r14}                                 \n""      cpsid i                                       \n""      bl vTaskSwitchContext                         \n""      cpsie i                                        \n""      pop {r2, r3}                                  \n" /* lr goes in r3. r2 now holds tcb pointer. */"      ldr r1, [r2]                                  \n""      ldr r0, [r1]                                  \n" /* The first item in pxCurrentTCB is the task top of stack. */"      adds r0, r0, #16                              \n" /* Move to the high registers. */"      ldmia r0!, {r4-r7}                            \n" /* Pop the high registers. */"      mov r8, r4                                    \n""      mov r9, r5                                    \n""      mov r10, r6                                   \n""      mov r11, r7                                  \n""      msr psp, r0                                   \n" /* Remember the new top of stack for the task. */"      subs r0, r0, #32                              \n" "      ldmia r0!, {r4-r7}                            \n" /* Pop low registers.  */"      bx r3                                          \n""      .align 4                                       \n""pxCurrentTCBConst: .word pxCurrentTCB   ");
}

有了上面这些知识,就很容易了,知道移植free时,需要干些什么事情了。

所以我需要,将这些寄存器,给到freertos系统,让它能够管理mcu,除了这些,还需要设置栈空间大小,时钟等等。

二、移植freertos
在这里插入图片描述
重要的文件如下
在这里插入图片描述

找一个工程文件,开始移植。
在这里插入图片描述
修改完如下
在这里插入图片描述

在这里插入图片描述

移植完这些还不够,还需要如下,
在这里插入图片描述

添加完,需要设置一下kell的环境
在这里插入图片描述

接下来,就可以开始编译了
在这里插入图片描述

#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)#include <stdint.h>extern uint32_t SystemCoreClock;
#endif

2、中断文件修改

此时在编译发现还是有错,分别是SysTick 中断、SVC 中断、PendSV 中断报错,这是因为 FreeRTOS里的port.c文件里已经实现同样功能的函数
如下,freertos实现的。
在这里插入图片描述

前面我们讲过,arm架构,有这些异常模式,7种模式,stm32就这这几种,所以,我们找到stm32f4xx_it.c中断文件,先将SVC_Handler、PendSV_Handler屏蔽掉
用freertos的。
在这里插入图片描述
继续往下找,找到SysTick_Handler,SysTick就是FreeRTOS的一个心跳时钟,FreeRTOS 帮我们实现了 SysTick 的启动的配置:

在 port.c文件中已经实现 vPortSetupTimerInterrupt()函数,并且 FreeRTOS 通用的 SysTick 中断服务函数也实现了:
在 port.c 文件中已经实现xPortSysTickHandler()函数,
所以移植的时候只需要我们在 stm32f10x_it.c文件中,实现我们对应(STM32)平台上的 SysTick_Handler()函数即可。按照以下修改,需要添加头文件FreeRTOS.h和task.h:

#include "FreeRTOS.h"
#include "task.h"extern void xPortSysTickHandler(void);
//systick中断服务函数
void SysTick_Handler(void)
{	if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED){xPortSysTickHandler();}
}

最后到FreeRTOSConfig.h,最下面找到 #define xPortSysTickHandler SysTick_Handler将其注释掉,这样就解决了3个重复定义的报错
在这里插入图片描述

所以上面报错,就是重定义而已。

3、钩子函数修改

修改好之后继续编译,发现以下与Hook有关的报错,这是因为FreeRTOS定义了,但是没用到
在这里插入图片描述
我们找到FreeRTOSConfig.h将上面4个定义为0
在这里插入图片描述
此时再编译发现没有错误了。

这是我以前企业级项目,移植后的目录。
在这里插入图片描述

三、任务管理

3.1.1 多任务系统
  
  首先对比不使用RTOS,即裸机编程方式,这种编程方式的框架一般都是在 main()函数中使用一个大循环,在循环中顺序地调用相应的函数以处理相应的事务,这个大循环的部分可以视为应用程序的后台,而应用程序的前台,则是各种中断的中断服务函数。因此单任务系统也叫做前后台系统,前后台系统的运行示意图,如下图所示:
在这里插入图片描述

而加上RTOS之后就变成多任务系统,多任务系统的任务调度器会根据相关的任务调度算法,将 CPU 的使用权分配给任务,在任务获取 CPU 使用权之后的极短时间(宏观角度)后,任务调度器又会将 CPU 的使用权分配给其他任务,如此往复,在宏观的角度看来,就像是多个任务同时运行了一样。
在这里插入图片描述
如上图,发现freertos是可以,支持,中断,高优先级抢占的,linux有多种模式,如果要设置为实时,抢占时,需要设置编译内核。
从上图可以看出,相较于单任务系统而言,多任务系统的任务也是具有优先级的,高优先级的任务可以像中断的抢占一样,抢占低优先级任务的 CPU 使用权;优先级相同的任务则各自轮流运行一段极短的时间(宏观角度),从而产生“同时”运行的错觉。以上就是抢占式调度和时间片调度的基本原理。在任务有了优先级的多任务系统中,用户就可以将紧急的事务放在优先级高的任务中进行处理,那么整个系统的实时性就会大大地提高。

我们来看下freertos系统,设置task的结构体,需要比较关注的是,taskname,栈大小,优先级,以及中断函数。

typedef struct
{void (*TaskSetupFunction)(void);TaskFunction_t  task_func;const char*	    task_name;uint16_t		task_statcksize;void *		    pvParameters;UBaseType_t     uxPriority;TaskHandle_t	taskhandle;uint8_t         queue_size;QueueHandle_t   queue_handle;
}task_info_st;
static task_info_st usrtask[]={{MainTaskSetup, 	DoMainTask,         "man", 	1024*4, NULL, 8, NULL, 160, NULL},{NULL,				gps_task,           "gps",  1024*3, NULL, 3, NULL, 80, NULL},{NULL, 				com_task,          	"com", 	1024,   NULL, 7, NULL, 10, NULL},{NbTaskSetup,		nb_task,          	"nbi", 	1024,   NULL, 4, NULL, 50, NULL},{WifiTaskSetup,		wifi_task,          "wifi", 1024,   NULL, 6, NULL, 8, NULL},{RadioTaskSetup,    RadioTask,          "bt",   1024,   NULL, 5, NULL, 8, NULL},{NULL, 				heart_rate_task,    "hrs", 	512 , 	NULL, 2, NULL, 10, NULL},{isrTaskSetup,		isr_task,			"hisr_task",	512 ,	NULL, configMAX_PRIORITIES-1, NULL, 0, NULL},{ATTaskSetup,		readerLoop, 		"AT_task",	512 ,	NULL, configMAX_PRIORITIES-2, NULL, 0, NULL},
};

xTaskCreate函数原型。

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,const char * const pcName,const uint16_t usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and */{TCB_t *pxNewTCB;BaseType_t xReturn;#if( portSTACK_GROWTH > 0 )     // if stack grows up{pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );if( pxNewTCB != NULL ){pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); if( pxNewTCB->pxStack == NULL ){/* Could not allocate the stack.  Delete the allocated TCB. */vPortFree( pxNewTCB );pxNewTCB = NULL;}}}#else /* portSTACK_GROWTH */ // Stack grows DOWN on M4F{StackType_t *pxStack;/* Allocate space for the stack used by the task being created. */pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 M */if( pxStack != NULL ){/* Allocate space for the TCB. */pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exceptioredundant for some paths. */if( pxNewTCB != NULL ){/* Store the stack location in the TCB. */pxNewTCB->pxStack = pxStack;}else{/* The stack cannot be used as the TCB was not created.  Freeit again. */vPortFree( pxStack );}}else{pxNewTCB = NULL;}}#endif /* portSTACK_GROWTH */if( pxNewTCB != NULL ){#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ){/* Tasks can be created statically or dynamically, so note thistask was created dynamically in case it is later deleted. */pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;}#endif /* configSUPPORT_STATIC_ALLOCATION */prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );prvAddNewTaskToReadyList( pxNewTCB );xReturn = pdPASS;}else{xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;}return xReturn;}

FreeRTOS 任务状态

FreeRTOS 中任务存在四种任务状态,分别为运行态、就绪态、阻塞态和挂起态。
运行态:如果一个任务得到 CPU 的使用权,即任务被实际执行时,那么这个任务处于运行态。如果
运行 RTOS 的 MCU 只有一个处理器核心,那么在任务时刻,都只能有一个任务处理运行态。就绪态:如果一个任务已经能够被执行(不处于阻塞态后挂起态),但当前还未被执行(具有相同优先级或更高优先级的任务正持有 CPU 使用权),那么这个任务就处于就绪态。阻塞态:如果一个任务因延时一段时间或等待外部事件发生,那么这个任务就处理阻塞态。例如任务调用了函数 vTaskDelay(),进行一段时间的延时,那么在延时超时之前,这个任务就处理阻塞态。任务也可以处于阻塞态以等待队列、信号量、事件组、通知或信号量等外部事件。通常情况下,处于阻塞态的任务都有一个阻塞的超时时间,在任务阻塞达到或超过这个超时时间后,即使任务等待的外部事件还没有发生,任务的阻塞态也会被解除。要注意的是,处于阻塞态的任务是无法被运行的。挂起态:任务一般通过函数 vTaskSuspend()和函数 vTaskResums()进入和退出挂起态与阻塞态一样,处于挂起态的任务也无法被运行。

在这里插入图片描述

3.1.3 任务调度器

FreeRTOS一共支持三种任务调度方式,分别为
抢占式调度、
时间片调度
协程式调度。
协程式调度FreeRTOS 官方对协程式调度的特殊说明,翻译过来就是“协程式调度是用于一些资源非常少的设备上的,但是现在已经很少用到了。虽然协程式调度的相关代码还没有被删除,但是今后也不打算继续开发协程式调度。抢占式调度抢占式调度主要时针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务可以抢占优先级低的任务,只有当优先级高的任务发生阻塞或者被挂起,低优先级的任务才可以运行。时间片调度时间片调度主要针对优先级相同的任务,当多个任务的优先级相同时,任务调度器会在每一次系统时钟节拍到的时候切换任务,也就是说 CPU 轮流运行优先级相同的任务,每个任务运行的时间就是一个系统时钟节拍。有关系统时钟节拍的相关内容,在下文讲解 FreeRTOS 系统时钟节拍的时候会具体分析。

在这里插入图片描述
config配置项

configUSE_PREEMPTION:此宏用于设置系统的调度方式。当宏 configUSE_PREEMPTION设置为1时,系统使用抢占式调度;当宏configUSE_PREEMPTION设置为0时,系统使用协程式调度。configUSE_PORT_OPTIMISED_TASK_SELECTION:当宏 configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 0 时,使用通用方法。通用方法是完全使用 C 实现的软件算法,因此支持所用硬件,并且不限制任务优先级的最大值,但效率相较于特殊方法低。当宏 configUSE_PORT_OPTIMISED_TASK_SELECTION 设置为 1 时,使用特殊方法。特殊方法的效率相较于通用方法高,但是特殊方法依赖于一个或多个特定架构的汇编指令,因此特殊方法并不支持所有硬件,并且对任务优先级的最大值一般也有限制,通常为 32。configUSE_TICKLESS_IDLE:当宏 configUSE_TICKLESS_IDLE 设置为 1 时,使能 tickless 低功耗模式;设置为 0 时,tick 中断则会移植运行。tickless 低功耗模式并不适用于所有硬件。configCPU_CLOCK_HZ:此宏应设置为 CPU 的内核时钟频率,单位为 Hz。configSYSTICK_CLOCK_HZ:此宏应设置为 SysTick 的时钟频率,当 SysTick 的时钟源频率与内核时钟频率不同时才可以定义,单位为 Hz。configTICK_RATE_HZ:此宏用于设置 FreeRTOS 系统节拍的中断频率,单位为HzconfigMAX_PRIORITIES:此宏用于定义系统支持的最大任务优先级 数 量 , 最 大 任 务 优 先 级 数 值 为configMAX_PRIORITIES-1。configMINIMAL_STACK_SIZE:此宏用于设置空闲任务的栈空间大小,单位为 wordconfigMAX_TASK_NAME_LEN:此宏用于设置任务名的最大字符数。configUSE_16_BIT_TICKS:此宏用于定义系统节拍计数器的数据类型,当宏 configUSE_16_BIT_TICKS 设置为 1 时,系统节拍计数器的数据类型为 16 位无符号整形;当宏 configUSE_16_BIT_TICKS 设置为 0 时,系统节拍计数器的数据类型为 32 为无符号整型。configIDLE_SHOULD_YIELD:当宏 configIDLE_SHOULD_YIELD 设置为 1 时,在抢占调度下,同等优先级的任务可抢占空闲任务,并延用空闲任务剩余的时间片。configUSE_TASK_NOTIFICATIONS:当宏 configUSE_TASK_NOTIFICATIONS 设置为 1 时,开启任务通知功能。当开启任务通知功能后,每个任务将多占用 8 字节的内存空间。configTASK_NOTIFICATION_ARRAY_ENTRIES:此宏用于定义任务通知数组的大小。configUSE_MUTEXES:此宏用于使能互斥信号量,当宏 configUSE_MUTEXS 设置为 1 时,使能互斥信号量;当宏configUSE_MUTEXS 设置为 0 时,则不使能互斥信号量。configUSE_RECURSIVE_MUTEXES:此宏用于使能递归互斥信号量,当宏 configUSE_RECURSIVE_MUTEXES 设置为 1 时,使能递归互斥信号量;当宏 configUSE_RECURSIVE_MUTEXES 设置为 0 时,则不使能递归互斥信号量。configUSE_COUNTING_SEMAPHORES:此宏用于使能计数型信号量,当宏 configUSE_COUNTING_SEMAPHORES 设置为 1 时,使能计数型信号量;当宏 configUSE_COUNTING_SEMAPHORES 设置为 0 时,则不使能计数型信号量。configUSE_ALTERNATIVE_API:此宏在 FreeRTOS V9.0.0 之后已弃用。configQUEUE_REGISTRY_SIZE:此宏用于定义可以注册的队列和信号量的最大数量。此宏定义仅用于调试使用。configUSE_QUEUE_SETS:此宏用于使能队列集,当宏 configUSE_QUEUE_SETS 设置为 1 时,使能队列集;当宏configUSE_QUEUE_SETS 设置为 0 时,则不使能队列集。configUSE_TIME_SLICING:此宏用于使能时间片调度,当宏 configUSE_TIMER_SLICING 设置为 1 且使用抢占式调度时,使能时间片调度;当宏 configUSE_TIMER_SLICING 设置为 0 时,则不使能时间片调度。configUSE_NEWLIB_REENTRANT:此宏用于为每个任务分配一个 NewLib 重 入 结 构 体 , 当 宏configUSE_NEWLIB_REENTRANT 设置为 1 时,FreeRTOS 将为每个创建的任务的任务控制块中分配一个 NewLib 重入结构体。configENABLE_BACKWARD_COMPATIBILITY:此宏用于兼容 FreeRTOS 老版本的 API 函数。configNUM_THREAD_LOCAL_STORAGE_POINTERS:此宏用于在任务控制块中分配一个线程本地存储指着数组,当此宏被定义为大于 0 时,configNUM_THREAD_LOCAL_STORAGE_POINTERS 为线程本地存储指针数组的元素个数;当宏 configNUM_THREAD_LOCAL_STORAGE_POINTERS 为 0 时,则禁用线程本地存储指针数组。configSTACK_DEPTH_TYPE:此宏用于定义任务堆栈深度的数据类型,默认为 uint16_t。configMESSAGE_BUFFER_LENGTH_TYPE:此宏用于定义消息缓冲区中消息长度的数据类型,默认为 size_t。configSUPPORT_STATIC_ALLOCATION:当宏 configSUPPORT_STSTIC_ALLOCATION 设置为 1 时,FreeRTOS 支持使用静态方式管理内存,此宏默认设置为 0。如果将configSUPPORT_STATIC_ALLOCATION 设置为 1,用户还 需 要 提 供 两 个 回 调 函 数 : vApplicationGetIdleTaskMemory()vApplicationGetTimerTaskMemory()configSUPPORT_DYNAMIC_ALLOCATION:当宏 configSUPPORT_DYNAMIC_ALLOCATION 设置为 1 时,FreeRTOS 支持使用动态方式管理内存,此宏默认设置为 1。configTOTAL_HEAP_SIZE:此宏用于定义用于 FreeRTOS 动态内存管理的内存大小,即 FreeRTOS 的内存堆,单位为Byte。configAPPLICATION_ALLOCATED_HEAP:此宏用于自定义 FreeRTOS 的内存堆,当宏 configAPPLICATION_ALLOCATED_HEAP 设置为 1 时,用户需要自行创建 FreeRTOS 的内存堆,否则 FreeRTOS 的内存堆将由编译器进行分配。利用此宏定义,可以使用 FreeRTOS 动态管理外扩内存。configSTACK_ALLOCATION_FROM_SEPARATE_HEAP:此宏用于自定义动态创建和删除任务时,任务栈内存的申请与释放函数pvPortMallocStack()vPortFreeStack(),当宏configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 设置为1是,用户需提供 pvPortMallocStack()vPortFreeStack()函数。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1、函数xTaskCreate()
  此函数用于使用动态的方式创建任务,任务的任务控制块以及任务的栈空间所需的内存,均由 FreeRTOS 从FreeRTOS管理的堆中分配,若使用此函数,需要在FreeRTOSConfig.h文件中将宏configSUPPORT_DYNAMIC_ALLOCATION配置为 1。此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。函数原型如下所示:

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,const char * const pcName, const configSTACK_DEPTH_TYPE usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask)

在这里插入图片描述
在这里插入图片描述

2、函数xTaskCreateStatic()

此函数用于使用静态的方式创建任务,任务的任务控制块以及任务的栈空间所需的内存,需要由用户分配提供, 若使用此函数,需 要 在FreeRTOSConfig.h文件中将宏configSUPPORT_STATIC_ALLOCATION配置为 1。此函数创建的任务会立刻进入就绪态,由任务调度器调度运行。函数原型如下所示:

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */const uint32_t ulStackDepth,void * const pvParameters,UBaseType_t uxPriority,StackType_t * const puxStackBuffer,StaticTask_t * const pxTaskBuffer )

函数xTaskCreateStatic()的形参描述,如下表所示:
在这里插入图片描述

3、函数vTaskDelete()

此函数用于删除已被创建的任务,被删除的任务将被从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除,要注意的是,空闲任务会负责释放被删除任务中由系统分配的内存,但是由用户在任务删除前申请的内存,则需要由用户在任务被删除前提前释放,否则将导致内存泄露。若使用此函数,需要在FreeRTOSConfig.h文件中将宏INCLUDE_vTaskDelete配置为 1。函数原型如下所示:

void vTaskDelete(TaskHandle_t xTaskToDelete);

4、 挂起和恢复任务相关函数

有时候需要暂停某个任务的运行,如果使用任务删除,那么任务变量的值就丢了。FreeRTOS使用任务挂起和恢复来达到对应效果。FreeRTOS 中用于挂起和恢复任务的 API 函数如下表所示:
在这里插入图片描述
4、函数vTaskSuspend()
  此函数用于 挂 起 任务, 若使用此函数,需 要 在 FreeRTOSConfig.h 文件中将宏INCLUDE_vTaskSuspend配置为 1。无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复。此函数并不支持嵌套,不论使用此函数重复挂起任务多少次,只需调用一次恢复任务的函数,那么任务就不再被挂起。函数原型如下所示:

void vTaskSuspend(TaskHandle_t xTaskToSuspend)

形参xTaskToSuspend:待挂起任务的任务句柄

5、函数vTaskResume()
  此函数用于在任务中恢复被挂起的任务,若使用此函数,需要在 FreeRTOSConfig.h 文件中将宏INCLUDE_vTaskSuspend配置为 1。不论一个任务被函数 vTaskSuspend()挂起多少次,只需要使用函数 vTakResume()恢复一次,就可以继续运行。函数原型如下所示:
形参xTaskToResume:待恢复任务的任务句柄

6、函数xTaskResumeFromISR()
  此函数用于在中断中恢复被挂起的任务,若使用此函数,需要在 FreeRTOSConfig.h 文件中将宏INCLUDE_xTaskResumeFromISR配置为 1。不论一个任务被函数 vTaskSuspend()挂起多少次,只需要使用函数 vTakResumeFromISR()恢复一次,就可以继续运行。函数原型如下所示:

BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)

形参xTaskToResume:待恢复任务的任务句柄

四、内核控制函数
  FreeRTOS 内核的控制函数描述,如下表所示:

在这里插入图片描述在这里插入图片描述

在任务创建好之后就开启任务调度。临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段,比如有的外设的初始化需要严格的时序,初始化过程中不能被打断。FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOS系统本身就有很多的临界段代码,这些代码都加了临界段代码保护,我们在写自己的用户程序的时候有些地方也需要添加临界段代码保护。

4 示例应用程序主要对main.c文件修改,加了一些FreeRTOS任务函数#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define LED0_TASK_PRIO		2
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);//任务优先级
#define LED1_TASK_PRIO		3
//任务堆栈大小	
#define LED1_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);//任务优先级
#define FLOAT_TASK_PRIO		4
//任务堆栈大小	
#define FLOAT_STK_SIZE 		128
//任务句柄
TaskHandle_t FLOATTask_Handler;
//任务函数
void float_task(void *pvParameters);int main(void)
{ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4delay_init(168);		//初始化延时函数uart_init(115200);     	//初始化串口LED_Init();		        //初始化LED端口//创建开始任务xTaskCreate((TaskFunction_t )start_task,            //任务函数(const char*    )"start_task",          //任务名称(uint16_t       )START_STK_SIZE,        //任务堆栈大小(void*          )NULL,                  //传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       //任务优先级(TaskHandle_t*  )&StartTask_Handler);   //任务句柄              vTaskStartScheduler();          //开启任务调度
}//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建LED0任务xTaskCreate((TaskFunction_t )led0_task,     	(const char*    )"led0_task",   	(uint16_t       )LED0_STK_SIZE, (void*          )NULL,				(UBaseType_t    )LED0_TASK_PRIO,	(TaskHandle_t*  )&LED0Task_Handler);   //创建LED1任务xTaskCreate((TaskFunction_t )led1_task,     (const char*    )"led1_task",   (uint16_t       )LED1_STK_SIZE, (void*          )NULL,(UBaseType_t    )LED1_TASK_PRIO,(TaskHandle_t*  )&LED1Task_Handler);        //浮点测试任务xTaskCreate((TaskFunction_t )float_task,     (const char*    )"float_task",   (uint16_t       )FLOAT_STK_SIZE, (void*          )NULL,(UBaseType_t    )FLOAT_TASK_PRIO,(TaskHandle_t*  )&FLOATTask_Handler);  vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//LED0任务函数 
void led0_task(void *pvParameters)
{while(1){LED0=~LED0;vTaskDelay(500);}
}   //LED1任务函数
void led1_task(void *pvParameters)
{while(1){LED1=0;vTaskDelay(200);LED1=1;vTaskDelay(800);}
}//浮点测试任务
void float_task(void *pvParameters)
{static float float_num=0.00;while(1){float_num+=0.01f;printf("float_num的值为: %.4f\r\n",float_num);vTaskDelay(1000);}
}

参考了一些,别人的文章,我自己能分析写出来,但是懒,不想造轮子,以前研究过源码,对进程管理,有比较深刻的认识。
原文链接:https://blog.csdn.net/weixin_44567668/article/details/135419275

这是我的实战项目,我把它简化了,具体任务,没有,只有任务名

int main(void)
{// Set the clock frequency// Set the default cache configuration// Enable the floating point module, and configure the core for lazy// stacking.// Flash bank power set.// Enable printing to the console.// Initialize plotting interface.//initalize gpio// Run the application.run_tasks();// We shouldn't ever get here.while (1){}
}void run_tasks(void)
{// Set some interrupt priorities before we create tasks or start the scheduler.// Create essential tasks.xTaskCreate(setup_task, "Setup", 512, 0, 3, &xSetupTask);// Start the scheduler.vTaskStartScheduler();
}void setup_task(void *pvParameters)
{uint16_t i;// Run setup functions.for(i = 0; i < sizeof(usrtask) / sizeof(usrtask[0]); i++){if(usrtask[i].TaskSetupFunction != NULL){usrtask[i].TaskSetupFunction(); //在启动任务时,如果需要先初始化,比如硬件,比如gpio等等。}if(usrtask[i].queue_size != 0){usrtask[i].queue_handle = xQueueCreate(usrtask[i].queue_size, sizeof(wolf_evt_t));}}// Create the functional tasksfor(i = 0; i < sizeof(usrtask)/sizeof(usrtask[0]); i ++){if(i == Mod_Bt){usrtask[i].taskhandle = radio_task_handle;continue;}xTaskCreate(usrtask[i].task_func, usrtask[i].task_name, usrtask[i].task_statcksize, usrtask[i].pvParameters, usrtask[i].uxPriority, &usrtask[i].taskhandle);usrtask[i].task_func, usrtask[i].task_name);usrtask[i].task_statcksize, usrtask[i].pvParameters, usrtask[i].uxPriority,usrtask[i].taskhandle);}// The setup operations are complete, so suspend the setup task now.vTaskSuspend(NULL);while (1);
}//启动创建下面这些任务
static task_info_st usrtask[]={{MainTaskSetup, 	DoMainTask,         "man", 	1024*4, NULL, 8, NULL, 160, NULL},{NULL,				gps_task,           "gps",  1024*3, NULL, 3, NULL, 80, NULL},{NULL, 				com_task,          	"com", 	1024,   NULL, 7, NULL, 10, NULL},{NbTaskSetup,		nb_task,          	"nbi", 	1024,   NULL, 4, NULL, 50, NULL},{WifiTaskSetup,		wifi_task,          "wifi", 1024,   NULL, 6, NULL, 8, NULL},{NULL, 				heart_rate_task,    "hrs", 	512 , 	NULL, 2, NULL, 10, NULL},{ATTaskSetup,	readerLoop, 		"AT_task",	512 ,	NULL, configMAX_PRIORITIES-2, NULL, 0, NULL},
};void MainTaskSetup(void)
{
}static void main_init(void)
{wolf_evt_t ev = {0,};//start appev.evtId = REVT_ADP_APP_START;SendEvtToMod(Mod_App, &ev);
}void GetEvtForMod (wolf_module_e mod, wolf_evt_t* pev)
{if(mod >= Mod_Num) return ;while(!xQueueReceive(usrtask[mod].queue_handle, pev, portMAX_DELAY));//轮询队列,是否就绪,就绪,就执行
}void DoMainTask(void *pvParameters)
{wolf_evt_t ev = {0,};gsnsr_init();vibmotor_start_default();InitProtocolEventHandler();initKey();GUI_Init();GUI_UC_SetEncodeUCS2();setLanguage(1); // 0 ÖÐÎÄ£¬1 Ó¢ÎÄdrv_rtc_enable_alm_1sec();main_init();while(1){GetEvtForMod(Mod_App, &ev);app_dbgEvt(1, ev.evtId);do{if(app_dispNbMsg(&ev)) break;//类似状态机if(app_dispAppMsg(&ev)) break;if(app_dispGpsMsg(&ev)) break;if(app_dispBtMsg(&ev)) break;if(app_dispWifiMsg(&ev)) break;if(app_dispMMIMsg(&ev)) break;continue;}while(0);app_dbgEvt(0, ev.evtId);}
}static void app_dbgEvt (uint8_t start, uint32_t evtId)
{if (start) {wolf_dbgEvt(0x3d3d3d10);wolf_dbgEvt(evtId);}else wolf_dbgEvt(0x3d3d3d11);
}uint8_t app_dispAppMsg (wolf_evt_t* pev)
{uint8_t ret = 1;switch (pev->evtId){case REVT_ADP_APP_START:app_sys_startInd(pev);break;case REVT_COM_APP_UART_SENDDATA_RSP:break;case REVT_COM_APP_UART_RECVDATA:break;case REVT_COM_APP_UART_IND:break;case REVT_COM_APP_GSENSOR_IND:break;case REVT_COM_APP_MISC_IND:app_sys_miscInd(pev);break;case REVT_APP_APP_TIMEOUT_IND:break;case REVT_ADP_APP_GSNSR_IND:app_gsnsr_ind(pev);break;case REVT_ISR_APP_TOUCH_START:watchserver_first_socket();break;case REVT_HRS_APP_HRS_GET_DATA:call_Run_Report_CB_HR(pev->p1);break;default:ret = 0;break;}return ret;
}

ok,主线终于领情了,还好几个nb,gps等等的,任务,在别的地方。想要研究透freertos,其实并不难,但是,我的方向,暂时linux方向
,这个系统,要是能研究透,年薪40w,真不难。

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

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

相关文章

安利一款开源企业级的报表系统SpringReport

SpringReport是一款企业级的报表系统&#xff0c;支持在线设计报表&#xff0c;并绑定动态数据源&#xff0c;无需写代码即可快速生成想要的报表&#xff0c;可以支持excel报表和word报表两种格式&#xff0c;同时还可以支持excel多人协同编辑&#xff0c;后续考虑实现大屏设计…

css:基础

前言 我们之前其实也可以写出一个看起来算是一个网页的网页&#xff0c;为什么我们还要学css&#xff1f; CSS&#xff08;Cascading Style Sheets&#xff09;也叫层叠样式表&#xff0c;是负责美化的&#xff0c;我们之前说html就是一个骨架&#xff0c;css就可以用来美化网…

qt QCompleter详解

1、概述 QCompleter是Qt框架中的一个类&#xff0c;用于为文本输入提供自动完成功能。它可以与Qt的输入控件&#xff08;如QLineEdit、QTextEdit等&#xff09;结合使用&#xff0c;根据用户的输入实时过滤数据源&#xff0c;并在输入控件下方或内部显示补全建议列表。用户可以…

探索 Move 编程语言:智能合约开发的新纪元

目录 引言 一、变量的定义 二、整型 如何在Move中表示小数和负数&#xff1f; 三、运算符 as运算符 布尔型 地址类型 四、什么是包&#xff1f; 五、什么是模块&#xff1f; 六、如何定义方法&#xff1f; 方法访问权限控制 init方法 总结 引言 Move 是一种专为区…

ETLCloud异常问题分析ai功能

在数据处理和集成的过程中&#xff0c;异常问题的发生往往会对业务运营造成显著影响。为了提高ETL&#xff08;提取、转换、加载&#xff09;流程的稳定性与效率&#xff0c;ETLCloud推出了智能异常问题分析AI功能。这一创新工具旨在实时监测数据流动中的潜在异常&#xff0c;自…

遥控器工作核心技术以及传输信号算法详解!

一、遥控器传输信号算法 无线通信技术&#xff1a;无人机遥控器信号传输算法主要基于无线通信技术&#xff0c;通过特定的调制、编码和信号处理技术&#xff0c;将遥控器的操作指令转化为无线电信号&#xff0c;并传输给被控制设备。被控制设备接收到信号后&#xff0c;再将其…

Java中的线程安全问题(如果想知道Java中有关线程安全问题的基本知识,那么只看这一篇就足够了!)

前言&#xff1a;多线程编程已经广泛开始使用&#xff0c;其可以充分利用系统资源来提升效率&#xff0c;但是线程安全问题也随之出现&#xff0c;它直接影响了程序的正确性和稳定性&#xff0c;需要对其进行深入的理解与解决。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解…

基于STM32设计的森林火灾监测系统

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成【4】研究背景与意义 1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】设备端开发【2】上位机开发 1.5 参考文献 二、部署华为云物联网平台2.1 物联网平台介绍2.2 开通物联网服务…

Webserver(4.9)本地套接字的通信

目录 本地套接字 本地套接字 TCP\UDP实现不同主机、网络通信 本地套接字实现本地的进程间的通信&#xff0c;类似的&#xff0c;一般采用TCP的通信流程 生成套接字文件 #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h&…

第十五届蓝桥杯C/C++B组题解——数字接龙

题目描述 小蓝最近迷上了一款名为《数字接龙》的迷宫游戏&#xff0c;游戏在一个大小为N N 的格子棋盘上展开&#xff0c;其中每一个格子处都有着一个 0 . . . K − 1 之间的整数。游戏规则如下&#xff1a; 从左上角 (0, 0) 处出发&#xff0c;目标是到达右下角 (N − 1, N …

jmeter常用配置元件介绍总结之安装插件

系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之取样器 jmeter常用配置元件介绍总结之安装插件 1.下载插件2.安装插件管理包3.不用插件管理包&#xff0c;直接官网插件下载安装 1.下载插件 jm…

InnoDB 存储引擎<六> Redo log

目录 关于Redo Log 的一些其余问题 小结 本篇承接自InnoDB存储引擎<五>的内容 InnoDB 存储引擎&#xff1c;五&#xff1e; 关于Redo Log 的一些其余问题 4.不同⽇志类型对应了哪些操作&#xff1f; 分析过程&#xff1a; 1.⽇志类型总体可以分为三⼤类&#xff0c;…

Linux挖矿病毒(kswapd0进程使cpu爆满)

一、摘要 事情起因:有台测试服务器很久没用了&#xff0c;突然监控到CPU飙到了95以上&#xff0c;并且阿里云服务器厂商还发送了通知消息&#xff0c;【阿里云】尊敬的xxh: 经检测您的阿里云服务&#xff08;ECS实例&#xff09;i-xxx存在挖矿活动。因此很明确服务器中挖矿病毒…

变电站接地电阻监测装置-输电铁塔接地电阻监测装置:实时监测,预防故障

变电站接地电阻监测装置 接地电阻对电力系统的安全和稳定性至关重要&#xff0c;但在高压环境和极端气候下&#xff0c;接地系统可能出现性能下降&#xff0c;增加故障和跳闸的风险。传统的人工检测方法常常无法及时发现这些问题&#xff0c;并且操作繁琐。为此&#xff0c;我…

练习LabVIEW第四十三题

学习目标&#xff1a; 模拟红绿灯&#xff0c;红灯亮十秒&#xff0c;绿灯亮五秒&#xff0c;交替&#xff0c;并用波形图将波形显示 开始编写&#xff1a; 前面板 两个指示灯&#xff0c;一个红色&#xff0c;一个绿色&#xff0c;一个波形图&#xff1b; 程序框图 创建…

MySQL45讲 第十一讲 怎么给字符串字段加索引?

文章目录 MySQL45讲 第十一讲 怎么给字符串字段加索引&#xff1f;一、引言二、前缀索引&#xff08;一&#xff09;概念与创建方式&#xff08;二&#xff09;数据结构与存储差异&#xff08;三&#xff09;确定前缀长度的方法 三、前缀索引对覆盖索引的影响四、其他索引创建方…

STM32CUBEIDE FreeRTOS操作教程(八):queues多队列

STM32CUBEIDE FreeRTOS操作教程&#xff08;八&#xff09;&#xff1a;queues多队列 STM32CUBE开发环境集成了STM32 HAL库进行FreeRTOS配置和开发的组件&#xff0c;不需要用户自己进行FreeRTOS的移植。这里介绍最简化的用户操作类应用教程。以STM32F401RCT6开发板为例&#…

2.Python解释器

python解释器程序&#xff0c;用来翻译python代码&#xff0c;并提交给计算机执行。 上一篇博客就是安装了python解释器程序 写一个python文件&#xff0c;在文件中写入多行代码并执行&#xff1a; 进入python后&#xff0c;输入exit()命令退出

JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习

先简单记录下简单使用跟测试&#xff0c;后续再补充具体&#xff0c;最近有用到&#xff0c;简单来说就是后端(服务端)编写个发射器&#xff0c;实现一次请求&#xff0c;一直向前端客户端发射数据&#xff0c;直到发射器执行完毕&#xff0c;模拟ai一句一句回复的效果 Respon…

Liunx:文件fd、重定向、管道

文件fd&#xff1a; 操作系统运行中一定存在着许多被打开的文件&#xff0c;这些文件需要被管理。一个进程会打开若干个文件。一个文件如果在操作系统中被打开&#xff0c;那么必须给该文件创建一个文件对象&#xff0c;包含被打开文件的各种属性。那么进程与文件的关系就变成…