打印
[学习资料]

Microchip单片机固定时间片轮询调度汇编代码

[复制链接]
1297|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
这是一个能在PIC16F877A开发板上运行的固定时间片轮询调度汇编程序代码。

为什么要用时间片轮询调度?

假如我们要写一个单片机程序,程序的目的就是用GPIO控制一个灯亮一秒再暗一秒…最简单的写法是用循环来计时,循环n次凑够一秒钟时间,然后去把GPIO的值反一下,然后再继续循环一秒钟…

这样写程序的问题就是,这个程序除了闪灯之外就啥也不能做了,给GPIO赋值只需要一秒钟执行一条指令,并没有消耗什么CPU资源。但CPU资源都浪费在了循环上。所以这种写法在实际应用中基本上是不可取的,而且这样计时也不够精确。

高级一点的写法是用单片机里面的硬件计时器,定时一秒钟,时间到了会触发中断,在中断响应程序中去赋值GPIO点灯。这样CPU在其它时间就空闲了可以干别的事情。但这样写法也有问题,那就是单片机里面的硬件计时器个数是有限的,如果有很多功能都需要定时,计数器不够了怎么办?

解决这个问题的一种方法就是时间片轮询调度法,调度的就是CPU的资源,也就是不同时间片会执行不同的程序。其实现在的操作系统底层都会用这种时间片调度法,也就是分时复用。某个时间片里执行的完成某项功能的程序就类似于一个线程。

使用特权

评论回复
沙发
过期的塔头|  楼主 | 2021-10-31 22:54 | 只看该作者
该程序干了啥?

程序在开发板上实现了秒表,流水灯,按键检测等几个功能。具体可见代码前面的注释。流水灯一秒走一次,码表100ms加一,按键检测需要10ms检测一次按键值进行按键去抖,数码管需要2ms循环刷新。所以这个程序需要很多个不同时间的定时。其基本程序是一个200μs的时间片循环,然后通过对时间片进行计数,并对这些计数进行判断来决定该在合适调用哪个子程序。具体时间片的分布图,和程序流程图可见“TimeSlice流程图.vsd”文件。

使用特权

评论回复
板凳
过期的塔头|  楼主 | 2021-10-31 22:55 | 只看该作者

使用特权

评论回复
地板
过期的塔头|  楼主 | 2021-10-31 22:56 | 只看该作者
#INCLUDE<P16F877A.INC>
__CONFIG 3F32H
;四个数码管右边两个实现了一个秒表,四个红色按键按最右边的K20一个这两个数会减一,按K19会加一。按K18会切换秒表的停止和开始,按K17数字清零
;数码管左边两个数字会显示执行程序最多的那个时间片在程序执行完了之后TMR0计时器的计数值,16进制。注意TMR0的计数是从初值28开始的

;............初始化一些值..............;
K_TMR0_200us    EQU  d'28'       ;给TMR0赋初值的立即数,具体的根据实际来调整;在18.432M下,这个值设28,TMR0计时大约就是200μs
F_CNT1ms        EQU  020H        ;用来计数产生1ms时间块
F_CNT10ms       EQU  021H        ;用来计数产生10ms时间块
F_CNT100ms      EQU  022H        ;用来计数产生100ms时间块
F_CNT1s         EQU  023H        ;用来计数产生1s时间块
                                 
F_X200us        EQU  024H        ;用来设置进入第X个200us时间块
F_X1ms          EQU  025H        ;用来设置进入第X个1ms时间块
F_X10ms         EQU  026H        ;用来设置进入第X个10ms时间块
F_X100ms        EQU  027H        ;用来设置进入第X个100ms时间块
F_LED           EQU  028H        ;用于LED显示

