FreeRTOS实时操作系统(1)

前言:FreeRTOS内容较多,分篇发布,较为基础,旨在梳理知识,适合入门的同学   

(基于正点原子STM32F103开发板V2

(对于本篇,若有疑问,欢迎在评论区留言,作者将在两小时内解答)

(本篇参考B站正点原子FreeRTOS)

目录

前言:FreeRTOS内容较多,分篇发布,较为基础,旨在梳理知识,适合入门的同学   

(基于正点原子STM32F103开发板V2)

(对于本篇,若有疑问,欢迎在评论区留言,作者将在两小时内解答)

(本篇参考B站正点原子FreeRTOS)

1.RTOS

1.1RTOS原理

1.2RTOS特点

2.FreeRTOS

 2.1FreeRTOS

2.2FreeRTOS特点

2.3FreeRTOS的API函数

3.FreeRTOS基础知识

3.1任务调度

3.2抢占式调度

3.3时间片调度

3.5任务状态

3.6任务状态列表

3.8总结

4.FreeRTOS任务创建和删除

 4.1任务控制块结构体

4.2动态创建任务

4.3静态创建任务

4.4任务删除函数

4.5空闲任务

4.6动态创建函数实战

4.7静态创建函数实战

4.8实战总结

5.总结


1.RTOS

1.1RTOS原理

RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时性,

宏观上是可多任务同时进行的操作系统,实际上是在规定的若干很小的时刻,交替进行各个任务的一个工作原理,从表面上看像是在同时进行多任务

1.2RTOS特点

1.多任务:实现功能划分为多个任务

2.延时函数:任务调度

3.抢占式:高优先级任务抢占低优先级任务

4.任务堆栈:每个任务都有自己的栈空间

注意1:中断可以打断任意任务

注意2:任务可以同等优先级

2.FreeRTOS

 2.1FreeRTOS

  FreeRTOS 可以分为两部分:“Free”和“RTOS”,
Free”就是免费的、 自由的、不受约束的意思
因此,从 FreeRTOS 的名字中就 能看出,FreeROTS 是一款免费的实时操作系统

2.2FreeRTOS特点

2.3FreeRTOS的API函数

FreeRTOS 是一个轻量级的实时操作系统内核,它提供了丰富的 API 函数,用于创建和管理任务、同步任务间的通信、调度任务以及进行系统配置等。以下是一些 FreeRTOS 中常见的 API 函数及其用途:

1.任务创建:

  • xTaskCreate: 创建一个新的任务
  • xTaskCreateStatic: 创建一个静态分配的任务

2.任务管理:

  • vTaskDelete: 删除一个任务
  • vTaskSuspend: 暂停一个任务
  • vTaskResume: 恢复一个任务
  • eTaskGetState: 获取任务当前的状态

3.任务同步:

  • xSemaphoreCreateBinary: 创建一个二值信号量
  • xSemaphoreCreateMutex: 创建一个互斥量
  • xSemaphoreGive: 释放一个信号量
  • xSemaphoreTake: 获取一个信号量

4.任务通信:

  • xQueueCreate: 创建一个队列
  • xQueueSend: 向队列发送数据
  • xQueueReceive: 从队列接收数据

5.定时器:

  • xTimerCreate: 创建一个定时器
  • vTimerStart: 启动一个定时器
  • vTimerStop: 停止一个定时器

6.任务调度:

  • vTaskDelay: 延迟一个任务执行
  • eTaskGetIdleTaskHandle: 获取空闲任务句柄

7.系统配置:

  • eKernelGetTickRateHz: 获取系统滴答时钟的频率
  • vApplicationGetIdleTaskMemory: 获取空闲任务的内存

8.内存管理:

  • pvPortMalloc: 动态分配内存
  • vPortFree: 释放动态分配的内存

这些只是 FreeRTOS API 的一部分。FreeRTOS 提供了全面的文档,详细介绍了每个函数的用法、参数和返回值。开发者在使用这些 API 时,需要根据具体的任务需求和环境配置来选择合适的函数

3.FreeRTOS基础知识

3.1任务调度

任务调度器就是使用相关的调度算法来决定当前需要执行的哪个任务

FreeRTOS 一共支持三种任务调度方式:

3.2抢占式调度

运行条件:

运行过程:

特点

1.高优先级任务,优先执行

2.高优先级任务不停止,低优先级任务无法执行

3.被抢占的任务将会进入就绪态

3.3时间片调度

时间片:同等优先级任务轮流地享有相同的 CPU 时间(可设置一个时间片的时间(SysTick 中断周期)),在FreeRTOS中,一个时间片就等于SysTick 中断周期,并且一个任务只能拥有一个时间片

运行条件:

运行过程:

特点:

1.同等优先级任务,轮流执行;时间片流转

2.一个时间片大小,取决为滴答定时器中断周期

3.注意没有用完的时间片不会再使用,下次任务Task3得到执行 还是按照一个时间片的时钟节拍运行

(在FreeRTOS中,当一个任务阻塞时,操作系统会立即停止该任务的执行,并将CPU的控制权切换给另一个就绪状态的任务。如果Task1已经准备好执行(即它处于就绪状态),那么它将会被调度器选中,并开始执行)

3.5任务状态

FreeRTOS中任务共存在4种状态:

四种任务状态之间的转换图:

特点:

1.仅就绪态可转变成运行态

2.其他状态的任务想运行,必须先转变成就绪态

3.6任务状态列表

FreeRTOS中无非就四种状态,运行态,就绪态、阻塞态、挂起态

这四种状态中,除了运行态,其他三种任务状态的任务都有其对应的任务状态列表

特点:

1. 32位的变量,当某个位,置一时,代表所对应的优先级就绪列表有任务存在

2. 列表跟链表很像,后续会讲到

3. 调度器总是在所有处于就绪列表的任务中,选择具有最高优先级的任务来执行

4. 相同优先级的任务会连接在同一个就绪列表上

3.8总结

4.FreeRTOS任务创建和删除

 任务的创建和删除本质就是调用FreeRTOS的API函数

 4.1任务控制块结构体

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动态创建任务

        动态创建任务:任务的任务控制块以及任务的栈空间所需的内存,均由 FreeRTOS 从

FreeRTOS 管理的堆中分配

动态创建任务函数:

BaseType_t xTaskCreate
( TaskFunction_t 				    pxTaskCode,	    /* 指向任务函数的指针 */ 				            const char * const                pcName          /* 任务名字,最大长度configMAX_TASK_NAME_LEN ,默认16个字符*/const configSTACK_DEPTH_TYPE 		usStackDepth, 	/* 任务堆栈大小,注意字为单位 */void * const 					    pvParameters,	/* 传递给任务函数的参数 */UBaseType_t 		                uxPriority,		/*  任务优先级,范围:0 ~ configMAX_PRIORITIES - 1(0~31) */TaskHandle_t * const 			    pxCreatedTask 	/* 任务句柄,就是任务的任务控制块 */
)

errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:一般是任务所需要的空间太大,FreeRTOS所管理的堆栈空间不够

实现动态创建任务流程

4.3静态创建任务

        静态创建任务任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供

静态创建任务函数

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				/* 任务控制块指针,由用户分配 */
); 	

