51多任务实时操作系统—汇编:
哈哈!今天发现用单片机处理多任务事件(多任务同时进行)的方法了!用单片机定时器中断进行任务的调度,定时的时间就是每项任务的一次可执行时间,每中断一次就切换一个任务,调度方法是定时器中断时保存上一任务的断点(出栈保存),跳到下一个任务(地址入栈,中断返回)。每个任务都必须是一个死循环,因为它是独立的!当然单片机不可能真正做到多任务同时进行,这个方法只是把各个任务拼接起来每个任务分配一定的时间!因为单片机的速度快,所以在一定意义上说这些任务是同时进行的!单片机的速度越快越好!这个方法的缺陷是软件延时很难计算,误差大.
一个CPU是不可能同时处理多条指令的,只能是一条一条的顺序执行。如果我们需要同时执行多个任务,但又不能等一个任务执行完了再执行下一个任务,有的任务还需要等待一个信号触发才能往下执行,信号迟迟未来那就什么也做不了了!!操作系统的出现,就是为了解决这一问题的。它的功能就是能让CPU“同时”处理多个任务,当然不是真正的同时,它只是把这些任务的分成不同的时间段来处理,每个任务只执行很短的的时间(只执行一部分指令),然后保存断点就切换到下一个任务的指令去执行,同样只执行很短的的时间(只执行一部分指令),再保存断点切换任务。等所有的任务都执行一次后再回来从断点处继续执行,时间到,再保存断点切换任务,如此反复。因为CPU的频率高,所以完成一次循环的时间极短,51单片机的频率一般40MHz以内,所需时间是ms、us级的,电脑的主频是GHz级别的,那更不用说,这样短的时间对于我们来说就是同时的。
学过C51的大都知道RTX51,它是单片机实时多任务系统,是kill51自带的,但是还有很多汇编爱好者不能使用它,因为它是C语言的。为此本人设计了两个个简单的汇编语言版的多任务实时系统,专为汇编爱好者提供一个参考。高手勿喷!
本程序使用定时器中断的方法产生时间轮片,每个任务分配在一个时间轮片上。如果一个时间轮片(定时器的时间)是250us,那每个任务执行一次的时间就是250us(因为任务切换的时间极短,所以里省略了);有4个任务要执行,那么系统完成一次循环的时间是250*4=1000us=1ms。即先执行任务一250us,然后保存任务一的断点,切换到任务二执行250us,再保存任务二的断点,切换到任务三执行250us,接着保存任务三的断点,切换到任务四执行250us,保存任务四的断点,再次切到任务一断点处继续往下执行250us,再保存再切换到下一个任务的断点处执行,照此循环。所以这些任务并不是同1us内完成的,而是同1ms内完成的,当然更是同时同分同秒完成的。
程序只是个演示程序,功能的开发看个人要求而定!
轻量四任务实时系统
ORG 0000H
RW1_SP DATA 20H;任务堆栈指针位置寄存器
RW2_SP DATA 21H
RW3_SP DATA 22H
RW4_SP DATA 23H
ORG 0000H
AJMP MAIN
ORG 000BH
LJMP RWQHCX
ORG0076H
;================任务切换程序===========================================
RWQHCX:
JNB RS0,LOOP3
JNB RS1,LOOP4
;==============任务4现场保护
PUSH ACC ;需注意与任务4现场还原对应
PUSH B
PUSH PSW
MOV RW4_SP,SP ;保存任务4的堆栈指针位置
;==============任务1现场还原
MOV SP,RW1_SP ;取任务1的堆栈指针位置,任务1使用寄存器0组
POP PSW ;需注意与任务1现场保护对应
POP B
POP ACC
RETI
LOOP2:
;==============任务1现场保护
PUSH ACC ;需注意与任务1现场还原对应
PUSH B
PUSH PSW
MOV RW1_SP,SP ;保存任务1的堆栈指针位置
;==============任务2现场还原
MOV SP,RW2_SP ;取任务2的堆栈指针位置,任务2使用寄存器1组
POP PSW ;需注意与任务2现场保护对应
POP B
POP ACC
RETI
LOOP3:
JNB RS1,LOOP2
;==============任务2现场保护
PUSH ACC ;需注意与任务2现场还原对应
PUSH B
PUSH PSW
MOV RW2_SP,SP ;保存任务2的堆栈指针位置
;==============任务3现场还原
MOV SP,RW3_SP ;取任务3的堆栈指针位置,任务3使用寄存器2组
POP PSW ;需注意与任务3现场保护对应
POP B
POP ACC
RETI
LOOP4:
;==============任务3现场保护
PUSH ACC ;需注意与任务3现场还原对应
PUSH B
PUSH PSW
MOV RW3_SP,SP ;保存任务4的堆栈指针位置
;==============任务4现场还原
MOV SP,RW4_SP ;取任务4的堆栈指针位置,任务4使用寄存器3组
POP PSW ;需注意与任务4现场保护对应
POP B
POP ACC
RETI
;========任务一程序===============================
RW1_KS: ;任务一入口(初始化)
CLR RS0
CLR RS1
;你的初始化程序
;========任务一程序===============================
RW1_MAIN: ;任务一主程序
;你的程序
LJMP RW1_MAIN
============任务一END===================================
;========任务二程序===============================
RW2_KS: ;任务二入口(初始化)
CLR RS0
SETB RS1
;你的初始化程序
RW2_MAIN: ;任务二主程序
;你的程序
LJMP RW2_MAIN
============任务二END===================================
;========任务三程序===============================
RW3_KS: ;任务三入口(初始化)
SETB RS0
CLR RS1
;你的初始化程序
RW3_MAIN: ;任务三主程序
;你的程序
LJMP RW3_MAIN
============任务三END===================================
;========任务四程序===============================
RW4_KS: ;任务四入口(初始化)
SETB RS0
SETB RS1
;你的初始化程序
RW4_MAIN: ;任务四主程序
;你的程序
LJMP RW4_MAIN
============任务四END===================================
===============初始化程序==================================
MAIN:
MOV SP,#09FH;取任务2堆栈指针开始位置
MOV DPTR,#RW2_KS
PUSH DPL
PUSH DPH
MOV RW2_SP,#0A4H;任务2堆栈指针寄存器初值=09FH+2+任务2现场保护入栈数
MOV SP,#0BFH;取任务3堆栈指针开始位置
MOV DPTR,#RW3_KS
PUSH DPL
PUSH DPH
MOV RW3_SP,#0C4H;任务3堆栈指针寄存器初值=0BFH+2+任务3现场保护入栈数
MOV SP,#0DFH;取任务4堆栈指针开始位置
MOV DPTR,#RW4_KS
PUSH DPL
PUSH DPH
MOV RW4_SP,#0E4H;任务4堆栈指针寄存器初值=0DFH+2+任务4现场保护入栈数
MOV SP,#7FH;取任务1堆栈指针开始位置
SETB EA ;开启维持个任务调度的定时器
SETB ET0
MOV TMOD,#02H
SETB TR0
LJMP RW1_KS
END
要求不严谨的20任务系统
此程序结构很空洞,使用请自己注意
ORG 0000H
RWJSQ data 7FH
RWJSQ1 data 7EH
ORG 0000H
AJMP MAIN
ORG 000BH
LJMP RWDD
ORG 0076H
RWDD: ;任务调度程序
MOV R0,RWJSQ ;取任务地址寄存器地址
POP ACC
MOV @R0,A ;保存当前任务断点
INC R0
POP ACC
MOV @R0,A
INC R0
MOV A,R0
CJNEA,RWJSQ1,LOOP2;所有需要进行的任务是否进行完
MOV R0,#216 ;任务地址寄存器起始地址
LOOP2:
INC R0
MOV A,@R0 ;取另一个任务断点
PUSH ACC
DEC R0
MOV A,@R0
PUSH ACC
MOV RWJSQ,R0 ;保存任务地址寄存器地址
CJNER0,#220,XXX ;220是任务三的地址寄存器地址
INC R4 ;延时基数
XXX:
RETI ;前去执行新任务
MAIN:
MOV RWJSQ,#216 ;任务地址寄存器起始地址(最多20个任务,需40个寄存器)
MOV RWJSQ1,#0;任务地址寄存器地址的最后一个地址加一(取值218-0)
MOVR0,RWJSQ ;取各个任务的地址
MOV DPTR,#RW1
LCALL DZSZ
MOV DPTR,#RW2
LCALL DZSZ
MOV DPTR,#RW3
LCALL DZSZ
MOV DPTR,#RW4
LCALL DZSZ
MOV DPTR,#RW5
LCALL DZSZ
MOV DPTR,#RW6
LCALL DZSZ
MOV DPTR,#RW7
LCALL DZSZ
MOV DPTR,#RW8
LCALL DZSZ
MOV DPTR,#RW9
LCALL DZSZ
MOV DPTR,#RW10
LCALL DZSZ
MOV DPTR,#RW11
LCALL DZSZ
MOV DPTR,#RW12
LCALL DZSZ
MOV DPTR,#RW13
LCALL DZSZ
MOV DPTR,#RW14
LCALL DZSZ
MOV DPTR,#RW15
LCALL DZSZ
MOV DPTR,#RW16
LCALL DZSZ
MOV DPTR,#RW17
LCALL DZSZ
MOV DPTR,#RW18
LCALL DZSZ
MOV DPTR,#RW19
LCALL DZSZ
MOV DPTR,#RW20
LCALL DZSZ
SETB EA ;开启维持个任务调度的定时器
SETB ET0
MOV TMOD,#02H ;
MOV TH0,#6;定时器初值设置
SETB TR0
LJMP RW1 ;跳到第一个任务
DZSZ:
MOV @R0,DPH
INC R0
MOV @R0,DPL
INC R0
RET
RW1: ;计数任务一
JB P1.0,$
JNB P1.0,$
INC R7
LJMP RW1 ;死循环
RW2: ;计数任务二
JB P1.1,$
JNB P1.1,$
DEC R7
LJMP RW2 ;死循环
RW3: ;计数任务三
MOV R4,#0
CJNE R4,#250,$ ;准确延时演示,延时时间=250*任务数*定时器时间,
CPL P1.5
LJMP RW3 ;死循环
RW4: ;计数任务四
MOV R6,#255 ;非准确延时,时间较难计算
BBB:
MOV R5,#255
DJNZ R5,$
DJNZ R6,BBB
CPL P1.6
LJMP RW4 ;死循环
RW5: ;计数任务五
JB P2.7,$
JNB P2.7,$
CPL P1.7
LJMP RW5 ;死循环
RW6: ;计数任务六
JB P1.2,$
JNB P1.2,$
CPL P2.2
LJMP RW6 ;死循环
RW7: ;计数任务七
LJMP RW7 ;死循环
RW8: ;计数任务八
LJMP RW8 ;死循环
RW9: ;计数任务九
LJMP RW9 ;死循环
RW10: ;计数任务十
LJMP RW10 ;死循环
RW11: ;计数任务十一
LJMP RW11 ;死循环
RW12: ;计数任务十二
LJMP RW12 ;死循环
RW13: ;计数任务十三
CPL P0.0
LJMP RW13 ;死循环
RW14: ;计数任务十四
CPL P0.1
LJMP RW14 ;死循环
RW15: ;计数任务十五
CPL P0.2
LJMP RW15 ;死循环
RW16: ;计数任务十六
CPL P0.3
LJMP RW16 ;死循环
RW17: ;计数任务十七
CPL P0.4
LJMP RW17 ;死循环
RW18: ;计数任务十八
CPL P0.5
LJMP RW18 ;死循环
RW19: ;计数任务十九
CPL P0.6
LJMP RW19 ;死循环
RW20: ;计数任务二十
CPL P0.7
LJMP RW20 ;死循环
END
|