51时间片程序,欢迎拍砖,共同提高
;****************************************************************************
;本时间片系统的原理是利用51单片机的T0定时一个中断,切换用户的任务轮流执行,
;用户的任务写法和一般的一样,此处用2个流水灯和一个按键程序做实例
;**************程序步骤****************
;1定义一个全局变量用于存储任务号,另一个存储任务数
task_name equ 7ch
task_amount equ 7bh
;2定义任务的堆栈区数组,由于任务的切换需要占用15个字节,所以每个任务的堆栈区大小要看程序的使用情况而定,
;如果按照任务有3级深度设计,则堆栈区的大小为15+6=21个字节
;tsp*_a 都只有一级深度,18个字节够用
tsp0_a equ 30h ;31,32,33,34,35,36,37,38,39,3a,3b,3c,3d,3e,3f,40,41,
tsp1_a equ 42h ;43,44,45,46,47,48,49,4a,4b,4c,4d,4e,4f,50,51,52,53,
tsp2_a equ 54h ;55,56,57,58,59,5a,5b,5c,5d,5e,5f,60,61,62,63,64,65,
tsp3_a equ 66h ;67,68,69,6a,6b,6c,6d,6e,6f,70,71,72,73,74,75,76,77,
;3定义一个数组,用于保存每个任务的栈顶,数组的长度等于任务数
sp_a equ 78h ;79,7a,
;哈哈,内存区快用完了,还好位寻址区还有空间,当然如果是52就够用了
;4定义一个数组,用于存储每个任务的标志位,因为有的任务有运行的先后之分,也就是有条件运行,在这里是这样
;处理的,只有任务的标志位为零时,在任务调度时才能运行,例如要设计一个按键程序,只有先运行按键检测程序
;后,相应的显示的程序才能运行。我们可以这样处理,在按键程序中给显示程序的一个标志位置1,按键程序结束后将
;显示程序的标志位置0 ,这个数组最好设置在位寻址区(20h),长度等于任务数
tnt_a equ 20h ;21,22,23,
org 0000h
ljmp clear ;清理内存
org 000bh
ljmp IntTime0 ;时间片中断
org 0030h
;***********主程序**********
main:
mov task_amount,#3 ;3个任务
;------------------
mov dptr,#task0 ;取得任务0的入口地址,存入堆栈区
mov tsp0_a,dpl
mov r0,#tsp0_a
inc r0
mov @r0,dph
;--------------------------
mov dptr,#task1 ;取得任务1的入口地址,存入堆栈区
mov tsp1_a,dpl
mov r0,#tsp1_a
inc r0
mov @r0,dph
;----------------------------
mov dptr,#task2 ;取得任务2的入口地址,存入堆栈区
mov tsp2_a,dpl
mov r0,#tsp2_a
inc r0
mov @r0,dph
;------------------
mov tmod,#00000001b ;T0的01模式,最大65536
mov th0,#0f8h ;ff9c=65436 f830=63536
mov tl0,#30h
setb ea
setb et0 ;开启中断
;---------------
mov task_name,#0 ;0号任务
;--------------------------
;将每个任务的初次栈顶地址存入sp_a数组
mov r0,#sp_a ;r0取得数组的首地址
mov b,#14 ;只有第一个运行的任务的sp栈顶不用加15,其 他任务的栈;顶都要在原来的基础上加13
;这个和调度的运行有关,因为中断调度时要将sp弹出13次
;-----------
mov a,#tsp0_a
inc a
mov @r0,a ;任务0栈顶地址(#tsp0_a) + 1
;---------
inc r0 ;#sp_a + 1
mov r1 ,#tsp1_a
mov a,r1
add a,b
mov @r0,a ;任务1栈顶地址 (#tsp1_a) + 15
;--------------
inc r0 ;#sp_a + 2
mov r1 ,#tsp2_a
mov a,r1
add a,b
mov @r0,a ;任务2栈顶地址 (#tsp2_a) + 15
;-------------
mov task_name,#0
mov sp,sp_a
setb tr0 ;启动定时器
ret ;启动0号任务,程序的调度交给中断处理
;*********清理内存*************
clear:
mov r0,#7fh
clr a
clear_0:mov @r0,a
djnz r0,clear_0
ljmp main
;************中断*************
IntTime0:
clr ea ;关掉中断
clr tr0
using 0
;存储当前断点的寄存器的值
push psw
push acc
push b
push dph
push dpl
push ar7
push ar6
push ar5
push ar4
push ar3
push ar2
push ar1
push ar0
;----保存当前任务的栈顶地址------
mov b,#sp_a
mov a,task_name
add a,b
mov r0,a
mov @r0,sp
;-----切换任务------
inttime0_2:inc task_name;
mov a,task_name
cjne a,task_amount,inttime0_3
mov task_name,#0
;--检查任务是否满足运行的条件
inttime0_3:
mov b,#tnt_a
mov a,task_name
add a,b
mov r0,a
mov a,@r0
jnz inttime0_2 ;不为零说明标志位还没有清零,重新切换任务
;--为零分配新任务的sp值----
mov b,#sp_a
mov a,task_name
add a,b
mov r0,a
mov sp,@r0
;------恢复新任务的寄存器值弹出13次,当任务是首次运行时尽管入口地址已经放在栈底了
;但是要将sp加上13--------
pop ar0
pop ar1
pop ar2
pop ar3
pop ar4
pop ar5
pop ar6
pop ar7
pop dpl
pop dph
pop b
pop acc
pop psw
mov th0,#0f8h ;ff9c=65436
mov tl0,#30h
setb tr0
setb ea ;启动中?
reti
;***********任务**************
task0: ;流水灯
clr c
mov r5,#8
mov a,#11111111b
task0_0:rlc a
mov p0,a
call delay1 ;调用延时函数
djnz r5,task0_0
sjmp task0
;----------
task1: ;流水灯
clr c
mov r5,#8 ;delay里面也用到了r*,不要冲突
mov a,#11111111b
task1_0:rlc a
mov p2,a
call delay1
djnz r5,task1_0
sjmp task1
;-------------
task2: ;按键检测
jb p1.0,task2_0 ;按下去亮,松开灭
call delay2
jb p1.0, task2_0
clr p3.0
sjmp task2_1
task2_0:
setb p3.0
;---------------
task2_1 :;按下去跳变,取反
jb p1.1,task2_2
call delay2
jb p1.1,task2_2
cpl p3.1
;------------------
task2_2 : ;按下去,松开后亮,再按下松开是跳变,取反
jb p1.3,task2_4
call delay2
jnb p1.3,task2_4
cpl p3.3
task2_4:
sjmp task2
;-----------
;************延时函数 *************
delay1:
mov r7,#100
delay_0:mov r6,#250
djnz r6,$
djnz r7,delay_0
ret
delay2:
mov r7,#10
delay2_0:mov r6,#200
djnz r6,$
djnz r7,delay2_0
ret
;到这里程序完成,在protues里面仿真通过,看着2路流水灯不停的闪烁,我可以悠闲的按着按键,不用担心流水灯会停滞不前
;到这路心里好有成就感哪
;QQ173173810 欢迎到51汇编吧做客
;sulsan版权所有2012-10-28于上海
End
;