FreeRTOS 源码结构解析与 STM32 HAL 库移植实践(任务创建、删除篇)

1. FreeRTOS源码结构介绍

1.1 下载源码

​ 点击官网地址,选择 FreeRTOS 202212.01非 LTS 版本(非长期支持版),因为这个版本有着最全的历程和更多型号处理器支持。

1.2 文件夹结构介绍

​ 下载后主文件 FreeRTOSv202212.01 下包含以下文件:

名称描述
FreeRTOSFreeRTOS 的核心源码,提供实时操作系统的**所有必要组件****,如任务调度、内存管理、信号量、队列等。
FreeRTOS-PlusFreeRTOS 的附加组件(非必要),如文件系统、TCP/IP 协议栈、图形界面库等。 提供了一些额外的功能,可以扩展应用场景。
tools这个文件夹通常包含一些辅助工具,用于支持 FreeRTOS 的开发,比如配置生成工具、调试工具、自动化脚本等。
其他文件一些网页链接和历史版本记录,项目以来关系,编译格式文件

1.2 FreeRTOS文件夹

​ 由于上面文件中仅有 FreeRTOS 是建立最小 FreeRTOS 例程的必要文件夹,因此对该文件下内容进一步做说明(在该文件下的 README.md 文件有官方的英文说明):

名称描述
DemoFreeRTOS 演示例程,支持多种芯片架构、多种型号芯片
SourceFreeRTOS 源码,最重要的文件夹(包含着移植FreeRTOS的必要文件)
Test公用以及移植层测试代码
其他文件许可证文件License,网页链接,说明文件

1.3 Source 文件夹

​ Source 文件夹存储着移植最小 FreeRTOS 系统的必要文件,可以说只要有Source文件就可以完成对 FreeRTOS 系统的最基础的功能移植,官网上也有说明所需的至少五个必要文件。

项目必须至少包含以下源文件

  • FreeRTOS/Source/tasks.c
  • FreeRTOS/Source/queue.c
  • FreeRTOS/Source/list.c
  • FreeRTOS/Source/portable/[compiler]/[architecture]/port.c
  • FreeRTOS/Source/portable/MemMang/heap_x.c 其中 “x” 可以是 1、2、3、4 或 5
名称描述
includeFreeRTOS 的公共头文件,定义了 FreeRTOS 的 API 和常用的宏、数据结构、常量等。
portableFreeRTOS 为不同硬件平台(微控制器、处理器架构等)提供适配层的地方。每个目标硬件平台(如 ARM Cortex-M、AVR、RISC-V 等)都会有一个专门的适配代码,用于处理硬件相关的操作,例如上下文切换、时钟中断、任务堆栈管理等。
croutine协程相关文件
event_groups事件相关文件
list提供与链表相关的功能实现。FreeRTOS 中许多数据结构(如任务控制块、定时器等)都使用链表来管理。
queue提供队列功能的实现,任务之间可以通过队列进行通信和数据交换。
stream_buffer流式缓冲区相关文件
taskFreeRTOS 的任务调度和任务管理的核心实现。
timersFreeRTOS 定时器管理的实现,定时器可用于执行周期性任务或超时任务。

1.4 portable文件夹

该文件的作用与意义:

  • 平台适配portable 文件夹的主要目的是提供硬件相关的低级实现,使得 FreeRTOS 能够在不同硬件平台上运行。不同的硬件平台可能有不同的上下文切换机制、定时器控制方法等,因此 FreeRTOS 必须为每个硬件平台提供专门的实现。
  • 跨平台支持:FreeRTOS 操作系统归根到底是一个软件层面的东西,需要跟硬件联系在一起, portable 文件夹里面的东西就是连接桥梁。通过 portable 文件夹,FreeRTOS 可以在多种架构上运行,如 ARM Cortex-M、AVR、RISC-V 等,确保嵌入式开发人员能够在多种平台上使用 FreeRTOS

对于在 Keil 平台开发 STM32 系列芯片,所以我们需要选择 Keil 文件夹,但是 Keil 文件夹下只有一个文本文件,指示我们去查看 RVDS 文件夹。此外 portable 文件夹下还有一个必须文件 MemMang。

