介绍
通用端口的源文件
- quenue.c 同时提供队列和信号量的服务
- timers.c 提供软件定时器功能
- event_groups.c 提供事件组件功能
- crountine.c 实现FreeRTOS的协同列程过程
特殊端口的源文件
在FreeRTOS/Source/portable目录下包含了编译器和处理器的系统结构堆内存管理器:只有在FreeRTOSConfig.h中将configSUPPORT_DYNAMIC_ALLOCATION
设置为一,或未定义时才需要使用堆内存管理器。
FreeRTOS演示
int main(void)
{
prvSetupHardware();
vTaskStartScheduler();
for(;;);
return 0;
}
从头创建一个FreeRTOS项目
1.使用工具创建一个没有任何RTOS文件的项目
2.确保可以成功编译并执行
3.按照如下表格添加FreeRTOS的源文件
File | Location |
---|---|
tasks.c | FreeRTOS/Source |
queue.c | FreeRTOS/Source |
list.c | FreeRTOS/Source |
timers.c | FreeRTOS/Source |
event_groups.c | FreeRTOS/Source |
All C and assembler file | FreeRTOS/Source/portable/[compiler]/[architecture] |
heap_n.c | FreeRTOS/Source/portable/MemMang |
4.将FreeRTOSConfig.h头文件复制到项目中 | |
5.将以下内容添加到头文件路径中 |
FreeRTOS/Source/include
FreeRTOS/Source/portable/[compiler]/[architecture]
The directory containing the FreeRTOSConfig.h header file
6.复制相关例子的编译器设置
7.安装FreeRTOS的中断处理程序
堆内存管理
当FreeRTOS需要RAM时,它调用的不是malloc()
而是pvPortMalloc()
释放调用的是vPortFree()
Heap_1
通常专用于小的嵌入式系统。只创建任务和其他内核项目在工作计划之前,内核才会动态分布内存。并且在应用程序的整个生命周期都会存在。
所选择的分配计划不必考虑任何复杂的内存分配问题,例如决定论与碎片化。
实施一个非常基础版本的pvPortMalloc()
但从不实施vPortFree()
从不删除任务和其他内核对象。
Heap_2
为了向后兼容,在FreeRTOS发行版中保留了Heap_2,但不建议在新设计中使用。
与Heap_1一样通过细分configTOTAL_Heap_SIZE
确定大小数来组合工作
Heap_3
使用标准库的malloc()
和free()
函数,所以堆的大小是由链接器定义的。并且configTOTAL_HEAP_SIZE
的设置没有任何影响
它通过暂停FreeRTOS调度程序
来实现malloc()
和free()
线程安全
Heap_4
首次使用拟合算法来分配内存。不同于Heap_2,Heap_4将相邻的空闲内存组合成一个更大的块,从而最大限度地降低内存碎片的风险。
Heap_5
所使用的分配和释放内存算法与heap_4的算法相同。
不同的是,Heap_5不仅限于从单个静态声明的数组分配内存;heap_5可以从多个单独的内存空间分配内存。
堆相关的程序函数
xPortGetFreeHeapSize()
API函数
这个API函数返回调用函数中的可用字节数。
它可以用于优化堆大小。例子如下:
使用xPortGetFreeHeapSize()
函数在创建所有内核对象之后返回2000,后来将configTOTAL_HEAP_SIZE
的值减少2000.
xPortGetMinimumEverFreeHeapSize()
API函数
返回自FreeRTOS应用开始后堆中存在的最小未分配字节数。
也表示离堆空间耗尽的距离。
Malloc
失败钩子函数
pvPortMalloc
可以直接从应用中调用。
同样的FreeRTOS会在源文件中调用,在每次内核项目被创建时。
与标准库Malloc
函数一样,当pvPortMalloc()
无法返回内存块时,则表示请求的大小的内存块不存在。返回NULL
且并不会创建。
如果调用pvPortMalloc()
返回NULL
,则所有示例堆分配方案都可以配置为调用钩子(或回调)函数
如果FreeRTOSConfig.h中将configUSE_MALLOC FAILED_HOOK
设置为1,则应用程序必须提供一个名为和原型如清单所示的MALLOC失败挂钩函数。
该功能可以采用任何适合于应用的方式实现。
malloc悬挂的钩子函数名和原型,如下:void vApplicationMallocFailedHook( void );
什么意思呢?就是在FreeRTOSConfig.h定义configUSE_MALLOC_FAILD_HOOK
为1时,当调用pvPortMalloc()
失败会执行malloc悬挂的钩子函数void vApplicationMallocFailedHook( void );
进行一些“补救”措施或错误处理。
任务管理
任务函数
每个任务其实就是一个小程序。它有一个入口点,通常会无限期的运行不退出。FreeRTOS任务必须不允许以任何方式从其实现函数返回。不能包含return
语句。
可以使用单个任务函数定义创建任意数量的任务,且都是独立的执行体,具有自己的堆栈和变量副本。
典型的任务函数结构:来源于FreeRTOS官方手册。
void ATaskFunction( void *pvParameters )
{
/* Variables can be declared just as per a normal function. Each instance of a task
created using this example function will have its own copy of the lVariableExample
variable. This would not be true if the variable was declared static – in which case
only one copy of the variable would exist, and this copy would be shared by each
created instance of the task. (The prefixes added to variable names are described in
section 1.5, Data Types and Coding Style Guide.) */
int32_t lVariableExample = 0;
/* A task will normally be implemented as an infinite loop. */
for( ;; )
{
/* The code to implement the task functionality will go here. */
}
/* Should the task implementation ever break out of the above loop, then the task
must be deleted before reaching the end of its implementing function. The NULL
parameter passed to the vTaskDelete() API function indicates that the task to be
deleted is the calling (this) task. The convention used to name API functions is
described in section 0, Projects that use a FreeRTOS version older than V9.0.0
must build one of the heap_n.c files. From FreeRTOS V9.0.0 a heap_n.c file is only
required if configSUPPORT_DYNAMIC_ALLOCATION is set to 1 in FreeRTOSConfig.h or if
configSUPPORT_DYNAMIC_ALLOCATION is left undefined. Refer to Chapter 2, Heap Memory
Management, for more information.
Data Types and Coding Style Guide. */
vTaskDelete( NULL );
}
顶级任务状态
通过之前的描述我们知道一个应用程序可以包含很多任务。这时候就得看硬件参数了,如果MCU(等其他)的处理器是单核,则在任何给定的时间只能执行一个任务。
众所周知啊,任务可以有两种状态:
- 运行
- 未运行
有点废话,哈哈哈哈哈哈。(简化过了)其实非运行状态包含了许多子状态这个后面再讨论。
任务在运行时,处理器会执行该任务的代码。
若任务在非运行时,则处于休眠模式。其状态被保存,等待调度器的唤醒。重新启动时,它会继续完成未处理的指令。(这里就有点像中断)
创建任务
xTaskCreate()
接口函数
任务创建就是使用这个API函数。这个API函数时所以中最重要的,也是最复杂的。
函数原型如下
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
参数与返回值
- pvTaskCode 任务只是永远不会退出的C函数,因此通常实现无限循环。这个参数只是一个指向实现任务函数的指针(实际上直接传入函数名)。
- pcName 任务的描述性名称。FreeRTOS不以任何方式使用该功能。纯属用于debug,方便开发者阅读。通过定义
configMAX_TASK_NAME_LEN
了任务名称的最大长度。当提供的字符串很长超过最大值,则会被系统默认截断。 - usStackDepth 之前说过每个任务都有独特的堆栈,该堆栈在内核创建任务时分配给任务。这个参数告诉内核堆栈的大小。
这个值指定的是堆栈可以容纳的“字数”(StackType_t 得看时多少位的MCU
)的数量,而不是字节数(bytes
)。如果你设置usStackDepth = 100
,那么任务栈实际大小是100 * sizeof(StackType_t)
字节。你传入的栈深度usStackDepth
乘以每个栈单元的宽度不能大于uint16_t
空闲(Idle)任务使用的堆栈大小由应用程序定义的常量configMINIMAL_STACK_SIZE
来决定的。
什么是(Idle task)空闲任务?
它是系统自动创建的任务;
它的优先级最低(0);
当没有其他任务运行时,就会运行这个FreeRTOS就会运行这个。
它也是负责释放已删除任务资源的任务。
这个configMINIMAL_STACK_SIZE
是可以在官方例子里能看到最低推荐值,这里就不赘述了。
没有简单的方法可以确定任务所需的堆栈空间。 - pvParameters 这个参数接收一个类型为
void*
指针,并传递给任务。 - uxPriority 定义任务执行的优先级。分配的优先级为:从0(最低优先级)到
configMAX_PRIORITIES – 1
(最高优先级) - pxCreatedTask 可以向正在创建的任务传递句柄。然后这个句柄可以在API调用中引用该任务,比如修改任务优先级或删除任务。
如果应用程序不需要任务句柄,则可以将pxCreatedTask
设置为NULL
。 - Returned value 有两个返回值 1.
pdPASS
代表任务创建成功。2.pdFAIL
表示任务未被创建因为没有足够的堆内存。