当前位置: 首页 > news >正文

FreeRTOS复习

1.FreeRTOS介绍

是一个实时操作系统,windows是通用操作系统

优点:

  • 处理多任务

  • 专注实时性

2.FreeRTOS基础知识

2.1.任务调度策略

抢占式调度:优先级高的,抢占优先级高的;

时间片轮询:优先级相同的,每个任务执行一个时间片;时间片就是时钟节拍tick,也就是一次系统时钟中断;(如果一个任务,时间片执行了一半任务结束了,那么会立马切换到下一个任务)

协程式:有高优先级的任务就绪时,需要等待这一个正在执行的低优先级任务执行结束才执行A;

任务调度器:任务切换;

默认任务调度策略:默认使用固定优先级的抢占式调度,对同等优先级执行时间片轮询;

FreeRTOS任务优先级:优先级数值越大,优先级越高;

高优先级会不会一直执行?:高优先级任务阻塞(延时或者等待),会执行就绪态的其他任务;

2.2.任务状态

2.2.1四种任务状态介绍

运行态:正在运行,同一时间只有一个任务处于运行态;

就绪态:已经准备就绪,随时可以执行;

阻塞态:正在等待延时或者外部事件;

挂起态:暂停,调用vTaskSuspend()挂起,调用vTaskResume()就绪;

在这里插入图片描述

2.2.2.任务状态保存方式

列表,内部其实就是双向链表。

就绪列表:pxReadyTasksLists[x],这个有32个,x代表任务优先级

阻塞列表:pxDelayedTaskList

挂起列表:xSuspendedTaskList

有一个32位变量,存标志位,如果对应的就绪列表有任务存在,就会把对应的位置1,调度器从最高位开始找,选择优先级最高的任务来执行;

在这里插入图片描述

2.3.FreeRTOS滴答

默认使用的systick系统滴答定时器;

每次滴答数增加时,实时内核必须检查是否现在是解除阻塞或唤醒任务的时间。

2.4.上下文切换

上下文:程序执行时的某一个状态的所有程序信息,数据、执行状态等等;保存了上下文就可以暂停任务,然后恢复到暂停的那个时刻;

要从任务a切换到任务b:1.保存a任务的上下文;2.恢复b任务的上下文

任务堆栈:每一个任务的上下文,会存在自己的任务堆栈中。(这是一种数据结构,实际上存在堆空间,FreeRTOS自己申请和释放)

上下文切换:每个栈有栈顶指针,根据栈顶指针来寻找任务堆栈;

上下问切换在PendSV的ISR中断来处理,默认用的最低优先级,避免任务切换抢占其他中断;

触发PendSV异常产生切换:1.RTOS滴答中断,处理就绪列表,判断是否要切换任务(包括抢占和时间片)2.任务执行完毕:主动调用任务切换函数强制切换;

2.5.空闲任务

RTOS任务调度器启动时,自动创建空闲任务,始终运行,优先级最低;

空闲任务的作用:1.保证系统时钟存在能运行的任务;2.释放删除任务的内存;3.低功耗支持;

3FreeRTOS移植

3.1.源码结构介绍

1)下载文件目录

名称描述
FreeRTOSFreeRTOS内核
FreeRTOS-PlusFreeRTOS组件,一般我们会选择使用第三方的组件
tools工具
GitHub-FreeRTOS-HomeFreeRTOS的GitHub仓库链接
Quick_Start_Guide快速入门指南官方文档链接
Upgrading-to-FreeRTOS-xxx升级到指定FreeRTOS版本官方文档链接
History.txtFreeRTOS历史更新记录
其他其他

2)FreeRTOS文件夹结构

名称描述
DemoFreeRTOS演示例程,支持多种芯片架构、多种型号芯片
LicenseFreeRTOS相关许可
SourceFreeRTOS源码,最重要的文件夹
Test公用以及移植层测试代码

3)Source文件夹结构如下

名称描述
include内包含了FreeRTOS的头文件
portable包含FreeRTOS移植文件:与编译器相关、keil编译环境
croutine.c协程相关文件
event_groups.c事件相关文件
list.c列表相关文件
queue.c队列相关文件
stream_buffer.c流式缓冲区相关文件
tasks.c任务相关文件
timers.c软件定时器相关文件
  • include文件夹和.c文件是通用的头文件和 C 文件,这两部分的文件适用于各种编译器和处理器,是通用的。

  • 加粗的是移植必需的,其他.c文件根据需要选取。

  • portable文件夹里根据编译器、内核等实际环境对应选取。