名称描述
Keil指向 RVDS 文件夹
RVDS是 ARM 公司提供的一个嵌入式开发工具链,广泛应用于 ARM Cortex-M 系列和其他 ARM 架构的微控制器。
MemMang包含与内存管理相关的文件,负责 FreeRTOS 中的动态内存分配策略。
1.4.1 RVDS 文件夹

​ RVDS 文件夹包含了各种处理器相关的文件夹,FreeRTOS 是一个软件,单片机是一个硬件,FreeRTOS 要想运行在一个单片机上面,它们就必须关联在一起。 关联还是得通过写代码来关联,这部分关联的文件叫接口文件,通常由汇编和 C 联合编写。这些接口文件都是跟硬件密切相关的,不同的硬件接口文件是不一样的,但都大同小异。编写这些接口文件的过程我们就叫移植,移植的过程通常由 FreeRTOS 和 mcu 原厂 的人来负责,移植好的这些接口文件就放在 RVDS 这个文件夹的目录下。

请添加图片描述

​ FreeRTOS 为我们提供了cortex-m0、m3、m4和m7等内核的单片机的接口文件,根据 mcu的内核选择对应的接口文件即可。其实准确来说,不能够叫移植,应该叫使用官方的 移植,因为这些跟硬件相关的接口文件,RTOS官方都已经写好了,我们只是使用而已。

​ 以ARM_CM3这个文件夹为例,里面只有“port.c”与“portmacro.h”两个文件:

  • port.c文件:里面的内容是由FreeRTOS官方的技术人员为Cortex-M3内核的处理 器写的接口文件,里面核心的上下文切换代码是由汇编语言编写而成,对技术员的要求比 较高,我们只是使用的话只需拷贝过来用即可
  • portmacro.h文件:port.c文件对应的头文件,主要是一些数据类型和宏定义
1.4.2 MemMang文件夹

​ MemMang 文件夹下存放的是跟内存管理相关的,总共有五个 heap文件以及一个 readme 说明文件。

请添加图片描述
​ 这五个heap文件在移植的时候必须使用一个,因为FreeRTOS在创建内核对象的时候 使用的是动态分配内存,而这些动态内存分配的函数则在这几个文件里面实现,不同的分 配算法会导致不同的效率与结果,我们选用heap4.c即可

2. FreeRTOS 基于 HAL 库移植步骤示例

2.1 添加必要的源码文件

  1. G:\FreeRTOSv202212.01\FreeRTOS\Source\include (整个文件夹)
  2. G:\FreevRTOSv202212.01\FreeRTOS\Source\croutine.c
  3. G:\FreeRTOSv202212.01\FreeRTOS\Source\event_groups.c
  4. G:\FreeRTOSv202212.01\FreeRTOS\Source\list.c
  5. G:\FreeRTOSv202212.01\FreeRTOS\Source\queue.c
  6. G:\FreeRTOSv202212.01\FreeRTOS\Source\stream_buffer.c
  7. G:\FreeRTOSv202212.01\FreeRTOS\Source\tasks.c
  8. G:\FreeRTOSv202212.01\FreeRTOS\Source\timers.c
  • G:\FreeRTOSv202212.01\FreeRTOS\Source\portable\RVDS\ARM_CM3\port.c
  • G:\FreeRTOSv202212.01\FreeRTOS\Source\portable\RVDS\ARM_CM3\portmacro.h

​ 前八个内容是通用内容,而后面俩个portable文件夹下内容需根据单片机内核架构的不同和开发环境的编译软件不同来进行适合的选择.
​ 在keil工程文件中加入这些.c文件,include这些.h源文件,就完成了系统源码文件的移植,接下来将进行编写 FreeRTOS 的工程配置文件.

2.2 系统配置文件 FreeRTOSConfig.h 的编写(必要)

FreeRTOSConfig.h 是 FreeRTOS 配置文件,负责定义 FreeRTOS 内核运行时的各种参数和选项。它允许用户根据具体的硬件平台、应用需求和性能目标来定制 FreeRTOS 的行为。这个文件是 每个 FreeRTOS 项目必需的配置文件,它提供了调度策略、内存管理、任务优先级、时钟设置等重要的内核配置。