F_NUM_SEL       EQU  029H        ;用于数码管位选
F_NUM0          EQU  02AH        ;数码管显示数值,0是最右边的那个
F_NUM1          EQU  02BH        ;
F_NUM2          EQU  02CH        ;
F_NUM3          EQU  02DH        ;
F_KEY0          EQU  02EH        ;用于移位寄存按键输入,进行按键检测
F_KEY1          EQU  02FH        ;
F_KEY2          EQU  030H        ;
F_KEY3          EQU  031H        ;
F_NUMCTRL       EQU  032H        ;第0位用于控制秒表的停止和开启
F_W             EQU  033H        ;用来暂存W
F_TMR0MAX       EQU  034H        ;缓存TMR0最大值,用来查看哪个时间片耗时最多,这个值会显示在数码管的左边两位,16进制


;............主  程  序..............;
   ORG                                0000H                           ;定义程序存放区域的地址
MAIN                             
   NOP                           ;设置一条ICD必须的空操作指令
   CALL     PORT_INIT            ;调用端口初始化
   BSF      STATUS,RP0           ;设置文件寄存器为体1
   MOVLW    01H                                     ;设置选项寄存器的值;00:2分频,01:4分频;
   MOVWF    OPTION_REG                 ;将00H赋值给选项寄存器 内部时钟源,4分频,将时钟分配给TMR0
   BCF      STATUS,RP0                 ;回到体0
   CLRF     INTCON               ;关中断,清溢出
   CALL     REG_INIT             ;调用各寄存器赋初值
   
LOOP_MAIN                        
   BCF                            INTCON,2         ;TMR0溢出清除
   MOVLW                    K_TMR0_200us     ;TMR0初值
   MOVWF                    TMR0                   ;给TMR0重新启动定时计数
   
;.....计数产生1ms时间块.....;      
   DECFSZ       F_CNT1ms,F       ;自减1,d为1,存回F,结果为零则跳一步
   GOTO         ELSE_1ms         ;
   MOVLW        d'5'             ;结果为零则赋回初值5,并执行下面的计数
   MOVWF        F_CNT1ms         
;.....计数产生10ms时间块....;   
   DECFSZ       F_CNT10ms,F      
   GOTO         ELSE_1ms         
   MOVLW        d'10'            ;结果为零则赋回初值10
   MOVWF        F_CNT10ms      
;.....计数产生100ms时间块...;   
   DECFSZ       F_CNT100ms,F     
   GOTO         ELSE_1ms         
   MOVLW        d'10'            ;结果为零则赋回初值10
   MOVWF        F_CNT100ms         
;.....计数产生1s时间块......;   
   DECFSZ       F_CNT1s,F        
   GOTO         ELSE_1ms         
   MOVLW        d'10'            ;结果为零则赋回初值10
   MOVWF        F_CNT1s
ELSE_1ms
;.....时间块产生结束.........;   

;.....选择某个时间片执行程序.;
  ;......5号200us时间片......;
   MOVLW        05H
   XORWF        F_CNT1ms,W        ;比较是否相等
   BTFSS        STATUS,Z          ;如果相等就跳过   
   GOTO         END_IF200us_5
  ;是5号200us时间片就执行这些程序  
   MOVLW        d'10'                    
   MOVWF        F_X1ms             
   MOVLW        d'10'                  
   MOVWF        F_X10ms         
   MOVLW        d'5'                    
   MOVWF        F_X100ms      
   CALL         Enter_x100ms.x10ms.x1ms  ;进入5.10.10这个时间片
   BTFSS        STATUS,Z         
   GOTO         END_5_10_10              ;不是就跳到结束
   CALL         LED_RUN                  ;是5.10.10这个时间片就调用需要执行的程序,跑马灯,1秒灯走一次  
   CALL         TMR0MAX_DISPLAY          ;显示耗时最长时间片程序结束是TRM0的值
END_5_10_10   
   MOVLW        d'5'                    
   MOVWF        F_X1ms             
   MOVLW        d'8'                  
   MOVWF        F_X10ms         
   MOVF         F_CNT1s,W                           
   MOVWF        F_X100ms                 ;让这个参数等于现在的计数就相当于忽略这个计数
   CALL         Enter_x100ms.x10ms.x1ms  
   BTFSS        STATUS,Z         
   GOTO         END_x_8_8              
   CALL         NUM_RUN                  ;是x.9.9这个时间片就调用需要执行的程序  
