今天来看下主要的一个系统函数,这个重量级的函数叫做任务创建,在
freertos
中的全名叫
xTaskCreate
,原型如下:
signed portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
const signed portCHAR * const pcName,
//task
名
unsigned portSHORT usStackDepth,
//task
栈的深度
void *pvParameters,
//task
的参数
unsigned portBASE_TYPE uxPriority,
//task
的优先级
xTaskHandle *pxCreatedTask )
//task
的句柄
几点说明:
(
1
)所谓的返回值
portBASE_TYPE
其实就是
int
(
2
)
pdTASK_CODE pvTaskCode
,该参数就是进程体,实为函数指针。定义如下:
typedef void (*pdTASK_CODE)( void * );
(
3
)
xTaskHandle
,定义为
typedef void * xTaskHandle;
下面来看看这个超级牛函数的实现,说是超级牛函数是因为该函数的地位。实现却并非想像般复杂。具体有对以下几个函数的调用
(1)
prvAllocateTCBAndStack
,用于分配该
task
的
TCB
【任务控制块】和栈
(2)
prvInitialiseTCBVariables
,初始化新分配的
task TCB
(3)
设置栈顶位置
(4)
pxPortInitialiseStack
,初始化新分配的栈
(5)
判断该新创建
task
是否为系统的第一个
task
。是则初始化
task
的各所需链表
(6)
设置当前系统的
TCB
(7)
prvAddTaskToReadyQueue
,添加新创建的
task
的
TCB
到
task
就绪队列中
(8)
如果调度已开始,并且新创建的
task
的优先级高于当前的
task
,则直接进行调度
taskYIELD
下面看下栈的初始化函数的实现:
portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )
{
/* Simulate the stack frame as it would be created by a context switch
interrupt. */
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( portSTACK_TYPE ) pxCode; /* PC */
pxTopOfStack--;
*pxTopOfStack = 0; /* LR */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}
这里别的都好理解,就是为什么
LR
,
R0
等这些的寄存器的入栈调用不怎么理解,为何是
0
,比如
LR
,接下来的就是
R12, R3, R2 and R1
。或许要看下硬件(
stm32
)的
spec
了
在这里顺便说一下,由于各
cpu
的栈实现方式不一样,以及寄存器地址的不同,所以每个平台(
cpu
)上的这个栈初始化函数的实现都不一样。具体实现见
source\portable\
平台
\cpu
类型
\port.c
中的实现,
source\portable\iar\arm_cm3
接着也是重量级的函数,
prvInitialiseTaskLists
,这个函数。对这个函数,源代码中的注释是这样的,
This is the first task to be created so do the preliminary initialisation required.
涉及到
tasklist
这里就先不介绍了。
再看这里的最后一个超级重量级的函数,直接调度函数
taskYIELD
。
#define taskYIELD() portYIELD()
【
source\include\task.h
】
#define portYIELD() vPortYieldFromISR()
【注意】
这个宏定义也是和栈初始化函数一样是和平台相关的,所以也就是在
portable
目录下的某个平台
+cpu
下了。实现在
portmacro.h
中
void vPortYieldFromISR( void )
{
/*
Set a PendSV to request a context switch.
*/
*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;
}
上面的这个最终实现是在
iar
上面的【
source\portable\iar\arm_cm3\port.c
】
#define portNVIC_INT_CTRL ( ( volatile unsigned portLONG *) 0xe000ed04 )
#define portNVIC_PENDSVSET 0x10000000
这两个宏没有看懂是什么意思,这里标识一下,后续待解
*************************