4)portable文件夹结构

FreeRTOS操作系统归根到底是一个软件层面的东西,需要跟硬件联系在一起,portable文件夹里面的东西就是和硬件的连接桥梁。由于我们使用MDK开发,因此这里只重点介绍其中的部分移植文件。

名称描述
Keil指向RVDS文件夹
RVDS不同内核芯片的移植文件
MemMang内存管理相关文件

Keil文件夹里只有一个See-also-the-RVDS-directory.txt,意思是让我们看RVDS文件夹。

3.2.移植步骤

3.2.1.移植7个FreeRTOS的.c文件和头文件

3.2.2.移植硬件关联的portable和内存管理文件

我使用的是stm32F407ZGT6,所以选ARM_CM4F

3.2.3.FreeRTOS配置文件

#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
// #define xPortSysTickHandler SysTick_Handler 这样不用注释掉it.c里面的SysTick_Handler

#define INCLUDE_xTaskGetSchedulerState 1

配置系统中断

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

4.FreeRTOS中任务的创建和删除

4.1.动态创建

  • 要开启宏
#define configSUPPORT_DYNAMIC_ALLOCATION 1	// 开启动态分配
  • 任务句柄,指向任务控制块TCB
void start_task(void *pvParameters);
TaskHandle_t start_task_handle;
#define START_TASK_STACK_SIZE    128
#define START_TASK_PRIORITY      1