实现静态创建任务流程

4.4任务删除函数

用于删除已被创建的任务,被删除的任务将从就绪态任务列表、阻塞态任务列表、挂起态任务列表和事件列表中移除

void vTaskDelete(TaskHandle_t xTaskToDelete);

注意:

1.当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)

2.2、空闲任务(下面会讲到)会负责释放被删除任务中由系统分配的内存(动态创建任务),但是由用户在任务删除前申请的内存, 则需要由用户在任务被删除前提前释放,否则将导致内存泄露(静态创建任务)

删除任务流程

4.5空闲任务

  • 当一个任务调用 vTaskDelete() 删除自身时,它会被标记为删除状态,但它的内存不会被立即回收。
  • 空闲任务周期性地运行,它会检查系统中是否有任务被标记为删除状态
  • 如果发现被删除的任务,空闲任务会释放该任务的堆栈空间和任务控制块,将其占用的内存归还给系统。
  • 通过这种方式,空闲任务帮助维护系统的内存资源,确保不会有内存泄漏

4.6动态创建函数实战

(基于正点原子STM32F103开发板V2   a盘资料)

第一步:

将宏configSUPPORT_DYNAMIC_ALLOCATION 配置为 1 (默认)

路径:   \User\freertos_demo.c\FreeRTOSConfig.h