​ 通过一些对变量的宏定义来选定是否开启指定功能(代码实现其实是通过上一节那些源码文件中的条件编译),整体的配置大致可以分为三类:

  • INCLUDE 开头:一般是“INCLUDE_函数名”,函数的使能,1 表示可用,0 表示禁用。
  • config 开头:FreeRTOS 的一些功能配置,比如基本配置、内存配置、钩子配置、中断配置等。
  • 其他配置:PendSV 宏定义、SVC 宏定义。

FreeRTOSConfig.h 中配置如下:

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H/*-----------------------------------------------------------* 应用程序特定的定义。** 这些定义应该根据您的硬件和应用程序需求进行调整。** 这些参数在FreeRTOS API文档的“配置”部分中有详细描述。** 请参阅:http://www.freertos.org/a00110.html*----------------------------------------------------------*/#define configUSE_PREEMPTION		1      /* 启用抢占式调度 */
#define configUSE_IDLE_HOOK			0      /* 禁用空闲钩子 */
#define configUSE_TICK_HOOK			0      /* 禁用滴答钩子 */
#define configCPU_CLOCK_HZ			( ( unsigned long ) 72000000 )    /* CPU时钟频率,72 MHz */
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )   /* 时钟节拍频率,每秒1000次 */
#define configMAX_PRIORITIES		( 5 )    /* 最大优先级数 */
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 128 )   /* 最小栈大小 */
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 17 * 1024 ) )   /* 堆大小,17KB */
#define configMAX_TASK_NAME_LEN		( 16 )    /* 任务名称最大长度 */
#define configUSE_TRACE_FACILITY	0      /* 禁用追踪功能 */
#define configUSE_16_BIT_TICKS		0      /* 禁用16位滴答 */
#define configIDLE_SHOULD_YIELD		1      /* 空闲任务应让出CPU *//* 设置以下定义为1以包含API函数,或设置为0以排除API函数 */#define INCLUDE_vTaskPrioritySet		1      /* 启用设置任务优先级的API */
#define INCLUDE_uxTaskPriorityGet		1      /* 启用获取任务优先级的API */
#define INCLUDE_vTaskDelete				1      /* 启用删除任务的API */
#define INCLUDE_vTaskCleanUpResources	0      /* 禁用清理任务资源的API */
#define INCLUDE_vTaskSuspend			1      /* 启用挂起任务的API */
#define INCLUDE_vTaskDelayUntil			1      /* 启用延迟直到的API */
#define INCLUDE_vTaskDelay				1      /* 启用延迟的API *//* 这是Cortex-M3 NVIC的原始值。值的范围为255(最低)到0(最高)。 */
#define configKERNEL_INTERRUPT_PRIORITY 		255    /* 内核中断优先级 *//* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY 不能设置为零 !!!!
请参阅 http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html。 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191    /* 等价于0xb0,或优先级11 *//* 这是根据ST库使用的值,允许16个优先级值,0到15。
这必须与configKERNEL_INTERRUPT_PRIORITY设置相匹配。
此处15对应最低的NVIC值255。 */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15     /* 库内核中断优先级 *//* 由于 stm32 和 FreeRTOS 对变量命名的不同,我们进行宏定义 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler/* 开启任务调度器API,允许查询 FreeRTOS 调度器的状态 */
#define INCLUDE_xTaskGetSchedulerState 1#endif /* FREERTOS_CONFIG_H */

2.3 在SysTick_Handler函数中添加

​ 由于 stm32f1xx_it.c 中有 PendSV_Handler 和 SVC_Handler 函数,我们上面进行的宏定义会使发生重复定义,所以删去 stm32f1xx_it.c 中这俩个同名函数。并且在该文件下的 SysTick_Handler 函数体中添加内容:

if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{xPortSysTickHandler();
}

​ if 语句确保 FreeRTOS 的 SysTick 仅在调度器启动后才执行,避免在调度器未启动时调用 xPortSysTickHandler() 造成错误。xPortSysTickHandler() 函数是 FreeRTOS 系统滴答(SysTick)中断处理函数,用于 管理任务调度、时间片轮转、任务延时、时间基准 等功能。

