任务创建
在FreeRTOS中,任务就是一个函数,原型如下:
void TaskFunction( void * );
此函数没有返回值,创建的每个任务有自己的栈。
注意:当创建任务时,使用的任务函数相同时,局部变量存放在各自的栈中,但全局变量和静态变量是同一个。
任务创建的方式有两种,分别为动态创建和静态创建,即任务函数的栈等空间的创建方式。
动态创建
动态创建的函数原型如下:
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 任务函数
const char * const pcName, // 任务名称,字符串
const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位word
void * const pvParameters, // 传递给任务函数的参数
UBaseType_t uxPriority, // 任务优先级
TaskHandle_t * const pxCreatedTask ) // 任务句柄
其中任务函数就是需要创建的任务,TaskFunction_t是函数指针,原型是:
typedef void (* TaskFunction_t)( void * );
任务结构体包括任务的一些信息,比如优先级等等。参数pxCreatedTask指向任务结构体可以用来修改已创建任务的优先级、删除任务等等,官方源码如下:
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
if( pxCreatedTask != NULL )
{
/* Pass the handle out in an anonymous way. The handle can be used to
* change the created task's priority, delete the created task, etc.*/
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
动态创建任务成功返回pdPASS,否则errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY。
静态创建
静态创建的函数原型如下:
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 ) // 任务控制块
静态创建任务时,参数puxStackBuffer和pxTaskBuffer必须是全局变量,否则创建失败,官方源码如下:
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
{
/* The memory used for the task's TCB and stack are passed into this
* function - use them. */
pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */
{
/* Tasks can be created statically or dynamically, so note this
* task was created statically in case the task is later deleted. */
pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
}
else
{
xReturn = NULL;
}
在keil中,通过调试发现,当未使用使用局部变量时,局部变量的地址为NULL,此时函数返回NULL。
函数返回值指向任务的控制模块,根据上述源码中prvInitialiseNewTask函数可知,返回值与动态创建时的变量pxCreatedTask作用相同。
当静态创建任务时,需在FreeRTOSConfig.h文件中添加配置项:
#define configSUPPORT_STATIC_ALLOCATION 1
并实现函数 vApplicationGetIdleTaskMemory,实现的方法如下所示:
StackType_t puxIdleTaskStackBuffer[100];
StaticTask_t xIdleTaskBuffer;
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskBuffer;
*ppxIdleTaskStackBuffer = puxIdleTaskStackBuffer;
*pulIdleTaskStackSize = 100;
}
删除任务
删除任务的函数原型是:
void vTaskDelete( TaskHandle_t xTaskToDelete );
只需传入需要删除任务的句柄即可,当传入参数为NULL时,表示删除自己。
注意:删除任务时,如果是自杀则需要空闲任务并释放TCB和栈,但如果是删除其他任务,则在删除函数中进行清理内存。由官方源码可知,如果任务自杀,同时空闲任务没有得到执行,则只是从链表中移除。
if( pxTCB == pxCurrentTCB )
{
/* A task is deleting itself. This cannot complete within the
* task itself, as a context switch to another task is required.
* Place the task in the termination list. The idle task will
* check the termination list and free up any memory allocated by
* the scheduler for the TCB and stack of the deleted task. */
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
/* Increment the ucTasksDeleted variable so the idle task knows
* there is a task that has been deleted and that it should therefore
* check the xTasksWaitingTermination list. */
++uxDeletedTasksWaitingCleanUp;
/* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
* portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */
traceTASK_DELETE( pxTCB );
/* The pre-delete hook is primarily for the Windows simulator,
* in which Windows specific clean up operations are performed,
* after which it is not possible to yield away from this task -
* hence xYieldPending is used to latch that a context switch is
* required. */
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
}
else
{
--uxCurrentNumberOfTasks;
traceTASK_DELETE( pxTCB );
prvDeleteTCB( pxTCB );
/* Reset the next expected unblock time in case it referred to
* the task that has just been deleted. */
prvResetNextTaskUnblockTime();
}
优先级
优先级范围:0~(configMAX_PRIORITIES – 1)。
数值越小优先级越低。在默认的调度算法中,优先级高的任务先执行,相同优先级的任务轮流执行。如果高优先级的任务没有放弃CPU资源,则一直执行。
相同优先级任务轮流执行实验代码如下:
void vmainTask1Function( void * param);
void vmainTask2Function( void * param);
StackType_t puxIdleTaskStackBuffer[100];
StaticTask_t xIdleTaskBuffer;
/*-----------------------------------------------------------*/
StackType_t puxTask2StackBuffer[100]; // 必须是全局变量
StaticTask_t xTask2TCBBuffer;
TaskHandle_t xTask1Handle;
UBaseType_t task1Run = 0;
UBaseType_t task2Run = 0;
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello world!\r\n");
xTaskCreate(vmainTask1Function, "task1", 100, NULL, 1, NULL); // 动态创建任务
xTaskCreateStatic(vmainTask2Function, "task2", 100, NULL, 1, puxTask2StackBuffer, &xTask2TCBBuffer); // 静态创建任务
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
static void prvSetupHardware( void )
{
/* Start with the clocks in their expected state. */
RCC_DeInit();
/* Enable HSE (high speed external clock). */
RCC_HSEConfig( RCC_HSE_ON );
/* Wait till HSE is ready. */
while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
{
}
/* 2 wait states required on the flash. */
*( ( unsigned long * ) 0x40022000 ) = 0x02;
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1 );
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1 );
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config( RCC_HCLK_Div2 );
/* PLLCLK = 8MHz * 9 = 72 MHz. */
RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );
/* Enable PLL. */
RCC_PLLCmd( ENABLE );
/* Wait till PLL is ready. */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source. */
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );
/* Wait till PLL is used as system clock source. */
while( RCC_GetSYSCLKSource() != 0x08 )
{
}
/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );
/* SPI2 Periph clock enable */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
/* Set the Vector Table base address at 0x08000000 */
NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* Configure HCLK clock as SysTick clock source. */
SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
serialPortInit();
}
/*-----------------------------------------------------------*/
void vmainTask1Function( void * param)
{
while(1) {
task1Run = 1;
task2Run = 0;
printf("1");
}
}
void vmainTask2Function( void * param)
{
// char buf[100];
// int i = 0;
while(1) {
task1Run = 0;
task2Run = 1;
printf("2");
// for(i = 0; i < 100; i ++) {
// buf[i] = 1;
// }
}
}
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskBuffer;
*ppxIdleTaskStackBuffer = puxIdleTaskStackBuffer;
*pulIdleTaskStackSize = 100;
}
实验现象如下图所示:
当把任务1的优先级设为2时,则一直执行,现象如下图:
栈大小
在堆上申请内存的格式如下:
头部包括申请内存长度等信息,同时栈的生长方向为向下生长,即高地址到低地址。 当局部变量超过栈大小后,将导致程序崩溃。
有关任务创建的详细分析见文章https://blog.csdn.net/tmd182/article/details/135976194