我是靠谱客的博主 淡淡面包,最近开发中收集的这篇文章主要介绍写一个属于自己的RTOS(day-1)RTOS Once More,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

RTOS Once More

大三的时候,我也看过有关实时操作系统的论文与程序,并没有太在意这一知识点、没有去深刻的学习有关的知识,显然我是被论文中的一句话所坑了“实时性最好的还是裸机跑程序”,确实。想想我大学四年的开发中无论是学习还是比赛,都是“裸奔”的。由于STM32F103的主频够快,也感觉不到控制输出有延迟。现在当我去做FlightFight游戏时,却实现不了两个对象的分别控制,才想起RTOS。2020/4/19今天是第一天学习,总结一下今天的成果。今天是从文章“建立一个属于自己的AVR的RTOS”开始的。
知识点回顾
专门与堆栈和PC指针打交道的一类指令
rcall 相对调用子程序指令
icall 间接调用子程序指令
ret 子程序返回指令
reti 中断返回指令
ret与reti,都是将堆栈顶的两个字节弹出来送入程序计数器PC中,一般用来从程序或中断中推出。其中reti还可以在退出中断时,重新开启全局中断使能。这些都是学习汇编的时候提到的。
启发
有了相应的知识点基础,并加上RTOS是利用函数地址调用函数的。我们可以模拟一下操作系统调用函数的过程:将函数的地址存到私有堆栈中。当某任务就绪时,将该任务的函数入口地址,给SP,当使用ret 返回时SP的值恢复到了PC中,进而PC指向了某个函数。所以先建立私有堆栈试一试。

void fun1(void)
{
	unsigned char i=0;
	while(1)
	{
		PORTB=i++;
		PORTC = 0X01<<(i%8);
	}
}
unsigned char Stack[100];//建立一个100字节的人工堆栈
void RunFunInNewStack(void (*pfun)(),unsigned char *pStack)
{
	*pStack --=(unsigned )pfun;//将函数的地址低位压入堆栈
	*pStack --=(unsigned int)pfun>>8;//将函数的地址高位压入堆栈
	SP = pStack;//将堆栈指针指向人工堆栈的栈顶
}
int main(void)
{
	RunFunInNewStack(fun1,&Stack[99]);
}

该程序的运行过程是:函数RunFunInNewStack()将指向函数的指针的值保存到一个unsigned char的数组Stack中,作为人工堆栈。并且将栈顶的数组传递给堆栈指针SP。因此当用ret返回时,从SP中恢复到PC中的值,就变成了指向fun1()的地址,开始运行fun1()。
问题发现
其一:通常大家都会认为,在任务调度时,当然要将所有的通用寄存器都保存,并且还应该保存程序状态寄存器SREG。然后再根据相反的次序,将任务内容恢复。这是不对的。反复试验用到的寄存器为第一类第二类第三类寄存器。如果在中断函数中有调用其它函数,会在进入中断后,固定的将第一类寄存器和第三类寄存器入栈 ,在退出中断又将他们入栈。
其二:前后台系统,协作式内核系统 ,抢占式内核系统,有何不同?
举个例子:排队打饭。你正在打饭,身后是院长,院长身后是校长。
如果是前后台系统:不管是谁都得排队一个一个来。也就是我们的裸奔系统。
如果是协作式系统:等你打完饭,让校长先吃上饭,院长最后才吃上饭。
如果是抢占式系统:不论是谁在打饭还是排队,只要有更高级别的人在等着,都要第一时间让出来,让最高级别的校长第一个打饭。
实战1--------写一个只有延时服务的协作式内核

