我一直在写裸机,写的多了自然会对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();
}
参考程序免积分下载链接,下一篇,讲解该参考程序。。。