【深入学习51单片机】一、基于8051的RTOS内核任务切换堆栈过程剖析

我一直在写裸机,写的多了自然会对rtos产生浓厚兴趣,最有意思的莫过于任务切换了,可以在多个死循环里面跳转,很神奇的样子。本文学习参考程序是网上一个基于8051的简易os,从哪里下的已经忘了。。。(好像是51黑,但搜不到了,,,下载链接放在文章末尾)。我会将学习过程整理成【深入学习51单片机】专辑文章,后面不间断更新吧(鸽)。。。

原理: 任务切换是通过操作堆栈指针SP来完成的。只要在切换任务时保存当前任务堆栈,然后切换到目标任务堆栈就完成了任务切换。
程序指针PC的切换也是通过操作堆栈指针SP来完成(51单片机并不能直接操作PC程序指针),程序在进入子程序时PC会最先入栈,子程序返回时PC会最后出栈,这样操作PC对应的堆栈位置就间接的操作了PC程序指针。

下面是我写的一个简单的测试程序,单文件就可以编译。 程序实现了在task1()和task2()之间的循环切换,举一反三,如果用定时器中断写一个任务切换,再加上现场保存和恢复、优先级判断,即实现了一个丐版的RTOS多任务内核。
注释比较详细了,方便理解,仿真跟踪SP和PC的值变化就清楚了。

#include "reg51.h"
//调用普通函数无变量参数的话只需要入栈:PC指针两个字节
//调用中断函数的话还另外需要入栈工作寄存器,详情可写个中断程序,参考反汇编入栈出栈过程
unsigned char idata stack1[2];		//任务堆栈,因为这里只需要保存PC指针,所以只需要两个字节
unsigned char idata stack2[2];

void task1(void);
void task2(void);

void task1(void)
{
	((unsigned char idata*)stack2)[0] = (unsigned int)task2;    //修改压入的PC指针地址,先压低八位
	((unsigned char idata*)stack2)[1] = (unsigned int)task2>>8; //再压高八位,操作SP后SP才指向这里
																//程序返回后PC会由硬件指向这里
	
	SP = (unsigned char)&stack2[1]; //修改出栈时的堆栈指针,指向要弹出到PC指针的高八位位置
}

void task2(void)
{
	//PC自动压栈时,先压低8位地址,再压高八位地址
	//因此PC出栈时,先出高八位地址,再出低八位地址
	((unsigned char idata*)stack1)[0] = (unsigned int)task1;    //修改压入的PC指针地址,先压低八位
	((unsigned char idata*)stack1)[1] = (unsigned int)task1>>8; //再压高八位,操作SP后SP才指向这里
																//程序返回后PC会由硬件指向这里
	SP = (unsigned char)&stack1[1]; //修改出栈时的堆栈指针,指向要弹出到PC指针的高八位位置
}

void main(void)
{
	SP = 0x50; 		//堆栈位置初始化,可以不要,只是为了仿真时方便知道起始堆栈位置
	task1();
}

参考程序免积分下载链接,下一篇,讲解该参考程序。。。

你可能感兴趣的:(单片机,51单片机,RTOS,堆栈)