51时间片程序,欢迎拍砖,共同提高

valtti 2012-10-28 06:33:13

;****************************************************************************
;本时间片系统的原理是利用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
;

...全文
292 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
gaopeng090610 2012-11-08
  • 打赏
  • 举报
回复
不会看汇编,还是C语言好···
melon_1 2012-11-04
  • 打赏
  • 举报
回复
看完之后,就知道自己的51还是很烂的
knate 2012-11-04
  • 打赏
  • 举报
回复
个人建议 入门用汇编,然后快速过渡到C. 汇编会让你知道什么时MCU, C能在编程维护时省不少事. 但是C一般效率没那么高,我一般估计在10%以内.搞得更好的人,可能在5%也说不准 前提:你的项目能允许这样损耗的话,建议用C.
yijiaosanmaoba 2012-11-04
  • 打赏
  • 举报
回复
刚接触,表示看不懂
失途老马 2012-11-03
  • 打赏
  • 举报
回复
汇编看的蛋疼。。自己用c语言写过多任务。。
s_syx 2012-11-02
  • 打赏
  • 举报
回复
以前试过用中断搞了三路pwm控制三盏亮度不一的灯移动,也是用时钟节拍的想法。楼主要把程序转成c语言分享给大家就更好了,汇编看不出哪里分线程,哪里是用户程序。
valtti 2012-11-02
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]

作为兴趣爱好学习提高,搞这个尝试并达到预期目的,值得称道,赞一下
作为C51包括C52列系的实用程序来说,这里走入了误区,之前你是因为延时做昨太烂且不合理才引起程序不良反应,现在你为了搞定那个问题,引入了一个高耗资源实时性差极不实用的东东,哎,功夫下错地方了啊

举个例子吧,舞台灯光产品中的小魔球,只用一个小小的STC12C5608AD来控制,晶振只用24M就以跑得很好,功能:上有一个步进……
[/Quote]
谢谢高手批评,在这里我想问一个争论很多的问题,你说的是产品成本控制的问题,无疑用汇编编程最好,但是但就产品开发的时间控制来看,用c编程最好,那到底应该学c,还是汇编呢,我正在学习中,对此非常迷茫,希望高手提提建议
valtti 2012-11-02
  • 打赏
  • 举报
回复
回复5楼
用c写过,没有成功,可能我的水平不够,用c写的话涉及到汇编和C混合编程,另外还要看编译器的脸色,
在keil2里面通过可能到keil4里面就不行,主要涉及到中断保护
个人见解,望高手指教
liwenwen_787 2012-10-31
  • 打赏
  • 举报
回复
程序写的好!
ytaxl0109 2012-10-30
  • 打赏
  • 举报
回复
看到汇编就晕了。。。= =
h_w_m 2012-10-29
  • 打赏
  • 举报
回复
作为兴趣爱好学习提高,搞这个尝试并达到预期目的,值得称道,赞一下
作为C51包括C52列系的实用程序来说,这里走入了误区,之前你是因为延时做昨太烂且不合理才引起程序不良反应,现在你为了搞定那个问题,引入了一个高耗资源实时性差极不实用的东东,哎,功夫下错地方了啊

举个例子吧,舞台灯光产品中的小魔球,只用一个小小的STC12C5608AD来控制,晶振只用24M就以跑得很好,功能:上有一个步进电机要速度可调整控制,LED灯红绿蓝三个要可控制亮度,四个按扭开关要软件防抖,四个8段数码管显示当前运行模式或DMX512地址,声控信号一个(无需很实时响应但也要50毫秒内响应并处理完),还有串口的接收和发送,DMX512协议的数据包要实现处理,有主从功能,这么多事务,如果你用那思维模式一定死翘翘,无论是可用的ram上还是实时性上,你那法子无论如何做不到了,而别人却能把它做得很好

工厂要是讲究成本的,芯片成本能省就省,就1万的订单来说,用几十块一个的ARM系和用几块钱一个51系单片机那个差别是多少,几十万啦,就算是同样51系,1K的xdata、0.5K xdata和无xdata差别也是几毛到几块的,再省几K的code长度,可能又省出几块到几毛钱,总价就是几万到几千的差别,浪费得起不?

实用的C51程序,是要求空间效率和时间效率的,
空间上,片内可选的几个 128byte,256byte,256+512byte,256+1025byte,更低价的能用就不用更高价的
时间上,客户要求的并不都是要精确到指令级,如按扭,实时间精确到0.1秒就够了,有些误差1秒都不碍事,有些则要精确到毫秒级,甚至是0.1毫秒级,而串口的收发中断几本是要实时响应的, 响应时间上在满足用户的要求就可以 一般的不要过分追求响应速度.
aydf1 2012-10-28
  • 打赏
  • 举报
回复
学习学习

27,382

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