OS任务切换源程序分析
先要声明任务指针,因为后面需要使用。
//任务指针.
volatile TASK_TCB* volatile g_pCurrentTask = NULL;
volatile TASK_TCB* volatile g_pCurrentTask1 = NULL;
volatile TASK_TCB* volatile g_pCurrentTask2 = NULL;
接着就需要初始化这些任务栈,用下面的代码进行初始化,为了简单,全部使用内存地址操作的方式,当然后面会改成动态地分配内存的方式。代码如下:
///////////////////////////////////////////////////////////////////////////////
//函数名称: TaskInitStack
//函数功能: 分配任务的栈。
//输入参数:
//输出参数:
//返 回 值:
//开发人员: 蔡军生
//时 间: 2006/02/26
//修改说明:
//
///////////////////////////////////////////////////////////////////////////////
void TaskInitStack(void)
{
g_pCurrentTask1 = (PTASK_TCB)0x0c700000;
g_pCurrentTask1->pStackStart = (UINT*)(0x0c700000+0x200);
g_pCurrentTask1->pStackTop = g_pCurrentTask1->pStackStart + 0x100;
g_pCurrentTask2 = (PTASK_TCB)(0x0c700000 + 0x400);
g_pCurrentTask2->pStackStart = (UINT*)(0x0c700000+0x400 + 0x200);
g_pCurrentTask2->pStackTop = g_pCurrentTask2->pStackStart + 0x100;
}
接着再创建两个简单的任务,它们都是输出一行字符串,就等待一会,代码如下:
//
void TaskTest1(void)
{
for(;;)
{
Lock();
puts("TaskTest1\n");
UnLock();
SoftDelay(100);
}
}
//
void TaskTest2(void)
{
for(;;)
{
Lock();
puts("TaskTest2\n");
UnLock();
SoftDelay(100);
}
}
然后再初始化任务栈,代码如下:
void TaskStart(void)
{
//
UINT* pTemp = g_pCurrentTask1->pStackTop;
//
*g_pCurrentTask1->pStackTop = (UINT)TaskTest1;
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x14141414; /* R14 */
g_pCurrentTask1->pStackTop--;
// *g_pCurrentTask1->pStackTop = (UINT)pTemp; /* R13 */
// g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x12121212; /* R12 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x11111111; /* R11 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x10101010; /* R10 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x09090909; /* R9 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x08080808; /* R8 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x07070707; /* R7 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x06060606; /* R6 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x05050505; /* R5 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x04040404; /* R4 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x03030303; /* R3 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x02020202; /* R2 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x01010101; /* R1 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0; /* R0 */
g_pCurrentTask1->pStackTop--;
*g_pCurrentTask1->pStackTop = (UINT)0x13; /* SPSR */
//
//
//
pTemp = g_pCurrentTask2->pStackTop;
//
*g_pCurrentTask2->pStackTop = (UINT)TaskTest2;
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x14141414; /* R14 */
g_pCurrentTask2->pStackTop--;
// *g_pCurrentTask2->pStackTop = (UINT)pTemp; /* R13 */
// g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x12121212; /* R12 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x11111111; /* R11 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x10101010; /* R10 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x09090909; /* R9 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x08080808; /* R8 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x07070707; /* R7 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x06060606; /* R6 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x05050505; /* R5 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x04040404; /* R4 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x03030303; /* R3 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x02020202; /* R2 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x01010101; /* R1 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0; /* R0 */
g_pCurrentTask2->pStackTop--;
*g_pCurrentTask2->pStackTop = (UINT)0x13; /* SPSR */
//设置首先运行的任务是1.
g_pCurrentTask = g_pCurrentTask1;
}
代码初始化了两个任务栈后,接着设置第一个任务为优先运行的任务。
最后就需要进行中断任务调度任务进行运行了,代码如下:
///////////////////////////////////////////////////////////////////////////////
//函数名称: EIntTickIsr
//函数功能: 时钟中断函数。
//输入参数:
//输出参数:
//返 回 值:
//开发人员: 蔡军生
//时 间: 2006/02/26
//修改说明:
//
///////////////////////////////////////////////////////////////////////////////
void EIntTickIsr(void) __attribute__((naked));
void EIntTickIsr(void)
{
//保存R0-R12寄存器到栈里.
asm volatile(" STMDB SP!, {R0-R12} ");
//关闭TICK中断.
INTMSK |= BIT_GLOBAL|BIT_TICK;
//取得保存IRQ的LR地址,并保存LR.
asm volatile (" LDR R0,=g_dwIRQLR" );
asm volatile (" SUBS LR,LR,#4 ");
asm volatile (" STR LR,[R0] ");
//取回前面保存的R0-R12寄存的值.
asm volatile (" LDMIA SP!, {R0-R12} ");
//取得最后返回的SPSR,就是SVC模式下的CPSR.
//从IRQ模式切换回到SVC模式.
asm volatile (" MRS LR,SPSR ");
asm volatile (" ORR LR,#0xc0 ");
asm volatile (" MSR CPSR,LR ");
//现在已经转换回到SVC模式,取回IRQ模式下保存的LR.
//把R0的值保存到栈中第二个位置.
asm volatile (" STR R0,[SP,#-8] ");
//从保存位置取回IRQ模式下保存的LR值.
asm volatile (" LDR R0,=g_dwIRQLR" );
asm volatile (" LDR R0,[R0] ");
//把IRQ模式下的LR保存到栈里,就是出栈时的PC值.
asm volatile (" STMDB SP!,{R0} ");
//从栈里第二个位置取回先前保存的R0值.
asm volatile (" SUBS SP,SP,#4 ");
asm volatile (" LDMIA SP!,{R0} ");
//保存R0-R12,LR到栈里,入栈顺序是刚好相反的.
asm volatile (" STMDB SP!,{R0-R12,LR} ");
//保存CPSR的值到栈里,就是SPSR的位置.
asm volatile (" MRS R4,CPSR ");
asm volatile (" BIC R4,#0xc0 ");
asm volatile (" STMDB SP!,{R4} ");
//保存栈顶到任务指针里.
asm volatile ( "LDR R0, %0" : : "m" (g_pCurrentTask) );
asm volatile ( "STR SP, [R0]" );
//增加时钟计数.
g_dwTickCount++;
//
printf("g_dwTickCount = (%d)\n",g_dwTickCount);
//
//清除屏蔽位.
I_ISPC = BIT_TICK;
INTMSK &= ~(BIT_GLOBAL|BIT_TICK);
//
if (g_dwTickCount < 3)
{
g_pCurrentTask = g_pCurrentTask1;
}
else if (g_dwTickCount < 4)
{
g_pCurrentTask = g_pCurrentTask2;
}
else
{
g_dwTickCount = 0;
}
//取得新任务指针
asm volatile ( "LDR R0, %0" : : "m" (g_pCurrentTask) );
asm volatile ( "LDR SP, [R0]" );
//取回SPSR值.
asm volatile ( "LDMIA SP!, {R0}" );
asm volatile ( "MSR SPSR, R0" );
//取回R0-R12,LR,PC值,并运行最后的任务.
asm volatile ( "LDMIA SP!, {R0-R12,LR,PC}^" );
}
写完上面的代码,就可以真正地实现了任务调度了。有了以上的代码,写其它复杂的任务调试都变得很容易了,这些代码得来是不太容易的,我经历了好几个星期的调试才通过的。
这些都是在我的S3C44B0开发板上调试通过的,如果你没有开发板,可以跟我购买。联系方法ccaimouse#gmail.com(请把#换成@)。