十年專注單片機方案開發(fā)的方案公司英銳恩,分享PIC16F877 單片機運算子程序。英銳恩現(xiàn)提供服務產品涉及主控芯片:8位單片機、16位單片機、32位單片機及各類運算放大器等。
1 PIC16F877單片機匯編語言程序主體框架
以下是一個典型的程序結構:
;***************程序說明區(qū)*******************
LIST p=16f877 ;指定微控制器型號和文件輸出格式
INCLUDE p16f877.inc ;讀入MPLAB提供的定義文件P16F877.INC
;***片內RAM常用資源、變量定義和相應的說明*********
ACCALO EQU 20 ;存放加數(shù)或減數(shù)低8位
ACCAHI EQU 21 ;存放加數(shù)或減數(shù)高8位
ACCBLO EQU 23 ;存放被加數(shù)或被減數(shù)低8位
ACCBHI EQU 24 ;存放被加數(shù)或被減數(shù)高8位
S_W EQU 25 ;棧存W寄存器值
S_STATUS EQU 26 ;棧存STATUS寄存器值
;****************芯片復位矢量*******************
ORG 0X0000 ;由于PIC16F877芯片復位矢量在0000h單
;元,所以常在0000h單元處放置一條跳轉
;指令,使單片機復位后能跳過中斷矢量,
;直接執(zhí)行主程序
START GOTO MAIN
;******************中斷矢量**********************
ORG 0X0004 ;由于PIC16F877的中斷矢量為0004h,所以
;當中斷開放時, 需在此處加入中斷程序,
;使單片機能在中斷到來時及時進入相應的
;中斷服務程序。為了可靠起見,如果單片
;機不使用中斷,則常常在該中斷矢量處放
;置RETFIE指令,可以使單片機不會因
;干擾產生誤中斷而導致程序跑飛
CALL PUSH ;調用保護現(xiàn)場子程序
BTFSS PIR1,ADIF
CALL AD ;若AD中斷到,則執(zhí)行中斷服務程序
…….. ;此處可放多個中斷子程序,并以軟件安排
;中斷優(yōu)先級
CALL POP ;恢復中斷現(xiàn)場
RETFIE ;中斷返回
;****************主程序區(qū)*****************
ORG 0X0100 ;將主程序、子程序和中斷服務程序等存放
;在0100h單元之后,在中斷矢量和主程序
;區(qū)之間預留一些存儲單元,以便寫入判
;跳指令和一些必要的現(xiàn)場保護程序。此外
;用戶也可以根據(jù)實際需要,使主程序從其
;它地址開始存放
MAIN BSF STATUS,RP0 ;選擇存儲體1
MOVLW 0XFF ;定義RA口為輸入端口
MOVWF TRISA
BCF STATUS,RP0 ;選擇存儲體0
MOVLW 0X04 ;初值化ACCALO
MOVWF ACCALO
CALL DX ;調用DX子程序
LOOP1 …… ;任務1
…… ;任務2
:
:
:
GOTO LOOP1 ;反復執(zhí)行任務一和任務二等
;***************子程序區(qū)*********************
DX MOVF ACCALO,0 ;ACCB和ACCA低半字節(jié)相加
ADDWF ACCBLO
RETURN ;子程序返回
;****************************************
PUSH MOVWF S_W ;保護W寄存器
MOVF STATUS,0 ;保護STATUS寄存器
MOVWF S_STATUS
RETURN ;子程序返回
;****************************************
POP MOVF S_STATUS,0 ;恢復STATUS寄存器
MOVWF STATUS
MOVF S_W,0 ;恢復W寄存器
RETURN ;子程序返回
;****************中斷服務子程序區(qū)************************
AD BCF PIR1,ADIF ;清AD中斷標志
…… ;中斷服務主體程序
RETURN ;子程序返回
END
2 四則運算子程序
2.1 16×16位定點數(shù)加、減法子程序
以下子程序實現(xiàn)2個16×16位有符號數(shù)加、減運算,其和或差用一個16位數(shù)表示。在子程序中,減法是通過對減數(shù)求補后再與被減數(shù)相加來實現(xiàn)的。因此,當程序從D_sub進入子程序時為減法,當從D_add進入子程序時為加法。
子程序的入口條件和出口條件如下:
入口條件:16位被加數(shù)/被減數(shù)存放在ACCBHI、ACCBLO中;
16位加數(shù)/減數(shù)存放在ACCAHI、ACCALO中;
出口條件:16位和/差存放在ACCBHI和ACCBLO中。
以下為16×16位有符號數(shù)加、減法子程序。
注意:在以下注釋程序中均以ACCA代替ACCAHI、ACCALO兩個字節(jié),以ACCB代替ACCBHI、ACCBLO兩個字節(jié)。
LIST p=16f877
INCLUDE p16f877.inc
ACCALO EQU 20 ;存放加數(shù)或減數(shù)低8位
ACCAHI EQU 21 ;存放加數(shù)或減數(shù)高8位
ACCBLO EQU 23 ;存放被加數(shù)或被減數(shù)低8位
ACCBHI EQU 24 ;存放被加數(shù)或被減數(shù)高8位
ORG 0X0000
START GOTO MAIN
;***雙字節(jié)減法子程序,入口地址ACCB-ACCA,出口地址ACCB***
D_sub CALL NEG_A ;求ACCA的補碼
;***雙字節(jié)加法子程序,入口地址ACCB+ACCA,出口地址ACCB***
D_add MOVF ACCALO,0 ;ACCB和ACCA低半字節(jié)相加
ADDWF ACCBLO
BTFSC STATUS,C ;有進位否?
INCF ACCBHI ;有,ACCB高字節(jié)加1,再加ACCAHI
MOVF ACCAHI,0 ;ACCA、ACCB高半字節(jié)相加
ADDWF ACCBHI
RETURN ;子程序返回
;************** ACCA取補子程序*****************
NEG_A COMF ACCALO ;ACCALO取反加1
INCF ACCALO
BTFSC STATUS,Z ;低8位有進位嗎?
DECF ACCAHI ;有,ACCAHI減1,再取反
COMF ACCAHI ;否則ACCAHI直接取反
RETURN ;子程序返回
【校驗舉例1】 19531+(-16594)=2937(十進制)
化為十六進制數(shù):4C46H+BF2EH
結果:0B79H(十六進制)
【校驗舉例2】 26222+3000=29222(十進制)
化為十六進制數(shù): 666EH+0BB8H
結果:7226H(十六進制)
【例程】
MAIN MOVLW 0X6E ;被加數(shù)666EH送ACCB
MOVWF ACCBLO
MOVLW 0X66
MOVWF ACCBHI
MOVLW 0XB8 ;加數(shù)BB8H送ACCA
MOVWF ACCALO
MOVLW 0X0B
MOVWF ACCAHI
CALL D_add ;調用雙字節(jié)加法子程序,求和
END
2.2 16×16位定點數(shù)乘法子程序
子程序采用部分積右移加法實現(xiàn)乘法運算。乘數(shù)和被乘數(shù)分別為16位二進制有符號數(shù)(均采用補碼表示,第16位為符號位),積為32位二進制有符號數(shù),第32位為符號位。子程序的入口條件和出口條件如下:
入口條件:被乘數(shù)存放在ACCBHI和ACCBLO單元中,
乘數(shù)存放在ACCAHI和ACCALO單元中。
出口條件:積存放在ACCBHI、ACCBLO、ACCCHI和ACCCLO單元中,ACCB為高16位,ACCC為低16位。
以下為本子程序的程序清單:
LIST p=16f877
INCLUDE p16f877.inc
ACCALO EQU 20 ;存放乘數(shù)低8位
ACCAHI EQU 21 ;存放乘數(shù)高8位
ACCBLO EQU 23 ;存放被乘數(shù)低8位和乘積第16~23位
ACCBHI EQU 24 ;存放被乘數(shù)高8位和乘積第24~31位
ACCCLO EQU 26 ;存放乘積低8位
ACCCHI EQU 27 ;存放乘積高8位
ACCDLO EQU 28 ;臨時寄存器
ACCDHI EQU 29 ;臨時寄存器
TEMP EQU 2A ;臨時寄存器
SIGN EQU 2B ;存放乘積的符號
ORG 0X0000
START GOTO MAIN
;***16×16位乘法子程序,入口地址ACCB×ACCA,出口地址ACCB和ACCC ***
ORG 0X0100
D_mpy CALL S_SIGN ;求取乘積的符號,并對負數(shù)取補
CALL SETUP ;調用子程序,將ACCB的值送ACCD
INCF TEMP
CLRF ACCCHI ;清ACCC
CLRF ACCCLO
MLOOP BCF STATUS,C ;清進位位
RRF ACCDHI ;ACCD右移
RRF ACCDLO
BTFSC STATUS,C ;判斷是否需要相加
CALL D_add ;加乘數(shù)至ACCB,見加法程序
BCF STATUS,C ;清進位位
RRF ACCBHI ;右移部分乘積
RRF ACCBLO
RRF ACCCHI
RRF ACCCLO
DECFSZ TEMP ;乘法完成否?
GOTO MLOOP ;否,繼續(xù)求乘積
BTFSS SIGN,7 ;是,確定乘積的符號
GOTO OVER ;為正,乘法結束
COMF ACCCLO ;為負,乘積取補
INCF ACCCLO
BTFSC STATUS,Z
DECF ACCCHI
COMF ACCCHI
BTFSC STATUS,Z
NEG_B DECF ACCBLO ;
COMF ACCBLO
BTFSC STATUS,Z
DECF ACCBHI
COMF ACCBHI
OVER RETURN ;子程序返回
;****************************************
SETUP MOVLW .15 ;初始化TEMP寄存器
MOVWF TEMP
MOVF ACCBHI,0 ;ACCB送ACCD
MOVWF ACCDHI
MOVF ACCBLO,0
MOVWF ACCDLO
CLRF ACCBHI ;清ACCB
CLRF ACCBLO
RETURN ;子程序返回
;*******乘法運算確定結果符號判斷子程序******
S_SIGN MOVF ACCAHI,0 ;ACCAHI異或ACCBHI,結果送SIGN單元
XORWF ACCBHI,0
MOVWF SIGN
BTFSS ACCBHI,7 ;ACCB為負嗎?
GOTO CHEK_A ;否,檢查ACCA
CALL NEG_B ;是,求取ACCB絕對值
CHEK_A BTFSC ACCAHI,7 ;ACCA為負嗎?
CALL NEG_A ;ACCA為負,求取ACCA絕對值,
;見雙字節(jié)加法程序
RETURN ;ACCA和ACCB均為正,返回
【校驗舉例1】:-24555×(-7391)=181486005(十進制)
化為十六進制數(shù):A015H×E321H
結果:0AD141B5H(十六進制)
【校驗舉例2】 16405×13089=214725045(十進制)
化為十六進制數(shù):4015H×3321H
結果:0CCC71B5H(十六進制)
【例程】
MAIN MOVLW 0X15 ;被乘數(shù)4015H送ACCB
MOVWF ACCBLO
MOVLW 0X40
MOVWF ACCBHI
MOVLW 0X21 ;乘數(shù)3321H送ACCA
MOVWF ACCALO
MOVLW 0X33
MOVWF ACCAHI
CALL D_mpy ;調用雙字節(jié)乘法子程序,求積
END
2.3 16×16位定點數(shù)除法子程序
子程序采用反復的減法算法,除數(shù)和被除數(shù)分別為16位二進制有符號數(shù)(均采用補碼表示,第16位為符號位),商為16位二進制有符號數(shù),第16位為符號位。子程序的入口條件和出口條件如下:
入口條件:被除數(shù)存放在ACCBHI、ACCBLO單元中;
除數(shù)存放在ACCAHI、ACCALO單元中。
出口條件:商存放在ACCBHI、ACCBLO單元中;
余數(shù)存放在ACCCHI、ACCCLO單元中。
LIST p=16f877
INCLUDE p16f877.inc
ACCALO EQU 20 ;存放除數(shù)低8位
ACCAHI EQU 21 ;存放除數(shù)高8位
ACCBLO EQU 22 ;存放被除數(shù)和商的低8位
ACCBHI EQU 23 ;存放被除數(shù)和商的高8位
ACCCLO EQU 24 ;存放余數(shù)低8位
ACCCHI EQU 25 ;存放余數(shù)高8位
ACCDLO EQU 26 ;臨時寄存器
ACCDHI EQU 27 ;臨時寄存器
TEMP EQU 28 ;臨時寄存器
SIGN EQU 29 ;存放商的符號
ORG 0X0000
START GOTO MAIN
;***16×16位數(shù)除法子程序,入口地址ACCB /ACCA,出口地址ACCB ***
ORG 0X0100
D_div CALL S_SIGN ;確定商的符號,并將負數(shù)取補
CALL SETUP ;初始化TEMP,將被除數(shù)移至ACCD,
;(SETUP子程序請參見16×16位定點數(shù)
;乘法子程序SETUP)
INCF TEMP
CLRF ACCCHI ;清余數(shù)寄存器
CLRF ACCCLO
DLOOP BCF STATUS,C ;清進位位
RLF ACCDLO ;被除數(shù)、余數(shù)左移1位
RLF ACCDHI
RLF ACCCLO
RLF ACCCHI
MOVF ACCAHI,0 ;ACCCHI-ACCAHI
SUBWF ACCCHI,0
BTFSS STATUS,Z ;ACCCHI=ACCAHI?
GOTO NOCHK
MOVF ACCALO,0 ;是,ACCCLO-ACCALO
SUBWF ACCCLO,0
NOCHK BTFSS STATUS,C ;ACCC>ACCA?
GOTO NOGO
MOVF ACCALO,0 ;是,余數(shù)減除數(shù)
SUBWF ACCCLO
BTFSS STATUS,C
DECF ACCCHI
MOVF ACCAHI,0
SUBWF ACCCHI
BSF STATUS,C ;置進位位
NOGO RLF ACCBLO ;商左移1位
RLF ACCBHI
DECFSZ TEMP ;循環(huán)完畢?
GOTO DLOOP
BTFSS SIGN,7 ;是,確定商的符號
GOTO DIVOVER ;為正,除法結束,跳轉到結束行
COMF ACCCLO ;為負,商和余數(shù)分別取補
INCF ACCCLO
BTFSC STATUS,Z
DECF ACCCHI
COMF ACCCHI
CALL NEG_B ;見乘法程序中間NEG_B
DIVOVER RETURN ;子程序返回
;************除法運算確定結果符號子程序*******************
S_SIGN MOVF ACCAHI,0 ;ACCAHI異或ACCBHI,結果送SIGN單元
XORWF ACCBHI,0
MOVWF SIGN
BTFSS ACCBHI,7 ;ACCB為負?
GOTO CHEK_A ;否,檢查ACCA
COMF ACCBLO ;是,ACCB取補
INCF ACCBLO
BTFSC STATUS,Z
DECF ACCBHI
COMF ACCBHI
CHEK_A BTFSC ACCAHI,7 ;ACCA為負?
CALL NEG_A ;ACCA為負,取補(NEG_A子程序請參見
;16×16位定點數(shù)乘法子程序NEG_A)
RETURN ;ACCA和ACCB均為負,返回
【校驗舉例1】 -23775÷(-240)=99.0625(十進制)
化為十六進制數(shù):A321H÷FF10H;
結果:(商)0063H,(余數(shù))000FH(十六進制)。
【校驗舉例2】 769÷3856=0.199429(十進制)
化為十六進制數(shù):0301H÷0F10H;
結果:(商)0000H,(余數(shù))0301H(十六進制)。
【例程】
MAIN MOVLW 0X01 ;被除數(shù)0301H送ACCB
MOVWF ACCBLO
MOVLW 0X03
MOVWF ACCBHI
MOVLW 0X10 ;除數(shù)0F10H送ACCA
MOVWF ACCALO
MOVLW 0X0F
MOVWF ACCAHI
CALL D_div ;調用雙字節(jié)除法子程序,求商
END
3 3字節(jié)浮點四則運算子程序
3.1 浮點數(shù)加(減)法子程序
以下為浮點加(減)運算例程:
LIST p=16f877
INCLUDE p16f877.inc
ACCALO EQU 20 ;存放加數(shù)或減數(shù)的尾數(shù)
ACCAHI EQU 21
EXPA EQU 22 ;存放加數(shù)或減數(shù)階碼
ACCBLO EQU 23 ;存放被加數(shù)或被減數(shù)尾數(shù)以及和或差
ACCBHI EQU 24
EXPB EQU 25 ;存放被加數(shù)或被減數(shù)階碼
ACCCLO EQU 26 ;臨時寄存器
ACCCHI EQU 27 ;臨時寄存器
ACCDLO EQU 28 ;臨時寄存器
ACCDHI EQU 29 ;臨時寄存器
TEMP EQU 2A ;臨時寄存器
TEMP1 EQU 30 ;臨時寄存器
TIMES EQU 31 ;臨時寄存器
ORG 0X000
START GOTO MAIN
ORG 0X0100
;**************浮點減法子程序****************
F_sub CALL NEG_A ;求ACCA的補碼,將減法轉換為補碼加法
;***********浮點加法子程序**************
F_add CALL SUBADJ ;調子程序判斷EXPB和EXPA的大小
BTFSC STATUS,Z ;參與運算的兩個數(shù)階碼相等?
GOTO PADD ;是,求尾數(shù)的和
BTFSC STATUS,C ;EXPB>EXPA?
CALL F_swap ;是,ACCB與ACCA互換
MOVF EXPA,0 ;否,求取兩者的差值
SUBWF EXPB
SCLOOP CALL SHFTSR ;ACCB右移規(guī)格化
INCFSZ EXPB ;EXPB=EXPA?
GOTO SCLOOP ;否,繼續(xù)右移
MOVF EXPA,0 ;是,存和(差)的階碼