第二步:

配置任务task1、task2、task3、start_task以及屏幕显示

路径:User\freertos_demo.c

详细细节在代码中有解释(注释)

#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"/******************************************************************************************************/
/*FreeRTOS配置*//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 128偏大定义*/
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 *//* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 *//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 *//* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK3_PRIO      4                   /* 任务优先级 */
#define TASK3_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task3Task_Handler;  /* 任务句柄 */
void task3(void *pvParameters);             /* 任务函数 *//******************************************************************************************************//* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,MAGENTA, GREEN, CYAN, YELLOW,BROWN, BRRED, GRAY};/*** @brief       FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);lcd_show_string(10, 47, 220, 24, 24, "Task Create & Del", RED);lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);lcd_draw_rectangle(5, 110, 115, 314, BLACK);lcd_draw_rectangle(125, 110, 234, 314, BLACK);lcd_draw_line(5, 130, 115, 130, BLACK);lcd_draw_line(125, 130, 234, 130, BLACK);lcd_show_string(15, 111, 110, 16, 16, "Task1: 000", BLUE);lcd_show_string(135, 111, 110, 16, 16, "Task2: 000", BLUE);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();
}/*** @brief       start_task* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           /* 进入临界区 *//* 创建任务1 */xTaskCreate((TaskFunction_t )task1,                 /* 任务函数 */(const char*    )"task1",               /* 任务名称 */(uint16_t       )TASK1_STK_SIZE,        /* 任务堆栈大小 */(void*          )NULL,                  /* 传入给任务函数的参数 */(UBaseType_t    )TASK1_PRIO,            /* 任务优先级 */(TaskHandle_t*  )&Task1Task_Handler);   /* 任务句柄 *//* 创建任务2 */xTaskCreate((TaskFunction_t )task2,                 /* 任务函数 */(const char*    )"task2",               /* 任务名称 */(uint16_t       )TASK2_STK_SIZE,        /* 任务堆栈大小 */(void*          )NULL,                  /* 传入给任务函数的参数 */(UBaseType_t    )TASK2_PRIO,            /* 任务优先级 */(TaskHandle_t*  )&Task2Task_Handler);   /* 任务句柄 *//* 创建任务3 */xTaskCreate((TaskFunction_t )task3,                 /* 任务函数 */(const char*    )"task3",               /* 任务名称 */(uint16_t       )TASK3_STK_SIZE,        /* 任务堆栈大小 */(void*          )NULL,                  /* 传入给任务函数的参数 */(UBaseType_t    )TASK3_PRIO,            /* 任务优先级 */(TaskHandle_t*  )&Task3Task_Handler);   /* 任务句柄 */vTaskDelete(StartTask_Handler); /* 删除开始任务 */taskEXIT_CRITICAL();            /* 退出临界区 */
}/*** @brief       task1* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task1(void *pvParameters)
{uint32_t task1_num = 0;while (1){lcd_fill(6, 131, 114, 313, lcd_discolor[++task1_num % 11]);lcd_show_xnum(71, 111, task1_num, 3, 16, 0x80, BLUE);vTaskDelay(500);}
}/*** @brief       task2* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)
{uint32_t task2_num = 0;while (1){lcd_fill(126, 131, 233, 313, lcd_discolor[11 - (++task2_num % 11)]);lcd_show_xnum(191, 111, task2_num, 3, 16, 0x80, BLUE);vTaskDelay(500);}
}/*** @brief       task3* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task3(void *pvParameters)
{uint8_t key = 0;while (1){key = key_scan(0);switch (key){case KEY0_PRES:                     /* 删除任务1 */{if (Task1Task_Handler != NULL){vTaskDelete(Task1Task_Handler);Task1Task_Handler = NULL;}break;}case KEY1_PRES:                     /* 删除任务2 */{if (Task2Task_Handler != NULL){vTaskDelete(Task2Task_Handler);Task2Task_Handler = NULL;}break;}default:{break;}}vTaskDelay(10);}
}

实验现象:

每500ms,两块区域颜色变化,并计数+1

4.7静态创建函数实战

(基于正点原子STM32F103开发板V2   a盘资料)

第一步:

将宏configSUPPORT_STATIC_ALLOCATION 配置为 1(默认为0)

路径:\User\freertos_demo.c\FreeRTOSConfig.h

