51上基于ucosII思想RTOS设计

首先我们应该知道的一些东西:

MSC-51中堆栈增长方向为低地址至高地址,即向上增长型,而ARM中堆栈默认是高地址至低地址,即向下增长型(也可以设置为向上增长型),在给任务块初始化栈顶指针和模拟入栈操作时应该注意这点。
由于51中没有软中断,所以自动入栈程序断点值就需要通过普通子程序来实现。(stm32中是利用PendSV软中断进行任务切换的,具体google吧)

  1. 思想:
    在看了ucosII的大概原理后,开始着手于在51上移植一个RTOS,但是这里的移植只是基于ucos的思想,所有代码均为重新设计编写。
    每个任务都是一个死循环,当需要从task1切换至task2时,先将当前任务即task1的程序断点(利用跳转到子函数会自动保存PC值的原理)和CPU使用到sfr(Acc,B,PSW,DPTR,R0-R7)手动入栈,并且将此时栈顶地址保存到任务块中去,接着从task2任务块中取出栈顶指针赋予SP,再出栈sfr(Acc,B,PSW,DPTR,R0-R7)和最后的程序断点,程序便会从task2处继续执行,至此,任务切换完成。

补充下:普通子程序和中断子程序的区别:
调用普通子程序钱系统会自动入栈程序断点,但是不包含其他sfr,这就需要我们手动去保存;而中断子程序则不同,进入中断前当然也会自动保存程序断点,但是其他sfr是要根据你的中断程序使用第几组寄存器,即是否使用当前程序使用的寄存器来决定要不要自动入栈这些sfr。
为了代码统一,我在中断程序处没有指定使用哪组寄存器,即默认使用同一组sfr,中断程序会自动入栈所有sfr。此外,我们可以在中断程序的汇编代码中观察到定时器对sfr的入栈、出栈顺序,并且我们在手动入栈、出栈时要跟它保持一致>

  1. 变量:
    a.系统中每个任务均对应一个反应其身份的唯一标示——任务优先级task_prio,对任务的所有操作均是以此为索引;
    b.每个任务拥有一个任务控制块,里面存放了任务的堆栈栈顶地址和任务的延时节拍数以及其他一些任务信息,并整合成数组TaskTCBTbl[8],通过task_prio可方便得到任务控制块内容;
    c.任务就绪表变量readytask__prio(8个任务);
    d.当前就绪任务优先级变量ready_prio;
    e.当前运行任务优先级变量taskprio_current;
    f.当前任务个数变量task_count;
    在这里插入图片描述
  2. 关于keil中混合编程
    在编写C代码的时候,遇到入栈、出栈操作,很多时候要用汇编语言编写,这就需要涉及到混合编程方法,首先要设置需要混合编程的c文件,如图,汇编代码则以以下形式插入:

开始 #pragma asm
汇编代码 POP R7 ;入栈R7
结束 #pragma endasm

注:51头文件中没有用sfr语句定义R0-R7寄存器,所以在入栈R0-R7时用地址00H-07H代替

  1. 任务调度机制:
    4.1. 调度时机:创建一个任务时可以通过形参Sched_Now来决定是否要立刻进行调度;当系统节拍程序检测到有任务延时结束时会进行任务调度;(因为在初始版本中只是实现了任务通过延时让出使用权来模拟任务互相切换,还没有涉及到修改任务优先级,挂起任务等辅助功能,后期会加入)
    4.2 调度算法:

    for( i=0;i<8;i++){
    if( (taskprio_readylist)&(0x01<<i)){
    ready_prio=i;
    break;
    }
    }

    从就绪表最低位(与ucos一样,prio越小优先级越高)开始判断是否有就绪任务,一旦发现便退出循环。
    4.3. 临界:
    在切换任务时涉及到入栈、出栈操作,为了保证系统稳定,切换前关闭总中断(EA=0),切换完成后再开启总中断(EA=1),这里用两个函数代替,大家都很熟悉。
    在这里插入图片描述

  2. 代码(由于keil中编码问题,直接复制代码的话注释会出现乱码,所以还是截图吧)
    A. 这是任务创建函数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 系统节拍时钟:
    我这里用的是定时器0(本来想用8位自动重装模式提高时间精度的,但是延时值太小,要在中断中累计中断次数来增加时间也不明智,同时发生中断频率过高会影响系统性能)作为系统节拍,节拍周期通过宏定义可以方便更改;在每次系统节拍服务程序里要遍历所有任务块中是否有延时,若有则减1,并判断延时是否结束再进行任务调度。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  4. 系统延时函数:
    首先要将延时节拍数写入任务快中,紧接着在就绪表中注销该任务,若此时就绪表中有其他任务则进行调度,否则就在原地等待定时器调度。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