void freertos_start(void)
{
	// 1.创建一个启动任务
	xTaskCreate((TaskFunction_t)start_task,					     // 任务函数地址
				(char *)"start_task", 						     // 任务名称	
				(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,  // 任务堆栈大小
				(void *)NULL,								     // 传递给任务函数的参数					
				(UBaseType_t)START_TASK_PRIORITY,			     // 任务优先级
				(TaskHandle_t *)&start_task_handle);			 // 任务句柄
	// 2.启动调度器:会自动创建空闲任务和软件定时器(如果开启)
	vTaskStartScheduler();
}

1.申请堆栈内存,任务控制块内存;

2.TCB结构体成员赋值;

3.添加新任务到就绪列表;

4.2.静态创建

  • 要开启一下宏
#define configSUPPORT_STATIC_ALLOCATION 1	// 开启静态分配
  • 没有TCB,堆栈自己管理
void start_task(void *pvParameters);
TaskHandle_t start_task_handle;
#define START_TASK_STACK_SIZE    128
#define START_TASK_PRIORITY      1
StackType_t start_task_stack[START_TASK_STACK_SIZE]; 	// 静态任务的任务栈,以数组形式存储
StaticTask_t start_task_tcb; 							// 声明静态任务控制块TCB

void freertos_start(void)
{
	// 1.创建一个启动任务
	start_task_handle = xTaskCreateStatic(	(TaskFunction_t) start_task,		// 任务函数
											(char *) "start_task", 				// 任务名称
											(uint32_t) START_TASK_STACK_SIZE,	// 任务堆栈大小
											(void *) NULL,						// 任务参数
											(UBaseType_t) START_TASK_PRIORITY,	// 任务优先级
											(StackType_t *)  start_task_stack,	// 任务堆栈地址
											(StaticTask_t *)  &start_task_tcb);	// 静态任务的控制块TCB结构体
	// 2.启动调度器:会自动创建空闲任务和软件定时器(如果开启),静态创建的方式需要去实现2个分配资源的接口函数
	vTaskStartScheduler();
}
  • 静态函数还需要手动创建空闲任务和软件定时器
  • 要开启以下宏
/* Software timer definitions. */
#define configUSE_TIMERS				1
#define configTIMER_TASK_PRIORITY		( 2 )
#define configTIMER_QUEUE_LENGTH		10
#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE * 2 )
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
									StackType_t ** ppxIdleTaskStackBuffer,
									uint32_t * pulIdleTaskStackSize )	
{
	*ppxIdleTaskTCBBuffer = &idle_task_tcb;
	*ppxIdleTaskStackBuffer = idle_task;
	*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
									 StackType_t ** ppxTimerTaskStackBuffer,
									 uint32_t * pulTimerTaskStackSize )
{
	*ppxTimerTaskTCBBuffer = &timer_task_tcb;
	*ppxTimerTaskStackBuffer = timer_task;
	*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

5.任务删除

这个函数只是任务标记删除,从就绪、阻塞、挂起列表中移除,并没有释放内存,空闲函数释放内存;

vTaskDelete(NULL);

6.任务/调度器 挂起

只有ISR的才能在中断里面调用;

  • 开启以下宏
#define INCLUDE_vTaskSuspend                            1		// 开启任务挂起/恢复
#define INCLUDE_xResumeFromISR                          1		// 开启中断里任务恢复

/* 开启跟踪task信息 */
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
char task_infop[500];
void task3(void *pvParameters)
{
	static uint32_t count = 0;

	while (1)
	{

		count++;
		if(count == 10)
		{
			usart_debug("count = 10\r\n");
			vTaskSuspend(task1_handle);			// 挂起任务1
		}else if (count == 20)
		{
			usart_debug("count = 20\r\n");
			vTaskResume(task1_handle);			// 恢复任务1
		}else if (count == 30)
		{
			usart_debug("count = 30\r\n");
			vTaskSuspendAll();					// 挂起任务调度器,会一直执行这个任务,出不去了
		}else if (count == 40)
		{
			usart_debug("count = 40\r\n");
			xTaskResumeAll();					// 恢复任务调度器
		}
		
		usart_debug("%d",count);
		usart_debug("task3 正在运行\r\n");
		LED3_Toggle();

		vTaskList(task_infop);
		debug_usart_send_string(task_infop);

		// 如果开启了任务调度器
		if(	xTaskGetSchedulerState())
		{
			vTaskDelay(500);
		}
	}
}

7.查看任务状态

char task_infop[500];
vTaskList(task_infop);
debug_usart_send_string(task_infop);
  • 这里是剩余堆栈,不是使用堆栈

在这里插入图片描述

8.中断管理

**BASEPRI:**中断管理,屏蔽中断的,是STM32里面的,这个寄存器只看高4位;例:0x50,高4位是0101,代表屏蔽5-15的中断优先级,只保留0-4的正常执行;

在FreeRTOS中,是configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY管理可不可以调用xxxFromISR

**中断处理流程:**发起中断请求 —> 响应中断请求 —> 保存现场 —> 执行ISR —> 恢复现场

  • 开关中断相关宏
#define portDISABLE_INTERRUPTS()                  vPortRaiseBASEPRI()  // 关中断,关闭要屏蔽的优先级的中断
#define portENABLE_INTERRUPTS()                   vPortSetBASEPRI( 0 )  // 开中断,打开所有中断
/*3. 中断嵌套行为相关配置 cm3内核:我们要求4个优先级位全部为抢占优先级位
    最高优先级是 0
    最低优先级是 15
*/
/* 设置 RTOS 内核自身使用的中断优先级。 一般设置为最低优先级, 不至于屏蔽其他优先级程序*/
#define configKERNEL_INTERRUPT_PRIORITY (15 << 4)
/* 设置了 调用中断安全的 FreeRTOS API 函数的最高中断优先级。 FreeRTOS 的管理的最高优先级 */        
#define configMAX_SYSCALL_INTERRUPT_PRIORITY  (5 << 4)
/* 同上. 仅用于新版移植。 这两者是等效的。 */  
#define configMAX_API_CALL_INTERRUPT_PRIORITY   configMAX_SYSCALL_INTERRUPT_PRIORITY

8.1.临界区

  • 临界区内部也是关闭中断

  • 临界区需要同时出现,进去了多次,也要退出多次

  • 挂起任务调度器,不会关闭中断,只是不切换任务

9.时间片调度

  • 开启相关宏
#define configUSE_TIME_SLICING                          1	// 开启时间片调度
#define configUSE_PREEMPTION                            1	// 起用抢占式调度
#define configTICK_RATE_HZ          ( ( TickType_t ) 20 )  // 时间片大小

10.任务相关API函数

10.1.任务状态查询类

任务相关的API主要如下:

函数描述
uxTaskPriorityGet()获取任务优先级
vTaskPrioritySet()设置任务优先级
uxTaskGetNumberOfTasks()获取系统中任务的数量
uxTaskGetSystemState()获取所有任务状态信息
vTaskGetInfo()获取指定单个的任务信息
xTaskGetCurrentTaskHandle()获取当前任务的任务句柄
xTaskGetHandle()根据任务名获取该任务的任务句柄
uxTaskGetStackHighWaterMark()获取任务的任务栈历史剩余最小值
eTaskGetState()获取任务状态
vTaskList()以“表格”形式获取所有任务的信息
vTaskGetRunTimeStats()获取任务的运行时间

官网:https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/04-API-references/03-Task-utilities/00-Task-utilities

  • 开启宏定义
#define INCLUDE_xTaskGetSchedulerState 1
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
#define INCLUDE_xTaskGetHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_eTaskGetState 1

10.2.任务时间统计类

函数描述
vTaskGetRunTimeStats()获取任务的运行时间
  • 需要一个精度比systick精度更高的
  • 声明ulHighFrequencyTimerTicks变量,在定时器中断里++
/* 运行时间和任务状态统计相关定义 */
#define configGENERATE_RUN_TIME_STATS    1      /* 1: 使能任务运行时间统计功能, 默认: 0 */
#if configGENERATE_RUN_TIME_STATS
extern volatile unsigned long ulHighFrequencyTimerTicks;
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL )
#define portGET_RUN_TIME_COUNTER_VALUE()    ulHighFrequencyTimerTicks
#endif
#define configUSE_TRACE_FACILITY               1                      
#define configUSE_STATS_FORMATTING_FUNCTIONS   1     
  • portCONFIGURE_TIMER_FOR_RUNTIME_STATE():用于初始化用于配置任务运行时间统计的时基定时器。它的时间精度需要比 tick 中断具有更高的精度,建议10到100倍。

  • portGET_RUN_TIME_COUNTER_VALUE():返回该定时器的计数值,即当前已运行的时间。