第二步:

定义空闲任务&定时器任务的任务堆栈及TCB

接口函数:vApplicationGetTimerTaskMemory ( ):这个函数用于获取计时器任务(Timer Task)的内存分配

目的:获取定时器服务任务的任务堆栈和任务控制块内存

static StackType_t  TimerTaskStack[configTIMER_TASK_STACK_DEPTH];   /* 定时器服务任务堆栈 */
static StaticTask_t TimerTaskTCB;                                   /* 定时器服务任务控制块 */* @brief       获取定时器服务任务的任务堆栈和任务控制块内存* @param       ppxTimerTaskTCBBuffer:任务控制块内存ppxTimerTaskStackBuffer:任务堆栈内存pulTimerTaskStackSize:任务堆栈大小* @retval      无*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,StackType_t  **ppxTimerTaskStackBuffer,uint32_t     *pulTimerTaskStackSize)
{*ppxTimerTaskTCBBuffer  = &TimerTaskTCB;*ppxTimerTaskStackBuffer= TimerTaskStack;*pulTimerTaskStackSize  = configTIMER_TASK_STACK_DEPTH;
}

第三步:

接口函数

vApplicationGetIdleTaskMemory( ):这个函数用于获取空闲任务(Idle Task)的内存分配

目的:获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该由用户来提供,FreeRTOS提供了接口函数(上面的两个函数)

static StackType_t  IdleTaskStack[configMINIMAL_STACK_SIZE];        /* 空闲任务任务堆栈 */
static StaticTask_t IdleTaskTCB;                                    /* 空闲任务控制块 */* @brief       获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该由用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()实现此函数即可。* @param       ppxIdleTaskTCBBuffer:任务控制块内存ppxIdleTaskStackBuffer:任务堆栈内存pulIdleTaskStackSize:任务堆栈大小* @retval      无*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t  **ppxIdleTaskStackBuffer, uint32_t     *pulIdleTaskStackSize)
{*ppxIdleTaskTCBBuffer   = &IdleTaskTCB;*ppxIdleTaskStackBuffer = IdleTaskStack;*pulIdleTaskStackSize   = configMINIMAL_STACK_SIZE;
}

第四步:

配置任务task1、task2、task3、start_task以及屏幕显示

路径:User\freertos_demo.c

详细细节在代码中有解释(注释)

完整代码(包括二三步):