END_x_8_8   
   
END_IF200us_5

;......4号200us时间片......;
   MOVLW        04H
   XORWF        F_CNT1ms,W        ;比较是否相等
   BTFSS        STATUS,Z          ;如果相等就跳过   
   GOTO         END_IF200us_4
  ;是4号200us时间片就执行这些程序
   MOVLW        09H
   XORWF        F_CNT10ms,W       ;比较是否相等
   BTFSS        STATUS,Z          ;如果相等就跳过   
   GOTO         END_IF200us_4
  ;..进入9号1ms时间片
   CALL         KEY_DETECT        ;按键检测,10ms一次
  
END_IF200us_4

;......3号200us时间片......;
   MOVLW        03H
   XORWF        F_CNT1ms,W        ;比较是否相等
   BTFSS        STATUS,Z          ;如果相等就跳过   
   GOTO         END_IF200us_3
  ;是3号200us时间片就执行这些程序
   BTFSS        F_CNT10ms,0       ;是偶数就去刷数码管,2ms刷一次
   CALL         DISPLAY_NUM                                                     

END_IF200us_3
     
         
;.....产生测试波形..........;
   MOVLW        01H
   XORWF        F_CNT1ms,W       ;比较是否相等
   BTFSS        STATUS,Z           ;如果相等就跳过
   GOTO         ELSE1            
   BCF          PORTB,RB5   
   GOTO         END_IF1
ELSE1
   BSF                        PORTB,RB5   
END_IF1

;...找出时间片最大占用率...;
   MOVF     TMR0,0
   SUBWF    F_TMR0MAX,W       ;MAX的值减去TMR0
   BTFSC    STATUS,C         
   GOTO     FINDMAX_ELSE
   MOVF     TMR0,W            ;小于就跳到这
   MOVWF    F_TMR0MAX           
   BCF                  PORTB,RB4
   GOTO     FINDMAX_END
FINDMAX_ELSE
   MOVF     TMR0,W
   SUBWF    F_TMR0MAX,W      
   BTFSC    STATUS,C
   GOTO     FINDMAX_ELSE
   BCF                PORTB,RB4
FINDMAX_END   
   
   BCF                        PORTB,RB4             ;输出置0,从RB4端口输出波形用

;检测TMR0是否有溢出,移出就跳出LOOP_OF,跳回LOOP_MAIN
LOOP_OF                     
   BTFSS    INTCON,2              ;如果溢出,跳过GOTO LOOP_OF
   GOTO     LOOP_OF               
   BSF      PORTB,RB4             ;跳到这条了
   GOTO     LOOP_MAIN


;............主程序结束..............;
;.......................................这是一条主程序和子程序的分割线................................;
   
;......数码管查表子程序.........;
NUM_TABLE
   MOVWF     F_W
   SUBLW     d'16'               ;判断是否大于16,大于就不能查表,返回个-
   MOVF      F_W,W
   BTFSS     STATUS,C
   RETLW     03FH
   ADDWF     PCL,F
   RETLW     0C0H
   RETLW     0F9H
   RETLW     0A4H
   RETLW     0B0H
   RETLW     099H
   RETLW     092H
   RETLW     082H
   RETLW     0F8H
   RETLW     080H
   RETLW     090H
   RETLW     088H
   RETLW     083H
   RETLW     0C6H
   RETLW     0A1H
   RETLW     086H
   RETLW     08EH
   RETLW     0BFH
   