xPortSysTickHandler() 主要功能

  1. 递增系统滴答计数(xTickCount

    • FreeRTOS 使用 滴答计数 作为时间基准,每次 SysTick 触发时都会增加 xTickCount
    • xTickCount 用于任务调度、定时器、任务延时(vTaskDelay())。
  2. 检查任务延时是否结束

    • 如果某个任务调用了 vTaskDelay()vTaskDelayUntil(),它会进入 阻塞状态,直到 xTickCount 递增到设定的时间。
    • xPortSysTickHandler() 负责检查 哪些任务的延时时间到了,并将其移动到 就绪 状态。
  3. 时间片轮转(任务切换)

    • FreeRTOS 采用 抢占式调度,多个任务可以 时间片轮转
    • xPortSysTickHandler() 负责检查 是否需要任务切换,如果当前任务的时间片到了,就触发任务切换。
  4. 触发上下文切换(PendSV 中断)

    • 如果 xPortSysTickHandler() 发现 需要切换任务,它会 触发 PendSV 进行任务切换

    • PendSV_Handler() 会完成实际的任务切换 (保存旧任务上下文,恢复新任务上下文)


3.FreeRTOS 数据类型与命名规范笔记

3.1 FreeRTOS 数据类型

3.1.1 TickType_t(系统滴答计数类型)
  • 作用:用于记录系统的滴答计数(Tick Count)。
  • 定义:
    • configUSE_16_BIT_TICKS = 116 位无符号类型(最大 65,535 Tick)。
    • configUSE_16_BIT_TICKS = 032 位无符号类型(默认,最大 4,294,967,295 Tick)。
3.1.2 BaseType_t(基本类型)
  • 作用:最适合 CPU 架构的 有符号整数,用于 API 返回值、状态标志等。
  • 定义:
    • 32 位架构 → int32_t
    • 16 位架构 → int16_t
3.1.3 UBaseType_t(无符号基本类型)
  • 作用BaseType_t 的无符号版本,常用于计数、索引等。
  • 定义:
    • 32 位架构 → uint32_t
    • 16 位架构 → uint16_t
3.1.4 StackType_t(堆栈数据类型)
  • 作用:存储任务堆栈的数据类型,适应不同架构的栈结构。
  • 定义:
    • 32 位架构 → uint32_t
    • 16 位架构 → uint16_t

3.2 FreeRTOS 命名规范

3.2.1 变量命名
  • 驼峰式命名,使用完整单词
  • 前缀规则:
    • uluint32_t(unsigned long)
    • usuint16_t(unsigned short)
    • ucuint8_t(unsigned char)
    • x → 非标准整数类型(如 BaseType_tTickType_t
    • uxUBaseType_t(无符号 BaseType_t
    • e → 枚举类型
    • p → 指针(如 pus 表示 uint16_t *
    • cunsigned char(仅存 ASCII)
    • pcchar *(仅存 ASCII 字符串)
3.2.2 函数命名
  • 驼峰式命名,使用完整单词
  • 前缀规则:
    • prv → 静态私有函数
    • v → 返回 void
    • API 以文件名为前缀,如 vTaskDelay():
      • vvoid
      • Task → 来自 task.c
      • Delay → 该函数用于任务延时
3.2.3 宏命名
  • 全部大写,使用下划线分隔
  • 前缀规则:
    • configFreeRTOSConfig.h 配置项(如 configUSE_PREEMPTION)。
    • 其他文件前缀为小写。

4. FreeRTOS 的任务创建和删除

4.1 任务创建和删除的 API 函数以及 TCB 介绍

​ 任务的创建和删除本质就是调用 FreeRTOS 的 API 函数,主要如下:

API 函数描述
xTaskCreate()动态方式创建任务
xTaskCreateStatic()静态方式创建任务
vTaskDelete()删除任务
  • 动态创建任务

    • 任务的 任务控制块(TCB)任务栈空间FreeRTOS 管理的堆 中分配。
  • 静态创建任务

    • 任务的 任务控制块(TCB)任务栈空间用户手动分配,不使用 FreeRTOS 的堆。
  • 删除任务

    • 通过输入 任务控制块(TCB) 来指导删除任务,输入 NULL 删除任务自身
任务控制块 tskTaskControlBlock(TCB) 介绍:
  • 每个任务都对应一个TCB,用于存储 每个任务的状态信息,类似身份证

  • 调度器通过 tskTaskControlBlock 管理任务状态(就绪、运行、挂起、阻塞等)

  • 任务切换时,内核保存并恢复 tskTaskControlBlock 中的栈指针,确保任务运行状态正确

    typedef struct tskTaskControlBlock
    {
    volatile StackType_t * pxTopOfStack; /* 任务栈栈顶,必须为 TCB 的第一个
    成员 */
    ListItem_t xStateListItem; /* 任务状态列表项 */
    ListItem_t xEventListItem; /* 任务事件列表项 */
    UBaseType_t uxPriority; /* 任务优先级,数值越大,
    优先级越大 */
    StackType_t * pxStack; /* 任务栈起始地址 */
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名字 */
    …省略很多条件编译的成员
    } tskTCB;
    

4.2 动态创建任务函数

4.2.1 动态创建任务原函数介绍

​ 我们可以在 task.h 文件中找到动态任务创建函数说明如下 :

请添加图片描述

configSUPPORT_DYNAMIC_ALLOCATION 宏默认为 1(默认开启).

请添加图片描述

4.2.2 动态创建任务步骤

开启宏:#define configSUPPORT_DYNAMIC_ALLOCATION 1 (默认开启)

定义函数入口参数 -> 编写任务函数 -> 此函数创建的任务会立刻进入就绪态,由任务调度器调度运行

实验目标:

  • start_task:用来创建其他的三个任务。
  • task1:实现 LED1 每 1000ms 闪烁一次。
  • task2:实现 LED2 每 100ms 闪烁一次。
  • task3:判断按键 KEY1 是否按下,按下则删掉 task2。

1)任务设置

/* 启动任务配置 */
#define TASK_START_STACK_SIZE 128
#define TASK_START_PRIORITY 1
TaskHandle_t TaskStart_Handler;
void task_start(void *pvParameters);/* 任务1配置 */
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1(void *pvParameters);/* 任务2配置 */
#define TASK2_STACK_SIZE 128
#define TASK2_PRIORITY 3
TaskHandle_t Task2_Handler;
void task2(void *pvParameters);/* 任务3配置 */
#define TASK3_STACK_SIZE 128
#define TASK3_PRIORITY 4
TaskHandle_t Task3_Handler;
void task3(void *pvParameters);
  1. 入口函数
/*** @brief : FreeRTOS 入口函数:创建任务函数并开始调度* */
void FreeRTOS_start()
{xTaskCreate((TaskFunction_t)task_start,                    // 指向任务函数的指针(char *)"task_start",                          // 任务名称(configSTACK_DEPTH_TYPE)TASK_START_STACK_SIZE, // 任务堆栈大小(void *)NULL,                                  // 传递给任务函数的参数(UBaseType_t)TASK_START_PRIORITY,              // 任务优先级(TaskHandle_t *)&TaskStart_Handler);           // 任务句柄/* 启动任务调度器:自动创建空闲任务 */vTaskStartScheduler();
}
  1. 启动任务函数
/*** @brief : 启动任务函数,创建三个任务,并删除自己* * @param pvParameters */
void task_start(void *pvParameters)
{/* 进入临界区:保护临界区里的代码不会被打断 */taskENTER_CRITICAL();/* 创建三个任务 */xTaskCreate((TaskFunction_t)task1,(char *)"task1",(configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,(void *)NULL,(UBaseType_t)TASK1_PRIORITY,(TaskHandle_t *)&Task1_Handler);xTaskCreate((TaskFunction_t)task2,(char *)"task2",(configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,(void *)NULL,(UBaseType_t)TASK2_PRIORITY,(TaskHandle_t *)&Task2_Handler);xTaskCreate((TaskFunction_t)task3,(char *)"task3",(configSTACK_DEPTH_TYPE)TASK3_STACK_SIZE,(void *)NULL,(UBaseType_t)TASK3_PRIORITY,(TaskHandle_t *)&Task3_Handler);/* 启动任务只需要执行一次即可,用完就删除自己 */vTaskDelete(NULL);/* 退出临界区 */taskEXIT_CRITICAL();
}

4 )三个任务

/*** @brief : 任务1函数,每隔1s翻转一次LED** @param pvParameters*/
void task1(void *pvParameters)
{while (1){led[0].state = !led[0].state;printf("task1 runing...\r\n");HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, (GPIO_PinState)led[0].state);vTaskDelay(1000);}
}/*** @brief : 任务2函数,每隔100ms翻转一次LED** @param pvParameters*/
void task2(void *pvParameters)
{while (1){led[1].state = !led[1].state;printf("task2 runing...\r\n");HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, (GPIO_PinState)led[1].state);vTaskDelay(100);}
}/*** @brief : 任务3函数,每隔100ms检测一次按键** @param pvParameters*/
void task3(void *pvParameters)
{while (1){for (int i = 0; i < 16; i++){if (key[i].flag == 1){if (i == 0){printf("task2 delete...\r\n");vTaskDelete(Task2_Handler);Task2_Handler = NULL;}sprintf((char *)text, " key%d pressed ", i + 1);OLED_ShowString(1, 0, (char *)text);key[i].flag = 0;}}vTaskDelay(100);}
}

在 task.h 中有 typedef struct tskTaskControlBlock * TaskHandle_t;,所以 Task1_Handler 实际上是 tskTaskControlBlock 的指针(TaskHandle_t

动态创建任务函数内部实现:

  • 申请堆栈内存&任务控制块内存。
  • TCB 结构体成员赋值。
  • 添加新任务到就绪列表中。

4.3 静态创建任务

4.3.1 静态创建任务原函数介绍
TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,         // 任务函数指针const char *const pcName,          // 任务名称const uint32_t ulStackDepth,       // 任务栈大小void *const pvParameters,          // 传递给任务函数的参数UBaseType_t uxPriority,            // 任务优先级StackType_t *const puxStackBuffer, // 任务栈指针(用户手动分配)StaticTask_t *const pxTaskBuffer); // 任务控制块(TCB)指针(用户手动分配)
4.3.2 静态创建任务步骤
  • #define configSUPPORT_STATIC_ALLOCATION 1 /* 启用静态创建任务 */
  • 当启用静态创建任务时,默认要求你提供 空闲任务(Idle Task) 的内存,所以需要对空闲任务进行配置

示例:动态创建任务改为静态创建任务,只要对任务栈和 TCB 进行手动分配即可

/* 启动任务配置 */
#define TASK_START_STACK_SIZE 128
#define TASK_START_PRIORITY 1
TaskHandle_t Task_start_Handle; // 如果后续不需要操作这个任务(删除,挂起等),可以省略这句
StackType_t start_task_stack[TASK_START_STACK_SIZE];
StaticTask_t start_task_tcb;
void task_start(void *pvParameters);/*** @brief : FreeRTOS 入口函数:创建任务函数并开始调度**/
void FreeRTOS_start()
{Task_start_Handle = xTaskCreateStatic((TaskFunction_t)task_start,                    // 任务函数指针(char *)"task_start",                          // 任务名称(configSTACK_DEPTH_TYPE)TASK_START_STACK_SIZE, // 任务栈大小(void *)NULL,                                  // 传递给任务函数的参数(UBaseType_t)TASK_START_PRIORITY,              // 任务优先级(StackType_t *)start_task_stack,               // 任务栈指针(用户手动分配)(StaticTask_t *)&start_task_tcb);              // 任务控制块(TCB)指针(用户手动分配)/* 启动任务调度器:自动创建空闲任务 */vTaskStartScheduler();
}
对空闲任务进行内存分配
/* 空闲任务配置 */
#define IDLE_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
StaticTask_t idle_task_tcb;
StackType_t idle_task_stack[IDLE_TASK_STACK_SIZE];/* 空闲任务内存申请函数 */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,StackType_t **ppxIdleTaskStackBuffer,uint32_t *pulIdleTaskStackSize)
{*ppxIdleTaskTCBBuffer = &idle_task_tcb;*ppxIdleTaskStackBuffer = idle_task_stack;*pulIdleTaskStackSize = IDLE_TASK_STACK_SIZE;
}

这就完成了一个任务的静态创建。

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

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

相关文章

【uniapp】图片添加canvas水印

目录 需求&背景实现地理位置添加水印 ios补充 需求&背景 需求&#xff1a;拍照后给图片添加水印, 水印包含经纬度、用户信息、公司logo等信息。 效果图&#xff1a; 方案&#xff1a;使用canvas添加水印。 具体实现&#xff1a;上传图片组件是项目里现有的&#xff…

基于Windows11的DockerDesktop安装和布署方法简介

基于Windows11的DockerDesktop安装和布署方法简介 一、下载安装Docker docker 下载地址 https://www.docker.com/ Download Docker Desktop 选择Download for Winodws AMD64下载Docker Desktop Installer.exe 双点击 Docker Desktop Installer.exe 进行安装 测试Docker安装是…

AI自习室渐兴:人工智能赋能教育新场景的深度剖析

在数字化浪潮席卷全球的今天&#xff0c;教育领域也迎来了前所未有的变革。近年来&#xff0c;AI自习室作为人工智能技术与传统教育融合的产物&#xff0c;在河北等多地悄然兴起&#xff0c;成为学生们的新宠。这一新兴的学习场所&#xff0c;不仅引发了社会的广泛关注&#xf…

Android中AIDL和HIDL的区别

在Android中&#xff0c;AIDL&#xff08;Android Interface Definition Language&#xff09; 和 HIDL&#xff08;HAL Interface Definition Language&#xff09; 是两种用于定义跨进程通信接口的语言。AIDL 是 Android 系统最早支持的 IPC&#xff08;进程间通信&#xff0…

从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动

目录 所以&#xff0c;键盘是如何工作的 说一说我们的8042 输出缓冲区寄存器 状态寄存器 控制寄存器 动手&#xff01; 注册中断 简单整个键盘驱动 Reference ScanCode Table 我们下一步就是准备进一步完善我们系统的交互性。基于这个&#xff0c;我们想到的第一个可以…

【JavaEE】-- 多线程(初阶)4

文章目录 8.多线程案例8.1 单例模式8.1.1 饿汉模式8.1.2 懒汉模式 8.2 阻塞队列8.2.1 什么是阻塞队列8.2.2 生产者消费者模型8.2.3 标准库中的阻塞队列8.2.4 阻塞队列的应用场景8.2.4.1 消息队列 8.2.5 异步操作8.2.5 自定义实现阻塞队列8.2.6 阻塞队列--生产者消费者模型 8.3 …

用Python分割并高效处理PDF大文件

在处理大型PDF文件时&#xff0c;将它们分解成更小、更易于管理的块通常是有益的。这个过程称为分区&#xff0c;它可以提高处理效率&#xff0c;并使分析或操作文档变得更容易。在本文中&#xff0c;我们将讨论如何使用Python和为Unstructured.io库将PDF文件划分为更小的部分。…

Python——计算机网络

一.ip 1.ip的定义 IP是“Internet Protocol”的缩写&#xff0c;即“互联网协议”。它是用于计算机网络通信的基础协议之一&#xff0c;属于TCP/IP协议族中的网络层协议。IP协议的主要功能是负责将数据包从源主机传输到目标主机&#xff0c;并确保数据能够在复杂的网络环境中正…

【MySQL】事务|概念|如何回滚|基本特性|MySQL事务隔离性具体怎么实现的

目录 1.为啥引入 2.是啥 3.如何回滚&#xff08;日志&#xff09; &#x1f525;4.面试题&#xff1a;谈谈事务的基本特性 &#xff08;1&#xff09;原子性 &#xff08;2&#xff09;一致性&#xff08;收入和支出相匹配&#xff09; &#xff08;3&#xff09;持久性…

deepseek 本地部署

deepseek 本地部署 纯新手教学&#xff0c;手把手5分钟带你在本地部署一个私有的deepseek&#xff0c;再也不用受网络影响。流畅使用deepseek&#xff01;&#xff01;&#xff01; 如果不想看文章&#xff0c;指路&#xff1a;Deep seek R1本地部署 小白超详细教程 &#xff0…

⭐算法OJ⭐N-皇后问题 II【回溯剪枝】(C++实现)N-Queens II

⭐算法OJ⭐N-皇后问题【回溯剪枝】&#xff08;C实现&#xff09;N-Queens 问题描述 The n-queens puzzle is the problem of placing n n n queens on an n n n \times n nn chessboard such that no two queens attack each other. Given an integer n, return the num…

关联封号率降70%!2025最新IP隔离方案实操手册

高效运营安全防护&#xff0c;跨境卖家必看的风险规避指南 跨境账号管理的核心挑战&#xff1a;关联封号风险激增 2024年&#xff0c;随着全球电商平台对账号合规的审查日益严苛&#xff0c;“关联封号”已成为跨境卖家最头疼的问题之一。无论是同一IP登录多账号、员工操作失误…

pytest框架 核心知识的系统复习

1. pytest 介绍 是什么&#xff1a;Python 最流行的单元测试框架之一&#xff0c;支持复杂的功能测试和插件扩展。 优点&#xff1a; 语法简洁&#xff08;用 assert 替代 self.assertEqual&#xff09;。 自动发现测试用例。 丰富的插件生态&#xff08;如失败重试、并发执…

搭建BOA服务器

BOA服务器是嵌入式常用的服务器类型&#xff0c;嵌入式程序作为后端时候如果想配合网页进行显示&#xff0c;利用BOA服务器搭建网络界面是不错的选择 首先下载boa官方安装包 Boa Webserver 下载后传输到Ubuntu随便文件夹&#xff0c;解压 tar -xvf boa-0.94.13.tar.gz 进入…

C# OPC DA获取DCS数据(提前配置DCOM)

OPC DA配置操作手册 配置完成后&#xff0c;访问远程ip&#xff0c;就能获取到服务 C#使用Interop.OPCAutomation采集OPC DA数据&#xff0c;支持订阅&#xff08;数据变化&#xff09;、单个读取、单个写入、断线重连

Ubuntu20.04搭建gerrit code review

一、环境准备 1. 安装 Java 环境‌ Gerrit 依赖 Java 运行环境&#xff08;推荐 JDK 8&#xff09;&#xff1a; sudo apt install openjdk-11-jdk 验证安装&#xff1a; java -version ‌2. 安装 Git sudo apt install git ‌3. 可选依赖 数据库‌&#xff1a;Gerrit …

【FSM-3: 串行序列】

FSM-3&#xff1a;串行序列 1 Serial receiver FSM使用总结&#xff1a; 所有涉及输出的driver原则上用cur_sta&#xff1b;若是使用nxt_sta的相当于是提前一拍知道结果&#xff0c;所以对于输出必须要使用clocked reg&#xff0c;这样才能和cur_sta对应起来&#xff1b;描述声…

蓝桥杯 之 前缀和与查分

文章目录 题目求和棋盘挖矿 前缀和有利于快速求解 区间的和、异或值 、乘积等情况差分是前缀和的反操作 前缀和 一维前缀和&#xff1a; # 原始的数组num,下标从1到n n len(num) pre [0]*(n1) for i in range(n):pre[i1] pre[i] num[i] # 如果需要求解num[l] 到num[r] 的区…

国产化板卡设计原理图:2330-基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡

基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于 FPGAJFM7K325T 芯片&#xff0c;pin_to_pin兼容FPGAXC7K410T-2FFG900 &#xff0c;支持PCIeX8、64bit DDR3容量2GByte&#xff0c;HPC的FMC连接器&#xff0c;板卡支持PXIE标准协议&#xff0c;其中XJ3…

计算机视觉之dlib人脸关键点绘制及微笑测试

dlib人脸关键点绘制及微笑测试 目录 dlib人脸关键点绘制及微笑测试1 dlib人脸关键点1.1 dlib1.2 人脸关键点检测1.3 检测模型1.4 凸包1.5 笑容检测1.6 函数 2 人脸检测代码2.1 关键点绘制2.2 关键点连线2.3 微笑检测 1 dlib人脸关键点 1.1 dlib dlib 是一个强大的机器学习库&a…