【1】关于堆栈深度
朱工在FreeRTOS高级篇2---FreeRTOS任务创建分析说到:
- BaseType_t xTaskCreate(
- TaskFunction_tp vTaskCode,
- const charchar * constpcName,
- unsigned short usStackDepth,
- voidvoid *pvParameters,
- UBaseType_t uxPriority,
- TaskHandle_t *pvCreatedTask
- );
- usStackDepth:指定任务堆栈大小,能够支持的堆栈变量数量(堆栈深度),而不是字节数。比如,在16位宽度的堆栈下,usStackDepth定义为100,则实际使用200字节堆栈存储空间。堆栈的宽度乘以深度必须不超过size_t类型所能表示的最大值。比如,size_t为16位,则可以表示堆栈的最大值是65535字节。这是因为堆栈在申请时是以字节为单位的,申请的字节数就是堆栈宽度乘以深度,如果这个乘积超出size_t所表示的范围,就会溢出,分配的堆栈空间也不是我们想要的。
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 查询任务 剩余的栈空间的 方法。这个看起来比较有用。
FreeRTOS 查询任务 剩余的栈空间的 方法
【5】理解freertos,哪些变量值得关注?【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();