;....进入x100ms.x10ms.x1ms时间片子程序..;
Enter_x100ms.x10ms.x1ms
   MOVF     F_X1ms,W             ;把F_X1ms参数值送到W;
   XORWF    F_CNT10ms,W          ;比较是否相等,相等时结果为0,送到了W  
   BTFSS    STATUS,Z             ;如果相等就跳过下一条
   GOTO     END_Enter_xxx        ;不想等就直接结束,此时W中的值不为0
   MOVF     F_X10ms,W            ;把F_X10ms参数值送到W;               
   XORWF    F_CNT100ms,W         ;比较是否相等,相等时结果为0,送到了W  
   BTFSS    STATUS,Z             ;如果相等就跳过下一条
   GOTO     END_Enter_xxx        ;不想等就直接结束,此时W中的值不为0
   MOVF     F_X100ms,W           ;把F_X100ms参数值送到W;               
   XORWF    F_CNT1s,W            ;比较是否相等,相等时结果为0,送到了W              
         
END_Enter_xxx                    ;如果相等W就是0,标志位Z就是1
   return
;....把F_TMR0MAX的值给数码管....;
TMR0MAX_DISPLAY
  MOVF      F_TMR0MAX,W   
  ANDLW     b'00001111'
  MOVWF     F_NUM2
  MOVF      F_TMR0MAX,W
ANDLW     b'11110000'
  MOVWF     F_NUM3
  SWAPF     F_NUM3     
  return
;.........跑马灯子程序..........;
LED_RUN
   BSF                        PORTC,RC5            ;开启LED的锁存使能
   MOVF     F_LED,W
   MOVWF    PORTD
   BCF                        PORTC,RC5            ;关闭LED的锁存使能
   ;循环左移F_LED
   BCF      STATUS,C             ;是0进位就设0
   BTFSC    F_LED,7              ;看最左边是0是1,是0则跳
   BSF      STATUS,C             ;是1进位就设1
   RLF      F_LED,1              ;移位产生跑马效果   
   return
   
;...........秒表子程序...........;
NUM_RUN
  BTFSC  F_NUMCTRL,0             ;控制第0位是0就不加秒表
  CALL   NUM_ADD
  return
  
;..........秒表加...............;
NUM_ADD
   MOVLW     09H
   XORWF     F_NUM0,W
   BTFSS     STATUS,Z               ;如果相等就跳过下一条
   GOTO      Add1_F_NUM0
   CLRF      F_NUM0                 ;加到9就清零
   MOVLW     09H
   XORWF     F_NUM1,W
   BTFSS     STATUS,Z             ;如果相等就跳过下一条
   GOTO      Add1_F_NUM1
   CLRF      F_NUM1
   GOTO      Add1_F_NUM1_END
  Add1_F_NUM1
     INCF      F_NUM1,F   
  Add1_F_NUM1_END   
   GOTO      Add1_F_NUM0_END  
Add1_F_NUM0
   INCF      F_NUM0,F
Add1_F_NUM0_END           
   return
;..........秒表减...............;
NUM_SUB
   MOVLW    09H
   MOVF     F_NUM0,F
   BTFSS    STATUS,Z                 ;等于0就去再设为9
   GOTO     SUB1_F_NUM0     
   MOVWF    F_NUM0                   ;设为9
   MOVF     F_NUM1,F
   BTFSS    STATUS,Z               ;等于0就去再设为9
   GOTO     SUB1_F_NUM1     
   MOVWF    F_NUM1                 ;设为9
   GOTO     SUB1_NUM1_END
   SUB1_F_NUM1
   DECF     F_NUM1,F
   SUB1_NUM1_END  
   GOTO     SUB1_NUM0_END
SUB1_F_NUM0
   DECF     F_NUM0,F
SUB1_NUM0_END
   
   return
;......数码管显示子程序.........;
DISPLAY_NUM
   MOVF     F_NUM_SEL,W
   BSF      PORTC,RC4            ;开启位选的锁存使能
   MOVWF    PORTD                ;输出位选
   BCF                        PORTC,RC4            ;关闭位选的锁存使能
  ;....根据位选从寄存器中读出相应位的值
   BTFSC    F_NUM_SEL,0          ;为0跳过,继续判断其它位
   GOTO     DISPLAY0
   BTFSC    F_NUM_SEL,1          ;为0跳过,继续判断其它位
   GOTO     DISPLAY1
   BTFSC    F_NUM_SEL,2          ;为0跳过,继续判断其它位
   GOTO     DISPLAY2
   BTFSC    F_NUM_SEL,3          ;为0跳过,继续判断其它位
   GOTO     DISPLAY3