static StackType_t  IdleTaskStack[configMINIMAL_STACK_SIZE];        /* 空闲任务任务堆栈 */
static StaticTask_t IdleTaskTCB;                                    /* 空闲任务控制块 */
static StackType_t  TimerTaskStack[configTIMER_TASK_STACK_DEPTH];   /* 定时器服务任务堆栈 */
static StaticTask_t TimerTaskTCB;                                   /* 定时器服务任务控制块 *//*** @brief       获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()实现此函数即可。* @param       ppxIdleTaskTCBBuffer:任务控制块内存ppxIdleTaskStackBuffer:任务堆栈内存pulIdleTaskStackSize:任务堆栈大小* @retval      无*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t  **ppxIdleTaskStackBuffer, uint32_t     *pulIdleTaskStackSize)
{*ppxIdleTaskTCBBuffer   = &IdleTaskTCB;*ppxIdleTaskStackBuffer = IdleTaskStack;*pulIdleTaskStackSize   = configMINIMAL_STACK_SIZE;
}/*** @brief       获取定时器服务任务的任务堆栈和任务控制块内存* @param       ppxTimerTaskTCBBuffer:任务控制块内存ppxTimerTaskStackBuffer:任务堆栈内存pulTimerTaskStackSize:任务堆栈大小* @retval      无*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,StackType_t  **ppxTimerTaskStackBuffer,uint32_t     *pulTimerTaskStackSize)
{*ppxTimerTaskTCBBuffer  = &TimerTaskTCB;*ppxTimerTaskStackBuffer= TimerTaskStack;*pulTimerTaskStackSize  = configTIMER_TASK_STACK_DEPTH;
}/* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
StackType_t StartTaskStack[START_STK_SIZE]; /* 任务堆栈 */
StaticTask_t            StartTaskTCB;       /* 任务控制块 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 *//* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
StackType_t Task1TaskStack[TASK1_STK_SIZE]; /* 任务堆栈 */
StaticTask_t            Task1TaskTCB;       /* 任务控制块 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 *//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
StackType_t Task2TaskStack[TASK2_STK_SIZE]; /* 任务堆栈 */
StaticTask_t            Task2TaskTCB;       /* 任务控制块 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 *//* TASK3 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK3_PRIO      4                   /* 任务优先级 */
#define TASK3_STK_SIZE  128                 /* 任务堆栈大小 */
StackType_t Task3TaskStack[TASK3_STK_SIZE]; /* 任务堆栈 */
StaticTask_t            Task3TaskTCB;       /* 任务控制块 */
TaskHandle_t            Task3Task_Handler;  /* 任务句柄 */
void task3(void *pvParameters);             /* 任务函数 *//******************************************************************************************************//* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,MAGENTA, GREEN, CYAN, YELLOW,BROWN, BRRED, GRAY};/*** @brief       FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);lcd_show_string(10, 47, 220, 24, 24, "Task Create & Del", RED);lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);lcd_draw_rectangle(5, 110, 115, 314, BLACK);lcd_draw_rectangle(125, 110, 234, 314, BLACK);lcd_draw_line(5, 130, 115, 130, BLACK);lcd_draw_line(125, 130, 234, 130, BLACK);lcd_show_string(15, 111, 110, 16, 16, "Task1: 000", BLUE);lcd_show_string(135, 111, 110, 16, 16, "Task2: 000", BLUE);StartTask_Handler = xTaskCreateStatic((TaskFunction_t   )start_task,        /* 任务函数 */(const char*      )"start_task",      /* 任务名称 */(uint32_t         )START_STK_SIZE,    /* 任务堆栈大小 */(void*            )NULL,              /* 传递给任务函数的参数 */(UBaseType_t      )START_TASK_PRIO,   /* 任务优先级 */(StackType_t*     )StartTaskStack,    /* 任务堆栈 */(StaticTask_t*    )&StartTaskTCB);    /* 任务控制块 */vTaskStartScheduler();
}/*** @brief       start_task* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           /* 进入临界区 *//* 创建任务1 */Task1Task_Handler = xTaskCreateStatic((TaskFunction_t   )task1,         /* 任务函数 */(const char*      )"task1",       /* 任务名称 */(uint32_t         )TASK1_STK_SIZE,/* 任务堆栈大小 */(void*            )NULL,          /* 传递给任务函数的参数 */(UBaseType_t      )TASK1_PRIO,    /* 任务优先级 */(StackType_t*     )Task1TaskStack,/* 任务堆栈 */(StaticTask_t*    )&Task1TaskTCB);/* 任务控制块 *//* 创建任务2 */Task2Task_Handler = xTaskCreateStatic((TaskFunction_t   )task2,         /* 任务函数 */(const char*      )"task2",       /* 任务名称 */(uint32_t         )TASK2_STK_SIZE,/* 任务堆栈大小 */(void*            )NULL,          /* 传递给任务函数的参数 */(UBaseType_t      )TASK2_PRIO,    /* 任务优先级 */(StackType_t*     )Task2TaskStack,/* 任务堆栈 */(StaticTask_t*    )&Task2TaskTCB);/* 任务控制块 *//* 创建任务3 */Task3Task_Handler = xTaskCreateStatic((TaskFunction_t   )task3,         /* 任务函数 */(const char*      )"task3",       /* 任务名称 */(uint32_t         )TASK3_STK_SIZE,/* 任务堆栈大小 */(void*            )NULL,          /* 传递给任务函数的参数 */(UBaseType_t      )TASK3_PRIO,    /* 任务优先级 */(StackType_t*     )Task3TaskStack,/* 任务堆栈 */(StaticTask_t*    )&Task3TaskTCB);/* 任务控制块 */vTaskDelete(StartTask_Handler); /* 删除开始任务 */taskEXIT_CRITICAL();            /* 退出临界区 */
}/*** @brief       task1* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task1(void *pvParameters)
{uint32_t task1_num = 0;while (1){lcd_fill(6, 131, 114, 313, lcd_discolor[++task1_num % 11]);lcd_show_xnum(71, 111, task1_num, 3, 16, 0x80, BLUE);vTaskDelay(500);}
}/*** @brief       task2* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)
{uint32_t task2_num = 0;while (1){lcd_fill(126, 131, 233, 313, lcd_discolor[11 - (++task2_num % 11)]);lcd_show_xnum(191, 111, task2_num, 3, 16, 0x80, BLUE);vTaskDelay(500);}
}/*** @brief       task3* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task3(void *pvParameters)
{uint8_t key = 0;while (1){key = key_scan(0);switch (key){case KEY0_PRES:                     /* 删除任务1 */{if (Task1Task_Handler != NULL){vTaskDelete(Task1Task_Handler);Task1Task_Handler = NULL;}break;}case KEY1_PRES:                     /* 删除任务2 */{if (Task2Task_Handler != NULL){vTaskDelete(Task2Task_Handler);Task2Task_Handler = NULL;}break;}default:{break;}}vTaskDelay(10);}
}

