51系列小型操作系统精髓 简单实现
源代码地址(专业定制程序:MCU。Windows,Android ,VC串口,Android蓝牙等不限。
1.切换任务并记录位置。保证在时间到后能切换回来。(在任务中切换出去。在定时器中切换回来。)(时间片轮转)
;******************************************** ;RTOS 用定时器0的多任务处理程序 ;项目添加方式使用。;程序不影响其他数据,眼下程序的前后段不能进行參数传递 。
最多能够同一时候运行8个任务, ;运行时使用定时器0。定时时间依据初始化时的參数确定,一般为10mS。 ;当指定优先级被占用时自己主动向高优先级调整,在R7中返回实际优先级。当全部高优先级都被占用时返回 0FFH,任务将被忽略。 ;占用24H个内存单元。 ;假设程序不可再入,须要设定 RWZT 的标志位。
调用前先检查对应位。 ;假设定时数设置过大,会引起一直在中断中。不能运行外部程序。假设任务运行时间过长。会引起堆栈溢出。
;18.432MHz,单时钟周期模式。不用任务单取反,输出频率 1.3MHz 。指令周期 384 nS 。速度是带系统的 100 倍。 ;8个任务,仅仅对管脚取反,T0=FEF0H,输出 2.8K 频率。为最高频率。运行周期不受任务影响。
18.432MHz。单时钟周期模式。 ;1个任务,仅仅对管脚取反,T0=FFE1H,输出 12K 频率,为最高频率。运行周期受任务影响。
每次调度时间为42US。
;********************************************* ;------------------------------------------------------------- 演示样例程序 ;*********************************************** NAME RTOS_C ;程序段名 ?PR?
RTOS?
RTOS_C SEGMENT CODE ;代码段 ; 段名 SEGMENT 段类型 ?DT?RTOS?
RTOS_C SEGMENT DATA ?ID?
RTOS?RTOS_C SEGMENT IDATA PUBLIC RTOS PUBLIC _RTOS_WAIT PUBLIC _RTOS_INIT ;--------------------------------------------- ;BANBH EQU 0A3H ;版本 101221 ;定义数据地址 ;---------------------------------------------- RSEG ?DT?
RTOS?RTOS_C ;RWZT EQU 29H ;任务状态。0为停止。1为正在运行 TL: DS 1 ;EQU 30H ;RTOS定时器值,C=FFFFH-ft TH: DS 1 ;EQU 31H ;18.432MHz(12周期)=C400H RWB: DS 1 ;EQU 33H ;任务表 RSEG ?ID?
RTOS?
RTOS_C RWDZ: DS 16 ;任务堆栈 ; EQU 80H ;任务地址 ; EQU 8FH RWS: DS 8 ;任务数 ; EQU 90H ;任务定时数 ; EQU 97H ;********************************************* CSEG AT 0BH ;TO入口 JMP RTOS ;********************************************* RSEG ?
PR?RTOS?RTOS_C _RTOS_INIT: ;任务初始化 ;-------------------------------------------- 在 R6 R7 中,设置定时值 ;RTOS定时器值,C=FFFFH-ft,18.432MHz(12周期)=C400H,40MHz(12周期)=7DC9H ;定时器0定时程序,18.432MHz,10MS,方式1 ORL TMOD,#01H ;定时10MS ;定时器0初始化,设置TMOD MOV TL0,R7 MOV TL,R7 ;设置TL MOV TH0,R6 MOV TH,R6 ;设置TH MOV RWB,#0 ;任务表清0 SETB TR0 ;启动定时器 SETB ET0 ;打开中断 SETB EA ;打开总中断 RET ;等待中断 ;********************************************** 等待函数,优先级为最低 0 级。等待时间放入 R7 中 ; 眼下函数不能传递參数 _RTOS_WAIT: MOV A,R7 ;接收參数 MOV R5,A ;接收的数存在R5 MOV R7,#0 ;R7清零 CALL YXJSZ ;优先级设置 CJNE R7,#8,RWSZ ;R7假设不等 于8,则任务设置 MOV R7,#0FFH ;返回值0xff 当指定优先级被占用时自己主动向高优先级调整,在R7中返回实际优先级,当全部高优先级都被占用时返回 0FFH,任务将被忽略。 RET ;返回 ;--------------------------------------------- RWSZ: MOV A,R7 ;送任务数 ADD A,#RWS ; MOV R0,A ;任务数保存到R0 MOV A,R5 ;R5上是什么 定时时间 MOV @R0,A ;保存定时时间数到【R7+RWS】位置 MOV A,R7 ;送 RL A ; ADD A,#RWDZ MOV R0,A POP ACC ;跳转地址,在堆栈中 POP B MOV @R0,B ;低地址在低字节 INC R0 MOV @R0,A MOV B,R7 ;设置任务列表 CLR A ; 当前正在运行的程序假设被中断。而且中断中使用了此任务位,则此次任务将会丢失。 INC B ;能够关中断来解决此问题。 SETB C RWSZ2: RLC A DJNZ B,RWSZ2 ORL RWB,A RET ;-------------------------- YXJSZ: MOV B,R7 ;0 ;优先级设置 INC B ;B++ 1 MOV A,RWB ;0 CLR C ;0 YXJ2: RRC A ;RRC是带进位的循环右移指令 DJNZ B,YXJ2 ;假设B不等于0, b=0 YXJ4: JNC YXJ3 ;C=0 ,则 RRC A ; INC R7 ; JMP YXJ4 ; YXJ3: RET ;返回 ;------------------------------- 中断程序 把全部相关寄存器内容压入堆栈 任务调度完后 再弹出来 RTOS: MOV TH0,TH ;重载定时数 MOV TL0,TL ; JB PSW.3,RTOS1 ;RS0==1 用于选择当前工作寄存器区。
8051有8个8位寄存器R0~R7 JB PSW.4,RTOS2 ;RS1==1 PUSH 0 ;RS0=0,RS1=0; 第一组寄存器区 PUSH 1 PUSH 2 PUSH 3 PUSH 4 PUSH 5 PUSH 6 PUSH 7 ;存入椎栈第一组寄存器上的内容 JMP RTOS0 ;都跳到RTOS0 RTOS1: JB PSW.4,RTOS3 ; PUSH 8 ;RS0=1。RS1=0; 第三组寄存器区 PUSH 9 PUSH 10 PUSH 11 PUSH 12 PUSH 13 PUSH 14 PUSH 15 JMP RTOS0 ; RTOS2: PUSH 10H ;RS0=0,RS1=1; 第二组寄存器区 PUSH 11H PUSH 12H PUSH 13H PUSH 14H PUSH 15H PUSH 16H PUSH 17H JMP RTOS0 RTOS3: PUSH 18H ;RS0=1,RS1=1; 第四组寄存器区 PUSH 19H PUSH 1AH PUSH 1BH PUSH 1CH PUSH 1DH PUSH 1EH PUSH 1FH RTOS0: PUSH PSW ;入栈 PUSH ACC ; PUSH B PUSH DPL PUSH DPH CALL RWDD0 ;任务调度 POP DPH ;出栈 POP DPL POP B POP ACC POP PSW JB PSW.3,RTOS01 JB PSW.4,RTOS02 POP 7 POP 6 POP 5 POP 4 POP 3 POP 2 POP 1 POP 0 ;弹出第一组寄存器区上的变量 RETI ;中断返回 RTOS01: JB PSW.4,RTOS3 POP 15 POP 14 POP 13 POP 12 POP 11 POP 10 POP 9 POP 8 RETI RTOS02: POP 17H POP 16H POP 15H POP 14H POP 13H POP 12H POP 11H POP 10H RETI RTOS03: POP 1FH POP 1EH POP 1DH POP 1CH POP 1BH POP 1AH POP 19H POP 18H RETI ;----------------------------- 中断任务调度 RWDD0: MOV B,#7 ;任务号 MOV R0,#RWS+7 ; MOV R1,#0FFH ;运行任务号 MOV A,RWB JNZ RWDD3 RET RWDD3: CLR C RLC A JNC RWDD6 CJNE @R0,#0,RWDD2 RWDD4: CJNE R1,#0FFH,RWDD6 MOV R1,B RWDD6: DEC R0 DEC B JNZ RWDD3 CJNE R1,#0FFH,RWCL RET RWDD2: DEC @R0 CJNE @R0,#0,RWDD6 JMP RWDD4 ;--------------------------------- RWDD: MOV R1,#7 MOV R0,#RWS+7 MOV A,RWB JNZ RWDD03 RET RWDD03: CLR C RLC A JNC RWDD06 CJNE @R0,#0,RWDD06 JMP RWCL RWDD06: DEC R0 DEC R1 JNZ RWDD03 RET ;-------------------------------------- RWCL: MOV DPTR,#RWDD ;任务处理? PUSH DPL PUSH DPH MOV A,R1 RL A ADD A,#RWDZ MOV R0,A MOV A,@R0 PUSH ACC ;低地址在低字节 INC R0 MOV ACC,@R0 PUSH ACC MOV A,#0FFH ;清除任务列表 当前正在运行的程序假设在下一个中断到来时还未运行完, 它的优先级将变为最低优先级, INC R1 ;能够添加一个运行标志 RWZT 来解决此问题。
CLR C RWCL22: RLC A DJNZ R1,RWCL22 ANL RWB,A RETI ;*********************************** END
C语言版切换演示样例(网摘)
#include <REG52.H> #define MAX_TASKS 2 //任务槽个数.必须和实际任务数一至 #define MAX_TASK_DEP 12 //最大栈深.最低不得少于2 个,保守值为12. unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];//任务堆栈. unsigned char idata task_sp[MAX_TASKS]; unsigned char task_id; //当前活动任务号 //任务切换函数(任务调度器) void task_switch(){ task_sp[task_id] = SP; if(++task_id == MAX_TASKS) task_id = 0; SP = task_sp[task_id]; } //任务装入函数.将指定的函数(參数1)装入指定(參数2)的任务槽中.假设该槽中原来就有任 //务,则原任务丢失,但系统本身不会错误发生. void task_load(unsigned int fn, unsigned char tid){ task_sp[tid] = task_stack[tid] + 1; task_stack[tid][0] = (unsigned int)fn & 0xff; //低字节 task_stack[tid][1] = (unsigned int)fn >> 8; //高字节 } //从指定的任务開始运行任务调度.调用该宏后,将永不返回. #define os_start(tid) {task_id = tid,SP = task_sp[tid];return;} /*============================下面为測试代码==========================*/ void task1(){ static unsigned char i; while(1){ i++; task_switch();//编译后在这里打上断点 } } void task2(){ static unsigned char j; while(1){ j+=2; task_switch();//编译后在这里打上断点 } } void main(){ //这里装载了两个任务,因此在定义MAX_TASKS 时也必须定义为2 task_load(task1, 0);//将task1 函数装入0 号槽 task_load(task2, 1);//将task2 函数装入1 号槽 os_start(0); }