参考博客:
ESP-IDF FreeRTOS 任务创建分析 - [Genius] - 博客园 (cnblogs.com)
1.什么是任务
1)独立的无法返回的函数称为任务
2)任务是无线循环
3)无返回数据
2.任务的实现过程
1.定义任务栈
裸机程序:统一分配到一个栈里面,栈就是RAM里面的一个一段连续的空间。栈的大小一般在启动文件或者链接文件里面定义的,最后由c库函数main初始化。【比如stm32的startup_stm32f10x_hd.s文件中】
操作系统:每一个任务都是独立的,所以每一个任务都有各自的栈空间。这个栈一般是全局数组也可以是动态分配的一段内存空间,也是存储在RAM。
2.定义任务函数
不能返回,无限循环
3.定义任务控制块:TCB
1)存储任务的全部信息
2)xStateListItem:相当于我们的任务是衣服,这个变量是衣架
4.列表:List_t
列表项:ListItem_t
迷你列表项:MiniListItem_t
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
//列表项的个数
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
//当前列表项索引号
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
//指向最后一个列表项
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
5.列表项
列表可以看做所有列表项的集合,就跟链表看做所有结点的集合一样
1.普通列表项:ListItem_t
功能最齐全
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
//列表项的值
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
//指向前一个链表项
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
//指向下一个链表项
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
//指明此时列表项属于谁,指向我们这个任务的任务控制块
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
//指定我们这个列表项属于哪一个列表
void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
2.迷你列表项:MiniListItem_t
功能较少
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
//列表项的值
configLIST_VOLATILE TickType_t xItemValue;
//指向下一个列表项
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
//指向上一个列表项
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
6.列表初始化
void vListInitialise( List_t * const pxList )
{
/* The list structure contains a list item which is used to mark the
end of the list. To initialise the list the list end is inserted
as the only list entry. */
//表示此时列表只有一个列表项
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* The list end value is the highest possible value in the list to
ensure it remains at the end of the list. */
//给最后一个列表项赋值
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
when the list is empty. */
//因为此时列表中只有一个列表项,所以最后一个列表项的next指向他自己
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
//因为此时列表中只有一个列表项,所以最后一个列表项的pre指向他自己
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
//列表中的最后一个列表项不计入我们的列表项计数值,所以初始化个数为0
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
/* Write known values into the list if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
//用于完整性检查的字段
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
7.列表项初始化
为什么就初始化了一个成员变量??
其他的成员变量什么时候初始化呢?这是因为列表项要根据实际使用情况来初始化,比如任务创建函数xTaskCreate()就会对任务堆栈中的xStateListItem和 xEventListItem这两个列表项中的其他成员变量在做初始化。
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
//表示此时TCB属于哪一个任务的
pxItem->pvContainer = NULL;
/* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
3.列表项
1.插入函数:双向链表
//表示此时输入的列表项==最大值,表示此时应该设置为end的pre
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else//此时输入的列表项<最大值,就要查找应该插入列表中的哪一个位置
{
//查找列表项的插入点【升序查询】
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
{
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
//找到待出入点的位置,就将待插入值与指针和指针的next联系起来
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
pxindex是当前列表项指针,指向头节点,pxindex->next就是ListItem1
/**
列表项插入函数
pxList:表示要插入哪一个列表中(相当于链表)
pxNewListItem:表示要将哪一个列表项插入(相当于节点)
*/
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
//获取当前要插入一个列表项
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
//检查列表和列表项当中用于完整性检查的变量值是否被改变
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
//表示此时输入的列表项==最大值,表示此时应该设置为end的pre
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else//此时输入的列表项<最大值,就要查找应该插入列表中的哪一个位置
{
//查找列表项的插入点【升序查询】
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
{
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
}
//找到待出入点的位置,就将待插入值与指针和指针的next联系起来
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* Remember which list the item is in. This allows fast removal of the
item later. */
//指定此时列表项属于哪一个列表
pxNewListItem->pvContainer = ( void * ) pxList;
//列表项数值要+1
( pxList->uxNumberOfItems )++;
}
2.插入列表末尾
列表项中的index变量是用于遍历列表的,这里我们指:插入列表末尾——-->实际上就是指插入index->pre中。而不是插入end->pre中。
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
/* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* Insert a new list item into pxList, but rather than sort the list,
makes the new list item the last item to be removed by a call to
listGET_OWNER_OF_NEXT_ENTRY(). */
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++;
}
3.列表删除
1)列表项删除后返回的是剩余的列表项个数
2)列表项删除:实际上将列表项从列表中删除,并不会将这个列表项在内存中释放
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
item. */
//获取此时要删除的列表项处于哪一个列表中
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
//实际上就将将待删除的列表项的pre和next分别指向前一个和后一个
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )//表示此时要删除的是index这一项
{
//新的index指向待删除的前一个
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//删除后的列表项的所属列表就没有了,为NULL
pxItemToRemove->pvContainer = NULL;
( pxList->uxNumberOfItems )--;
//返回列表中剩余的列表项个数
return pxList->uxNumberOfItems;
}
4.列表遍历
1)列表遍历会自动的将index移动到下一位
2)并且返回pxown
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
List_t * const pxConstList = ( pxList ); \
/* Increment the index to the next item and return the item, ensuring */ \
/* we don't return the marker used at the end of the list. */ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ \ //当index遍历到end的时候,会自动将index重新指向第一个列表项
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
//将新的owner赋值给TCB
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
}
4.任务创建
1.动态创建:xTaskCreate
pxTaskCode:空指针(任务函数)
pcName:任务名称
usStackDepth:任务堆栈大小
pvParameters:传递给任务函数的参数
uxPriority:任务优先级
pxCreatedTask:任务控制块
//当这个宏定义(动态申请内存)设置为1,才可以调用这个函数
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
/**
pxTaskCode:空指针(任务函数)
pcName:任务名称
usStackDepth:任务堆栈大小
pvParameters:传递给任务函数的参数
uxPriority:任务优先级
pxCreatedTask:任务控制块
*/
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
//任务控制块
TCB_t *pxNewTCB;
//任务句柄:指向任务控制块TCB【空指针】
BaseType_t xReturn;
/* If the stack grows down then allocate the stack then the TCB so the stack
does not grow into the TCB. Likewise if the stack grows up then allocate
the TCB then the stack. */
#if( portSTACK_GROWTH > 0 )
{
/* Allocate space for the TCB. Where the memory comes from depends on
the implementation of the port malloc function and whether or not static
allocation is being used. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
/* Allocate space for the stack used by the task being created.
The base of the stack memory stored in the TCB so the task can
be deleted later if required. */
pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
if( pxNewTCB->pxStack == NULL )
{
/* Could not allocate the stack. Delete the allocated TCB. */
vPortFree( pxNewTCB );
pxNewTCB = NULL;
}
}
}
#else /* portSTACK_GROWTH */
{
StackType_t *pxStack;
/* Allocate space for the stack used by the task being created. */
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
if( pxStack != NULL )
{
/* Allocate space for the TCB. */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */
if( pxNewTCB != NULL )
{
/* Store the stack location in the TCB. */
pxNewTCB->pxStack = pxStack;
}
else
{
/* The stack cannot be used as the TCB was not created. Free
it again. */
vPortFree( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
#endif /* portSTACK_GROWTH */
if( pxNewTCB != NULL )
{
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
/* Tasks can be created statically or dynamically, so note this
task was created dynamically in case it is later deleted. */
pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
//在真正创建任务之前需要进行的一些初始化操作
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
//将任务添加到就绪列表中
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
2.静态创建:xTaskCreateStatic
pxTaskCode:空指针(任务函数)
pcName:任务名称
usStackDepth:任务堆栈大小
pvParameters:传递给任务函数的参数
uxPriority:任务优先级
puxStackBuffer:任务堆栈
pxCreatedTask:任务控制块
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
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 ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
TCB_t *pxNewTCB;
TaskHandle_t xReturn;
configASSERT( puxStackBuffer != NULL );
configASSERT( pxTaskBuffer != NULL );
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 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 )
{
/* 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 /* configSUPPORT_DYNAMIC_ALLOCATION */
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
prvAddNewTaskToReadyList( pxNewTCB );
}
else
{
xReturn = NULL;
}
return xReturn;
}
#endif /* SUPPORT_STATIC_ALLOCATION */
3.prvInitialiseNewTask
作用:函数prvInitialiseNewTask ()用于完成对任务的初始化。
1)列表项的初始化【初始化任务控制块(TCB),配置任务堆栈,保存现场信息,设置任务名称和优先级】--->vListInitialiseItem
2)将设置好的列表项存储到就绪队列---->pxPortInitialiseStack
4.pxPortInitialiseStack
参考博客:
Freertos初始化任务堆栈_pxportinitialisestack-CSDN博客
1) 调用函数pxPortInitialiseStack()初始化任务堆栈,并将最新的栈顶指针赋值给任务TCB的pxTopOfStack字段。
2)在FreeRTOS的任务创建过程中,
xTaskCreate
或xTaskCreateStatic
函数会调用pxPortInitialiseStack
来初始化新任务的堆栈。初始化完成后,新任务就可以被添加到就绪列表中,等待调度器在合适的时间将其切换为运行态。
//这里说的栈应该是任务堆栈,他们传给cpu的寄存器,相当于这个任务临时再用这个cpu
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
/* Simulate the stack frame as it would be created by a context switch
interrupt. */
//加载寄存器
pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}
5.实现就绪列表
1)定义
2)初始化就绪列表
3)将任务添加到就绪列表中
1.定义就绪列表:pxReadyTasksLists[configMAX_PRIORITIES]
实际上是一个数组
2.初始化就绪列表:prvInitialiseTaskLists
1)初始化绪列表函数【prvAddNewTaskToReadyList(这个函数又在创建任务函数中被调用)】中被调用
2)FreeRTOS使用列表来跟踪和管理任务,包括任务的就绪列表、延迟列表、挂起列表等。
prvInitialiseTaskLists
函数确保这些列表在系统启动或第一个任务创建时被正确初始化。3)
prvInitialiseTaskLists
函数通常只在系统启动或第一个任务创建时被调用一次。在后续的任务创建过程中,FreeRTOS会使用其他函数来管理和维护任务列表。
3.将任务添加到就绪列表:prvAddTaskToReadyList
1)这个函数是在将任务添加到就绪列表函数【prvAddNewTaskToReadyList(这个函数又在创建任务函数中被调用)】中被调用
2)在这个函数底部调用了【vListInsertEnd】,所以表示插入到index->pre中
6.实现调度器
1.启动调度器
1.最外层的调度函数:vTaskStartScheduler
1)调度器是任务调度的核心。
2)主要作用:实现任务的切换。从就绪列表中查找优先级最高的任务执行。
1.启动静态任务
该函数中调用的其他函数:
1)vApplicationGetIdleTaskMemory-->获取空闲任务的任务堆栈和任务控制块内存
2)xTimerCreateTimerTask-->启动软件定时器
2.启动动态任务
2.启动硬件调度:xPortStartScheduler
1)这个函数也是在最外层的任务调度函数【vTaskStartScheduler】中调用
2)专门启动与硬件相关的任务,例如滴答定时器,中断等
3.启动第一个任务:prvStartFirstTask
1)这个函数是在硬件调度的函数【xPortStartScheduler】中被调用
2)这个函数被调用后不会被返回,是汇编编写
3)该函数的作用:
4.真正启动第一个任务:vPortSVCHandler
参考博客:
【FreeRTOS】2. SVC系统调用_freertos svc-CSDN博客
FreeRTOS启动第一个任务和任务的切换实现过程_vportsvchandler-CSDN博客
vPortSVCHandler 与 SVC_Handler的关系
我们在stm32f103xx_it.c中有一个中断函数(中断向量表)【SVC_Handler】这个与我们此时的FreeRTOS的【vPortSVCHandler】实际上是一致的,但是函数不一样无法进行调用。
所以我们只能配置FreeRTOS的配置文件来进行宏定义。
1)FreeRTOS中的
vPortSVCHandler
函数是一个中断处理函数,用于处理SVC(Supervisor Call)中断。2)SVC中断是一种特殊的异常/中断类型,在ARM Cortex-M系列微控制器中用于在用户模式和特权模式之间进行切换。
3)当FreeRTOS启动第一个任务时,它会调用
prvStartFirstTask
函数来初始化MSP(主堆栈指针)并产生SVC系统调用。这会导致执行流程跳转到vPortSVCHandler
中断服务函数。
5.真正实现任务切换:xPortPendSVHandler
当系统需要进行任务切换时,例如在调用
taskYIELD()
函数或者从中断服务例程返回时,FreeRTOS会在适当的时候触发PendSV中断,从而调用xPortPendSVHandler
函数进行任务切换。
2.vPortSVCHandler和xPortPendSVHandler
FreeRTOS是一个实时操作系统(RTOS),它特别适用于微控制器和其他小型嵌入式系统。在FreeRTOS中,
vPortSVCHandler
和xPortPendSVHandler
是两个关键的中断处理函数,它们各自在系统中扮演着重要的角色。
7.静态任务的创建
0.基本过程
1)定义一些我们需要使用到的堆栈,空闲任务堆栈,控制块,控制块句柄等变量
2)main函数
3)初始化相关的硬件
4)创建任务
创建任务函数
创建
删除任务(在内存中进行删除)
参考博客:
FreeRTOS之vTaskDelete()-CSDN博客
5)启动调度器
1.设置相关的宏定义
当我们使用静态全局变量创建任务【configSUPPORT_STATIC_ALLOCATION】设置为1
2.静态创建需要定义的两个函数
静态创建的任务需要自己手动创建内存。【空闲任务内存+时间任务内存】
//获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
//静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
//有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
//实现此函数即可。
//ppxIdleTaskTCBBuffer:任务控制块内存
//ppxIdleTaskStackBuffer:任务堆栈内存
//pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;
*ppxIdleTaskStackBuffer=Idle_Task_Stack;
*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}
//获取定时器任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer : 任务控制块内存
//ppxTimerTaskStackBuffer: 任务堆栈内存
//pulTimerTaskStackSize : 任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */
*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */
*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
}
3.完整代码
#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
//任务堆栈
StackType_t StartTaskStack[START_STK_SIZE];
//任务控制块
StaticTask_t StartTaskTCB;
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_TASK_PRIO 2
//任务堆栈大小
#define TASK1_STK_SIZE 128
//任务堆栈
StackType_t Task1TaskStack[TASK1_STK_SIZE];
//任务控制块
StaticTask_t Task1TaskTCB;
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);
//任务优先级
#define TASK2_TASK_PRIO 3
//任务堆栈大小
#define TASK2_STK_SIZE 128
//任务堆栈
StackType_t Task2TaskStack[TASK2_STK_SIZE];
//任务控制块
StaticTask_t Task2TaskTCB;
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);
/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];
/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;
//获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
//静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
//有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
//实现此函数即可。
//ppxIdleTaskTCBBuffer:任务控制块内存
//ppxIdleTaskStackBuffer:任务堆栈内存
//pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer=&Idle_Task_TCB;
*ppxIdleTaskStackBuffer=Idle_Task_Stack;
*pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}
//获取定时器任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer : 任务控制块内存
//ppxTimerTaskStackBuffer: 任务堆栈内存
//pulTimerTaskStackSize : 任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */
*ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */
*pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
}
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
USART1_Init(115200);
//创建开始任务
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(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
/*
临界区:在进行任务的时候,不会受到其他任务调度的影响
*/
taskENTER_CRITICAL(); //进入临界区
//创建TASK1任务
Task1Task_Handler=xTaskCreateStatic((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint32_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(StackType_t* )Task1TaskStack,
(StaticTask_t* )&Task1TaskTCB);
//创建TASK2任务
Task2Task_Handler=xTaskCreateStatic((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint32_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(StackType_t* )Task2TaskStack,
(StaticTask_t* )&Task2TaskTCB);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//任务1函数
void task1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
//任务2函数
void task2_task(void *pvParameters)
{
while(1)
{
LED2=0;
vTaskDelay(800);
LED2=1;
vTaskDelay(200);
}
}
8.动态任务创建
会自动申请内存空间
1.完整代码
#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);
}
}
9.FreeRTOS启动流程
1.任务创建
1)启动后-->调用复位函数(Reset handler)--->然后再复位函数中调用__main(触发栈和堆)-->然后才进入主函数--->外设初始化--->创建任务(内部会自动申请文件)-->任务调度(创建空闲任务+定时器任务【最后真正启动调度器】)
2)FreeRTOS初始化:初始化相关的板载外设+创建任务+启动调度器
2.任务调度
空闲任务不可以被挂起和删除
3.主函数
1)当正在执行的任务发生阻塞的时候,会将此时的任务挂起,然后去就绪列表中查找当前优先级最高的继续执行。
2)临界区的重要性!!!!
10.临界区
参考博客:
第七章 FreeRTOS的临界区与任务调度器_freertos 创建临界区-CSDN博客