除了定义接口函数外,其余与动态创建的函数相同

实验现象:

每500ms,两块区域颜色变化,并计数+1

4.8实战总结

注意:

1.动态创建相对简单,在实际的应用中,动态方式创建任务是比较常用的,除非有特殊的需求,一般都会使用动态方式创建任务
2.静态创建:可将任务堆栈放置在特定的内存位置,并且无需关心对内存分配失败的处理

5.总结

FreeRTOS官网:https://www.freertos.org/

正点原子STM32F103开发板V2   a盘资料: https://pan.baidu.com/s/1iebOVd87jBVtoudMijboIg 提取码:1ypa

FreeRTOS内容较多,内容分篇发布,感谢大家观看

连载中...  

作者留言:本人学生,大多数为网络资源,如有侵权,及时联系

创作时间:2024.10.22

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

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

相关文章

Wooden UI(木头UI纹理按钮边框 背景图标 带PNG素材)

资源包包含以下元素:按钮、图标、框架、复选框等,提供分层的 PSD 文件。 下载:​​Unity资源商店链接资源下载链接 效果图:

橡皮鸭调试法(Rubber Duck Debugging)

橡皮鸭调试法(Rubber Duck Debugging)是一种编程中的调试技巧,名字来源于一本书《The Pragmatic Programmer》,其中提到程序员可以把问题讲给一只橡皮鸭听,以便在讲解的过程中梳理思路并找到问题的根源。 基本原理&am…

微服务基础架构(图)

微服务基础架构是一种现代化的软件架构模式,旨在将大型复杂的应用程序拆分为多个小型、独立的服务。每个微服务专注于特定的业务功能,可独立开发、部署和扩展。 在微服务基础架构中,通常会使用轻量级的通信机制,如 RESTful API 或…

RISC-V笔记——RVWMO基本体

1. 前言 RISC-V使用的内存模型是RVWMO(RISC-V Weak Memory Ordering),它是Release Consistency的扩展,因此,RVWMO的基本特性类似于RC模型。 2. RC模型 Release consistency(RC)的提出是基于一个观察:将所有同步操作用FENCE围在一…

升级Unity后产生的Objects内存泄露现象

1)升级Unity后产生的Objects内存泄露现象 2)能否使用OnDemandRendering API来显示帧率 3)Unity闪退问题 4)配置表堆内存如何优化 这是第405篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答…

PHP 8.1.0-dev后门远程命令执行漏洞

#简介 PHP verion 8.1.0-dev于2021年3月28日与后门一起发布,但是后门很快被发现并删除。 #漏洞概述 PHP verion 8.1.0-dev的PHP在服务器上运行,则攻击者可以通过发送User-Agentt标头执行任意代码。 #影响版本 PHP 8.1.0-dev #环境搭建 新建docker…

LLM之RAG实战(四十四)| rag-chatbot:支持Huggingface和Ollama任意模型的多PDF本地RAG方案

特点: 支持本地运行和Kaggle (new)运行支持Huggingface 和Ollama 的任意模型Process multiple PDF inputs.Chat with multiples languages (Coming soon).Simple UI with Gradio. 一、安装使用 1.1 Kaggle(推荐) Step1:把https…

Android 10.0 Camera2 拍照镜像功能实现

1.前言 在10.0的系统rom定制化开发中,在进行camera2的相关拍照功能开发中,在某些时候会遇到拍照照片 左右镜像的问题,就是照片左半边和右半边是反的,所以就需要在拍照的时候保存图片的时候实现 左右镜像功能,接下来就来分析下拍照保存图片的流程 2.Camera2 拍照镜像功能实…