#define OS_TASKS 3
unsigned char Stack[200];
register unsigned char OSRdyTb1 asm("r2");  //运行任务就绪表
register unsigned char OSTaskRunningPrio asm("r3");
//正在运行的任务
struct TaskCtrBlock{ //任务控制块
	unsigned int OSTaskStackTop;//保存任务的栈顶
	unsigned int OSWaitTick;//任务延时时钟
}TCB[OS_TASKS+1];
//防止被编译器占用
register unsigned char tempR4 asm("r4");
register unsigned char tempR5 asm("r5");
register unsigned char tempR6 asm("r6");
register unsigned char tempR7 asm("r7");
register unsigned char tempR8 asm("r8");
register unsigned char tempR9 asm("r9");
register unsigned char tempR10 asm("r10");
register unsigned char tempR11 asm("r11");
register unsigned char tempR12 asm("r12");
register unsigned char tempR13 asm("r13");
register unsigned char tempR14 asm("r14");
register unsigned char tempR15 asm("r15");
register unsigned char tempR16 asm("r16");
register unsigned char tempR17 asm("r17");
void OSTaskCreate(void (*Task)(void),unsigned char *Stack,unsigned char TaskID)
{
	unsigned char i;
	*Stack --= (unsigned int)Task>>8;
    *Stack --= (unsigned int)Task;
	*Stack--= 0x00;
	*Stack--= 0x00;
	*Stack--= 0x80;
	for(i=0;i<14;i++)
	{
		*Stack --=i;
	}
	TCB[TaskID].OSTaskStackTop=(unsigned int)Stack;
	OSRdyTbl|=0x01<<TaskID;
}
void OSStartTask()
{
	OSTaskRunningPrio = OS_TASKS;
	SP = TCB[TaskID].OSTaskStackTop+17;
	asm volatile("reti t");
}
void OSSched(void)
{
	asm volatile("PUSH R0 t");
	asm volatile("PUSH R1 t");
	asm volatile("IN R1,SERGt");
	asm volatile("PUSH R1t");
	asm volatile("CLR R0 t");
	asm volatile("PUSH R18 t");
	asm volatile("PUSH R19 t");
	asm volatile("PUSH R20 t");
	asm volatile("PUSH R21 t");
	asm volatile("PUSH R22 t");
	asm volatile("PUSH R23 t");
	asm volatile("PUSH R24 t");
	asm volatile("PUSH R25 t");
	asm volatile("PUSH R26 t");
	asm volatile("PUSH R27 t");
	asm volatile("PUSH R30 t");
	asm volatile("PUSH R31 t");
	asm volatile("PUSH R28 t");//R28R29用于建立堆栈的指针
	asm volatile("PUSH R29t");//到此入栈成功
	TCB[OSTaskRunningPrio].OSTaskStackTop = SP;//将正在运行的堆栈第保存。
	unsigned char OSNextTaskID;
	for(OSNextTaskID=0;OSNextTaskID<OS_TASKS&&!(OSRdyTb1&(0x01<<OSNextTaskID));OSNextTaskID++)
	OSTaskRunningPrio = OSNextTaskID;
	cli();//保护堆栈转换
	SP = TCB[OSTaskRunningPrio].OSTaskStackTop;
	sei();
	//根据中断时的出栈次序
	asm volatile("POP R29 t");
	asm volatile("POP R28 t");
	asm volatile("POP R31 t");
	asm volatile("POP R30 t");
	asm volatile("POP R27 t");
	asm volatile("POP R26 t");
	asm volatile("POP R25 t");
	asm volatile("POP R24 t");
	asm volatile("POP R23 t");
	asm volatile("POP R22 t");
	asm volatile("POP R21 t");
	asm volatile("POP R29 t");
	asm volatile("POP R20 t");
	asm volatile("POP R19 t");
	asm volatile("POP R18 t");
	asm volatile("POP SERGt");
	asm volatile("POP R1 t");
	asm volatile("OUT SERG,R1t");
	asm volatile("POP R1 t");
	asm volatile("POP R0 t");
	//中断时出栈完成
}
void OSTimeDly(unsigned int  ticks)
{
	if(ticks)//当延时有效时
	{
		OSRdyTbl&=~(0x01<<OSTaskRunningPrio);
		TCB[OSTaskRunningPrio].OSWaitTick=ticks;
		OSSched();//重新调度
	}
}
void TCN0Init()//定时器0
{
	TCCR0=0;
	TCCR0|=(1<<CS02);//定时器256预分频
	TIMSK|=(1<<TOIE0);//T0溢出中断允许
	TCNTO=100;//置计数起始值
}
SIGNAL(SIG_OVERFLOW0)
{
	unsigned char i;
	for(i=0;i<OS_TASKS;i++)
	{
		if(TCB[i].OSWaitTick)
		{
			TCB[i].OSWaitTick--;
			if(TCB[i].OSWaitTick==0)
			{
				OSRdyTb1|=(0x01<<i);
			}
		}
	}
	TCNT0 =100;
}
void Task0()
{
	unsigned int j=0;
	while(1)
	{
		PORTB=j++;
		OSTimeDly(2);
	}
}
void Task1()
{
	unsigned int j=0;
	while(1)
	{
		PORTC=j++;
		OSTimeDly(4);
	}
}
void Task2()
{
	unsigned int j=0;
	while(1)
	{
		PORTD=j++;
		OSTimeDly(8);
	}
}
void TaskScheduler()
{
	while(1)
	{
		OSSched();反复调度
	}
}
int main()
{
	TCN0Init();
	OSRdyTb1=0;
	OSTaskRunningPrio=0;
	OSTaskCreate(Task0,&Stack[49],0);
	OSTaskCreate(Task1,&Stack[99],1);
	OSTaskCreate(Task2,&Stack[149],2);
	OSTaskCreate(TaskScheduler,&Stack[199],OS_TASKS);
	OSStartTask();
}

上面的程序要表达的流程是:三个正在运行的主任务,都通过延时服务,主动放弃对CPU的控制权。在时间中断中对各个任务的延时进行计时,如果某个任务延时结束,将任务重新在就绪表中置位。最低级的系统任务TaskScheduler();在三个主任务在放弃对CPU的控制权后开始不断的进行调度。如果某个任务在就绪表中置位,通过调度,进入最高级别的任务中继续进行。下面由我整理的流程图会看起来明白一些。
RTOS流程图
下面是我本人的理解 ,CPU运行有运行环境(主要是寄存器),由于有四个任务因此有四个私有堆栈。当任务3完成后就不再调用,主要是任务0、任务1、任务2的反复调用执行。假如任务0在CPU中(相应寄存器中)运行,当任务0运行结束后,会将有关任务0的信息,存到任务0的私有堆栈当中,此时的信息指的是,当任务0运行时各个相应寄存器处于什么状态,存入堆栈的原因在于为下一次任务0执行做准备,比如其中一个值加加到了10,当任务再次调度时,接着10继续加加。任务0结束,任务1运行,CPU会重新分配寄存器,来用于运行任务1。任务1若要是执行结束,把寄存器中有关任务1的信息存到其私有堆栈当中。原理与任务0相同。再来个流程图加深一下印象:
现场的保护与恢复
上面的流程图是大体上的工作流程,下面再以任务0举例,来说明任务的调度与进栈出栈的关系,先来一张图重申一下问题。
任务0的工作调度
来聊一下进栈出栈的问题,出栈即任务被调度,而且我们知道,先进栈的后出栈,后进栈的先出栈。有图说明问题:
入栈
上图为入栈过程。
出栈
上图为出栈过程。
小结
对于RTOS理解不到位的地方,请大家指出,共同进步。

最后

以上就是淡淡面包为你收集整理的写一个属于自己的RTOS(day-1)RTOS Once More的全部内容,希望文章能够帮你解决写一个属于自己的RTOS(day-1)RTOS Once More所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部