📄 os.asm
字号:
;工程名:51嵌入式系统
;功能:同时运行4个进程,时间片轮转算法
;开发者: 龙继培
;版本号: 1.2
;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TimerH EQU 0D8H ;设定溢出速率为10mS,12M->D8F0H 6M->EC78H
TimerL EQU 0F0H
STACK0 EQU 40H-1 ;定义任务堆栈,每个任务占用16字节
STACK1 EQU 50H-1 ;如果没有任务占用,可以用于它用
STACK2 EQU 60H-1
STACK3 EQU 70H-1
SP0 EQU 3CH ;定义任务堆栈指针,保存各个任务的SP值。
SP1 EQU 3DH
SP2 EQU 3EH
SP3 EQU 3FH
TaskPoint EQU 3BH ;任务指针,指向当前运行的任务
TaskList EQU 3AH ;任务映像表,暂时使用低4位
SleepList EQU 39H ;挂起任务映像表
time_data EQU 35H ;定义一个指向时钟计数器的指针,时钟计数器为4字节
;其实地址为35H,开机到现在的时间=time_data x 0.01s
ERR EQU 0FFH ;定义错误
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;中断向量区
ORG 0000H
LJMP main
ORG 0003H ;外部中断0
ORG 000BH ;定时器/计数器0
ORG 0013H ; 外部中断1
ORG 001BH ; 定时器/计数器1 ---------系统占用
LJMP IRQTimer1
ORG 0023H ; 串行中断
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;系统主函数
Main:
CALL OSInit ;系统初始化
MOV DPTR,#Task0 ;创建任务
MOV R1,#00H
CALL OSTaskCreat
MOV DPTR,#Task1
MOV R1,#01H
CALL OSTaskCreat
MOV DPTR,#Task2
MOV R1,#02H
CALL OSTaskCreat
MOV DPTR,#Task3
MOV R1,#03H
CALL OSTaskCreat
CALL OSStart ;开始多任务
JMP $
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;用户任务区,最多支持4个任务
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Task0:
;添加用户代码
MOV R1,#01H
CALL OSTaskSuspend
MOV R1,#01H
CALL OSTaskResume
MOV R1,#00H
CALL OSTaskSuspend
LJMP Task0
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Task1:
LJMP Task1
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Task2:
LJMP Task2
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Task3:
LJMP Task3
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;系统函数区,供调用
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSInit:
;系统初始化函数
MOV TMOD, #10H ;初识化定时器1,为定时工作方式1
MOV TH1 , #TimerH ;溢出速率为10mS,即时间片的值为10mS
MOV TL1 , #TimerL
MOV R0,#time_data ;清时间计数器
MOV R1,#04H
CLR A
Timer_Clear:
MOV @R0,A
INC R0
DJNZ R1,Timer_Clear
MOV TaskList,#00H
MOV TaskPoint,#03H ;为了能一开始就调度第一个任务,初始化值指向任务3
MOV SP0,#STACK0 ;初识话各个人物指针
MOV SP1,#STACK1
MOV SP2,#STACK2
MOV SP3,#STACK3
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSTaskCreat:
;函数功能:创建任务
;输入参数:DPTR-----任务入口指针,R1------任务号(0-3)
;输出: 出错,R7 = ERR,未出错,R1 = 2 ^ R1
CLR EA ;关中断
MOV R2,DPL
MOV R3,DPH
MOV A,R1
ANL A,#03H ;除4取余
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
MOV R7,A ;保存屏蔽字
ANL A,TaskList
JNZ OSTaskCreatErr ;任务级已经被占用
MOV A,R7
ORL TaskList,A ;将任务写入任务列表
MOV A,R1 ;初始化任务堆栈
ADD A,#SP0
MOV R0,A
MOV B,SP ;保存当前SP值
MOV SP,@R0 ;取堆栈地址
MOV A,R2
PUSH A ;写入DPL
MOV A,R3
PUSH A ;写入DPH
MOV DPTR,#PSWMark ;写入PSW
MOV A,R1
MOVC A,@A+DPTR
PUSH A
CLR A ;写入ACC
PUSH A
PUSH A ;写入B
PUSH P0
PUSH P1
PUSH P2
MOV A,R1 ;写入SP(n),保存SP值
ADD A,#SP0
MOV R0,A
MOV @R0,SP
MOV SP,B ;恢复SP值
SETB EA ;开中断
RET
OSTaskCreatErr: ;创建任务失败
MOV R1,#ERR
SETB EA ;开中断
RET
PSWMark:
DB 00H ;PSW0
DB 04H ;PSW1
DB 10H ;PSW2
DB 14H ;PSW3
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSTaskDel:
;函数功能:删除任务
;输入参数: R1------删除任务号
PUSH A ;如果R1>3,则只取低两位的值
MOV A,R0 ;保存R0
PUSH A
MOV A,R1
ANL A,#03H
MOV R1,A
CLR EA ;关中断
;MOV A,R1
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
CPL A
ANL TaskList,A ;删除任务
SETB EA ;开中断
MOV A,#SP0 ;堆栈指针复位
ADD A,R1
MOV R0,A
MOV A,R1
MOV DPTR,#STACKTABLE
MOVC A,@A+DPTR
MOV @R0,A
POP A ;恢复R0
MOV R0,A
MOV A,TaskPoint ;判断删除任务是否是当前任务,若是,则进行任务调度
XRL A,R1
JZ DelSelf
DelOtherTask: ;删除其他正在等待运行的任务
POP A
RET
DelSelf: ;删除当前正在运行的任务
POP A
CALL OSTaskSchedule
STACKTABLE:
DB 40H-1 ;定义任务堆栈,每个任务占用16字节
DB 50H-1 ;如果没有任务占用,可以用于它用
DB 60H-1
DB 70H-1
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSTaskSuspend:
;函数功能:挂起一个任务,将当前进程的挂起位置1
;入口参数:R1------要挂起任务的任务号
PUSH A ;如果R1>3,则只取低两位的值
MOV A,R1
ANL A,#03H
MOV R1,A
CLR EA ;关中断
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
CPL A
ANL TaskList,A ;删除任务
SETB EA ;开中断
MOV A,R1 ;将挂起任务写入挂起任务列表
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
ORL SleepList,A
MOV A,TaskPoint ;判断休眠任务是否是当前任务,若是,则进行任务调度
XRL A,R1
JZ SuspendSelf
SuspendOther: ;休眠其他正在等待运行的任务
POP A
RET
SuspendSelf: ;休眠当前正在运行的任务
POP A
CALL OSTaskSchedule
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSTaskResume:
;函数功能:恢复一个任务,将相应进程的挂起位置0
;入口参数:R1-----要恢复任务的ID
;出口参数:出错返回R1 = ERR
PUSH A
MOV A,R1
ANL A,#03H
MOV R1,A
MOV A,TaskPoint
XRL A,R1
JZ ResumeSelf
ResumeOther:
MOV A,R1
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
ORL TaskList,A ;将任务写入运行任务列表
CPL A
ANL SleepList,A ;将任务从休眠列表中删除
POP A
RET
ResumeSelf:
POP A
MOV R1,#ERR
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSStart:
;开始多任务
SETB TR1 ;开始时钟计数
SETB ET1 ;允许定时器1中断
;SETB EA ;开总中断
MOV A,TaskList ;检查有没有任务
JNZ OSTaskSchedule ;有任务则进行任务调度
RET ;没有任务则出错放回
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;中断函数区
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IRQTimer1:
CLR EA ;关中断
CLR TR1 ;计数停止
MOV TH1 , #TimerH ;置计数器初值
MOV TL1 , #TimerL
SETB TR1 ;计数开始,在中断后立即执行,确保计时的准确性
PUSH PSW ;任务保护,在复位计时器后立即进行
PUSH ACC
PUSH B
PUSH P0
PUSH P1
PUSH P2
MOV B,R0 ;保护R0的值,不使用堆栈
MOV A,TaskPoint ;保存当前任务的SP值
ADD A,#SP0
MOV R0,A
MOV @R0,SP
;每10mS加1,在中断中执行
MOV R0,#time_data
loop: CLR A
SETB C ;进位位置1
ADDC A,@R0
MOV @R0,A ;回写
JNC OSTaskSchedule ;没有进位,则其高位不用改变
INC R0
CJNE R0,#time_data+04H,loop ;检查是否溢出
;有进位,R0指向上高位,进行加1运算
OSTaskSchedule:
INC TaskPoint ;任务选择
MOV A,TaskPoint
ANL A,#03H ;除4取余
MOV TaskPoint,A
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
ANL A,TaskList
JNZ TaskChange
INC TaskPoint ;任务选择
MOV A,TaskPoint
ANL A,#03H ;除4取余
MOV TaskPoint,A
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
ANL A,TaskList
JNZ TaskChange
INC TaskPoint ;任务选择
MOV A,TaskPoint
ANL A,#03H ;除4取余
MOV TaskPoint,A
MOV DPTR,#TaskMake
MOVC A,@A+DPTR ;取屏蔽字
ANL A,TaskList
JNZ TaskChange
TaskNoChange: ;没有任务切换
MOV A,SP
CLR C
SUBB A,#06H
SETB EA ;开中断
RETI
TaskChange: ;任务切换
MOV A,TaskPoint ;任务的SP值切换
ADD A,#SP0
MOV R0,A
MOV A,@R0
MOV R0,B ;恢复R0值
MOV SP,A
POP P2
POP P1
POP P0
POP B
POP ACC
POP PSW
SETB EA ;开中断
RETI
TaskMake:
DB 00000001B
DB 00000010B
DB 00000100B
DB 00001000B
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -