2009-5-10
今天来看下主要的一个系统函数,这个重量级的函数叫做任务创建,在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
这两个宏没有看懂是什么意思,这里标识一下,后续待解*************************