在前面实验中,我们使用静态内存方法创建了 2 个任务,任务的栈,任务的 控制块用的都是静态内存,必须由用户预先定义,这种方法我们在使用 FreeRTOS 的时候用的比较少,通常的方法是使用动态内存方法创建任务,由系统自动分配 任务栈和控制块
1 动态内存分
在使用静态方法创建任务的例程中,任务控制块和任务栈的内存空间都是从 内部的 SRAM 里面分配的,具体分配到哪个地址由编译器决定。现在我们开始使 用动态内存,即堆,其实堆也是内存,也属于 SRAM。FreeRTOS 做法是在 SRAM 里 面定义一个大数组,也就是堆内存,供 FreeRTOS 的动态内存分配函数使用,在 第一次使用的时候,系统会将定义的堆内存进行初始化,这些代码在 FreeRTOS 提供的内存管理方案中实现(heap_1.c、heap_2.c、heap_4.c 等,具体的内存 管理方案后面详细讲解),具体见代码。
//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))// (1)
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];// (2)
/* 如果这是第一次调用 malloc 那么堆将需要初始化,以设置空闲块列表。*/
if ( pxEnd == NULL )
{
prvHeapInit(); (3)
}
else
{
mtCOVERAGE_TEST_MARKER();
代码(1):堆内存的大小为 configTOTAL_HEAP_SIZE ,在 FreeRTOSConfig.h 中由我们自己定义,configSUPPORT_DYNAMIC_ALLOCATION 这个宏定义在使用 FreeRTOS 操作系统的时候必须开启,且关闭 configSUPPORT_STATIC_ALLOCATION 这个宏。 代码(2):从内部 SRAMM 里面定义一个静态数组 ucHeap,大小由 configTOTAL_HEAP_SIZE 这个宏决定,目前定义为 36KB。定义的堆大小不能超 过内部 SRAM 的总大小。 代码(3):如果这是第一次调用 malloc 那么需要将堆进行初始化,以设置 空闲块列表,方便以后分配内存,初始化完成之后会取得堆的结束地址,在 MemMang 中的 5 个内存分配 heap_x.c 文件中实现。
2 动态创建任务函数
使用动态内存的时,使用 xTaskCreate()函数来创建任务。如下:
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数 (1)
(const char* )"start_task", //任务名称 (2)
(uint16_t )START_STK_SIZE, //任务堆栈大小 (3)
(void* )NULL, //传递给任务函数的参数 (4)
(UBaseType_t )START_TASK_PRIO, //任务优先级 (5)
(TaskHandle_t* )&StartTask_Handler); //任务句柄 (6
代码(1):任务入口函数,即任务函数的名称,需要我们自己定义并且实现。 代码(2):任务名字,字符串形式,最大长度由 FreeRTOSConfig.h 中定义 的 configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字 最好要与任务函数入口名字一致,方便进行调试。 代码(3):任务堆栈大小,单位为字,在 32 位的处理器下(STM32),一个 字等于 4 个字节,那么任务大小就为 128 * 4 字节。 代码(4):任务入口函数形参,不用的时候配置为 0 或者 NULL 即可。 代码(5):任务的优先级。优先级范围根据 FreeRTOSConfig.h 中的宏 configMAX_PRIORITIES 决定,如果使能 configUSE_PORT_OPTIMISED_TASK_SELECTION,这个宏定义,则最多支持 32 个 优先级;如果不用特殊方法查找下一个运行的任务,那么则不强制要求限制最大 可用优先级数目。在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级。 代码(6):任务控制块指针,在使用内存的时候,需要给任务初始化函数 xTaskCreateStatic()传递预先定义好的任务控制块的指针。在使用动态内存的 时候,任务创建函数 xTaskCreate()会返回一个指针指向任务控制块,该任务控 制块是 xTaskCreate()函数里面动态分配的一块内存
整体代码
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 50
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
USART1_Init(115200);
//创建开始任务
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(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED2任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
//LED2任务函数
void led2_task(void *pvParameters)
{
while(1)
{
LED2=0;
vTaskDelay(800);
LED2=1;
vTaskDelay(200);
}
}