#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