参数说明:https://freertos.org/zh-cn-cmn-s/rtos-run-time-stats.html

11.delay

vTaskDelay(n):相对延时,从这里开始延时n个滴答节拍;(只管delay自己延时这么久)

xTaskDelayUntil(n);绝对延时,从函数开始到结束,一共n个滴答节拍;(整个任务执行这么久)

12.消息队列queue

任务 中断,相互之间发送消息;

一个任务把消息放到queue里面,另一个任务可以从里面读出来;

  • 创建队列
函数描述
xQueueCreate()动态方式创建队列
xQueueCreateStatic()静态方式创建队列
  • 写队列
xQueueSend()往队列的尾部写入消息
xQueueSendToBack()同 xQueueSend()
xQueueSendToFront()往队列的头部写入消息
xQueueOverwrite()覆写队列消息(只用于队列长度为 1 的情况)
xQueueSendFromISR()在中断中往队列的尾部写入消息
xQueueSendToBackFromISR()同 xQueueSendFromISR()
xQueueSendToFrontFromISR()在中断中往队列的头部写入消息
xQueueOverwriteFromISR()在中断中覆写队列消息(只用于队列长度为 1 的情况)
  • 读队列
函数描述
xQueueReceive()从队列头部读取消息,并删除消息
xQueuePeek()从队列头部读取消息
xQueueReceiveFromISR()在中断中从队列头部读取消息,并删除消息
xQueuePeekFromISR()在中断中从队列头部读取消息

13.信号量semphore

信号量与队列的区别如下:

信号量队列
主要用于管理对共享资源的访问,确保在同一时刻只有一个任务可以访问共享资源用于任务之间的数据通信,通过在任务之间传递消息,实现信息的传递和同步。
可以是二进制信号量(Binary Semaphore)或计数信号量(Counting Semaphore)存储和传递消息的数据结构,任务可以发送消息到队列,也可以从队列接收消息。
适用于对资源的互斥访问,控制任务的执行顺序,或者限制同时访问某一资源的任务数量。适用于在任务之间传递数据,实现解耦和通信。

13.1.二值信号量Binary

介绍:

  • 二值信号量只有0和1;

  • 底层实现就是队列,不过这里只判断队列满了没有;释放了就满了,获取了就不满了;

用途:

  • 任务同步:任务1执行完了,释放信号量,任务2收到信号量,开始执行。1和2同步;

  • 互斥访问(对共享资源管理):一个资源,只能被一个任务使用;eg:任务1和任务2都要对a++,由于a++不是原子操作,所以可能会加的不够,所以任务执行的时候获取信号量,执行结束释放信号量,保证a++同时只有1个任务在操作;

二值信号量相关函数:

函数描述
xSemaphoreCreateBinary()使用动态方式创建二值信号量
xSemaphoreCreateBinaryStatic()使用静态方式创建二值信号量
xSemaphoreGive()释放信号量
xSemaphoreGiveFromISR()在中断中释放信号量
xSemaphoreTake()获取信号量
xSemaphoreTakeFromISR()在中断中获取信号量

13.2.计数型信号量

介绍

  • 计数型信号量,有多个,可以计数
  • 释放多少个,就+n,获取了多少个,就-n

用途

  • 事件计数:事件A执行多次释放多次,事件B执行多次获取多次;
  • 资源管理:计数值表示可用的资源数量;

开启宏

#define configUSE_COUNTING_SEMAPHORES 1
函数描述
xSemaphoreCreateCounting()使用动态方法创建计数型信号量。
xSemaphoreCreateCountingStatic()使用静态方法创建计数型信号量
uxSemaphoreGetCount()获取信号量的计数值

13.3.优先级翻转问题

在这里插入图片描述

三个任务,优先级为1、2、3,1和3同步执行,公用一个二值信号量;

1.任务1获取了信号量,还没释放被3打断了;

2.3要获取信号量,发现没有,被阻塞了,又回到1;

3.1还没释放信号量,任务2就绪了,被任务2抢占了;

4.任务2执行完,才执行任务1;

5.任务1执行完,释放了信号量,任务3才获取信号量继续执行;

这里发现,任务执行顺序为2,1,3,并不是3,2,1;

13.4.互斥信号量

介绍

  • 比二值信号量多了优先级继承机制;
  • 当一个高优先级任务需要等待一个低优先级任务的资源时,会提升低任务的优先级;这样避免了高优先级任务长时间等待;
  • 高优先级任务获取到资源后,会立马把刚刚低优先级的任务的优先级放回去;

在这里插入图片描述

注意!!!!!!!!!!!!

  • 不能完全解决优先级翻转问题;
  • 不能在中断中使用互斥信号量;1.中断没有任务优先级,优先级继承机制无法生效;2.中断无法阻塞等待一个互斥信号量保护的资源;

互斥信号量相关函数:

函数描述
xSemaphoreCreateMutex()使用动态方法创建互斥信号量。
xSemaphoreCreateMutexStatic()使用静态方法创建互斥信号量。
  • 要配置的宏
#define configUSE_MUTEXES 1

14.队列集

**介绍:**一种数据结构,用于管理多个队列

**作用:**一个任务从a、b两个队列里面接收,如果代码a写在前面,b写在后面,那么如果b队列有数据,a阻塞了,并不会收到;

  • 开启一下宏
#define configUSE_QUEUE_SETS    
函数描述
xQueueCreateSet()创建队列集
xQueueAddToSet()队列添加到队列集中
xQueueRemoveFromSet()从队列集中移除队列
xQueueSelectFromSet()获取队列集中有有效消息的队列
xQueueSelectFromSetFromISR()在中断中获取队列集中有有效消息的队列

15.事件标志组

介绍:相当于一组信号量,有多个位,可以等待一个位,也可以等待多个位

事件标志组是多少位,怎么设置:

Ø 如果 configUSE_16_BIT_TICKS 设置为 1,则事件组内实现的位数(或标志数)为 8; 如果 configUSE_16_BIT_TICKS 设置为 0,则为 24。

Ø 如果 configTICK_TYPE_WIDTH_IN_BITS 设为 TICK_TYPE_WIDTH_16_BITS,则事件组内实现的位数(或标志数)为 8。

Ø 如果 configTICK_TYPE_WIDTH_IN_BITS 设为 TICK_TYPE_WIDTH_32_BITS,则为 24 。

Ø 如果 configTICK_TYPE_WIDTH_IN_BITS 设为 TICK_TYPE_WIDTH_64_BITS,则为 56。

configTICK_TYPE_WIDTH_IN_BITS 只在新版本中有,老版本只有configUSE_16_BIT_TICKS 配置

事件标志组相关函数:

函数描述
xEventGroupCreate()使用动态方式创建事件标志组
xEventGroupCreateStatic()使用静态方式创建事件标志组
xEventGroupClearBits()清零事件标志位
xEventGroupClearBitsFromISR()在中断中清零事件标志位
xEventGroupSetBits()设置事件标志位
xEventGroupSetBitsFromISR()在中断中设置事件标志位
xEventGroupWaitBits()等待事件标志位
xEventGroupSync()设置事件标志位,并等待事件标志位

