序
自从03年以来,对单片机的rtos的学习和应用的热潮可谓一浪高过一浪.03年,在离开校园前的,非典的那几个月,在华师的后门那里买了本邵贝贝的《ucosii》,通读了几次,没有实验器材,也不了了之。
在21ic上,大家都可以看到杨屹写的关于ucosii在51上的移植,于是掀起了51上的rtos的热潮。
再后来,陈明计先生推出的smallrots,展示了一个用在51上的微内核,足以在52上进行任务调度。
前段时间,在ouravr上面开有专门关于avr的rtos的专栏,并且不少的兄弟把自己的作品拿出来,着实开了不少眼界。这时,我重新回顾了使用单片机的经历,觉得很有必要,从根本上对单片机的rtos的知识进行整理,于是,我开始了编写一个用在avr单片机的rtos。
当时,我所有的知识和资源有:
proteus6.7可以用来模拟仿真avr系列的单片机
winavrv2.0.5.48基于gccavr的编译环境,好处在于可以在c语言中插入asm的语句
mega81k的ram有8k的rom,是开发8位的rtos的一个理想的器件,并且我对它也比较熟悉。
写ucos的jeanj.labrosse在他的书上有这样一句话,“渐渐地,我自然会想到,写个实时内核直有那么难吗?不就是不断地保存,恢复cpu的那些寄存器嘛。”
好了,当这一切准备好后,我们就可以开始我们的rtosformega8的实验之旅了。
本文列出的例子,全部完整可用。只需要一个文件就可以编译了。我相信,只要适当可用,最简单的就是最好的,这样可以排除一些不必要的干扰,让大家专注到每一个过程的学习。
第一篇:函数的运行
在一般的单片机系统中,是以前后台的方式(大循环+中断)来处理数据和作出反应的。
例子如下:
makefile的设定:运行winavr中的mfile,设定如下
mcutype:mega8
optimizationlevel:s
debugformat:avr-coff
c/c++sourcefile:选译要编译的c文件
#include
voidfun1(void)
{
unsignedchari=0;
while(1)
{
portb=i++;
portc=0x01<<(i%8);
}
}
intmain(void)
{
fun1();
}
首先,提出一个问题:如果要调用一个函数,真是只能以上面的方式进行吗?
相信学习过c语言的各位会回答,no!我们还有一种方式,就是“用函数指针变量调用函数”,如果大家都和我一样,当初的教科书是谭浩强先生的《c程序设计》的话,请找回书的第9.5节。
例子:用函数指针变量调用函数
#include
voidfun1(void)
{
unsignedchari=0;
while(1)
{
portb=i++;
portc=0x01<<(i%8);
}
}
void(*pfun)();//指向函数的指针
intmain(void)
{
pfun=fun1;//
(*pfun)();//运行指针所指向的函数
}
第二种,是“把指向函数的指针变量作函数参数”
#include
voidfun1(void)
{
unsignedchari=0;
while(1)
{
portb=i++;
portc=0x01<<(i%8);
}
}
voidrunfun(void(*pfun)())//获得了要传递的函数的地址
{
(*pfun)();//在runfun中,运行指针所指向的函数
}
intmain(void)
{
runfun(fun1);//将函数的指针作为变量传递
}
看到上面的两种方式,很多人可能会说,“这的确不错”,但是这样与我们想要的rtos,有什么关系呢?各位请细心向下看。
以下是gcc对上面的代码的编译的情况:
对main()中的runfun(fun1);的编译如下
ldir24,lo8(pm(fun1))
ldir25,hi8(pm(fun1))
rcallrunfun
对voidrunfun(void(*pfun)())的编译如下
/*voidrunfun(void(*pfun)())*/
/*(*pfun)();*/
.lm6:
movwr30,r24
icall
ret
在调用voidrunfun(void(*pfun)())的时候,的确可以把fun1的地址通过r24和r25传递给runfun()。但是,rtos如何才能有效地利用函数的地址呢? 第二篇:人工堆栈
在单片机的指令集中,一类指令是专门与堆栈和pc指针打道的,它们是
rcall相对调用子程序指令
icall间接调用子程序指令
ret子程序返回指令
reti中断返回指令
对于ret和reti,它们都可以将堆栈栈顶的两个字节被弹出来送入程序计数器pc中,一般用来从子程序或中断中退出。其中reti还可以在退出中断时,重开全局中断使能。
有了这个基础,就可以建立我们的人工堆栈了。
例:
#include
voidfun1(void)
{
unsignedchari=0;
while(1)
{
portb=i++;
portc=0x018;//将函数的地址高位压入堆栈,
*pstack--=(unsignedint)pfun;//将函数的地址低位压入堆栈,
sp=pstack;//将堆栈指针指向人工堆栈的栈顶
__asm____volatile__(retnt);//返回并开中断,开始运行fun1()
}
intmain(void)
{
runfuninnewstack(fun1,&stack[99]);
}
runfuninnewstack(),将指向函数的指针的值保存到一个unsignedchar的数组stack中,作为人工堆栈。并且将栈顶的数值传递组堆栈指针sp,因此当用ret返回时,从sp中恢复到pc中的值,就变为了指向fun1()的地址,开始运行fun1().
上面例子中在runfuninnewstack()的最后一句嵌入了汇编代码ret,实际上是可以去除的。因为在runfuninnewstack()返回时,编译器已经会加上ret。我特意写出来,是为了让大家看到用ret作为返回后运行fun1()的过程。
第三篇:gcc中对寄存器的分配与使用
在很多用于avr的rtos中,都会有任务调度时,插入以下的语句:
入栈:
__asm____volatile__(pushr0nt);
__asm____volatile__(pushr1nt);
......
__asm____volatile__(pushr31nt);
出栈
__asm____volatile__(popr31nt);
......
__asm____volatile__(popr1nt);
__asm____volatile__(popr0nt);
通常大家都会认为,在任务调度开始时,当然要将所有的通用寄存器都保存,并且还应该保存程序状态寄存器sreg。然后再根据相反的次序,将新任务的寄存器的内容恢复。
但是,事实真的是这样吗?如果大家看过陈明计先生写的smallrots51,就会发现,它所保存的通用寄存器不过是4组通用寄存器中的1组。
在winavr中的帮助文件avr-libcmanual中的relatedpages中的frequentlyaskedquestions,其实有一个问题是whatregistersareusedbytheccompiler?回答了编译器所需要占用的寄存器。一般情况下,编译器会先用到以下寄存器
1call-usedregisters(r18-r27,r30-r31):调用函数时作为参数传递,也就是用得最多的寄存器。
2call-savedregisters(r2-r17,r28-r29):调用函数时作为结果传递,当中的r28和r29可能会被作为指向堆栈上的变量的指针。
3fixedregisters(r0,r1):固定作用。r0用于存放临时数据,r1用于存放0。
还有另一个问题是howtopermanentlybindavariabletoaregister?,是将变量绑定到通用寄存器的方法。而且我发现,如果将某个寄存器定义为变量,编译器就会不将该寄存器分配作其它用途。这对rtos是很重要的。
在inlineasm中的cnamesusedinassemblercode明确表示,如果将太多的通用寄存器定义为变量,刚在编译的过程中,被定义的变量依然可能被编译器占用。
大家可以比较以下两个例子,看看编译器产生的代码:(在*.lst文件中)
第一个例子:没有定义通用寄存器为变量
#include
unsignedcharadd(unsignedcharb,unsignedcharc,unsignedchard)
{
returnb+c*d;
}
intmain(void)
{
unsignedchara=0;
while(1)
{
a++;
portb=add(a,a,a);
}
}
在本例中,add(a,a,a);被编译如下:
movr20,r28
movr22,r28
movr24,r28
rcalladd
第二个例子:定义通用寄存器为变量
#include
unsignedcharadd(unsignedcharb,unsignedcharc,unsignedchard)
{
returnb+c*d;
}
registerunsignedcharaasm(r20);//将r20定义为变量a
intmain(void)
{
while(1)
{
a++;
portb=add(a,a,a);
}
}
在本例中,add(a,a,a);被编译如下:
movr22,r20
movr24,r20
rcalladd
当然,在上面两个例子中,有部份代码被编译器优化了。
通过反复测试,发现编译器一般使用如下寄存器:
第1类寄存器,第2类寄存器的r28,r29,第3类寄存器
如在中断函数中有调用基它函数,刚会在进入中断后,固定地将第1类寄存器和第3类寄存器入栈,在退出中断又将它们出栈。
第四篇:只有延时服务的协作式的内核
cooperativemultitasking
前后台系统,协作式内核系统,与占先式内核系统,有什么不同呢?
记得在21ic上看过这样的比喻,“你(小工)在用厕所,经理在外面排第一,老板在外面排第二。如果是前后台,不管是谁,都必须按排队的次序使用厕所;如果是协作式,那么可以等你用完厕所,老板就要比经理先进入;如果是占先式,只要有更高级的人在外面等,那么厕所里无论是谁,都要第一时间让出来,让最高级别的人先用。”
#include
#include
#include
unsignedcharstack[200];
registerunsignedcharosrdytblasm(r2);//任务运行就绪表
registerunsignedcharostaskrunningprioasm(r3);//正在运行的任务
#defineos_tasks3//设定运行任务的数量
structtaskctrblock//任务控制块
{
unsignedintostaskstacktop;//保存任务的堆栈顶
unsignedintoswaittick;//任务延时时钟
}tcb[os_tasks+1];
//防止被编译器占用
registerunsignedchartempr4asm(r4);
registerunsignedchartempr5asm(r5);
registerunsignedchartempr6asm(r6);
registerunsignedchartempr7asm(r7);
registerunsignedchartempr8asm(r8);
registerunsignedchartempr9asm(r9);
registerunsignedchartempr10asm(r10);
registerunsignedchartempr11asm(r11);
registerunsignedchartempr12asm(r12);
registerunsignedchartempr13asm(r13);
registerunsignedchartempr14asm(r14);
registerunsignedchartempr15asm(r15);
registerunsignedchartempr16asm(r16);
registerunsignedchartempr16asm(r17);
//建立任务
voidostaskcreate(void(*task)(void),unsignedchar*stack,unsignedchartaskid)
{
unsignedchari;
*stack--=(unsignedint)task>>8;//将任务的地址高位压入堆栈,
*stack--=(unsignedint)task;//将任务的地址低位压入堆栈,
*stack--=0x00;//r1__zero_reg__
*stack--=0x00;//r0__tmp_reg__
*stack--=0x80;//sreg在任务中,开启全局中断
for(i=0;i<14;i++)//在avr-libc中的faq中的whatregistersareusedbytheccompiler?
*stack--=i;//描述了寄存器的作用
tcb[taskid].ostaskstacktop=(unsignedint)stack;//将人工堆栈的栈顶,保存到堆栈的数组中
osrdytbl|=0x01<
}
//开始任务调度,从最低优先级的任务的开始
voidosstarttask()
{
ostaskrunningprio=os_tasks;
sp=tcb[os_tasks].ostaskstacktop+17;
__asm____volatile__(retint);
}
//进行任务调度
voidossched(void)
{
//根据中断时保存寄存器的次序入栈,模拟一次中断后,入栈的情况
__asm____volatile__(push__zero_reg__nt);//r1
__asm____volatile__(push__tmp_reg__nt);//r0
__asm____volatile__(in__tmp_reg__,__sreg__nt);//保存状态寄存器sreg
__asm____volatile__(push__tmp_reg__nt);
__asm____volatile__(clr__zero_reg__nt);//r0重新清零
__asm____volatile__(pushr18nt);
__asm____volatile__(pushr19nt);
__asm____volatile__(pushr20nt);
__asm____volatile__(pushr21nt);
__asm____volatile__(pushr22nt);
__asm____volatile__(pushr23nt);
__asm____volatile__(pushr24nt);
__asm____volatile__(pushr25nt);
__asm____volatile__(pushr26nt);
__asm____volatile__(pushr27nt);
__asm____volatile__(pushr30nt);
__asm____volatile__(pushr31nt);
__asm____volatile__(pushr28nt);//r28与r29用于建立在堆栈上的指针
__asm____volatile__(pushr29nt);//入栈完成
tcb[ostaskrunningprio].ostaskstacktop=sp;//将正在运行的任务的堆栈底保存
unsignedcharosnexttaskid;//在现有堆栈上开设新的空间
for(osnexttaskid=0;//进行任务调度
osnexttaskidosnexttaskid++);
ostaskrunningprio=osnexttaskid;
cli();//保护堆栈转换
sp=tcb[ostaskrunningprio].ostaskstacktop;
sei();
//根据中断时的出栈次序
__asm____volatile__(popr29nt);
__asm____volatile__(popr28nt);
__asm____volatile__(popr31nt);
__asm____volatile__(popr30nt);
__asm____volatile__(popr27nt);
__asm____volatile__(popr26nt);
__asm____volatile__(popr25nt);
__asm____volatile__(popr24nt);
__asm____volatile__(popr23nt);
__asm____volatile__(popr22nt);
__asm____volatile__(popr21nt);
__asm____volatile__(popr20nt);
__asm____volatile__(popr19nt);
__asm____volatile__(popr18nt);
__asm____volatile__(pop__tmp_reg__nt);//serg出栈并恢复
__asm____volatile__(out__sreg__,__tmp_reg__nt);//
__asm____volatile__(pop__tmp_reg__nt);//r0出栈
__asm____volatile__(pop__zero_reg__nt);//r1出栈
//中断时出栈完成
}
voidostimedly(unsignedintticks)
{
if(ticks)//当延时有效
{
osrdytbl&=~(0x01<tcb[ostaskrunningprio].oswaittick=ticks;
ossched();//从新调度
}
}
voidtcn0init(void)//计时器0
{
tccr0=0;
tccr0|=(1<timsk|=(1<tcnt0=100;//置计数起始值
}
signal(sig_overflow0)
{
unsignedchari;
for(i=0;i{
if(tcb[i].oswaittick)
{
tcb[i].oswaittick--;
if(tcb[i].oswaittick==0)//当任务时钟到时,必须是由定时器减时的才行
{
osrdytbl|=(0x01<>8;//将任务的地址高位压入堆栈,
*stack--=(unsignedint)task;//将任务的地址低位压入堆栈,
*stack--=0x00;//r1__zero_reg__
*stack--=0x00;//r0__tmp_reg__
*stack--=0x80;
//sreg在任务中,开启全局中断
for(i=0;i<14;i++)//在avr-libc中的faq中的whatregistersareusedbytheccompiler?
*stack--=i;//描述了寄存器的作用
tcb[taskid].ostaskstacktop=(unsignedint)stack;//将人工堆栈的栈顶,保存到堆栈的数组中
osrdytbl|=0x01<}
//开始任务调度,从最低优先级的任务的开始
voidosstarttask()
{
ostaskrunningprio=os_tasks;
sp=tcb[os_tasks].ostaskstacktop+17;
__asm____volatile__(retint);
}
//进行任务调度
voidossched(void)
{
//根据中断时保存寄存器的次序入栈,模拟一次中断后,入栈的情况
__asm____volatile__(push__zero_reg__nt);//r1
__asm____volatile__(push__tmp_reg__nt);//r0
__asm____volatile__(in__tmp_reg__,__sreg__nt);//保存状态寄存器sreg
__asm____volatile__(push__tmp_reg__nt);
__asm____volatile__(clr__zero_reg__nt);//r0重新清零
__asm____volatile__(pushr18nt);
__asm____volatile__(pushr19nt);
__asm____volatile__(pushr20nt);
__asm____volatile__(pushr21nt);
__asm____volatile__(pushr22nt);
__asm____volatile__(pushr23nt);
__asm____volatile__(pushr24nt);
__asm____volatile__(pushr25nt);
__asm____volatile__(pushr26nt);
__asm____volatile__(pushr27nt);
__asm____volatile__(pushr30nt);
__asm____volatile__(pushr31nt);
__asm____volatile__(pushr28nt);//r28与r29用于建立在堆栈上的指针
__asm____volatile__(pushr29nt);//入栈完成
tcb[ostaskrunningprio].ostaskstacktop=sp;//将正在运行的任务的堆栈底保存
unsignedcharosnexttaskid;//在现有堆栈上开设新的空间
for(osnexttaskid=0;//进行任务调度
osnexttaskidosnexttaskid++);
ostaskrunningprio=osnexttaskid;
cli();//保护堆栈转换
sp=tcb[ostaskrunningprio].ostaskstacktop;
sei();
//根据中断时的出栈次序
__asm____volatile__(popr29nt);
__asm____volatile__(popr28nt);
__asm____volatile__(popr31nt);
__asm____volatile__(popr30nt);
__asm____volatile__(popr27nt);
__asm____volatile__(popr26nt);
__asm____volatile__(popr25nt);
__asm____volatile__(popr24nt);
__asm____volatile__(popr23nt);
__asm____volatile__(popr22nt);
__asm____volatile__(popr21nt);
__asm____volatile__(popr20nt);
__asm____volatile__(popr19nt);
__asm____volatile__(popr18nt);
__asm____volatile__(pop__tmp_reg__nt);//serg出栈并恢复
__asm____volatile__(out__sreg__,__tmp_reg__nt);//
__asm____volatile__(pop__tmp_reg__nt);//r0出栈
__asm____volatile__(pop__zero_reg__nt);//r1出栈
//中断时出栈完成
}
////////////////////////////////////////////任务处理
//挂起任务
voidostasksuspend(unsignedcharprio)
{
tcb[prio].oswaittick=0;
osrdytbl&=~(0x01<if(ostaskrunningprio==prio)//当要挂起的任务为当前任务
ossched();//从新调度
}
//恢复任务可以让被ostasksuspend或ostimedly暂停的任务恢复
voidostaskresume(unsignedcharprio)
{
osrdytbl|=0x01ossched();//从新调度//从新调度
}
//任务延时
voidostimedly(unsignedintticks)
{
if(ticks)//当延时有效
{
osrdytbl&=~(0x01<tcb[ostaskrunningprio].oswaittick=ticks;
ossched();//从新调度
}
}
//信号量
structsemblk
{
unsignedcharoseventtype;//型号0,信号量独占型;1信号量共享型
unsignedcharoseventstate;//状态0,不可用;1,可用
unsignedcharostaskpendtbl;//等待信号量的任务列表
}sem[10];
//初始化信号量
voidossemcreat(unsignedcharindex,unsignedchartype)
{
sem[index].oseventtype=type;//型号0,信号量独占型;1信号量共享型
sem[index].ostaskpendtbl=0;
sem[index].oseventstate=0;
}
//任务等待信号量,挂起
unsignedcharostasksempend(unsignedcharindex,unsignedinttimeout)
{
//unsignedchari=0;
if(sem[index].oseventstate)//信号量有效
{
if(sem[index].oseventtype==0)//如果为独占型
sem[index].oseventstate=0x00;//信号量被独占,不可用
}
else
{//加入信号的任务等待表
sem[index].ostaskpendtbl|=0x01<osrdytbl&=~(0x01<tcb[ostaskrunningprio].oswaittick=timeout;//如延时为0,刚无限等待
ossched();//从新调度
if(tcb[ostaskrunningprio].oswaittick==0)return0;
}
return1;
}
//发送一个信号量,可以从任务或中断发送
voidossempost(unsignedcharindex)
{
if(sem[index].oseventtype)//当要求的信号量是共享型
{
sem[index].oseventstate=0x01;//使信号量有效
osrdytbl|=sem[index].ostaskpendtbl;//使在等待该信号的所有任务就绪
sem[index].ostaskpendtbl=0;//清空所有等待该信号的等待任务
}
else//当要求的信号量为独占型
{
unsignedchari;
for(i=0;iif(i{
sem[index].ostaskpendtbl&=~(0x01<osrdytbl|=0x01<}
else
{
sem[index].oseventstate=1;//使信号量有效
}
}
}
//从任务发送一个信号量,并进行调度
voidostasksempost(unsignedcharindex)
{
ossempost(index);
ossched();
}
//清除一个信号量,只对共享型的有用。
//对于独占型的信号量,在任务占用后,就交得不可以用了。
voidossemclean(unsignedcharindex)
{
sem[index].oseventstate=0;//要求的信号量无效
}
voidtcn0init(void)//计时器0
{
tccr0=0;
tccr0|=(1<timsk|=(1<tcnt0=100;//置计数起始值
}
signal(sig_overflow0)
{
unsignedchari;
for(i=0;i{
if(tcb[i].oswaittick)
{
tcb[i].oswaittick--;
if(tcb[i].oswaittick==0)//当任务时钟到时,必须是由定时器减时的才行
{
osrdytbl|=(0x01<>8;//将任务的地址高位压入堆栈,
*stack--=(unsignedint)task;//将任务的地址低位压入堆栈,
*stack--=0x00;//r1__zero_reg__
*stack--=0x00;//r0__tmp_reg__
*stack--=0x80;
//sreg在任务中,开启全局中断
for(i=0;i<14;i++)//在avr-libc中的faq中的whatregistersareusedbytheccompiler?
*stack--=i;//描述了寄存器的作用
tcb[taskid].ostaskstacktop=(unsignedint)stack;//将人工堆栈的栈顶,保存到堆栈的数组中
osrdytbl|=0x01<=os_tasks)//轮流运行各个任务,没有优先级
ostaskrunningprio=0;
//cli();//保护堆栈转换
sp=tcb[ostaskrunningprio].ostaskstacktop;
//sei();
//根据中断时的出栈次序
__asm____volatile__(popr29nt);
__asm____volatile__(popr28nt);
__asm____volatile__(popr31nt);
__asm____volatile__(popr30nt);
__asm____volatile__(popr27nt);
__asm____volatile__(popr26nt);
__asm____volatile__(popr25nt);
__asm____volatile__(popr24nt);
__asm____volatile__(popr23nt);
__asm____volatile__(popr22nt);
__asm____volatile__(popr21nt);
__asm____volatile__(popr20nt);
__asm____volatile__(popr19nt);
__asm____volatile__(popr18nt);
__asm____volatile__(pop__tmp_reg__nt);//serg出栈并恢复
__asm____volatile__(out__sreg__,__tmp_reg__nt);//
__asm____volatile__(pop__tmp_reg__nt);//r0出栈
__asm____volatile__(pop__zero_reg__nt);//r1出栈
__asm____volatile__(retint);//返回并开中断
//中断时出栈完成
}
voidintswitch(void)
{
__asm____volatile__(popr31nt);//去除因调用子程序而入栈的pc
__asm____volatile__(popr31nt);
__asm____volatile__(rjmpint_osschednt);//重新调度
}
voidtcn0init(void)//计时器0
{
tccr0=0;
tccr0|=(1<timsk|=(1<>8;//将任务的地址高位压入堆栈,
*stack--=(unsignedint)task;//将任务的地址低位压入堆栈,
*stack--=0x00;//r1__zero_reg__
*stack--=0x00;//r0__tmp_reg__
*stack--=0x80;
//sreg在任务中,开启全局中断
for(i=0;i<14;i++)//在avr-libc中的faq中的whatregistersareusedbytheccompiler?
*stack--=i;//描述了寄存器的作用
tcb[taskid].ostaskstacktop=(unsignedint)stack;//将人工堆栈的栈顶,保存到堆栈的数组中
osrdytbl|=0x01<}
//开始任务调度,从最低优先级的任务的开始
voidosstarttask()
{
ostaskrunningprio=os_tasks;
sp=tcb[os_tasks].ostaskstacktop+17;
__asm____volatile__(retint);
}
//进行任务调度
voidossched(void)
{
__asm____volatile__(ldir16,0x01nt);
//清除中断要求任务切换的标志位,设置正在任务切换标志位
__asm____volatile__(seint);
//开中断,因为如果因中断在任务调度中进行,要重新进行调度时,已经关中断
//根据中断时保存寄存器的次序入栈,模拟一次中断后,入栈的情况
__asm____volatile__(push__zero_reg__nt);//r1
__asm____volatile__(push__tmp_reg__nt);//r0
__asm____volatile__(in__tmp_reg__,__sreg__nt);//保存状态寄存器sreg
__asm____volatile__(push__tmp_reg__nt);
__asm____volatile__(clr__zero_reg__nt);//r0重新清零
__asm____volatile__(pushr18nt);
__asm____volatile__(pushr19nt);
__asm____volatile__(pushr20nt);
__asm____volatile__(pushr21nt);
__asm____volatile__(pushr22nt);
__asm____volatile__(pushr23nt);
__asm____volatile__(pushr24nt);
__asm____volatile__(pushr25nt);
__asm____volatile__(pushr26nt);
__asm____volatile__(pushr27nt);
__asm____volatile__(pushr30nt);
__asm____volatile__(pushr31nt);
__asm____volatile__(int_ossched:nt);//当中断要求调度,直接进入这里
__asm____volatile__(seint);
//开中断,因为如果因中断在任务调度中进行,已经关中断
__asm____volatile__(pushr28nt);//r28与r29用于建立在堆栈上的指针
__asm____volatile__(pushr29nt);//入栈完成
tcb[ostaskrunningprio].ostaskstacktop=sp;//将正在运行的任务的堆栈底保存
unsignedcharosnexttaskprio;//在现有堆栈上开设新的空间
for(osnexttaskprio=0;//进行任务调度
osnexttaskprioosnexttaskprio++);
ostaskrunningprio=osnexttaskprio;
cli();//保护堆栈转换
sp=tcb[ostaskrunningprio].ostaskstacktop;
sei();
//根据中断时的出栈次序
__asm____volatile__(popr29nt);
__asm____volatile__(popr28nt);
__asm____volatile__(popr31nt);
__asm____volatile__(popr30nt);
__asm____volatile__(popr27nt);
__asm____volatile__(popr26nt);
__asm____volatile__(popr25nt);
__asm____volatile__(popr24nt);
__asm____volatile__(popr23nt);
__asm____volatile__(popr22nt);
__asm____volatile__(popr21nt);
__asm____volatile__(popr20nt);
__asm____volatile__(popr19nt);
__asm____volatile__(popr18nt);
__asm____volatile__(pop__tmp_reg__nt);//serg出栈并恢复
__asm____volatile__(out__sreg__,__tmp_reg__nt);//
__asm____volatile__(pop__tmp_reg__nt);//r0出栈
__asm____volatile__(pop__zero_reg__nt);//r1出栈
//中断时出栈完成
__asm____volatile__(clint);//关中断
__asm____volatile__(sbrcr16,1nt);//sbrc当寄存器位为0刚跳过下一条指令
//检查是在调度时,是否有中断要求任务调度0x02是中断要求调度的标志位
__asm____volatile__(rjmposschednt);//重新调度
__asm____volatile__(ldir16,0x00nt);
//清除中断要求任务切换的标志位,清除正在任务切换标志位
__asm____volatile__(retint);//返回并开中断
}
//从中断退出并进行调度
voidintswitch(void)
{
//当中断无嵌套,并且没有在切换任务的过程中,直接进行任务切换
if(oscorestate==0x02&&intnum==0)
{
//进入中断时,已经保存了sreg和r0,r1,r18~r27,r30,r31
__asm____volatile__(popr31nt);//去除因调用子程序而入栈的pc
__asm____volatile__(popr31nt);
__asm____volatile__(ldir16,0x01nt);
//清除中断要求任务切换的标志位,设置正在任务切换标志位
__asm____volatile__(rjmpint_osschednt);//重新调度
}
}
//任务延时
voidostimedly(unsignedintticks)
{
if(ticks)//当延时有效
{
osrdytbl&=~(0x01<tcb[ostaskrunningprio].oswaittick=ticks;
ossched();//从新调度
}
}
voidtcn0init(void)//计时器0
{
tccr0=0;
tccr0|=(1<timsk|=(1<tcnt0=100;//置计数起始值
}
signal(sig_overflow0)
{
intnum++;//中断嵌套+1
sei();//在中断中,重开中断
unsignedchari,j=0;
for(i=0;i{
if(tcb[i].oswaittick)
{
tcb[i].oswaittick--;
if(tcb[i].oswaittick==0)//当任务时钟到时,必须是由定时器减时的才行
{
osrdytbl|=(0x01<>8;//将任务的地址高位压入堆栈,
*stack--=(unsignedint)task;//将任务的地址低位压入堆栈,
*stack--=0x00;//r1__zero_reg__
*stack--=0x00;//r0__tmp_reg__
*stack--=0x80;
//sreg在任务中,开启全局中断
for(i=0;i<14;i++)//在avr-libc中的faq中的whatregistersareusedbytheccompiler?
*stack--=i;//描述了寄存器的作用
tcb[taskid].ostaskstacktop=(unsignedint)stack;//将人工堆栈的栈顶,保存到堆栈的数组中
osrdytbl|=0x01<}
//开始任务调度,从最低优先级的任务的开始
voidosstarttask()
{
ostaskrunningprio=os_tasks;
sp=tcb[os_tasks].ostaskstacktop+17;
__asm____volatile__(retint);
}
//进行任务调度
voidossched(void)
{
__asm____volatile__(ldir16,0x01nt);
//清除中断要求任务切换的标志位,设置正在任务切换标志位
__asm____volatile__(seint);
//开中断,因为如果因中断在任务调度中进行,要重新进行调度时,已经关中断
//根据中断时保存寄存器的次序入栈,模拟一次中断后,入栈的情况
__asm____volatile__(push__zero_reg__nt);//r1
__asm____volatile__(push__tmp_reg__nt);//r0
__asm____volatile__(in__tmp_reg__,__sreg__nt);//保存状态寄存器sreg
__asm____volatile__(push__tmp_reg__nt);
__asm____volatile__(clr__zero_reg__nt);//r0重新清零
__asm____volatile__(pushr18nt);
__asm____volatile__(pushr19nt);
__asm____volatile__(pushr20nt);
__asm____volatile__(pushr21nt);
__asm____volatile__(pushr22nt);
__asm____volatile__(pushr23nt);
__asm____volatile__(pushr24nt);
__asm____volatile__(pushr25nt);
__asm____volatile__(pushr26nt);
__asm____volatile__(pushr27nt);
__asm____volatile__(pushr30nt);
__asm____volatile__(pushr31nt);
__asm____volatile__(int_ossched:nt);//当中断要求调度,直接进入这里
__asm____volatile__(seint);
//开中断,因为如果因中断在任务调度中进行,已经关中断
__asm____volatile__(pushr28nt);//r28与r29用于建立在堆栈上的指针
__asm____volatile__(pushr29nt);//入栈完成
tcb[ostaskrunningprio].ostaskstacktop=sp;//将正在运行的任务的堆栈底保存
unsignedcharosnexttaskprio;//在现有堆栈上开设新的空间
for(osnexttaskprio=0;//进行任务调度
osnexttaskprioosnexttaskprio++);
ostaskrunningprio=osnexttaskprio;
cli();//保护堆栈转换
sp=tcb[ostaskrunningprio].ostaskstacktop;
sei();
//根据中断时的出栈次序
__asm____volatile__(popr29nt);
__asm____volatile__(popr28nt);
__asm____volatile__(popr31nt);
__asm____volatile__(popr30nt);
__asm____volatile__(popr27nt);
__asm____volatile__(popr26nt);
__asm____volatile__(popr25nt);
__asm____volatile__(popr24nt);
__asm____volatile__(popr23nt);
__asm____volatile__(popr22nt);
__asm____volatile__(popr21nt);
__asm____volatile__(popr20nt);
__asm____volatile__(popr19nt);
__asm____volatile__(popr18nt);
__asm____volatile__(pop__tmp_reg__nt);//serg出栈并恢复
__asm____volatile__(out__sreg__,__tmp_reg__nt);//
__asm____volatile__(pop__tmp_reg__nt);//r0出栈
__asm____volatile__(pop__zero_reg__nt);//r1出栈
//中断时出栈完成
__asm____volatile__(clint);//关中断
__asm____volatile__(sbrcr16,1nt);//sbrc当寄存器位为0刚跳过下一条指令
//检查是在调度时,是否有中断要求任务调度0x02是中断要求调度的标志位
__asm____volatile__(rjmposschednt);//重新调度
__asm____volatile__(ldir16,0x00nt);
//清除中断要求任务切换的标志位,清除正在任务切换标志位
__asm____volatile__(retint);//返回并开中断
}
//从中断退出并进行调度
voidintswitch(void)
{
//当中断无嵌套,并且没有在切换任务的过程中,直接进行任务切换
if(oscorestate==0x02&&intnum==0)
{
//进入中断时,已经保存了sreg和r0,r1,r18~r27,r30,r31
__asm____volatile__(popr31nt);//去除因调用子程序而入栈的pc
__asm____volatile__(popr31nt);
__asm____volatile__(ldir16,0x01nt);
//清除中断要求任务切换的标志位,设置正在任务切换标志位
__asm____volatile__(rjmpint_osschednt);//重新调度
}
}
////////////////////////////////////////////任务处理
//挂起任务
voidostasksuspend(unsignedcharprio)
{
tcb[prio].oswaittick=0;
osrdytbl&=~(0x01<if(ostaskrunningprio==prio)//当要挂起的任务为当前任务
ossched();//从新调度
}
//恢复任务可以让被ostasksuspend或ostimedly暂停的任务恢复
voidostaskresume(unsignedcharprio)
{
osrdytbl|=0x01ossched();//从新调度//从新调度
}
//任务延时
voidostimedly(unsignedintticks)
{
if(ticks)//当延时有效
{
osrdytbl&=~(0x01<tcb[ostaskrunningprio].oswaittick=ticks;
ossched();//从新调度
}
}
//信号量
structsemblk
{
unsignedcharoseventtype;//型号0,信号量独占型;1信号量共享型
unsignedcharoseventstate;//状态0,不可用;1,可用
unsignedcharostaskpendtbl;//等待信号量的任务列表
}sem[10];
//初始化信号量
voidossemcreat(unsignedcharindex,unsignedchartype)
{
sem[index].oseventtype=type;//型号0,信号量独占型;1信号量共享型
sem[index].ostaskpendtbl=0;
sem[index].oseventstate=0;
}
//任务等待信号量,挂起
//当timeout==0xffff时,为无限延时
unsignedcharostasksempend(unsignedcharindex,unsignedinttimeout)
{
//unsignedchari=0;
if(sem[index].oseventstate)//信号量有效
{
if(sem[index].oseventtype==0)//如果为独占型
sem[index].oseventstate=0x00;//信号量被独占,不可用
}
else
{//加入信号的任务等待表
sem[index].ostaskpendtbl|=0x01<tcb[ostaskrunningprio].oswaittick=timeout;//如延时为0,刚无限等待
osrdytbl&=~(0x01<ossched();//从新调度
if(tcb[ostaskrunningprio].oswaittick==0)//超时,未能拿到资源
return0;
}
return1;
}
//发送一个信号量,可以从任务或中断发送
voidossempost(unsignedcharindex)
{
if(sem[index].oseventtype)//当要求的信号量是共享型
{
sem[index].oseventstate=0x01;//使信号量有效
osrdytbl|=sem[index].ostaskpendtbl;//使在等待该信号的所有任务就绪
sem[index].ostaskpendtbl=0;//清空所有等待该信号的等待任务
}
else//当要求的信号量为独占型
{
unsignedchari;
for(i=0;iif(i{
sem[index].ostaskpendtbl&=~(0x01<osrdytbl|=0x01<}
else
{
sem[index].oseventstate=1;//使信号量有效
}
}
}
//从任务发送一个信号量,并进行调度
voidostasksempost(unsignedcharindex)
{
ossempost(index);
ossched();
}
//清除一个信号量,只对共享型的有用。
//对于独占型的信号量,在任务占用后,就交得不可以用了。
voidossemclean(unsignedcharindex)
{
sem[index].oseventstate=0;//要求的信号量无效
}
voidtcn0init(void)//计时器0
{
tccr0=0;
tccr0|=(1<timsk|=(1<tcnt0=100;//置计数起始值
}
signal(sig_overflow0)
{
intnum++;//中断嵌套+1
sei();//在中断中,重开中断
unsignedchari;
for(i=0;i{
if(tcb[i].oswaittick&&tcb[i].oswaittick!=0xffff)
{
tcb[i].oswaittick--;
if(tcb[i].oswaittick==0)//当任务时钟到时,必须是由定时器减时的才行
{
osrdytbl|=(0x01<0)
{
pstr_uart_send=res;//发送字串的指针
nuart_sending=len;//发送字串的长度
ucsrb=0xb8;//发送中断使能
}
}
//signal在中断期间,其它中断禁止
signal(sig_uart_data)//串口发送数据中断
{
intnum++;//中断嵌套+1,不充许中断
if(nuart_sending)//如果未发完
{
udr=*pstr_uart_send;//发送字节
pstr_uart_send++;//发送字串的指针加1
nuart_sending--;//等待发送的字串数减1
}
if(nuart_sending==0)//当已经发送完
{
ossempost(0);
oscorestate|=0x02;//要求任务切换的标志位
ucsrb=0x98;
}
cli();//关发送中断
intnum--;
intswitch();//进行任务调度
}
voiduartinit()//初始化串口
{
#definefosc8000000//晶振8mhzubrrl=(fosc/16/(baud+1))%256;
#definebaud9600//波特率
osccal=0x97;//串口波特率校正值,从编程器中读出
//ucsrb=(1<ucsrb=0x98;
//ucsrb=0x08;
ubrrl=(fosc/16/(baud+1))%256;
ubrrh=(fosc/16/(baud+1))/256;
ucsrc=(1<ucsrb=0xb8;
udr=0;
}
//打印unsignedint到字符串中00000
voidstrput_uint(unsignedchar*des,unsignedinti)
{
unsignedcharj;
des=des+4;
for(j=0;j<5;j++)
{
*des=i%10+’0’;
i=i/10;
des--;
}
}
voidstrput_star(unsignedchar*des,unsignedchari)
{
unsignedcharj;
for(j=0;j{
*des++=’*’;
}
*des++=13;
}
unsignedintstrput_taskstate(unsignedchar*des,
unsignedchartaskid,
unsignedcharnum)
{
//unsignedinti=0;
*(des+4)=’0’+taskid;
strput_uint(des+6,num);
strput_star(des+12,taskid);
return12+taskid+1;
}
voidtask0()
{
unsignedintj=0;
while(1)
{
portb=j++;
if(ostasksempend(0,0xffff))
{
unsignedintm;
m=strput_taskstate(stra,ostaskrunningprio,j);
uart_send(stra,m);
}
ostimedly(200);
}
}
voidtask1()
{
unsignedintj=0;
while(1)
{
portc=j++;
if(ostasksempend(0,0xffff))
{
unsignedintm;
m=strput_taskstate(stra,ostaskrunningprio,j);
uart_send(stra,m);
}
ostimedly(100);
}
}
voidtask2()
{
unsignedintj=0;
while(1)
{
if(ostasksempend(0,0xffff))
{
unsignedintm;
m=strput_taskstate(stra,ostaskrunningprio,j);
uart_send(stra,m);
}
portd=j++;
ostimedly(50);
}
}
voidtaskscheduler()
{
ossched();
while(1)
{
}
}
intmain(void)
{
strlcpy_p(stra,prostra,20);
uartinit();
tcn0init();
osrdytbl=0;
intnum=0;
ostaskcreate(task0,&stack[99],0);
ostaskcreate(task1,&stack[199],1);
ostaskcreate(task2,&stack[299],2);
ostaskcreate(taskscheduler,&stack[399],os_tasks);
osstarttask();
}
结束语
本文中的例子,基本上用winavr和proteus调试仿真成功,一定可能存在某些方面的缺陷,因为工作上时间的压力,就没有进一步查找。
拆解2018年自动驾驶汽车的成与败
废水氧化处理设备PLC数据采集远端监控系统解决方案
三菱PLC不同指示灯的故障分析
关于ATX-100S高速导通线束测试仪的简单介绍
虹科荣获国家级专精特新“小巨人”称号
建立一个属于自己的AVR的RTOS
固高科技推出 GTSD13系列网络型智能伺服驱动器
电位差的计算案例
RT-Thread开源作品运动手表展示
如何在定义后启动一个sequence
建发集团 | 爱数助力世界500强企业奏响数据管理创新乐章
联发科技推出全球最完整3D智能机解决方案—酷3D平台
New Flyer推美国首辆Level 4自动驾驶关键词
康宁扩大美国肯塔基州制造设施 支持Gorilla(R)玻璃增
“HEED-HARP”技术或用于内窥镜摄像头,大幅提高性能
追频固态特斯拉线圈详细制作图解
电冰箱高低压保护电路_电冰箱保护器控制电路
怎么区分锰锌磁环电感和镍锌磁环电感
探讨华为战略布局AI芯片的背后
如何做好水库大坝实时安全监测