最后来说说我遇到的各种问题:

  1. 以前没用过混合编程,汇编代码写好了编译时出现问题,按照我上面那个图打上两个钩就好了,还有就是R0-R7用00H-01H代替(气得当时真是想把电脑砸了···)。
  2. 调用任务调度函数可以正常切换,但是在时钟节拍中切换任务后会程序跑飞,后来发现是因为若就绪表中最后一个运行任务进入延时状态后使得就绪表为空,系统找不到要运行的任务,上下文切换必定出错<解决方法:若判断就绪表为空时,先复制当前任务prio,然后开启总中断,利用while( TaskTCBTbl[Delay_TaskPrio].Task_DelayTicks);语句等待定时器调度,为什么这么写呢!因为任务调用延时函数时已将任务断点入栈,而在开启总中断后,中断发生在延时函数中,于是延时函数断点及其他sfr又被自动入栈,并且此时的SP被保存到当前任务的任务块中,当该任务重新被调度时,程序先是返回到延时函数中,之前用while(1);语句,发现该任务延时完了没有继续执行,很是纳闷,后来才发现问题,改用了上面的语句,在退出延时函数后再出栈任务断点至PC,这时才真正返回到了原来的任务中去!
    其他的好像也没什么了,主要就是任务切换那边入栈,出栈顺序什么的注意点,看看普通子程序和中断子程序区别。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于51单片机ucos实时操作系统 #include "includes.h" #include "serial.h" sbit LED1=P1^5; sbit LED2=P1^6; unsigned char xdata strbuf[8]; OS_STK TaskStartStk1[MaxStkSize],TaskStartStk2[MaxStkSize],TaskStartStk3[MaxStkSize]; void Task1(void *nouse) reentrant; void Task2(void *nouse) reentrant; void Task3(void *nouse) reentrant; void DecTochar(unsigned int n,unsigned char *buf) { unsigned char i; unsigned char buffer[8]; for(i=0;i0;i--)*buf++=buffer[i]; *buf++=buffer[i]; *buf='\r'; buf++; *buf='\n'; } void main(void) { OSInit(); InitHardware(); OSTaskCreate(Task1, (void *)0, &TaskStartStk1[0],2); OSTaskCreate(Task2, (void *)0, &TaskStartStk2[0],3); OSTaskCreate(Task3, (void *)0, &TaskStartStk3[0],4); OSStart(); } void Task1(void *nouse) reentrant { unsigned char const Str0[]="Welcome to MCU123.COM \r\n"; unsigned char const Str1[]="Task1 is running! LED1=ON \r\n"; unsigned char const Strv[]="uCosII_Ver"; nouse=nouse; SendStr(Str0, sizeof(Str0)); DecTochar(OSVersion(),strbuf); SendStr(Strv,sizeof(Strv)); SendStr(strbuf, sizeof(strbuf)); for(;;) { LED1 = 0; SendStr(Str1, sizeof(Str1)); OSTimeDly(OS_TICKS_PER_SEC*2); } } void Task2(void *nouse) reentrant { unsigned char const Str2[]="Task2 is running! LED2=ON \r\n"; nouse=nouse; for(;;) { LED2 = 0; SendStr(Str2, sizeof(Str2)); OSTimeDly(OS_TICKS_PER_SEC*2); } } void Task3(void *nouse) reentrant { unsigned char const Str3[]="Task3 is running! LED1=OFF LED2=OFF \r\n"; nouse=nouse; for(;;) { LED1 = 1; LED2 = 1; SendStr(Str3, sizeof(Str3)); OSTimeDly(OS_TICKS_PER_SEC); } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值