① 定义任务栈

作用:每一个任务都有一个独立的栈空间。

:::info 任务栈其实就是一个预先定义好的全局数据,数据类型为 StackType_t。
大小由 TASK1_STACK_SIZE 这个宏来定义,默认为 128,单位为字,即 512 字节,这也是 FreeRTOS 推荐的最小的任务栈。 :::

②定义任务函数

任务是一个独立的函数,函数主体无限循环且不能返回

  1. /* 软件延时 */
  2. void delay (uint32_t count)
  3. {
  4. for (; count!=0; count--);
  5. }
  6. /* 任务 1 */
  7. void Task1_Entry( void *p_arg ) (1)
  8. {
  9. for ( ;; )
  10. {
  11. flag1 = 1;
  12. delay( 100 );
  13. flag1 = 0;
  14. delay( 100 );
  15. }
  16. }
  17. /* 任务 2 */
  18. void Task2_Entry( void *p_arg ) (2)
  19. {
  20. for ( ;; )
  21. {
  22. flag2 = 1;
  23. delay( 100 );
  24. flag2 = 0;
  25. delay( 100 );
  26. }

③定义任务控制块(tskTCB)

任务控制块的作用: :::info 这个任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针, 任务名称,任务的形参等。 :::

  1. typedef struct tskTaskControlBlock
  2. {
  3. volatile StackType_t *pxTopOfStack; /* 栈顶 */ (1)
  4. ListItem_t xStateListItem; /* 任务节点 */ (2)
  5. StackType_t *pxStack; /* 任务栈起始地址 */ (3)
  6. /* 任务名称,字符串形式 */(4)
  7. char pcTaskName[ configMAX_TASK_NAME_LEN ];
  8. } tskTCB;
  9. typedef tskTCB TCB_t;
  • 栈顶指针
  • 任务节点
    • 内置在TCB控制块中的链表节点
    • 通过这个节点可以挂接到各种链表中。
  • 任务栈起始地址
  • 任务名称
    • 字符串的形式
    • 长度由宏 configMAX_TASK_NAME_LEN 来控制,默认为16
  • 数据类型重定义

④实现任务创建函数

xTaskCreateStatic()函数

  1. #if( configSUPPORT_STATIC_ALLOCATION == 1 ) (1)
  2. TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, (2)
  3. const char * const pcName, (3)
  4. const uint32_t ulStackDepth, (4)
  5. void * const pvParameters, (5)
  6. StackType_t * const puxStackBuffer, (6)
  7. TCB_t * const pxTaskBuffer ) (7)
  8. {
  9. TCB_t *pxNewTCB;
  10. TaskHandle_t xReturn; (8)
  11. if ( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
  12. {
  13. pxNewTCB = ( TCB_t * ) pxTaskBuffer;
  14. pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
  15. /* 创建新的任务 */ (9)
  16. prvInitialiseNewTask( pxTaskCode, /* 任务入口 */
  17. pcName, /* 任务名称,字符串形式 */
  18. ulStackDepth, /* 任务栈大小,单位为字 */
  19. pvParameters, /* 任务形参 */
  20. &xReturn, /* 任务句柄 */
  21. pxNewTCB); /* 任务栈起始地址 */
  22. }
  23. else
  24. {
  25. xReturn = NULL;
  26. }
  27. /* 返回任务句柄,如果任务创建成功,此时 xReturn 应该指向任务控制块 */
  28. return xReturn; (10)
  29. }
  30. #endif /* configSUPPORT_STATIC_ALLOCATION *
  • configSUPPORT_STATIC_ALLOCATION
    • 等于0是动态创建
      • 动态创建是创建任务时 任务栈控制块的内存动态分配好,任务删除时,内存被释放。
    • 等于1是静态创建
      • 静态创建时,任务控制块和栈的内存需要事先定义好,是静态的内存 ,任务删除时 , 内存 不能释放
  • pxTaskCode:任务入口
    • 即任务的函数名称。
    • TaskFunction_t 是在 projdefs.h中重定义的一个数据类型,是空指针。
    • typedef void (TaskFunction_t)( void );
  • pcName:任务名称
    • 字符串形式
  • ulStackDepth:任务栈大小
    • 单位为字
  • pvParameters:任务形参
    • 任务形参
  • uxStackBuffer:任务栈起始地址
  • pxTaskBuffer:任务控制块指针

**


prvInitialiseNewTask()

  1. static void prvInitialiseNewTask(TaskFunction_t pxTaskCode, (1)
  2. const char * const pcName, (2)
  3. const uint32_t ulStackDepth, (3)
  4. void * const pvParameters, (4)
  5. TaskHandle_t * const pxCreatedTask, (5)
  6. TCB_t *pxNewTCB ) (6)
  7. {
  8. StackType_t *pxTopOfStack;
  9. UBaseType_t x;
  10. /* 获取栈顶地址 */ (7)
  11. pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
  12. /* 向下做 8 字节对齐 */ (8)//将栈顶指针向下做 8 字节对齐,M3中总线宽度是32位,8字节是64位,因为存在浮点运算。
  13. pxTopOfStack = ( StackType_t * ) \
  14. ( ( ( uint32_t ) pxTopOfStack ) & ( ~( ( uint32_t ) 0x0007 ) ) );
  15. /* 将任务的名字存储在 TCB 中 */ (9)
  16. for ( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
  17. {
  18. pxNewTCB->pcTaskName[ x ] = pcName[ x ];
  19. if ( pcName[ x ] == 0x00 )
  20. {
  21. break;
  22. }
  23. }
  24. /* 任务名字的长度不能超过 configMAX_TASK_NAME_LEN ,要以'\0'结尾*/ (10)
  25. pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
  26. /* 初始化 TCB 中的 xStateListItem 节点 */ (11)
  27. vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
  28. /* 设置 xStateListItem 节点的拥有者 */ (12)
  29. listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
  30. /* 初始化任务栈 */ (13)
  31. pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack,
  32. pxTaskCode,
  33. pvParameters );
  34. /* 让任务句柄指向任务控制块 */ (14)
  35. if ( ( void * ) pxCreatedTask != NULL )
  36. {
  37. *pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
  38. }
  39. }

pxPortInitialiseStack()函数

初始化任务栈,并更新栈顶指针

  1. #define portINITIAL_XPSR ( 0x01000000 )
  2. #define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )
  3. static void prvTaskExitError( void )
  4. {
  5. /* 函数停止在这里 */
  6. for (;;);
  7. }
  8. StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack,
  9. TaskFunction_t pxCode,
  10. void *pvParameters )
  11. {
  12. /* 异常发生时,自动加载到 CPU 寄存器的内容 */ (1)
  13. pxTopOfStack--;
  14. *pxTopOfStack = portINITIAL_XPSR; (2)
  15. pxTopOfStack--;
  16. *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; (3)
  17. pxTopOfStack--;
  18. *pxTopOfStack = ( StackType_t ) prvTaskExitError; (4)
  19. pxTopOfStack -= 5; /* R12, R3, R2 and R1 默认初始化为 0 */
  20. *pxTopOfStack = ( StackType_t ) pvParameters; (5)
  21. /* 异常发生时,手动加载到 CPU 寄存器的内容 */ (6)
  22. pxTopOfStack -= 8;
  23. /* 返回栈顶指针,此时 pxTopOfStack 指向空闲栈 */
  24. return pxTopOfStack; (7)
  25. }

image.png
任务栈初始化完后栈空间分布图