DISPLAY0
   MOVF     F_NUM0,W
   GOTO     END_NUM_SELECT  
DISPLAY1
   MOVF     F_NUM1,W
   GOTO     END_NUM_SELECT
DISPLAY2
   MOVF     F_NUM2,W
   GOTO     END_NUM_SELECT   
DISPLAY3
   MOVF     F_NUM3,W
   GOTO     END_NUM_SELECT  
   
END_NUM_SELECT     
  ;......把W里的数值查表转换成显示码
   CALL     NUM_TABLE            ;查表转码
   BSF      PORTC,RC3            ;开启段选的锁存使能
   MOVWF    PORTD                ;输出段选
   BCF      PORTC,RC3            ;关闭段选的锁存使能
   
   ;循环左移F_NUM_SEL
   BCF      STATUS,C             ;是0进位就设0
   BTFSC    F_NUM_SEL,7          ;看最左边是0是1,是0则跳
   BSF      STATUS,C             ;是1进位就设1   
   RLF      F_NUM_SEL,F          ;移位循环刷数码管
   return

;......按键检测子程序...........;
KEY_DETECT
  ;...检测按键K17,RB0...;  
  BCF      STATUS,C             ;先把进位清零
  BTFSC    PORTB,W              
  BSF      STATUS,C             ;按键输入是1就把进位设为1
  RLF      F_KEY0               ;每10毫秒把按键输入移位寄存到这个寄存器里面
  MOVLW    b'11110000'          ;这个序列检测开始按下
  XORWF    F_KEY0,W            
  BTFSS    STATUS,Z             ;如果相等就跳过下一条
  GOTO     END_KEY_DETECTK17_1
  CLRF     F_NUM0
  CLRF     F_NUM1
  CLRF     F_NUM2
  CLRF     F_NUM3
END_KEY_DETECTK17_1
  MOVLW    b'00000000'          ;这个序列检测一直按下
  XORWF    F_KEY0,W            
  BTFSS    STATUS,Z             ;如果相等就跳过下一条
  GOTO     END_KEY_DETECTK17_2
  CLRF     F_NUM0
  CLRF     F_NUM1
  CLRF     F_NUM2
  CLRF     F_NUM3
END_KEY_DETECTK17_2
  ;...检测按键K18,RB1...;  
  BCF      STATUS,C             ;先把进位清零
  BTFSC    PORTB,F              
  BSF      STATUS,C             ;按键输入是1就把进位设为1
  RLF      F_KEY1               ;每10毫秒把按键输入移位寄存到这个寄存器里面
  MOVLW    b'11110000'          ;这个序列检测开始按下
  XORWF    F_KEY1,W            
  BTFSS    STATUS,Z             ;如果相等就跳过下一条
  GOTO     END_KEY_DETECTK18_1
  COMF     F_NUMCTRL,1  
END_KEY_DETECTK18_1   
  ;...检测按键K19,RB2...;  
  BCF      STATUS,C             ;先把进位清零
  BTFSC    PORTB,2              
  BSF      STATUS,C             ;按键输入是1就把进位设为1
  RLF      F_KEY2               ;每10毫秒把按键输入移位寄存到这个寄存器里面
  MOVLW    b'11110000'          ;这个序列检测开始按下
  XORWF    F_KEY2,W            
  BTFSS    STATUS,Z             ;如果相等就跳过下一条
  GOTO     END_KEY_DETECTK19_1
  CALL     NUM_ADD