16.任务通知

介绍:

任务与任务直接发送数据,减少了中间量(消息队列,信号量,事件通知组)

通知接收方法:

Ø 覆盖原值,无论接收任务是否已读取被覆盖的值。

Ø 覆盖原值(仅当接收任务已读取被覆盖的值时)。

Ø 在值中设置一个或多个位。

Ø 对值进行增量(添加 1)。

10.4.0版本之前,是任务通知变量(只能接收一个任务的通知)

10.4.0版本之后,是任务通知组(可以接收多个任务的通知)

  • 要开启的宏
configUSE_TASK_NOTIFICATIONS 设为0可以禁用。

任务通知相关函数如下:

函数描述
xTaskNotify()发送通知,带有通知值
xTaskNotifyAndQuery()发送通知,带有通知值并且保留接收任务的原通知值
xTaskNotifyGive()发送通知,不带通知值
xTaskNotifyFromISR()在中断中发送任务通知
xTaskNotifyAndQueryFromISR()
vTaskNotifyGiveFromISR()
ulTaskNotifyTake()获取任务通知,可选退出函数时对通知值清零或减1
xTaskNotifyWait()获取任务通知,可获取通知值和清除通知值的指定位

注意:发送通知有相关ISR函数,接收通知没有ISR函数,不能在ISR中接收任务通知。

16.1.任务通知模拟信号量

  • 发送通知,不带通知值,xTaskNotifyGive每调用一次,给任务通知组的值+1;
  • 获取通知,可以清除通知值,也可以-1;

16.2.任务通知模拟队列

  • 发送通知,带通知值 xTaskNotify(task2_handle,num,eSetValueWithOverwrite);
  • 获取通知,退出时清除通知值

16.3.任务通知模拟事件标志组

  • 发送通知:不带通知值 xTaskNotify(task2_handle,1<<n,eSetBits);
  • 获取通知,清除指定位

16.4.任务通知数组新用法

任务通知组,新版本是数组,老版本的index是0,新版本的index可以自己给,给0就第一个,给1就第二个……;并且数组的长度可以修改;

17.软件定时器

**介绍:**软件利用心跳实现的定时器;

**状态:**未创建、已创建、已运行;

**原理:**有一个存放命令的消息队列,把创建、开始等等命令放在消息队列中,软件定时器从消息队列中读命令来执行对应操作;

  • 开启以下宏
/* 软件定时器相关定义 */
#define configUSE_TIMERS 1                                          /* 1: 使能软件定时器, 默认: 0。使能后需指定下面3个 */
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1)        /* 定义软件定时器任务的优先级 */
#define configTIMER_QUEUE_LENGTH 5                                  /* 定义软件定时器命令队列的长度*/
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小*/

软件定时器相关函数如下:

函数描述
xTimerCreate()动态方式创建软件定时器
xTimerCreateStatic()静态方式创建软件定时器
xTimerStart()开启软件定时器定时
xTimerStartFromISR()在中断中开启软件定时器定时
xTimerStop()停止软件定时器定时
xTimerStopFromISR()在中断中停止软件定时器定时
xTimerReset()复位软件定时器定时
xTimerResetFromISR()在中断中复位软件定时器定时
xTimerChangePeriod()更改软件定时器的定时超时时间
xTimerChangePeriodFromISR()在中断中更改定时超时时间

回调函数:把一个函数当成另一个函数的参数来用;

18.Tickless低功耗模式

在执行空闲任务的时候,会对下一次任务切换的时间进行计算,然后关闭systick,等到需要任务切换的时候,再打开systick,为了避免tick值发生紊乱,会把这段时间的tick应该+的值计算出来加上;即在Tickless模式下,时钟中断频率明显降低,之后会把时钟节拍数补上;

  • 利用了STM32的睡眠模式,关CPU时钟;

  • Tickless模式相关配置项(掌握)

配置项说明
configUSE_TICKLESS_IDLE使能低功耗 Tickless 模式,默认0
configEXPECTED_IDLE_TIME_BEFORE_SLEEP系统进入相应低功耗模式的最短时长,默认2
configPRE_SLEEP_PROCESSING(x)在系统进入低功耗模式前执行的事务,比如关闭外设时钟
configPOST_SLEEP_PROCESSING(x)系统退出低功耗模式后执行的事务,比如开启之前关闭的外设时钟
  • FreeRTOSConfig.h代码清单
