; Simple 4-digit 0-20 volt DVM. File: dvm.asm ; ; Global Assembler directives list c=132, n=80 list p=16F873A errorlevel -302 ; Disable messages for register banking errorlevel -306 ; Disable messages for page crossing ; ; This program reads a 8:1 scaled voltage on AN0 and displays it's value ; on a multiplexed 4-digit LED display. Uses a 2.5 volt reference. ; ; Copyright (c) 2007 Dan Julio. Some rights reserved. ; Licensed under a Creative Commons Attribution 3.0 License ; Please see http://creativecommons.org/licenses/by/3.0/legalcode ; ; Revision History ; 05/17/07 Revision 1.0 DJJ First pass ; ; ; ------------------------------------------------------------------------ ; Processor Information: ; ; Target processor: PIC 16C873A ; Processor frequency: 10 MHz (400 ns instruction cycle) ; ; Memory Layout ; Page 0: 0x0000 - 0x07FF Main code, Interrupt handler, subroutines ; Page 1: 0x0800 - 0x0FFF Not used ; ; Input/Output Pin Assignments (PDIP/SOIC package) ; ; Pin Signal Polarity I/O Default State ; ------------------------------------------------------------------------ ; 2 RA0 Analog In - Input 0 ; 3 RA1 Reserved - Output 0 ; 4 RA2 Vref - In - Input 0 ; 5 RA3 Vref + In - Input 0 ; 6 RA4 Reserved - Output 0 open drain output ; 7 RA5 Reserved - Output 0 ; 21 RB0 Segment 'a' Active Low Output 1 ; 22 RB1 Segment 'b' Active Low Output 1 ; 23 RB2 Segment 'c' Active Low Output 1 ; 24 RB3 Segment 'd' Active Low Output 1 ; 25 RB4 Segment 'e' Active Low Output 1 ; 26 RB5 Segment 'f' Active Low Output 1 ; 27 RB6 Segment 'g' Active Low Output 1 ; 28 RB7 Segment 'dp' Active Low Output 1 ; 11 RC0 Digit 0 Anode Active High Output 0 ; 12 RC1 Digit 1 Anode Active High Output 0 ; 13 RC2 Digit 2 Anode Active High Output 0 ; 14 RC3 Digit 3 Anode Active High Output 0 ; 15 RC4 Reserved - Output 0 ; 16 RC5 Reserved - Output 0 ; 17 RC6 Reserved - Output 0 ; 18 RC7 Reserved - Output 0 ; ; ; ; ------------------------------------------------------------------------ ; File Layout: ; ; 1. Configuration Bits ; 2. Program Constants ; 3. Macro definitions ; 4. Data EEPROM assembler initialization directives ; 5. Main code ; 6. Main code subroutines ; 7. Interrupt routines ; 8. Interrupt subroutines ; ; ; ------------------------------------------------------------------------ ; Configuration Bits _CP_ALL EQU H'0FCF' _CP_HALF EQU H'1FDF' _CP_UPPER_256 EQU H'2FEF' _CP_OFF EQU H'3FFF' _DEBUG_ON EQU H'37FF' _DEBUG_OFF EQU H'3FFF' _WRT_ENABLE_ON EQU H'3FFF' _WRT_ENABLE_OFF EQU H'3DFF' _CPD_ON EQU H'3EFF' _CPD_OFF EQU H'3FFF' _LVP_ON EQU H'3FFF' _LVP_OFF EQU H'3F7F' _BODEN_ON EQU H'3FFF' _BODEN_OFF EQU H'3FBF' _PWRTE_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' ; Set the device configuration bits ; CP1:CP0 = 11 (code protection off) ; DEBUG = 1 (RB6/7 are general purpose IO pins) ; WRT = 1 (unprotected program memory may be written) ; CPD = 1 (Data EE code protection off) ; LVP = 0 (RB3 is digital IO) ; BODEN = 1 (Brown-out reset enabled) ; PWRTE_ = 1 (Power-up Timer enabled) ; WDTE = 1 (WDT enabled) ; FOSC1:0 = 10 (High-speed osc) __config _HS_OSC & _LVP_OFF & _WRT_ENABLE_OFF & _BODEN_ON & _PWRTE_ON ; ------------------------------------------------------------------------ ; Program Constants ; ; PIC Internal register equates W equ .0 F equ .1 ; Bank 0 and registers in all banks INDF equ 0x0 TMR0 equ 0x1 PCL equ 0x2 STATUS equ 0x3 FSR equ 0x4 PORT_A equ 0x5 PORT_B equ 0x6 PORT_C equ 0x7 PCLATH equ 0xA INTCON equ 0xB PIR1 equ 0xC PIR2 equ 0xD TMR1L equ 0xE TMR1H equ 0xF T1CON equ 0x10 TMR2 equ 0x11 T2CON equ 0x12 SSPBUF equ 0x13 SSPCON equ 0x14 CCPR1L equ 0x15 CCPR1H equ 0x16 CCP1CON equ 0x17 RCSTA equ 0x18 TXREG equ 0x19 RCREG equ 0x1A CCPR2L equ 0x1B CCPR2H equ 0x1C CCP2CON equ 0x1D ADRESH equ 0x1E ADCON0 equ 0x1F ; Bank 1 specific OPTION_REG equ 0x1 TRISA equ 0x5 TRISB equ 0x6 TRISC equ 0x7 PIE1 equ 0xC PIE2 equ 0xD PCON equ 0xE SSPCON2 equ 0x11 PR2 equ 0x12 SSPADD equ 0x13 SSPSTAT equ 0x14 TXSTA equ 0x18 SPBRG equ 0x19 ADRESL equ 0x1E ADCON1 equ 0x1F ; Bank 2 specific EEDATA equ 0xC EEADR equ 0xD EEDATH equ 0xE EEADRH equ 0xF ; Bank 3 specific EECON1 equ 0xC EECON2 equ 0xD ; ; PIC Internal STATUS register bits CARRY equ .0 DC equ .1 Z equ .2 PD equ .3 TO equ .4 RP0 equ .5 RP1 equ .6 IRP equ .7 ; ; Interrupt sources in the INTCON register RBIF_INT equ .0 INTF_INT equ .1 TMR0_INT equ .2 ; ; Interrupt enable bits in the INTCON register RBIE equ .3 INTE equ .4 T0IE equ .5 PEIE equ .6 GIE equ .7 ; ; Interrupt sources in the PIR1 register TMR1IF equ .0 TMR2IF equ .1 CCP1IF equ .2 SSPIF equ .3 TXIF equ .4 RCIF equ .5 ADIF equ .6 PSPIF equ .7 ; ; Status/Control bits in the RCSTA register OERR equ .1 FERR equ .2 CREN equ .4 ; ; Status/Control bits in the ADCON0 ADC_GO equ .2 ADC_ON equ .0 ; ; EECON1 control bits RD equ .0 WR equ .1 WREN equ .2 WRERR equ .3 EEPGD equ .7 ; ; Flags variable bits Trigger equ .0 ; Set to trigger a calculation and display update ; ; Variable register equates TempData equ 0x20 ; Scratch register Digit0 equ 0x21 ; BCD value for digit 0 (values > 9 blank display) Digit1 equ 0x22 Digit2 equ 0x23 Digit3 equ 0x24 Flags equ 0x26 ReadCounter equ 0x27 ; Counts ADC slots to trigger a ADC reading DigitCount equ 0x28 ; 0-4 (0-3-> digit, 4-> possible ADC reading slot) ; ; Math routine ACCaHI equ 0x30 ; Operand 1 hi ACCaLO equ 0x31 ; Operand 1 lo ACCbHI equ 0x32 ; Operand 2 hi / Result hi ACCbLO equ 0x33 ; Operand 2 lo / Result lo ACCcHI equ 0x34 ; Result hi ACCcLO equ 0x35 ; Result lo ACCdHI equ 0x36 ; Temporary operand / Result hi ACCdLO equ 0x37 ; Temporary operand / Result lo ACCeHI equ 0x38 ; Temporary operand ACCeLO equ 0x39 ; Temporary operand ACCfHI equ 0x3A ; Temporary operand ACCfLO equ 0x3B ; Temporary operand ; ; Additional temp registers for math routines ACCtemp equ 0x3C ; ; Interrupt state save registers FSR_Temp equ 0x7C STATUS_Temp equ 0x7D PCLATH_Temp equ 0x7E W_Temp equ 0x7F ; Note, shadowed in bank 1 as well (see PIC16F87X spec 12.11) ; ; ; Initialization constants STATUS_SETUP equ B'00011000' ; 7: IRP = 0 (page 0-1) ; 6:5: RP1:0 = 00b (page 0) ; 4: TO = 1 (default state) ; 3: PD = 1 (default state) ; 2: Z = 0 ; 1: DC = 0 ; 0: C = 0 OPTION_SETUP equ B'11000100' ; 7: RBPU = 1 (disable pull-ups) ; 6: INTEDG = 1 (Int on rising edge) ; 5: T0CS = 0 (internal TMR0 clock) ; 4: T0SE = 0 (rising TMR0 clock) ; 3: PSA = 0 (prescaler to TMR0) ; 2:0: prescaler = 100b (1:32) INTCON_SETUP equ B'10100000' ; 7: GIE = 1 (interrupts enabled) ; 6: PEIE = 0 (Peripheral ints disabled) ; 5: T0IE = 1 (TMR0 ints enabled) ; 4: INTE = 0 (INT ints disabled) ; 3: RBIE = 0 (RB ints disabled) ; 2: T0IF = 0 ; 1: INTF = 0 ; 0: RBIF = 0 PIE1_SETUP equ B'00000000' ; 7: PSPIE = 0 (Parallel Slave) ; 6: ADIE = 0 (A/D converter) ; 5: RCIE = 0 (USART RX Enable) ; 4: TXIE = 0 (USART TX Enable) ; 3: SSPIE = 0 (Sync Serial Port) ; 2: CCP1IE = 0 (CCPQ Int enable) ; 1: TMR2IE = 0 (TMR2 to PR2 match) ; 0: TMR1IE = 0 (TMR1 Overflow) PIE2_SETUP equ B'00000000' ; 7: Unimplemented ; 6: Reserved ; 5: Unimplemented ; 4: EEIE = 0 (EEPROM Write) ; 3: BCLIE = 0 (Bus collision) ; 2: Unimplemented ; 1: Unimplemented ; 0: CCP2IE = 0 (CCP2 Int Enable) PORTA_INIT equ B'00000000' ; No outputs enabled PORTB_INIT equ B'11111111' ; No outputs enabled PORTC_INIT equ B'00000000' ; No outputs enabled PORTA_OE equ B'00001101' ; RA[5:4], RA[1] outputs PORTB_OE equ B'00000000' ; All outputs PORTC_OE equ B'00000000' ; All outputs TXSTA_INIT equ B'00000100' ; 7: CSRC = 0 (Don't care) ; 6: TX9 = 0 (8-bit transmission)` ; 5: TXEN = 0 (transmit disabled) ; 4: SYNC = 0 (asynch mode) ; 3: Reserved ; 2: BRGH = 1 (high speed mode) ; 1: TRMT = 0 ; 0: TX9D = 0 (9th xmit data) RCSTA_INIT equ B'00010000' ; 7: SPEN = 0 (Serial port disabled) ; 6: RX9 = 0 (8-bit reception) ; 5: SREN = 0 (Don't care) ; 4: CREN = 1 (enable continuous) ; 3: ADDEN = 0 (disable addr detection) ; 2: FERR = 0 ; 1: OERR = 0 ; 0: RX9D = 0 ADCON0_EN_CH0 equ B'10000001' ; 7:6: ADCS[1:0] = 10 (Fosc/32) ; 5:3: CHS[2:0] = 000 (Channel 0) ; 2: GO/~DONE = 0 ; 1: Reserved ; 0: ADON = 1 (ADC on) ADCON1_INIT equ B'10001111' ; 7: ADFM = 1 (Right justify results) ; 6:4: Reserved ; 3:0: PCFG3:0 = 1111 ; (RA5:4,1 digital ports) ; ; Global constants TMR0_PRESET equ .256 - .156 ; 156 x 32 = 4992 cycles => approx 500 Hz int rate ADC_READ_COUNT equ .33 ; Number of 100 Hz intervals between ADC readings NUM_DIGITS equ .4 ; Number of multiplexed digits NUM_SLOTS equ NUM_DIGITS+.1 ; Add slot for ADC readings ; ; Digit Enable Masks EN_NO_DIGITS equ 0x00 EN_DIGIT0 equ 0x01 EN_DIGIT1 equ 0x02 EN_DIGIT2 equ 0x04 EN_DIGIT3 equ 0x08 ; ; Segment combinations CHAR_0 equ ~B'00111111' CHAR_1 equ ~B'00000110' CHAR_2 equ ~B'01011011' CHAR_3 equ ~B'01001111' CHAR_4 equ ~B'01100110' CHAR_5 equ ~B'01101101' CHAR_6 equ ~B'01111101' CHAR_7 equ ~B'00000111' CHAR_8 equ ~B'01111111' CHAR_9 equ ~B'01101111' CHAR_B equ ~B'00000000' ; Blank display ; ------------------------------------------------------------------------ ; Macro Definitions ; SET_DIGIT macro Val movf PORT_C,W andlw 0xF0 iorlw Val movwf PORT_C endm BLANK_DIGITS macro movf PORT_C,W andlw 0xF0 movwf PORT_C endm ; ------------------------------------------------------------------------ ; Program EEPROM Initialization ; ; ------------------------------------------------------------------------ ; Data EEPROM Initialization ; ; ------------------------------------------------------------------------ ; Code Block ; ; Reset Vector goto MAIN_VECTOR ; ------------------------------------------------------------------------ ; Interrupt Code Block org 0x4 ; ISR: Executed each time TIMER0 counts down, approximately 500 Hz. Each time ; through this code we process one Digit or we read the ADC: ; ; Digit0 - Digit1 - Digit2 - Digit3 - Read ADC ; ; resulting in a display update of about 100 Hz. The Read ADC activity is done ; only every ADC_READ_COUNT times since we don't need to ; ; On entry: DigitCount indicates which digit (or ADC slot) to execute ; Digit3:0 contain 7-segment data for each digit ; ReadCounter contains current ADC slot timer count value ; ; On exit: Flags.Trigger set when the ADC has been read ; TMR0 setup to countdown to next interrupt ; ; Save interrupt state movwf W_Temp swapf STATUS,W clrf STATUS movwf STATUS_Temp movf PCLATH,W movwf PCLATH_Temp clrf PCLATH ; Re-Setup TMR0 movlw TMR0_PRESET movwf TMR0 ; Jump to code based on DigitCount movf DigitCount,W andlw 0x07 addwf PCL,F goto HANDLE_DIGIT0 goto HANDLE_DIGIT1 goto HANDLE_DIGIT2 goto HANDLE_DIGIT3 goto HANDLE_ADC goto ISR_RESTORE ; Should never be reached... goto ISR_RESTORE goto ISR_RESTORE HANDLE_DIGIT0 ; Don't need to do inner-digit blank here SET_DIGIT EN_DIGIT0 movf Digit0,W call Calc7segment movwf PORT_B goto SETUP_NEXT_DIGIT HANDLE_DIGIT1 BLANK_DIGITS SET_DIGIT EN_DIGIT1 movf Digit1,W call Calc7segment movwf PORT_B goto SETUP_NEXT_DIGIT HANDLE_DIGIT2 BLANK_DIGITS SET_DIGIT EN_DIGIT2 movf Digit2,W call Calc7segment andlw 0x7F ; Set the decimal point movwf PORT_B goto SETUP_NEXT_DIGIT HANDLE_DIGIT3 BLANK_DIGITS SET_DIGIT EN_DIGIT3 movf Digit3,W call Calc7segment movwf PORT_B goto SETUP_NEXT_DIGIT HANDLE_ADC BLANK_DIGITS decfsz ReadCounter,F ; Time to read the ADC? goto SETUP_NEXT_DIGIT ; N: Done for now movlw ADC_READ_COUNT ; Y: Reset for next time and read ADC movwf ReadCounter bsf Flags,Trigger ; Note to do calculations in the main loop ; Trigger the ADC bsf ADCON0,ADC_GO GET_ANALOG_VAL_POLL btfsc ADCON0,ADC_GO goto $-1 SETUP_NEXT_DIGIT incf DigitCount,F ; Next Digit movf DigitCount,W sublw NUM_SLOTS ; Reset if DigitCount = NUM_SLOTS btfsc STATUS,Z clrf DigitCount ISR_RESTORE ; Finally, clear this interrupt before returning bcf INTCON,TMR0_INT ; Restore interrupt state movf PCLATH_Temp,W movwf PCLATH swapf STATUS_Temp,W movwf STATUS swapf W_Temp,F swapf W_Temp,W ; Return from the interrupt (and re-enable interrupts) retfie ; ------------------------------------------------------------------------ ; Main Code Block ; MAIN_VECTOR ; Initialize unique PIC control registers in page 0 clrf TMR0 clrf FSR clrf PCLATH movlw STATUS_SETUP movwf STATUS movlw PORTA_INIT movwf PORT_A movlw PORTB_INIT movwf PORT_B movlw PORTC_INIT movwf PORT_C movlw RCSTA_INIT movwf RCSTA movlw ADCON0_EN_CH0 movwf ADCON0 ; Initialize unique PIC control registers in page 1 bsf STATUS,RP0 ; Point to page 1 registers movlw OPTION_SETUP movwf OPTION_REG movlw PORTA_OE movwf TRISA movlw PORTB_OE movwf TRISB movlw PORTC_OE movwf TRISC movlw TXSTA_INIT movwf TXSTA movlw PIE1_SETUP movwf PIE1 movlw PIE2_SETUP movwf PIE2 movlw ADCON1_INIT movwf ADCON1 bcf STATUS,RP0 ; Point to page 0 registers again ; Initialize unique PIC control registers in page 2 bsf STATUS,RP1 ; Point to page 2 registers clrf EEADRH clrf EEDATH bcf STATUS,RP1 ; Point to page 0 registers again ; Initialize variables clrf Digit0 clrf Digit1 clrf Digit2 clrf Digit3 clrf Flags movlw ADC_READ_COUNT movwf ReadCounter clrf DigitCount movlw TMR0_PRESET movwf TMR0 ; Finally, enable interrupts before starting normal operation movlw INTCON_SETUP movwf INTCON MAIN_LOOP ; Main loop: wait for a trigger then update the measured voltage ; reading and digit variables clrwdt ; Clear the watchdog timer ; Check for a trigger from the last ADC read in the ISR btfss Flags,Trigger goto MAIN_LOOP bcf Flags,Trigger ; For four digits in the for II.FF with a range of 0-20.00 volts: ; Ideal Voltage = (AdcCount * 2000) / 1023 ; Calculate AdcCount * 2000 movf ADRESH,W movwf ACCaHI bsf STATUS,RP0 movf ADRESL,W bcf STATUS,RP0 movwf ACCaLO ; Check for special case of AdcCount = 0 movf ACCaHI,W btfss STATUS,Z goto MAIN_DO_CALC movf ACCaLO,W btfsc STATUS,Z goto MAIN_ADC_EQ_0 MAIN_DO_CALC movlw (.2000 >> 8) & 0xFF movwf ACCbHI movlw .2000 & 0xFF movwf ACCbLO call mul32 ; Results in {ACCb, ACCc} ; Calculate divide by 1023 movlw (.1023 >> 8) & 0xFF movwf ACCaHI movlw .1023 & 0xFF movwf ACCaLO call div32 ; Result in {ACCb, ACCc} remainder ACCd ; ACCb should be 0 (ACCc <= 2000) ; Round the result in {ACCb, ACCc} up if ACCd > 511 movlw (.511 >> 8) & 0xFF movwf ACCbHI movlw .511 & 0xFF movwf ACCbLO movf ACCdHI,W movwf ACCaHI movf ACCdLO,W movwf ACCaLO call comp16 addlw 0 ; Update Z: W = 1 if ACCd > 511 btfsc STATUS,Z ; Z = 0? => W = 1 goto MAIN_CONVERT_TO_BCD incf ACCcLO,F btfsc STATUS,Z ; rollover? incf ACCcHI,F goto MAIN_CONVERT_TO_BCD MAIN_ADC_EQ_0 clrf ACCcHI clrf ACCcLO MAIN_CONVERT_TO_BCD ; Convert to BCD movf ACCcHI,W movwf ACCaHI movf ACCcLO,W movwf ACCaLO call bin2bcd ; Results in {ACCdLO[3:0], ACCcHI, ACCcLO} ; Move packed BCD to digits and do leading 0 blanking movf ACCbLO,W andlw 0x0F movwf Digit0 swapf ACCbLO,W andlw 0x0F movwf Digit1 movf ACCbHI,W andlw 0x0F movwf Digit2 swapf ACCbHI,W andlw 0x0F btfsc STATUS,Z ; Digit 3 = 0? movlw 0xF ; Y: Force this digit to be blanked movwf Digit3 goto MAIN_LOOP ; ------------------------------------------------------------------------ ; Start of Page 0 subroutines ; ; Calc7segment: Return segment enable bits in W ; ; On entry: W contains the BCD value ; ; On exit: W contains segment data ; Calc7segment andlw 0x0F addwf PCL,F retlw CHAR_0 retlw CHAR_1 retlw CHAR_2 retlw CHAR_3 retlw CHAR_4 retlw CHAR_5 retlw CHAR_6 retlw CHAR_7 retlw CHAR_8 retlw CHAR_9 retlw CHAR_B retlw CHAR_B retlw CHAR_B retlw CHAR_B retlw CHAR_B retlw CHAR_B ; sub16, add16: 16-bit unsigned subtraction and addition routines. Taken from ; Microchip AN-00526. These routines implement 16-bit unsigned operations. ; ; On entry: ACCbHI, ACCbLO contain operand 1 ; ACCaHI, ACCaLO contain operand 2 ; ; On exit: ACCbHI, ACCbLO contain the result, either ACCb - ACCa or ; ACCb + ACCa ; ; Notes: These routines are together because sub16 uses add16 ; ; Timing: sub16 requires 13 cycles ; add16 requires 8 cycles ; sub16 ; Negate ACCa comf ACCaLO,F incf ACCaLO,F btfsc STATUS,Z decf ACCaHI,F comf ACCaHI,F ; Then add (ACCb + negated ACCa) add16 movf ACCaLO,W addwf ACCbLO,F ; add lsb btfsc STATUS,CARRY ; add in carry incf ACCbHI,F movf ACCaHI,W addwf ACCbHI,F ; add msb return ; comp16: Return true if B=A ; W = 1 (true) if B=A ; Negate ACCa comf ACCaLO,F incf ACCaLO,F btfsc STATUS,Z decf ACCaHI,F comf ACCaHI,F ; Then add (ACCb + negated ACCa) with carry out movf ACCaLO,W addwf ACCbLO,F ; Add lsb btfss STATUS,CARRY ; Carry out of lsb addition? goto COMP16_MSB_ADD ; N: Just add msb incfsz ACCbHI,F ; Y: Add carry to msb, check for rollover? goto COMP16_MSB_ADD ; N: Then add msb bsf STATUS,CARRY ; Y: There will be carry out of msb, movf ACCaHI,W ; Can just set ACCbHI = ACCaHI movwf ACCbHI ; since ACCbHI = 0 and leave C set goto COMP16_CHECK_C COMP16_MSB_ADD movf ACCaHI,W addwf ACCbHI,F COMP16_CHECK_C ; Check the CARRY out of the 16-bit subtraction btfsc STATUS,CARRY ; CARRY = 1 => B>=A (CARRY = 0 => B ACCb) addwf ACCbLO,F btfsc STATUS,CARRY ; add in carry incf ACCbHI,F movf ACCaHI,W addwf ACCbHI,F ; add msb MUL_UPDATE_RES rrf ACCbHI,F rrf ACCbLO,F rrf ACCcHI,F rrf ACCcLO,F decfsz ACCtemp,F goto MUL_32_LOOP return ; div32: Divide routine for a 32/16 = 16 bit result with 16 bit remainder. Taken ; from a routine in Microchip AN-00526. This routine implements an unsigned divide. ; ; On entry: ACCaHI, ACCaLO contain the denominator ; {ACCbHI, ACCbLO, ACCcHI, ACCcLO} contain the numerator ; ; On exit: 32-bit result in {ACCbHI, ACCbLO, ACCcHI, ACCcLO} remainder in {ACCcHI, ACCcLO} ; ; Notes: The numerator must be >= denominator ; ; Timing: Requires 721 - 945 cycles ; div32 ; Setup movlw .32 ; setup shift counter movwf ACCtemp movf ACCbHI,W ; move ACCb to ACCf movwf ACCfHI movf ACCbLO,W movwf ACCfLO movf ACCcHI,W ; move ACCc to ACCe movwf ACCeHI movf ACCcLO,W movwf ACCeLO clrf ACCbHI clrf ACCbLO clrf ACCcHI clrf ACCcLO clrf ACCdHI clrf ACCdLO DIV_32_LOOP bcf STATUS,CARRY rlf ACCeLO,F rlf ACCeHI,F rlf ACCfLO,F rlf ACCfHI,F rlf ACCdLO,F rlf ACCdHI,F movf ACCaHI,W subwf ACCdHI,W ; check if a>d btfss STATUS,Z goto DIV32_NO_CHK movf ACCaLO,W subwf ACCdLO,W ; if msb equal then check lsb DIV32_NO_CHK btfss STATUS,CARRY ; carry set if d>a goto DIV32_NOGO movf ACCaLO,W ; d-a into d subwf ACCdLO,F btfss STATUS,CARRY decf ACCdHI,F movf ACCaHI,W subwf ACCdHI,F bsf STATUS,CARRY ; shift a 1 into b (result) DIV32_NOGO rlf ACCcLO,F rlf ACCcHI,F rlf ACCbLO,F rlf ACCbHI,F decfsz ACCtemp,F ; loop until all bits checked goto DIV_32_LOOP return ; bin2bcd: Convert a 16-bit binary number into a 5-digit packed BCD result. Taken ; from Microchip AN526. ; ; On entry: {ACCaHI, ACCaLO} contain the number to convert ; ; On exit: {ACCcLO[3:0], ACCbHI, ACCbLO} contain the converted result ; ; Timing: Requires 935 cycles ; bin2bcd bcf STATUS,CARRY movlw .16 movwf ACCtemp clrf ACCbLO clrf ACCbHI clrf ACCcLO bin2bcdloop rlf ACCaLO, F rlf ACCaHI, F rlf ACCbLO, F rlf ACCbHI, F rlf ACCcLO, F decfsz ACCtemp,F goto adjDEC retlw 0 adjDEC movlw ACCbLO movwf FSR call adjBCD movlw ACCbHI movwf FSR call adjBCD movlw ACCcLO movwf FSR call adjBCD goto bin2bcdloop adjBCD movlw .3 addwf INDF,W movwf TempData btfsc TempData,.3 ; test if result > 7 movwf INDF movlw 0x30 addwf INDF,W movwf TempData btfsc TempData,.7 ; test if result > 7 movwf INDF ; save as MSD retlw 0 end