END_KEY_DETECTK19_1   
;...检测按键K20,RB3...;  
  BCF      STATUS,C             ;先把进位清零
  BTFSC    PORTB,3              
  BSF      STATUS,C             ;按键输入是1就把进位设为1
  RLF      F_KEY3               ;每10毫秒把按键输入移位寄存到这个寄存器里面
  MOVLW    b'11110000'          ;这个序列检测开始按下
  XORWF    F_KEY3,W            
  BTFSS    STATUS,Z             ;如果相等就跳过下一条
  GOTO     END_KEY_DETECTK20_1
  CALL     NUM_SUB
END_KEY_DETECTK20_1   
  return      
  
;.......端口初始化子程序........;
PORT_INIT
   CLRF         STATUS                 ;选体0;Bank0
   CLRF         PORTA                  ;清空端口输出
   CLRF         PORTB                  ;
   CLRF         PORTC                  ;
   CLRF         PORTD                  ;
   BSF           STATUS,RP0                   ;设置文件寄存器为体1
   MOVLW        b'00000000'            ;把设置端口为输入还是输出,0是输出
   MOVWF        TRISA
   MOVLW        b'00001111'
   MOVWF        TRISB
   MOVLW        b'00000000'
   MOVWF        TRISC
   MOVLW        b'00000000'
   MOVWF        TRISD
   return                        

;.......各寄存器赋初值子程序.........;
REG_INIT   
   MOVLW    b'11111110'            
   MOVWF    F_LED                ;给LED寄存器赋初值,用于跑马灯  
   MOVLW    b'00010001'
   MOVWF    F_NUM_SEL
   MOVLW    d'0'
   MOVWF    F_NUM0              ;数码管上电时会显示“FF00”
   MOVLW    d'0'
   MOVWF    F_NUM1
   MOVLW    0FH
   MOVWF    F_NUM2
   MOVLW    0FH
   MOVWF    F_NUM3
   CLRF     F_TMR0MAX
  return

END   

使用特权

评论回复
5
MianQi| | 2021-11-1 09:55 | 只看该作者
本帖最后由 MianQi 于 2021-11-1 09:56 编辑

楼主是用什么工具测试地,我在MPLAB X IDE中测试时,出现了诸多报错:

newAsmTemplate.asm:3:: error: (876) syntax error
newAsmTemplate.asm:13:: error: (876) syntax error
newAsmTemplate.asm:75:: error: (876) syntax error
newAsmTemplate.asm:81:: error: (876) syntax error
newAsmTemplate.asm:89:: error: (876) syntax error
newAsmTemplate.asm:105:: error: (876) syntax error
newAsmTemplate.asm:115:: error: (876) syntax error
newAsmTemplate.asm:123:: error: (876) syntax error
newAsmTemplate.asm:130:: error: (876) syntax error
newAsmTemplate.asm:133:: error: (876) syntax error
newAsmTemplate.asm:153:: error: (876) syntax error
newAsmTemplate.asm:157:: error: (876) syntax error
newAsmTemplate.asm:161:: error: (876) syntax error
newAsmTemplate.asm:165:: error: (844) lexical error
newAsmTemplate.asm:165:: error: (876) syntax error
newAsmTemplate.asm:165:: error: (844) lexical error
newAsmTemplate.asm:175:: error: (876) syntax error
newAsmTemplate.asm:177:: error: (876) syntax error
newAsmTemplate.asm:181:: error: (876) syntax error
newAsmTemplate.asm:189:: error: (844) lexical error
newAsmTemplate.asm:189:: advisory: (1) too many errors (21)

<img src="https://bbs.21ic.com/forum.php?mod=image&aid=1781302&size=300x300&key=f83123be81677cef&nocache=yes&type=fixnone" aid="attachimg_1781302" alt="" border="0">

Screenshot from 2021-11-01 09-56-03.png (342.7 KB )

Screenshot from 2021-11-01 09-56-03.png

使用特权

评论回复
6
elephant00| | 2021-11-1 16:22 | 只看该作者
学习一下,楼主的分享的资料很棒了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

923

帖子

0

粉丝