#define configUSE_TICKLESS_IDLE             1
#include "freertos_demo.h"
#define configPRE_SLEEP_PROCESSING( x )     PRE_SLEEP_PROCESSING() // 这两个函数可以在进入低功耗和退出低功耗的时候做一些事情,名字可以自己写
#define configPOST_SLEEP_PROCESSING( x )     POST_SLEEP_PROCESSING()

19.内存管理

Ø heap_1:最简单,不允许释放内存。

Ø heap_2:允许释放内存,但不会合并相邻的空闲块。最佳适应算法:选择能满足自己最接近的;

Ø heap_3:简单包装了标准 malloc() 和 free(),以保证线程安全。把所有的内存分成固定大小的块;

Ø heap_4:合并相邻的空闲块以避免碎片化。包含绝对地址放置选项。第一适应算法:选择可用的内存块中第一个;

Ø heap_5:如同 heap_4,能够跨越多个不相邻内存区域的堆。(操作太多)

  • 使用动态创建的方法,就是采用内存管理算法自动处理;

  • 手动的内存管理相关函数

函数描述
void * pvPortMalloc( size_t xWantedSize );申请内存
void vPortFree( void * pv );释放内存
size_t xPortGetFreeHeapSize( void );获取当前空闲内存的大小
LEEP_PROCESSING( x ) PRE_SLEEP_PROCESSING() // 这两个函数可以在进入低功耗和退出低功耗的时候做一些事情,名字可以自己写
#define configPOST_SLEEP_PROCESSING( x ) POST_SLEEP_PROCESSING()

# 20.内存管理

Ø heap_1:最简单,不允许释放内存。

Ø heap_2:允许释放内存,但不会合并相邻的空闲块。**最佳适应算法**:选择能满足自己最接近的;

Ø heap_3:简单包装了标准 malloc() 和 free(),以保证线程安全。把所有的内存分成固定大小的块;

Ø heap_4:合并相邻的空闲块以避免碎片化。包含绝对地址放置选项。**第一适应算法**:选择可用的内存块中第一个;

Ø heap_5:如同 heap_4,能够跨越多个不相邻内存区域的堆。(操作太多)

- 使用动态创建的方法,就是采用内存管理算法自动处理;

- 手动的内存管理相关函数

| **函数**                                   | **描述**               |
| ------------------------------------------ | ---------------------- |
| void * pvPortMalloc( size_t xWantedSize ); | 申请内存               |
| void vPortFree( void * pv );               | 释放内存               |
| size_t xPortGetFreeHeapSize( void );       | 获取当前空闲内存的大小 |

相关文章:

  • 洛谷 U273725:树的叶子节点
  • 眨眼睛查看密码工具类
  • Java Web从入门到精通:全面探索与实战(二)
  • 虚拟机上安装openEuler和openGauss数据库
  • 移动端六大语言速记:第9部分 - 并发与多线程
  • 自动驾驶---苹果又要造车了吗?
  • 【多模态mllm之audio encoder】openai whisper模型解析
  • 2025最新系统 Git 教程(三)
  • 【Project】高并发内存池
  • Qt 入门 4 之标准对话框
  • MySQL高可用性
  • WordPress超简洁的主题:果果CMS主题
  • LeetCode 3396.使数组元素互不相同所需的最少操作次数:O(n)一次倒序遍历
  • GEO, TCGA 等将被禁用?!这40个公开数据库可能要小心使用了
  • 250408_解决加载大量数据集速度过慢,耗时过长的问题
  • 在 macOS 上连接 PostgreSQL 数据库(pgAdmin、DBeaver)
  • 第十四届蓝桥杯大赛软件赛国赛C/C++研究生组
  • SVT-AV1学习-函数selfguided_restoration_fast_internal
  • 机器学习课堂7用scikit-learn库训练SVM模型
  • duckdb源码阅读学习路径图
  • 最高降九成!特朗普签署降药价行政令落地存疑,多家跨国药企股价收涨
  • 英国首相斯塔默住所起火,警方紧急调查情况
  • 言短意长|西湖大学首次“走出西湖”
  • 全球前瞻|特朗普访问中东三国,印巴军方将于12日再次对话
  • 上海与世界|环城生态公园带是上海绿色发展新名片
  • 不到1小时就能速发证件?央媒曝光健康证办理乱象