使用freertos V9.0.0需要注意的几个问题

时间:2024-04-07 17:12:24

【1】关于堆栈深度

朱工在FreeRTOS高级篇2---FreeRTOS任务创建分析说到:

  1. BaseType_t xTaskCreate(  
  2.                     TaskFunction_tp vTaskCode,  
  3.                     const charchar * constpcName,  
  4.                     unsigned short usStackDepth,  
  5.                     voidvoid *pvParameters,  
  6.                     UBaseType_t uxPriority,  
  7.                     TaskHandle_t *pvCreatedTask  
  8.                   );
  • usStackDepth:指定任务堆栈大小,能够支持的堆栈变量数量(堆栈深度),而不是字节数。比如,在16位宽度的堆栈下,usStackDepth定义为100,则实际使用200字节堆栈存储空间。堆栈的宽度乘以深度必须不超过size_t类型所能表示的最大值。比如,size_t为16位,则可以表示堆栈的最大值是65535字节。这是因为堆栈在申请时是以字节为单位的,申请的字节数就是堆栈宽度乘以深度,如果这个乘积超出size_t所表示的范围,就会溢出,分配的堆栈空间也不是我们想要的。
但我现在用的是V9.0.0版本了,函数式这样的:

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )

函数内部有:

对于STM32这种

StackType_t *pxStack;
             /* Allocate space for the stack used by the task being created. */
pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );

因此关键看StackYype_t的数据类型,在portmacro.h里面有

#define portSTACK_TYPEuint32_t

typedef portSTACK_TYPE StackType_t;

这样就很清楚了。就是占用的内存字节数量是堆栈深度乘以4字节

另外在prvInitialiseNewTask有

#if( portSTACK_GROWTH < 0 )
{  
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */


/* Check the alignment of the calculated top of stack is correct. */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
}

那么从这句话pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );也印证了占用的内存字节数量是“堆栈深度乘以4字节”这个说法。我在freertos V9.0.0之xTaskCreateStatic对此也有解释。

========================================

【2】xTaskCreateStatic这个函数比xTaskCreate更靠谱么?

以前真是觉得是更靠谱。现在看来,如果xTaskCreate采用的是heap_1.c的话,都是一样的靠谱。

从学习角度看用xTaskCreateStatic更好,从实际应用的角度看xTaskCreate更好,似乎更不容易犯错!

【3】函数xTaskCreate的参数pxCreatedTask是干什么用的?

答:是个钩子函数,或者叫回调函数,就是个函数指针。

官方源码里的解释是:

 * @param pvCreatedTask Used to pass back a handle by which the created task
 * can be referenced.

就是说是个handle,有说法是这个是个句柄,比如可以用来查询任务的剩余栈。

官方代码说:

if( ( void * ) 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;
}

看出来了吧还可以改变任务优先级,删除任务等。

FreeRTOS 查询任务 剩余的栈空间的 方法。这个看起来比较有用。

【4】如何查询任务堆栈使用情况?

FreeRTOS 查询任务 剩余的栈空间的 方法

【5】理解freertos,哪些变量值得关注?

使用freertos V9.0.0需要注意的几个问题

【6】freertos代码的大部分都在实现啥?

根据”FreeRTOS高级篇4---FreeRTOS任务切换分析“,FreeRTOS任务相关的代码大约占总代码的一半左右,这些代码都在为一件事情而努力,即找到优先级最高的就绪任务,并使之获得CPU运行权。

【7】xTaskCreate的作用是什么?

根据 “FreeRTOS高级篇2---FreeRTOS任务创建分析”,这个API函数的作用是创建新的任务并将它加入到任务就绪列表

【8】if A context switch is required,咋整?

根据 “FreeRTOS高级篇4---FreeRTOS任务切换分析”,可以清晰地看出切换的流程:

首先出发PendSV interrupt,这个可以理解。

举个别的例子,如想进入串口接收中断,如果没收到数据,如何进入中断呢?强制那个标志位就可以了。就可以进入接收中断了。

freertos源码在xPortSysTickHandler中断里面是这样写的:

/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
/* A context switch is required.  Context switching is performed in
the PendSV interrupt.  Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}

在vTaskDelay有:
/* Force a reschedule if xTaskResumeAll has not already done so, we may
have put ourselves to sleep. */
if( xAlreadyYielded == pdFALSE )
{
   /* Set a PendSV to request a context switch. */
   portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
           /* Barriers are normally not required but do ensure the code is completely
           within the specified behaviour for the architecture. */
           __dsb( portSY_FULL_READ_WRITE );
           __isb( portSY_FULL_READ_WRITE );
}
执行了上面两种情况的一种后,就进入xPortPendSVHandler,然后进入vTaskSwitchContext,然后进入taskSELECT_HIGHEST_PRIORITY_TASK();