; ************************************************************ ; MegMeterMk2.asm - firmware for Mk2 version of ; Digital Megohm and Insulation Leakage Meter ; ; Written by Jim Rowe for Silicon Chip, using a PIC16F88 ; processor running from its internal clock oscillator ; at 8MHz, so one machine cycle (mc) = 0.5us. ; Program last revised 30/04/2010. ; (added 10-reading averaging, leading zero blanking) ; ; Note: Program makes use of 24-bit and 32-bit floating point & ; fixed point maths routines for Microchip Technology Inc's ; 8-bit PIC processors, written by Frank J. Testa and ; described in MTI's App Notes AN575, AN617, AN660 and AN670, ; downloadable from the MTI website at www.microchip.com ; (Routines used here are all in FPRF24.TXT) ; ; ********************************************************** ; ; CPU configuration ; list p=PIC16f88 #include "p16f88.inc" __CONFIG _CONFIG1, h'3F39' __CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF ; ;********************************************************** ; ; First define some handy macros ; ; Select Register Bank 0 bank0 macro errorlevel +302 ; Re-enable bank warning BCF STATUS,RP0 ; Select Bank 0 BCF STATUS, RP1 BCF STATUS, IRP ; and also indir addr banks 0,1 endm ; Select Register Bank 1 bank1 macro BSF STATUS,RP0 ; Select Bank 1 errorlevel -302 ; disable warning endm ; Swap bytes in register file via W swap macro this,that MOVF this,w ; get this XORWF that,f ; Swap using Microchip XORWF that,w ; Tips'n Tricks XORWF that,f ; #18 MOVWF this endm ; Copy bytes in register file via W copy macro from,to MOVF from,W MOVWF to endm ; Prepare to call or goto page1 (800-FFFh) Ppage1 macro BCF PCLATH,4 ; clear bit4 of PCLATH BSF PCLATH,3 ; but set bit3 endm ; Prepare to call or goto page0 (000-7FFh) Ppage0 macro BCF PCLATH,4 ; clear bit4 of PCLATH BCF PCLATH,3 ; and also clear bit3 endm ; ; ********************************************************** ; STATUS bit definitions (used in FPRF24.TXT) #define _C STATUS,0 #define _Z STATUS,2 ; ;************************************************************** ; ; storage register declarations: Counter1 equ h'20' ; gp counter variable 1 Counter2 equ h'21' ; gp counter variable 2 Counter3 equ h'22' ; gp counter variable 3 Counter4 equ h'23' ; gp counter variable 4 MLoopCtr equ h'24' ; loop counter for measurement averaging Temp1 equ h'25' ; working storage location 1 Temp2 equ h'26' ; working storage location 2 VoltFlag equ h'27' ; test voltage flag ; (02h = 1000V, 01h = 500V, 00h = 250V) MRFlag equ h'28' ; current range flag (bit 0 = 1 for 10mA) VDig1 equ h'29' ; first digit of test volts indication (1/ / ) VDig2 equ h'2A' ; second digit of test volts indication (0/5/2) VDig3 equ h'2B' ; third digit of test volts indication (0/0/5) IDig1 equ h'2C' ; first digit of leakage current reading IDig2 equ h'2D' ; second digit of leakage current reading IDig3 equ h'2E' ; third digit of leakage current reading IDig4 equ h'2F' ; fourth digit of leakage current reading (m/mu) RDig1 equ h'30' ; first digit of resistance reading RDig2 equ h'31' ; second digit of resistance reading (DP on Hi range) RDig3 equ h'32' ; third digit of resistance reading ; storage of the 24-bit FP test voltage (pre calculated) TVoltEXP equ h'33' ; exponent TVoltB0 equ h'34' ; MSB TVoltB1 equ h'35' ; LSB ; storage for Ix value in 24-bit form, after calculation IxEXP equ h'36' ; exponent IxB0 equ h'37' ; MSB & sign bit IxB1 equ h'38' ; LSB ; storage for Rx value in 24-bit form, after calculation RxEXP equ h'39' ; exponent RxB0 equ h'3A' ; MSB & sign bit RxB1 equ h'3B' ; and LSB ; storage for ADC reading in 24-bit form, after calculation ADCEXP equ h'3C' ; exponent ADCB0 equ h'3D' ; MSB & sign bit ADCB1 equ h'3E' ; LSB ; *********************************************************** ; Floating Point Stack & other locations used by FPRF24.TXT ; routines ; AARGB7 equ h'50' ; AARGB7 byte for FP argument A AARGB6 equ h'51' ; AARGB6 byte AARGB5 equ h'52' ; AARGB5 byte AARGB4 equ h'53' ; AARGB4 byte AARGB3 equ h'54' ; AARGB3 AARGB2 equ h'55' ; AARGB2 AARGB1 equ h'56' ; AARGB1 AARGB0 equ h'57' ; AARGB0 AEXP equ h'58' ; 8 bit biased exponent for argument A BARGB3 equ h'59' ; BARGB3 byte for argument B BARGB2 equ h'5A' ; BARGB2 BARGB1 equ h'5B' ; BARGB1 BARGB0 equ h'5C' ; BARGB0 BEXP equ h'5D' ; 8 bit biased exponent for argument B TEMPB3 equ h'5E' ; TEMPB3 byte TEMPB2 equ h'5F' ; TEMPB2 byte TEMPB1 equ h'60' ; TEMPB1 byte TEMPB0 equ h'61' ; TEMPB0 byte CARGB1 equ h'62' ; CARGB1 byte for argument C CARGB0 equ h'63' ; CARGB0 byte CEXP equ h'64' ; 8 bit biased exponent for argument C DARGB3 equ h'65' ; DARGB3 byte for argument D DARGB2 equ h'66' ; DARGB2 byte DARGB1 equ h'67' ; DARGB1 byte DARGB0 equ h'68' ; DARGB0 byte DEXP equ h'69' ; 8-bit biased exponent for argument D EARGB3 equ h'6A' ; needed by EXP1024, it seems SIGN equ h'6B' ; location for saving sign in MSB FPFLAGS equ h'6C' ; floating point library exception flags FPError equ h'6D' ; floating point routine error code (FFh = error) ; Fixed point storage locations REMB3 equ h'70' ; remainder LSB REMB2 equ h'71' ; remainder middle byte REMB1 equ h'72' ; remainder upper middle byte REMB0 equ h'73' ; remainder MSB LOOPCOUNT equ h'74' ; loop counter ; Locations used by float_ascii to store result digits cblock h'75' ones ; where units digit is stored (75h) tenths ; where tenths digit is stored (76h) hundredths ; where hundredths digit is stored (77h) thousandths ; where thousandths digit is stored (78h) tenthous ; where ten thousandths digit is stored (79h) endc digit_count equ h'7A' ; digit counter used by float_ascii ; floating point library exception flag bits ; IOV equ 0 ; bit0 = integer overflow flag FOV equ 1 ; bit1 = FP overflow flag FUN equ 2 ; bit2 = FP underflow flag FDZ equ 3 ; bit3 = FP divide by zero flag NAN equ 4 ; bit4 = not-a-number exception flag DOM equ 5 ; bit5 = domain error exception flag RND equ 6 ; bit6 = FP rounding flag ; 0 = truncation ; 1 = unbiased rounding to nearest LSB SAT equ 7 ; bit7 = FP saturate flag ; 0 = term on exception w/out saturation ; 1 = term on exception with saturation ; to appropriate value EXP equ AEXP TEMP equ TEMPB0 ; define assembler constants B0 equ 0 B1 equ 1 B2 equ 2 B3 equ 3 B4 equ 4 B5 equ 5 B6 equ 6 B7 equ 7 MSB equ 7 LSB equ 0 ; Floating point literal constants ; EXPBIAS equ h'7F' ; = 127d, bias for exponents ;************************************************************* ; Program itself now begins ; org h'00' ; normal start vector GOTO Initialise org h'04' ; interrupt service vector GOTO Initialise ; (we are not using interrupts here) Initialise: ; first we set up CPU and INTOSC, I/O ports and ADC modules bank0 ; make sure we're set for bank 0 CLRF INTCON ; disable interrupts Ppage0 ; make sure PCLATH is set for page0 CLRF CCP1CON ; disable the CCP module CLRF RCSTA ; and also the serial port CLRF PORTA ; clear PORTA CLRF PORTB ; also PORTB bank1 ; then switch to bank1 MOVLW h'70' ; set INTOSC for 8MHz MOVWF OSCCON CLRF OSCTUNE ; and also set to centre frequency CLRF PIE1 ; turn off peripheral interrupts CLRF CVRCON ; now disable comparator Vref module MOVLW h'07' ; and disable the comparators MOVWF CMCON ; (by setting them for mode 111) MOVLW h'40' ; then set RB0-5, RB7 as outputs, RB6 as an input MOVWF TRISB MOVLW h'9D' ; also set RA0, RA2-4, RA7 as inputs, RA1,RA5-6 as outputs MOVWF TRISA MOVLW h'0C' ; then set RA2 to be AN2, RA3 to be Vref+ input MOVWF ANSEL MOVLW h'E0' ; now set ADC for right justify, range 0V to Vref+ MOVWF ADCON1 ; and divide system clock by 2 bank0 ; then down to bank 0 again, so we can MOVLW h'11' ; now turn on ADC, make Tad = Toscx4, reset ADC MOVWF ADCON0 ; and set AN2 as only ADC input channel CALL SetVolts ; now check S1, set TestVolt flag and constant values CALL DispInit ; now initialise the LCD module CALL Display1 ; and show greeting display CALL Wait4sec ; then pause for 4 seconds CALL Display2 ; now display 'begin test' information CALL SetHiRange ; then ensure we're initially set for 10mA range Mainloop ; Main operating loop begins here, to process and display ; readings via the ADC and also sense S1 (test voltage range) ; We take a reading via the ADC module. Note that ; result appears in ADRESH (1Eh) and ADRESL (9Eh) registers CALL InitDig ; first initialise VDig, IDig, RDig display digits CALL SetVolts ; then check S1, set TestVolt flag & constant BTFSS PORTA,4 ; now test RA4 to see if S2 is pressed GOTO $-1 ; else keep looping until it is pressed CALL TakeaReading ; once S2 pressed, go take a set of 10 readings CALL Display3 ; then update LCD display ; (a delay here would slow down sampling rate) GOTO Mainloop ; and keep looping ; main program loop ends -- subroutines follow ;************************************************************** ; Check4FPE: ; routine to check if floating point functions had errors IORLW h'00' ; check if w came back with 00h or FFh BTFSC STATUS,Z ; if Z=0, must have been FFh (= an error) RETURN ; if Z=1, must have been 00h (= no error) BTFSS FPFLAGS,FDZ ; check if divide-by-zero flag is set GOTO $+8 ; if not, must be some other error MOVLW h'88' ; but if it is, just set Rtotal to 1010M MOVWF AEXP ; (88 7C 80 in Microchip 24b FP format) MOVLW h'7C' ; which we place in AARG regs so it will MOVWF AARGB0 ; be used in calculating Rx MOVLW h'80' ; (so O/C input should display as Ix = 0, MOVWF AARGB1 ; and Rx = 999) RETURN ; then resume CALL ErrorDisp ; was an error, so display message RETURN ; and then return to resume anyway ClearLCD: ;routine to clear LCD and reset address counter MOVLW h'01' ; clear display & reset addr ptr CALL DispAddress CALL Delay160ms ; pause 160ms to give it time to clear CALL Delay160ms ; and again, just for sloooow LCDs RETURN ; then return Delay1ms: ;routine to delay approx 1ms (2058 x 0.5us = 1029us) before return MOVLW h'0A' ; set Counter1 for 10 outer loops MOVWF Counter1 OuterLoop: MOVLW h'42' ; and Counter2 for 66 inner loops MOVWF Counter2 ; (66 x 3mc = 198mc, + 7mc) DECFSZ Counter2, 1 ; decrement Counter2 & skip when zero GOTO $-1 ; not zero yet, so loop back DECFSZ Counter1, 1 ; did reach zero, so decrement Counter1 GOTO OuterLoop ; didn't hit zero, so loop back RETURN ; reached zero (10 x 66 loops) so return Delay10ms: ;routine to delay approx 10ms before returning MOVLW h'0A' ; set Counter3 for 10 outer loops MOVWF Counter3 CALL Delay1ms ; then wait 1ms DECFSZ Counter3, 1 ; then decrement Counter3 & skip when zero GOTO $-2 ; not zero yet, so keep going RETURN ; reached zero, so return Delay160ms: ;routine to delay approx 160ms before returning MOVLW h'A0' ; set Counter3 for 160 outer loops MOVWF Counter3 CALL Delay1ms ; then wait 1ms DECFSZ Counter3, 1 ; then decrement Counter3 & skip when zero GOTO $-2 ; not zero yet, so keep going RETURN ; reached zero, so return DispAddress: ;routine to translate & load display address (in w reg) into LCD BCF PORTB,5 ; first set RS pin of LCD low, for instr/addr CALL Nibbles2LCD ; then send addr/cmd nibbles to LCD BCF PORTB,5 ; make sure RS is is left low GOTO BusyWait ; then jump to delay 250us before return DisplayData: ;routine to display a data byte in w reg at the current LCD address BSF PORTB,5 ; RS pin of LCD high, for sending data CALL Nibbles2LCD ; then send data nibbles to LCD BusyWait: ; routine to wait until LCD module is not busy, after writing MOVLW h'7D' ; set delay counter for 125 loops MOVWF Counter1 ; (should give about 125 x 4 x 0.5 = 250us) NOP DECFSZ Counter1,1 ; decrement counter & skip when zero GOTO $-2 ; loop back until we reach zero RETURN ; then return DispInit: ; routine to initialise LCD display module BCF PORTB,4 ; first make sure EN and RS lines are low BCF PORTB,5 CALL Delay160ms ; then wait about 160ms before proceeding BSF PORTB,0 ; now load init code 03h into RB0-3 BSF PORTB,1 ; (= DB4 to DB7, so 03 -> 30h) CALL ToggleEN ; then toggle EN to write to LCD CALL Delay10ms ; then wait about 10ms CALL ToggleEN ; then toggle EN to write to LCD again CALL Delay10ms ; then wait about 10ms again CALL ToggleEN ; then toggle EN to write to LCD again CALL Delay10ms ; then wait about 10ms a third time BCF PORTB,5 ; make sure RS is still low BCF PORTB,0 ; now change code in RB to 02 (-> 20h) CALL ToggleEN ; then toggle EN to write to LCD CALL Delay10ms ; then wait about 10ms one last time MOVLW h'28' ; now set LCD functions (4 bit i/f, 2 lines, 5x10 chars) CALL DispAddress ; (also delays for 200us) MOVLW h'0C' ; also set display mode (disp on, no blink or cursor) CALL DispAddress ; (also delays for 200us) CALL ClearLCD ; then clear LCD screen (& delay 320ms) MOVLW h'06' ; set entry mode (increm addr, no shift) CALL DispAddress ; (also delays for 200us) RETURN ; should now be set up & ready to go, so leave Display1: ; routine to display initial greeting info on LCD MOVLW h'80' ; first set address to line 1, char 0 CALL DispAddress ; (also delays for 160us) MOVLW "S" ; then send "SC Megohm Meter" CALL DisplayData MOVLW "C" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "M" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "g" CALL DisplayData MOVLW "o" CALL DisplayData MOVLW "h" CALL DisplayData MOVLW "m" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "M" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "r" CALL DisplayData MOVLW " " CALL DisplayData MOVLW h'C0' ; now move down to line 2 CALL DispAddress MOVLW " " ; and display " & Leakage Meter" CALL DisplayData MOVLW "&" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "L" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "a" CALL DisplayData MOVLW "k" CALL DisplayData MOVLW "a" CALL DisplayData MOVLW "g" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "M" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "r" CALL DisplayData RETURN ; before leaving Display2: ; routine to display Test begin info on LCD MOVLW h'80' ; first set address to line 1, char 0 CALL DispAddress ; (also delays for 160us) MOVLW "S" ; then send "Set Volts, Press" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "V" CALL DisplayData MOVLW "o" CALL DisplayData MOVLW "l" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW "s" CALL DisplayData MOVLW "," CALL DisplayData MOVLW " " CALL DisplayData MOVLW "P" CALL DisplayData MOVLW "r" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "s" CALL DisplayData MOVLW "s" CALL DisplayData MOVLW h'C0' ; now move down to line 2 CALL DispAddress MOVLW "b" ; and display "button to Test: " CALL DisplayData MOVLW "u" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW "o" CALL DisplayData MOVLW "n" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "t" CALL DisplayData MOVLW "o" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "T" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "s" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW ":" CALL DisplayData MOVLW " " CALL DisplayData RETURN ; before leaving Display3: ; routine to display measurement info on LCD MOVLW h'80' ; set address to line 1, char 0 CALL DispAddress ; (also delays for 160us) MOVLW "T" ; then send "Test Volts=" CALL DisplayData MOVLW "e" CALL DisplayData MOVLW "s" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "V" CALL DisplayData MOVLW "o" CALL DisplayData MOVLW "l" CALL DisplayData MOVLW "t" CALL DisplayData MOVLW "s" CALL DisplayData MOVLW "=" CALL DisplayData MOVF VDig1,0 ; followed by VDig1, VDig2 & VDig3 CALL DisplayData MOVF VDig2,0 CALL DisplayData MOVF VDig3,0 CALL DisplayData MOVLW "0" ; and then "0V" CALL DisplayData MOVLW "V" CALL DisplayData MOVLW h'C0' ; now move down to line 2 CALL DispAddress MOVLW "I" ; then show "Ix=" CALL DisplayData MOVLW "x" CALL DisplayData MOVLW "=" CALL DisplayData MOVF IDig1,0 ; followed by IDig1-3 CALL DisplayData MOVF IDig2,0 ; (IDig2 = decimal point on hi range) CALL DisplayData MOVF IDig3,0 CALL DisplayData MOVF IDig4,0 ; and IDig4 (= m on hi range, mu on low range) CALL DisplayData MOVLW "A" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "R" CALL DisplayData MOVLW "=" CALL DisplayData MOVF RDig1,0 ; followed by RDig1-3 CALL DisplayData MOVF RDig2,0 ; (RDig2 = "." on hi range) CALL DisplayData MOVF RDig3,0 CALL DisplayData MOVLW "M" ; and ending with "Mohm" CALL DisplayData MOVLW h'F4' CALL DisplayData RETURN ; before leaving ErrorDisp: ; routine to display "Error" on LCD, then pause & return ; (called only if FP functions flag an error other than ; divide-by-zero error which does occur with O/C input) CALL ClearLCD MOVLW h'80' ; next set address to line 1, char 0 CALL DispAddress ; (also delays for 160us) MOVLW "F" ; then send "FP Error!" CALL DisplayData MOVLW "P" CALL DisplayData MOVLW " " CALL DisplayData MOVLW "E" CALL DisplayData MOVLW "r" CALL DisplayData MOVLW "r" CALL DisplayData MOVLW "o" CALL DisplayData MOVLW "r" CALL DisplayData MOVLW "!" CALL DisplayData CALL Wait4sec ; now pause for 4 seconds for user to see CALL ClearLCD ; then wipe away again CALL Display2 ; restore normal display RETURN ; and return InitDig: ; routine to initialise display digits for VDig, IDig, RDig ; (setting multiplier IDig4 according to the range) CALL SetVolts ; first go check S1 & set volts digits etc MOVLW h'20' ; now load IDig1-3 with blank code MOVWF IDig1 MOVWF IDig2 MOVWF IDig3 MOVWF RDig1 ; also RDig1-3 MOVWF RDig2 MOVWF RDig3 BTFSS MRFlag,0 ; then test range flag (bit 0), skip if 1 GOTO LowRange ; if not set, go to low range setup MOVLW "m" ; on high range, so make IDig4 = "m" MOVWF IDig4 MOVLW "." ; and IDig2 = "." (as decimal point) MOVWF IDig2 RETURN ; then return LowRange: MOVLW h'E4' ; on low range, so make IDig4 = micro symbol MOVWF IDig4 RETURN ; before returning Nibbles2LCD: ; routine to test bits of data byte (passed in w) then send ; to LCD module as two nibbles (high nibble first) MOVWF Temp2 ; first save byte to be sent in Temp2 BTFSC Temp2,7 ; now test bit 7 (upper nibble) GOTO $+3 ; if it's a 1, skip down BCF PORTB,3 ; if it's a 0, clear RB3 GOTO $+2 ; and proceed to next bit BSF PORTB,3 ; was a 1, so set RB3 instead BTFSC Temp2,6 ; now test bit 6 GOTO $+3 ; if it's a 1, skip down BCF PORTB,2 ; if it's a 0, clear RB2 GOTO $+2 ; and proceed to next bit BSF PORTB,2 ; was a 1, so set RB2 instead BTFSC Temp2,5 ; now test bit 5 GOTO $+3 ; if it's a 1, skip down BCF PORTB,1 ; if it's a 0, clear RB1 GOTO $+2 ; and proceed to next bit BSF PORTB,1 ; was a 1, so set RB1 instead BTFSC Temp2,4 ; now test bit 4 GOTO $+3 ; if it's a 1, skip down BCF PORTB,0 ; if it's a 0, clear RB0 GOTO $+2 ; and proceed to next bit BSF PORTB,0 ; was a 1, so set RB0 instead CALL ToggleEN ; now toggle EN to write hi nibble to LCD BTFSC Temp2,3 ; next test bit 3 (lower nibble) GOTO $+3 ; if it's a 1, skip down BCF PORTB,3 ; if it's a 0, clear RB3 GOTO $+2 ; and proceed to next bit BSF PORTB,3 ; was a 1, so set RB3 instead BTFSC Temp2,2 ; now test bit 2 GOTO $+3 ; if it's a 1, skip down BCF PORTB,2 ; if it's a 0, clear RB2 GOTO $+2 ; and proceed to next bit BSF PORTB,2 ; was a 1, so set RB2 instead BTFSC Temp2,1 ; now test bit 1 GOTO $+3 ; if it's a 1, skip down BCF PORTB,1 ; if it's a 0, clear RB1 GOTO $+2 ; and proceed to next bit BSF PORTB,1 ; was a 1, so set RB1 instead BTFSC Temp2,0 ; now test bit 0 GOTO $+3 ; if it's a 1, skip down BCF PORTB,0 ; if it's a 0, clear RB0 GOTO $+2 ; and proceed to next bit BSF PORTB,0 ; was a 1, so set RB0 instead CALL ToggleEN ; toggle EN again to write low nibble to LCD RETURN ; and return, since both nibbles sent ; --------------------------------------------------------------- SetBARG10k: ; routine to set BARG to 10000d, before calling float_ascii ; for Rx conversions (N.NN display format) MOVLW h'8C' ; exponent first MOVWF BEXP MOVLW h'1C' ; then MSB/sign byte MOVWF BARGB0 MOVLW h'40' MOVWF BARGB1 ; then remaining two bytes CLRF BARGB2 RETURN ; and return SetBARG1k: ; routine to set BARG to 1000d, before calling float_ascii ; for Rx conversions (NNN display format) MOVLW h'88' ; exponent first MOVWF BEXP MOVLW h'7A' ; then MSB/sign byte MOVWF BARGB0 CLRF BARGB1 ; then remaining two bytes CLRF BARGB2 RETURN ; and return SetBARG100: ; routine to set BARG to 100d, before calling float_ascii ; for Ix conversions (NNN display format) MOVLW h'85' ; exponent first MOVWF BEXP MOVLW h'48' ; then MSB/sign byte MOVWF BARGB0 CLRF BARGB1 ; then remaining two bytes CLRF BARGB2 RETURN ; and return SetBARG10d: ; routine to set BARG to 10d, for use in TakeaReading ; routines (10d = 82 20 00 00, precalc using Fprep) MOVLW h'82' ; exponent first MOVWF BEXP MOVLW h'20' ; then MSB/sign byte MOVWF BARGB0 CLRF BARGB1 ; then remaining two bytes CLRF BARGB2 RETURN ; and return ; --------------------------------------------------------------- SetHiRange: ; routine to set the PIC for measurement on the 0-10mA range BSF MRFlag,0 ; set range flag for 10mA (9.9mA) range BCF PORTA,1 ; also drop RA1 to turn on relay CALL InitDig ; then re-initialise digits & multiplier RETURN ; then return SetLoRange: ; routine to set the PIC for measurement on the 0-100uA range BCF MRFlag,0 ; set range flag for 100uA range BSF PORTA,1 ; also raise RA1 to turn off relay CALL InitDig ; then re-initialise digits & multiplier RETURN ; then return ; --------------------------------------------------------------- SetVolts: ; routine to check position of S1, set TestVolt flag for setting ; and also set VDig1-3 and Test Voltage Constant value ; for calculation of Rx BTFSC PORTA,7 ; begin by testing RA7, skipping if clear GOTO Try500V ; but if set, go look for 500V setting CLRF VoltFlag ; must be set for 1000V, so set flag bits BSF VoltFlag,1 ; (02h = 1000V) MOVLW h'31' ; then set VDig1-3 to "100" MOVWF VDig1 MOVLW h'30' MOVWF VDig2 MOVLW h'30' MOVWF VDig3 MOVLW h'88' ; also set 24-bit TVolt values for 1000V MOVWF TVoltEXP ; first exponent MOVLW h'7A' MOVWF TVoltB0 ; then MSB/sign byte MOVLW h'00' MOVWF TVoltB1 ; and finally LSbyte RETURN ; then return Try500V: BTFSC PORTA,0 ; now skip if RA0 is clear GOTO Mustbe250V ; but if set, go set up for 250V CLRF VoltFlag ; must be set for 500V, so set flag bits BSF VoltFlag,0 ; (01h = 500V) MOVLW h'20' ; then set VDig1-3 to " 50" MOVWF VDig1 MOVLW h'35' MOVWF VDig2 MOVLW h'30' MOVWF VDig3 MOVLW h'87' ; also set 24-bit TVolt values for 500V MOVWF TVoltEXP ; first exponent MOVLW h'7A' MOVWF TVoltB0 ; then MSB/sign byte MOVLW h'00' MOVWF TVoltB1 ; and finally LSbyte RETURN ; then return Mustbe250V: CLRF VoltFlag ; since S1 is set for 250V, clear flag bits MOVLW h'20' ; then set VDig1-3 to " 25" MOVWF VDig1 MOVLW h'32' MOVWF VDig2 MOVLW h'35' MOVWF VDig3 MOVLW h'86' ; also set 24-bit TVolt values for 250V MOVWF TVoltEXP ; first exponent MOVLW h'7A' MOVWF TVoltB0 ; then MSB/sign byte MOVLW h'00' MOVWF TVoltB1 ; and finally LSbyte RETURN ; then return ; --------------------------------------------------------------- TakeaReading: ; routine to use the PIC's ADC module to take 10 readings, which ; are added together and then divided by 10 to get an average. Then ; we check to see if an uprange is needed (ADC average = 1023) or ; a downrange is needed (ADC average <10). Then it upranges or ; downranges if necessary, before taking another set of readings and ; then (or otherwise) calculating the corresponding leakage current ; (Ix) and leakage resistance (Rx) values ready for display. MOVLW h'0A' ; first set MLoopCtr for a loop/sequence MOVWF MLoopCtr ; of 10 readings MOVLW h'76' ; then set ADCEXP, ADCB0 and ADCB1 for a near MOVWF ADCEXP ; zero initial number (76 03 13 = 0.0020000338, MOVLW h'03' ; which is close enough to zero for our purposes) MOVWF ADCB0 MOVLW h'13' MOVWF ADCB1 MeasLoop: BSF ADCON0,2 ; start ADC conversion by setting GO bit BTFSC ADCON0,2 ; then wait until conversion complete by GOTO $-1 ; looping back until GO bit cleared again MOVF ADRESH,0 ; next fetch high byte of ADC result into w MOVWF AARGB0 ; and place into AARGB0 bank1 MOVF ADRESL,0 ; then fetch low byte of result as well bank0 MOVWF AARGB1 ; and place it into AARGB1 CALL FLO24 ; then go convert it into 24-bit FP form CALL Check4FPE ; (duck away to check for any FP errors) copy ADCEXP,BEXP ; now AARG reg have ADC reading, so bring copy ADCB0,BARGB0 ; accumulated readings into BARG regs copy ADCB1,BARGB1 CALL FPA24 ; now add the two together using FPA24 CALL Check4FPE ; (duck away to check for any FP errors) copy AEXP,ADCEXP ; then save sum back into ADC reading regs copy AARGB0,ADCB0 ; (so sum of readings will be accumulating copy AARGB1,ADCB1 ; in these regs) DECFSZ MLoopCtr,1 ; decrement loop counter, skip if it -> 0 GOTO MeasLoop ; otherwise loop back for another reading CALL SetBARG10d ; done 10, so now load dec10 into BARG regs copy ADCEXP, AEXP ; and bring sum of the 10 readings into AARG regs copy ADCB0,AARGB0 copy ADCB1,AARGB1 CALL FPD24 ; then call FPD24 to divide the sum by 10 CALL Check4FPE ; (duck away to check for any FP errors) copy AEXP,ADCEXP ; then save average back into ADC reading regs copy AARGB0,ADCB0 ; (so these regs will now have the average copy AARGB1,ADCB1 ; of the 10 readings, in 24b FP form) ; AARG regs still have 24b version of average reading, so now check ; for full scale reading, in case upranging may be needed MOVLW h'88' ; now load 24b FP version of 1023 MOVWF BEXP ; (pre calc using Fprep) into BARG regs MOVLW h'7F' ; so it can be used as subtractant MOVWF BARGB0 ; (to see if we should uprange) MOVLW h'C0' MOVWF BARGB1 CALL FPS24 ; now subtract BARG from AARG (-> AARG) CALL Check4FPE ; (duck away to check for any FP errors) ; AARG regs now have (average ADC result - 1023) BTFSS AARGB0,7 ; so check sign bit of AARGB0, skip if set (neg result) GOTO ChekHirange ; positive result, so we do have a FS reading copy ADCEXP,AEXP ; neg result, so bring back average ADC reading copy ADCB0,AARGB0 ; into AARG regs copy ADCB1,AARGB1 CALL SetBARG10d ; and load BARG regs with 10d in 24b FP form CALL FPS24 ; now subtract BARG from AARG (-> AARG) CALL Check4FPE ; (duck away to check for any FP errors) ; AARG regs now have (ADC result - 10) BTFSS AARGB0,7 ; so check sign bit of AARGB0, skip if set (neg result) GOTO KeepGoing ; otherwise continue to process (reading > 10) BTFSS MRFlag,0 ; reading is <10, so check if we are in low range GOTO KeepGoing ; flag clear, so OK to continue (in low range already) CALL SetLoRange ; range flag was set, so swing down to lo range GOTO TakeaReading ; and go back for another set of readings ChekHirange: BTFSC MRFlag,0 ; FS reading, but check if we are already in hi range GOTO KeepGoing ; flag is set, so OK to continue (in hi range already) CALL SetHiRange ; flag is clear, so swing up to hi range GOTO TakeaReading ; and go back for another set of readings KeepGoing: ; reading average in normal value range or at least we're on the right ; range, so proceed to process reading and calculate leakage current copy ADCEXP,AEXP ; first bring back ADC reading copy ADCB0,AARGB0 ; into AARG regs copy ADCB1,AARGB1 BTFSS MRFlag,0 ; are we on high range or low range? GOTO Lower ; flag clear, so we must be on low range MOVLW h'85' ; flag set, so set FP divisor for MOVWF BEXP ; 0-10mA conversion (854635 = 99.103515) MOVLW h'46' ; (cf theoretical divisor = 99.103125) MOVWF BARGB0 MOVLW h'35' MOVWF BARGB1 CALL FPD24 ; now do the division AARG/BARG (-> AARG) CALL Check4FPE ; (duck away to check for any FP errors) ; AARG regs have Ix reading in mA (0.0 - 9.9mA) copy AEXP, IxEXP ; so save them in Ix regs copy AARGB0, IxB0 copy AARGB1, IxB1 ; (but also leaves them in AARG) MOVLW h'82' ; now we do need to check if Ix >9.9mA MOVWF BEXP ; so load BARG with 24b version of decimal 9.9 MOVLW h'1E' ; (9.9021d = 821E6F) MOVWF BARGB0 MOVLW h'6F' MOVWF BARGB1 CALL FPS24 ; then subtract 9.9 from Ix (AARG - BARG -> AARG) CALL Check4FPE ; (duck away to check for any FP errors) ; now AARG regs now have Ix - 9.9, in 24b form BTFSC AARGB0,7 ; so check if sign bit in AARGB0 is 1 or not GOTO NotOverFSD ; if it is, neg result so proceed (Ix < 9.9mA) MOVLW "9" ; but if not, pos result so Ix > 9.9; fix it MOVWF IDig1 ; by changing digits to "9.9" MOVLW "." MOVWF IDig2 MOVLW "9" MOVWF IDig3 GOTO ScaleupmA ; then skip over Ix calculation NotOverFSD: copy IxEXP, AEXP ; restore Ix to AARG regs copy IxB0, AARGB0 copy IxB1, AARGB1 CLRF AARGB2 ; next make sure AARGB2 is cleared, just in case CALL SetBARG10k ; set BARG for initial multiply by 10000 Ppage1 CALL float_ascii ; then call FP-ASCII conversion routine Ppage0 copy ones, IDig1 ; and place result in IDig positions copy tenths, IDig3 ; (we don't use third/fourth/fifth digits) ScaleupmA: copy IxEXP, AEXP ; now bring back Ix values (in mA) into AARG regs copy IxB0, AARGB0 copy IxB1, AARGB1 CALL SetBARG1k ; set BARG for multiply by 1000d CALL FPM24 ; then multiply them (so AARG will have Ix in uA) CALL Check4FPE ; (duck away to check for any FP errors) copy AEXP, IxEXP ; then re-save the revised Ix values (now in uA) copy AARGB0, IxB0 copy AARGB1, IxB1 GOTO CalculateRx ; now skip to Rx calculation section Lower: MOVLW h'82' ; on lower range, so set FP divisor for MOVWF BEXP ; 0-100uA conversion (821E91 = 9.9104) MOVLW h'1E' ; (cf theoretical divisor = 9.9103125) MOVWF BARGB0 MOVLW h'91' MOVWF BARGB1 CALL FPD24 ; now do the division AARG/BARG (-> AARG) CALL Check4FPE ; (duck away to check for any FP errors) ; AARG regs have Ix reading in uA (00.00 - 99.99uA) copy AEXP, IxEXP ; so save them in Ix regs copy AARGB0, IxB0 copy AARGB1, IxB1 CLRF AARGB2 ; make sure AARGB2 is cleared, just in case CALL SetBARG1k ; set BARG for initial multiply by 1000 Ppage1 CALL float_ascii ; then call FP-ASCII conversion routine Ppage0 copy ones, IDig2 ; and place result in IDig positions copy tenths, IDig3 ; (we don't use third/fourth/fifth digits) MOVLW h'20' ; then make IDig1 a space MOVWF IDig1 MOVLW h'30' ; now we look in case IDig2 & IDig3 = "0" XORWF IDig2,0 ; by checking them -- IDig2 first BTFSS STATUS,Z ; if Z=1, we had a match (IDig2 = "0") GOTO CalculateRx ; but if Z=0, IDig2 wasn't "0" - just move on MOVLW h'20' ; IDig2 was "0", so blank it MOVWF IDig2 ; (i.e., blank leading zero) MOVLW h'30' ; and now check IDig3 as well XORWF IDig3,0 BTFSS STATUS,Z ; if Z=1, we had a match (IDig3 = "0" also) GOTO CalculateRx ; but if Z=0, IDig3 wasn't "0" - so move on MOVLW h'88' ; both are "0", so set Rx regs for 999M# MOVWF RxEXP ; (because we can't divide by zero) MOVLW h'79' ; (999d = 8879C0 in MTI 24-bit FP format) MOVWF RxB0 ; (pre calc using Fprep) MOVLW h'C0' MOVWF RxB1 GOTO MakeRxDigs ; and just skip to display them CalculateRx: copy IxEXP,BEXP ; next copy Ix values (in uA) into BARG regs copy IxB0,BARGB0 ; so we can use it as divisor for next step copy IxB1,BARGB1 copy TVoltEXP,AEXP ; then copy TVolt reg values into AARG regs copy TVoltB0,AARGB0 copy TVoltB1,AARGB1 CALL FPD24 ; now do the division AARG/BARG (TVolt/Ix) CALL Check4FPE ; (duck away to check for any FP errors) ; so AARG regs now have Rtotal in Megohms MOVLW h'78' ; now load 24b FP version of 0.01010108 MOVWF BEXP ; (pre calc using Fprep) into BARG regs MOVLW h'25' ; so it can be used as subtractant MOVWF BARGB0 ; (to allow for internal 0.0101M) MOVLW h'7F' MOVWF BARGB1 CALL FPS24 ; and subtract BARG from AARG (-> AARG) CALL Check4FPE ; (duck away to check for any FP errors) ; AARG regs now have Rtotal - 0.0101M, = Rx (Megohms) BTFSS AARGB0,7 ; now check sign bit of AARGB0 GOTO $+8 ; if bit not set, just continue (pos result) MOVLW h'72' ; but if bit is set (neg result), make Rx zero MOVWF RxEXP ; (or effectively zero: +0.00016) MOVLW h'27' MOVWF RxB0 MOVLW h'C6' MOVWF RxB1 GOTO MakeRxDigs ; and skip final transfer copy AEXP,RxEXP ; + result, so transfer result from AARG regs copy AARGB0,RxB0 ; into Rx regs (24-bit number, value 000-999) copy AARGB1,RxB1 ; this is value of Rx, in megohms MakeRxDigs: ; Last section - using the calculated 24-bit FP values to produce ; the display chars for Rx (000-999M# or 0.0 - 9.9M# on hi range) copy RxEXP, AEXP ; first copy 24-bit Rx values into AARG copy RxB0, AARGB0 copy RxB1, AARGB1 CLRF AARGB2 ; make sure AARGB2 is cleared BTFSC MRFlag,0 ; now check whether hi or lo current range GOTO HiRangeFmt ; if hi range, go to format as "N.N M#" CALL SetBARG100 ; lo range, so set BARG for initial multiply by 100 Ppage1 CALL float_ascii ; then call FP-ASCII conversion routine copy ones, RDig1 ; and place result in RxDig positions copy tenths, RDig2 copy hundredths, RDig3 ; (we ignore 4th & 5th digits here) Ppage0 MOVLW h'30' ; before we leave, check if we have leading zeroes XORWF RDig1,0 ; in the first two positions, & blank them if so BTFSS STATUS,Z ; (will have Z=1 if RDig1 = "0") RETURN ; if not, just return MOVLW h'20' ; since RDig1 was a zero, make it a blank MOVWF RDig1 MOVLW h'30' ; now try RDig2 XORWF RDig2,0 BTFSS STATUS,Z ; (will have Z=1 if RDig2 = "0") RETURN ; if not, just return MOVLW h'20' ; but if RDig2 was zero too, blank it as well MOVWF RDig2 RETURN ; before returning HiRangeFmt: CALL SetBARG10k ; hi range, so set BARG for initial multiply by 10k Ppage1 CALL float_ascii ; then call FP-ASCII conversion routine copy ones, RDig1 ; and place result in RxDig positions copy tenths, RDig3 Ppage0 MOVLW "." ; also make RDig2 a decimal point MOVWF RDig2 RETURN ; all done now, so return ; ------------------------------------------------------------ ToggleEN: ;routine to toggle EN line of LCD, to write an instr or data nibble BSF PORTB,4 ; take LCD's EN line high (RB4) NOP ; pause 2us (4mc) to let it stabilise NOP NOP NOP BCF PORTB,4 ; then low again, to write into LCD controller RETURN ; then return Wait4sec: ; routine to pause for about 4 seconds, to allow reading display MOVLW h'19' ; first set Counter4 for 25d loops MOVWF Counter4 ; (because 25 x 160ms = 4sec) CALL Delay160ms ; then wait for 160ms DECFSZ Counter4,1 ; now decrement Counter4, skip when zero GOTO $-2 ; otherwise keep looping RETURN ; return when done ; ********************************************************** ; include floating point routines ; (in FPRF24.TXT) ; #include END