SQL 干货 | SQL 反连接

最强大的 SQL 功能之一是 JOIN 操作,它提供了一种优雅而简单的方法,将一个表中的每一条记录与另一个表中的每一条记录结合起来。不过,有时我们可能想从一个表中找到另一个表中没有的值。正如我们将在今天的博客文章中看到的,通过包…

图片无损放大工具Topaz Gigapixel AI v7.4.4 绿色版

Topaz A.I. Gigapixel是这款功能齐全的图象无损变大运用,应用可将智能机拍摄的图象也可以有着专业相机的高质量大尺寸作用。你可以完美地放大你的小照片并大规模打印,它根本不会粘贴。它具有清晰的效果和完美的品质。 借助AIGigapixel,您可以…

[Linux网络编程]01-网络基础

此部分为《计算机网络》理论基础,可简要了解 一.计算机网络体系结构 常见的体系结构 OSI体系结构(法律上的国际标准):物理层->数据链路层->网络层->运输层->会话层->表示层->应用层 TCP/IP体系结构(事实上的国际标准):…

web网页QQ登录

代码&#xff1a; <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>QQ登录ent</title> </head> <style>ul > li{list-style: none; } a …

13.1 Linux_网络编程_TCP/UDP

字节序 1、概述 什么是字节序&#xff1a; 字节序就是字节的存储顺序&#xff0c;分为大端字节序和小端字节序。 大端字节序&#xff1a;低地址存高位&#xff08;网络&#xff09;小端字节序&#xff1a;低地址存低位&#xff08;主机&#xff09; 检验主机字节序模式&…

【Linux】-权限

&#x1f511;&#x1f511;博客主页&#xff1a;阿客不是客 &#x1f353;&#x1f353;系列专栏&#xff1a;深入代码世界&#xff0c;了解掌握 Linux 欢迎来到泊舟小课堂 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ​ 一、权限的概念 在Linux 中&…

开源图像超分ECBSR项目源码分析

相关介绍 项目GitHub地址&#xff1a;https://github.com/xindongzhang/ECBSR项目相关论文&#xff1a;https://www4.comp.polyu.edu.hk/~cslzhang/paper/MM21_ECBSR.pdf&#xff08;也可以点这里下载&#xff09;论文解读&#xff1a;Edge-oriented Convolution Block for Re…

秃姐学AI系列之:语义分割 + 数据集 | 转置卷积 + 代码

语义分割 语义分割将图片中的每个像素分类到对应的类别 通常来说现在的会议软件的背景虚化这个功能用的就是语义分割技术 无人车进行路面识别也是语义分割技术 语义分割 vs 实例分割 语义分割将图像划分为若干组成区域&#xff0c;这类问题的方法通常利用图像中像素之间的相关…

基于Multisim三极管B放大系数放大倍数测量电路设计(含仿真和报告)

【全套资料.zip】三极管B放大系数放大倍数测量电路电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.用三个数码管显示B的大小&#xff0c;分别显示个位、十位和百位。 2.显示范围…

【论文精炼分享】GPU Memory Exploitation for Fun and Profit 24‘USENIX

今天分享的论文《GPU Memory Exploitation for Fun and Profit》来自2024年USENIX Security。在本文中&#xff0c;作者团队对 CUDA 程序中的缓冲区溢出问题进行了全面的研究&#xff1a; &#xff08;1&#xff09;对用于访问各种 GPU 内存空间的机制进行了逆向工程&#xff…

纯css 轮播图片,鼠标移入暂停 移除继续

核心 滚动&#xff1a; animation: 动画名称 20s linear infinite normal;停止&#xff1a; animation: 动画名称 20s linear infinite paused; 完整例子&#xff1a; html: <div class"carousel-wrapper"><div class"carousel"><div cl…

Docker学习笔记(2)- Docker的安装

1. Docker的基本组成 镜像&#xff08;image&#xff09;&#xff1a;Docker镜像就像是一个模板&#xff0c;可以通过这个模板来创建容器服务。通过一个镜像可以创建多个容器。最终服务运行或者项目运行就是在容器中。容器&#xff08;container&#xff09;&#xff1a;Docker…