FreeRTOS 源码结构解析与 STM32 HAL 库移植实践(任务创建、删除篇)
1. FreeRTOS源码结构介绍
1.1 下载源码
点击官网地址,选择 FreeRTOS 202212.01非 LTS 版本(非长期支持版),因为这个版本有着最全的历程和更多型号处理器支持。
1.2 文件夹结构介绍
下载后主文件 FreeRTOSv202212.01 下包含以下文件:
名称 | 描述 |
---|---|
FreeRTOS | FreeRTOS 的核心源码,提供实时操作系统的**所有必要组件****,如任务调度、内存管理、信号量、队列等。 |
FreeRTOS-Plus | FreeRTOS 的附加组件(非必要),如文件系统、TCP/IP 协议栈、图形界面库等。 提供了一些额外的功能,可以扩展应用场景。 |
tools | 这个文件夹通常包含一些辅助工具,用于支持 FreeRTOS 的开发,比如配置生成工具、调试工具、自动化脚本等。 |
其他文件 | 一些网页链接和历史版本记录,项目以来关系,编译格式文件 |
1.2 FreeRTOS文件夹
由于上面文件中仅有 FreeRTOS 是建立最小 FreeRTOS 例程的必要文件夹,因此对该文件下内容进一步做说明(在该文件下的 README.md 文件有官方的英文说明):
名称 | 描述 |
---|---|
Demo | FreeRTOS 演示例程,支持多种芯片架构、多种型号芯片 |
Source | FreeRTOS 源码,最重要的文件夹(包含着移植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
名称 | 描述 |
---|---|
include | FreeRTOS 的公共头文件,定义了 FreeRTOS 的 API 和常用的宏、数据结构、常量等。 |
portable | FreeRTOS 为不同硬件平台(微控制器、处理器架构等)提供适配层的地方。每个目标硬件平台(如 ARM Cortex-M、AVR、RISC-V 等)都会有一个专门的适配代码,用于处理硬件相关的操作,例如上下文切换、时钟中断、任务堆栈管理等。 |
croutine | 协程相关文件 |
event_groups | 事件相关文件 |
list | 提供与链表相关的功能实现。FreeRTOS 中许多数据结构(如任务控制块、定时器等)都使用链表来管理。 |
queue | 提供队列功能的实现,任务之间可以通过队列进行通信和数据交换。 |
stream_buffer | 流式缓冲区相关文件 |
task | FreeRTOS 的任务调度和任务管理的核心实现。 |
timers | FreeRTOS 定时器管理的实现,定时器可用于执行周期性任务或超时任务。 |
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 添加必要的源码文件
- G:\FreeRTOSv202212.01\FreeRTOS\Source\include (整个文件夹)
- G:\FreevRTOSv202212.01\FreeRTOS\Source\croutine.c
- G:\FreeRTOSv202212.01\FreeRTOS\Source\event_groups.c
- G:\FreeRTOSv202212.01\FreeRTOS\Source\list.c
- G:\FreeRTOSv202212.01\FreeRTOS\Source\queue.c
- G:\FreeRTOSv202212.01\FreeRTOS\Source\stream_buffer.c
- G:\FreeRTOSv202212.01\FreeRTOS\Source\tasks.c
- 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() 主要功能:
递增系统滴答计数(
xTickCount
)
- FreeRTOS 使用 滴答计数 作为时间基准,每次
SysTick
触发时都会增加xTickCount
。xTickCount
用于任务调度、定时器、任务延时(vTaskDelay()
)。检查任务延时是否结束
- 如果某个任务调用了
vTaskDelay()
或vTaskDelayUntil()
,它会进入 阻塞状态,直到xTickCount
递增到设定的时间。xPortSysTickHandler()
负责检查 哪些任务的延时时间到了,并将其移动到 就绪 状态。时间片轮转(任务切换)
- FreeRTOS 采用 抢占式调度,多个任务可以 时间片轮转。
xPortSysTickHandler()
负责检查 是否需要任务切换,如果当前任务的时间片到了,就触发任务切换。触发上下文切换(PendSV 中断)
如果
xPortSysTickHandler()
发现 需要切换任务,它会 触发 PendSV 进行任务切换
PendSV_Handler()
会完成实际的任务切换 (保存旧任务上下文,恢复新任务上下文)。
3.FreeRTOS 数据类型与命名规范笔记
3.1 FreeRTOS 数据类型
3.1.1 TickType_t(系统滴答计数类型)
- 作用:用于记录系统的滴答计数(Tick Count)。
- 定义:
configUSE_16_BIT_TICKS = 1
→ 16 位无符号类型(最大 65,535 Tick)。configUSE_16_BIT_TICKS = 0
→ 32 位无符号类型(默认,最大 4,294,967,295 Tick)。
3.1.2 BaseType_t(基本类型)
- 作用:最适合 CPU 架构的 有符号整数,用于 API 返回值、状态标志等。
- 定义:
- 32 位架构 →
int32_t
- 16 位架构 →
int16_t
- 32 位架构 →
3.1.3 UBaseType_t(无符号基本类型)
- 作用:
BaseType_t
的无符号版本,常用于计数、索引等。 - 定义:
- 32 位架构 →
uint32_t
- 16 位架构 →
uint16_t
- 32 位架构 →
3.1.4 StackType_t(堆栈数据类型)
- 作用:存储任务堆栈的数据类型,适应不同架构的栈结构。
- 定义:
- 32 位架构 →
uint32_t
- 16 位架构 →
uint16_t
- 32 位架构 →
3.2 FreeRTOS 命名规范
3.2.1 变量命名
- 驼峰式命名,使用完整单词。
- 前缀规则:
ul
→uint32_t
(unsigned long)us
→uint16_t
(unsigned short)uc
→uint8_t
(unsigned char)x
→ 非标准整数类型(如BaseType_t
、TickType_t
)ux
→UBaseType_t
(无符号BaseType_t
)e
→ 枚举类型p
→ 指针(如pus
表示uint16_t *
)c
→unsigned char
(仅存 ASCII)pc
→char *
(仅存 ASCII 字符串)
3.2.2 函数命名
- 驼峰式命名,使用完整单词。
- 前缀规则:
prv
→ 静态私有函数v
→ 返回void
- API 以文件名为前缀,如 vTaskDelay():
v
→void
Task
→ 来自task.c
Delay
→ 该函数用于任务延时
3.2.3 宏命名
- 全部大写,使用下划线分隔。
- 前缀规则:
config
→FreeRTOSConfig.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);
- 入口函数
/*** @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();
}
- 启动任务函数
/*** @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;
}
这就完成了一个任务的静态创建。
相关文章:
FreeRTOS 源码结构解析与 STM32 HAL 库移植实践(任务创建、删除篇)
1. FreeRTOS源码结构介绍 1.1 下载源码 点击官网地址,选择 FreeRTOS 202212.01非 LTS 版本(非长期支持版),因为这个版本有着最全的历程和更多型号处理器支持。 1.2 文件夹结构介绍 下载后主文件 FreeRTOSv202212.01 下包…...
第五节:基于Winform框架的串口助手小项目---串口收发《C#编程》
“路漫漫其修远兮,吾将上下而求索” , -----------------------WHAPPY 目标任务: 1 从本地设备列表获取串口。 RegistryKey keyCom Registry.LocalMachine.OpenSubKey("Hardware\DeviceMap\SerialComm"); RegistryKey 是.NET 框…...
CSS2.1基础学习
1.定位盒模型相关 2.浮动 3.使用定位实现三列布局(不可取,定位父级为初始包含块) 4.使用浮动实现三列布局(不可取的原因是中间主列无法优先加载) 5.圣杯布局 6.等高布局 7.双飞翼布局 8.解决IE6下fixed失效问题 9.短暂总结 10.粘连布局 11.BFC是什么 12.BFC实现两列…...
alpine linux 系统最新版安装及使用教程
1.下载镜像包 官网地址 官网下载阿里云镜像站下载华为云镜像站下载清华大学镜像站下载中科大镜像站下载 官方安装文档 2.新建虚拟机 3.编辑虚拟机并开机 4.虚拟机安装 开启虚拟机 首次启动使用root登录,没有密码 登录成功,执行 setup-alpine 命令进…...
【Hudi-SQL DDL创建表语法】
CREATE TABLE 命令功能 CREATE TABLE命令通过指定带有表属性的字段列表来创建Hudi Table。 命令格式 CREATE TABLE [ IF NOT EXISTS] [database_name.]table_name[ (columnTypeList)]USING hudi[ COMMENT table_comment ][ LOCATION location_path ][ OPTIONS (options_lis…...
[Web 安全] PHP 反序列化漏洞 —— POP 链构造思路
关注这个专栏的其他相关笔记:[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客 0x01:什么是 POP 链? POP 链(Payload On Purpose Chain)是一种利用 PHP 中的魔法方法进行多次跳转以获取敏感数据的技术。它通常出现在 CTF…...
GPU/CUDA 发展编年史:从 3D 渲染到 AI 大模型时代
目录 文章目录 目录1960s~1999:GPU 的诞生:光栅化(Rasterization)3D 渲染算法的硬件化实现之路学术界算法研究历程工业界产品研发历程光栅化技术原理光栅化技术的软件实现:OpenGL 3D 渲染管线设计1. 顶点处理ÿ…...
除了DeepSpeed,在训练和推理的时候,显存不足还有什么优化方法吗?FlashAttention 具体是怎么做的
除了DeepSpeed,训练和推理时显存不足的优化方法及FlashAttention原理详解 DeepSpeed的基础内容:ZeRO分布式训练策略 一、显存不足的优化方法 1. 混合精度训练(Mixed Precision Training) 原理 使用FP16和FP32混合精度ÿ…...
GCC RISCV 后端 -- GCC 后端框架的一些理解
GCC 已经提供了一整套的编译框架,从前端(Frontend / GENERIC-Tree)对编程语言的语法语义处理,到中端(Middle-End / GIMPLE-Tree)的目标机器无关(Target Indepndent)的优化处理&#…...
庖丁解java(一篇文章学java)
(大家不用收藏这篇文章,因为这篇文章会经常更新,也就是删除后重发) 一篇文章学java,这是我滴一个执念... 当然,真一篇文章就写完java基础,java架构,java业务实现,java业务扩展,根本不可能.所以,这篇文章,就是一个索引,索什么呢? 请看下文... 关于决定开始写博文的介绍 …...
Spring框架自带的定时任务:Spring Task详解
文章目录 一、基本使用1、配置:EnableScheduling2、触发器:Scheduled 二、拓展1、修改默认的线程池2、springboot配置 三、源码分析参考资料 一、基本使用 1、配置:EnableScheduling import org.springframework.context.annotation.Config…...
DeepSeek 助力 Vue3 开发:打造丝滑的弹性布局(Flexbox)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
jwt 存在的无状态的安全问题与解决方案
背景我的需求 解决方法方案1:黑名单存在的问题解决方案问题成本估算: 方案2: 双token刷新核心解决的问题存在的问题 方案3: token与session结合成本估算 方案4: 长连接(websocket) 背景 jwt是无状态的,除了自动失效之外无法手动过期 举个例子: 当我们的token泄露或者密码泄露…...
【Python爬虫】爬取公共交通路网数据
程序来自于Github,以下这篇博客作为完整的学习记录,也callback上一篇爬取公共交通站点的博文。 Bardbo/get_bus_lines_and_stations_data_from_gaode: 这个项目是基于高德开放平台和公交网获取公交线路及站点数据,并生成shp文件,…...
Deepseek API+Python测试用例一键生成与导出-V1
在实际使用场景中,可能只需要获取需求文档中的部分内容,例如特定标题的正文部分、特定段落的表格内容,或者指定图片中的内容。为了满足这一需求,可以对文档清理工具进行优化,支持按标题提取内容、按章节提取表格和图片…...
如何为JAR设置定时重启?
AI越来越火了,我们想要不被淘汰就得主动拥抱。推荐一个人工智能学习网站,通俗易懂,风趣幽默,最重要的屌图甚多,忍不住分享一下给大家。点击跳转到网站 前面我们说过了如何将jar交由Systemctl管理,下面我们…...
麒麟V10-SP2-x86_64架构系统下通过KVM创建虚拟机及配置虚机的NAT、Bridge两种网络模式全过程
文章目录 一、什么是虚拟化?虚拟化具有哪些优势 二、常见的虚拟化技术1、kvm介绍2、kvm工作原理3、kvm功能 三、安装kvm并启动第一个kvm机器1、环境准备2、安装kvm工具3、启动并设置开机自启 libvirtd 服务4、验证 KVM 模块是否加载5、上传系统镜像到指定目录6、网络…...
三方库总结
一、Glide 目标:熟练掌握各种使用方法,了解其设计,背后原理 如果我设计一个图片显示框架,我会怎么做? 1.加载图片资源-从网络上下载图片资源、从本地加载图片资源 如果是从网络上加载图片,需要用到相关的网…...
用DeepSeek-R1-Distill-data-110k蒸馏中文数据集 微调Qwen2.5-7B-Instruct!
下载模型与数据 模型下载: huggingface: Qwen/Qwen2.5-7B-Instruct HF MirrorWe’re on a journey to advance and democratize artificial intelligence through open source and open science.https://hf-mirror.com/Qwen/Qwen2.5-7B-Instruct 魔搭&a…...
知识图谱的推荐实现方案(Vue)
使用 Vue 实现知识图谱思维导图展示的完整方案,结合主流库和最佳实践: 一、技术选型 组件库特点适用场景MindElixir国产开源、中文文档完善、支持关系线教育类知识图谱GoJS功能强大、商业许可、适合复杂交互企业级应用(需付费)D3…...
Nessus安装
Nessus:https://pan.quark.cn/s/f5fb09b6d4fb 1.软件安装 点击安装,剩下的下一步即可。 直接下一步安装即可 2.Web端安装 会弹出一个web窗口 开始初始化 创建用户 开始初始化 3.Cracker 会弹一个黑窗口 运行完,回车即可。访问https://loc…...
【大模型基础_毛玉仁】0.概述
更多内容:XiaoJ的知识星球 【大模型基础_毛玉仁】 系列文章参考 系列文章 【大模型基础_毛玉仁】0.概述 【大模型基础_毛玉仁】1.1 基于统计方法的语言模型 更新中。。。。。。 参考 书籍:大模型基础_完整版.pdf Github:https://github.co…...
quillEditor 禁用复制粘贴图片,以及class转style等问题
<template><div><div class"search-term"><el-form :inline"true" :model"searchInfo" class"demo-form-inline"><el-form-item label"案例标题"><el-input v-model"searchInfo.titl…...
C语⾔数据类型和变量
C 语言的数据类型 类型分类: C 语言提供丰富的数据类型,包括字符型(char、signed char、unsigned char)、整型(short、int、long 等多种,且各有 signed 和 unsigned 修饰形式) 、浮点型&#x…...
centOS 环境 安装redis方法
一、准备centOS环境 参考文章:Hyper-V 安装CentOS7_代码草率了的博客-CSDN博客 二、redis官网 地址:Download | Redis 演示版本为?redis-5.0.14.tar.gz 三、redis源码编译 登录后创建soft目录 进入目录使用wget下载所需资源包 命令:w…...
【Mac】2025-MacOS系统下常用的开发环境配置
早期版本的一个环境搭建参考 1、brew Mac自带终端运行: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" Installation successful!成功后运行三行命令后更新环境(xxx是mac的username&a…...
深入理解动态绑定与多态
动态绑定介绍 Java中的动态绑定,是一种在程序运行时确定方法执行版本的过程。它与多态紧密联系。 在Java中,动态绑定通常发生在以下情况: 方法覆盖:当子类重写父类的一个方法时,调用该方法的行为会根据对象的实际类…...
【数据结构】二叉树总结篇
遍历 递归 递归三部曲: 1.参数和返回值 2.终止条件 3.单层逻辑(遍历顺序) var preorderTraversal function(root) { // 第一种let res[];const dfsfunction(root){if(rootnull)return ;//先序遍历所以从父节点开始res.push(root.val);//递归…...
Zookeeper 及 基于ZooKeeper实现的分布式锁
1 ZooKeeper 1.1 ZooKeeper 介绍 ZooKeeper是一个开源的分布式协调服务,它的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。 原语:操作系统或…...
内嵌踢脚线怎么做能省钱?
家里装修内嵌踢脚线应该怎么做?哪种做法更经济? 先回答内嵌踢脚线应该怎么做? 做内嵌踢脚线有两种办法,简单点来说就是前凸和后挖。 前凸是踢脚线安到原来墙面上,踢脚线是从墙面上凸出来的,想要实现内嵌的效…...
DeepSeek集成到VScode工具,让编程更高效
DeepSeek与VScode的强强联合,为编程效率树立了新标杆。 DeepSeek,一款卓越的代码搜索引擎,以其精准的索引和高速的检索能力,助力开发者在浩瀚的代码海洋中迅速定位关键信息。 集成至VScode后,开发者无需离开熟悉的编辑…...
大模型应用:多轮对话(prompt工程)
概述 在与大型语言模型(如ChatGPT)交互的过程中,我们常常体验到与智能助手进行连贯多轮对话的便利性。那么,当我们开启一个新的聊天时,系统是如何管理聊天上下文的呢? 一、初始上下文的建立 1. 创建新会…...
洛谷 P1601 A+B Problem(高精)详解c++
我们之前做题碰到的数据范围一般是10^9,多点会达到10^18级别,处理10^9用int就可以存下,10^18次方要用到long long,接着解决加减乘除的问题,但是当数据范围达到了10^10^6的时候,当数据的值特别⼤,…...
【云原生】Spring Cloud是什么?Spring Cloud版本介绍
什么是SpringCloud 上一章节介绍了总体的SpringCloud的总体学习章节,因为最近项目刚好需要用到SpringCloud来搭建微服务项目、所以就跟着大家一起来再学习巩固下SpringCloud的相关知识 SpringCloud是基于SpringBoot提供了一套微服务解决方案,包括服务注…...
最节省成本的架构方案:无服务器架构
无服务器架构(Serverless Architecture)是一种颠覆性的云计算执行模型,它允许开发者专注于编写和部署代码,而无需担心底层服务器基础设施的管理。这种架构以其按需付费、自动伸缩和简化部署等优势,在成本优化方面表现出…...
C++入门续集:
1. 缺省参数: 我们看我们的上图,我们可以看我们的函数Func,我们可以看到我们的函数里面的参数写的是int a 0;这个写法是我们没有见过的,我们之前在C语言里面只见到过说是函数里面会设置参数,但是参数是没有…...
线代[9]|线性代数主要内容及其发展简史(任广千《线性代数的几何意义》的附录1)
文章目录 向量行列式矩阵线性方程组二次型 向量 向量又称为矢量,最初应用与物理学。很多物理量如力、速度、位移以及电场强度、磁感应强度等等都是向量。大约公元前350年前,古希腊著名学者亚里士多德就知道了力可以表示成向量,两个力的组合作…...
C++ Primer 动态内存与智能指针
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
AI在原型设计中的革命性应用:效率与创新的双重突破
引言 在数字化转型加速的今天,产品开发周期被压缩至前所未有的程度。原型设计作为产品开发的核心环节,其效率和质量直接影响最终产品的市场竞争力。传统原型设计流程依赖人工绘制、反复修改和用户测试,耗时且成本高昂。而人工智能࿰…...
网络学习(四)HTTPS中,SSL的单向认证与双向认证
目录 一、什么是SSL?1.1 SSL 的主要功能1.2 SSL 的工作原理1.3 SSL 的核心组件1.4 SSL 的应用场景1.5 SSL 与 TLS 的区别 二、SSL 单向认证、双向认证2.1 SSL 单向认证2.2 SSL 双向认证2.3 总结:SSL 单向认证和双向认证的区别 一、什么是SSL?…...
分类任务和回归任务的区别
分类任务和回归任务是机器学习中两种常见的监督学习任务,尽管它们都属于预测性的分析,但它们的目标和应用场景存在显著的差异。 分类任务 定义:分类任务是指将输入样本映射到一个有限且离散的类别集合中的过程。目标是对数据进行分类&#…...
android接入rocketmq
一 前言 RocketMQ 作为一个功能强大的消息队列系统,不仅支持基本的消息发布与订阅,还提供了顺序消息、延时消息、事务消息等高级功能,适应了复杂的分布式系统需求。其高可用性架构、多副本机制、完善的运维管理工具,以及安全控制…...
V4L2框架基础
一、V4L2视频设备驱动基础 1.V4L2是专门为Linux设备设计的整合视频框架(其主要核心在Linux内核,相当于Linux操作系统上层的视频源捕获驱动框架)。为上层访问系统底层的视频设备提供一个统一的标准接口。V4L2驱动框架能够支持多种类型&#x…...
【微知】如何通过mlxlink查看Mellanox网卡和光模块相关的信息?( mlxlink -d 01:00.0 -m)
背景 通过mlxlink可以查看Mellanox网卡的一些链路信息和硬件信息,也可以查看所插入的光模块的一些信息。 兄弟篇通过ethtool查看的方法:如何查看Mellanox网卡上的光模块的信息? 命令 mlxlink -d 01:00.0 -mman手册介绍: 如果…...
使用pytorch和opencv根据颜色相似性提取图像
需求:将下图中的花朵提取出来。 代码: import cv2 import torch import numpy as np import timedef get_similar_colors(image, color_list, threshold):# 将图像和颜色列表转换为torch张量device torch.device(cuda if torch.cuda.is_available() el…...
HTML label 标签使用
点击 <label> 标签通常会使与之关联的表单控件获得焦点或被激活。 通过正确使用 <label> 标签,可以使表单更加友好和易于使用,同时提高整体的可访问性。 基本用法 <label> 标签通过 for 属性与 id 为 username 的 <input> 元素…...
SQL注入的分类靶场实践
前言 SQL 注入(SQL Injection)是一种常见且危险的 Web 安全漏洞,攻击者通过在输入字段中插入恶意 SQL 代码,能够绕过应用程序的验证机制,直接操纵数据库。本文将介绍 SQL 注入的分类,并通过 Pikachu 靶场进…...
用matplotlib构建BI看板:Superset插件开发实战
目录 前言:当经典可视化库遇见BI航母 一、Superset插件架构精要 1.1 核心模块解析 1.2 插件通信机制 二、开发环境准备 2.1 依赖矩阵 三、开发自定义可视化插件 3.1 插件脚手架 3.2 渲染引擎适配 四、Superset深度集成 4.1 控制面板配置 4.2 动态参数传递…...
【Linux】之【Bug】VMware 虚拟机开机 一直卡在黑屏左上角下划线闪烁界面
解决 参考: 解决Ubuntu20.04 开机黑屏光标闪烁进不去系统 Centos根目录100%解决思路 当前界面 ctrlaltf3-f6 暂时进入终端界面 df -h 查看发现根目录 磁盘空间已满 执行命令 查看当前目录占用内存明细 sudo du -h -x --max-depth1清理无用的大内存文件 或者安装…...
【练习】【链表】力扣热题100 19. 删除链表的倒数第 N 个结点
题目 删除链表的倒数第 N 个结点 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例 1: 输入:head [1,2,3,4,5], n 2 输出:[1,2,3,5] 示例 2: 输入:head [1], n 1 输出&…...