; ; Original idea and code: Luhan Monat, http://mondo-technology.com - lunhanxmonat at yahoo dot com ; not copyright or license claims in original code. ; Fully rewrote code and improvements (cc) Heli Tejedor, November 2010, http://heli.xbot.es - helitp at arrakis dot es ; ; License: Creative Commons 3.0 Attribution-NonCommercial-ShareAlike 3.0 (CC BY-NC-SA 3.0) ; Licencia: Creative Commons 3.0 Reconocimiento, No Comercial, Compartir Igual (CC BY-NC-SA 3.0) ; http://creativecommons.org/licenses/by-nc-sa/3.0/deed.es_ES ; http://creativecommons.org/licenses/by-nc-sa/3.0/ ; ; Comatible with SuperProbe hardware swapping R150 and R100K, without transistors, No R10KA and pin 17 grounded. ; ; Current Functions ; ----------------- ; PB2 then PB1 = mode UP ; PB1 then PB2 = mode DOWN ; ; Scale switch = Hi Voltmeter 0-25.00V, override all other modes. ; Adc reads multiplied by 5, for use with external by 5, 1% divider (1M // 249K). ; The voltmeter uses the 5V logic voltage as reference, 25mV resolution. Use a good regulator (lp2950) to improve precision ~1%. ; Do not connect the probe to voltages that exceed 25 volts under any conditions. ; ; Logic Probe, Float, L, H, Pulses: ; The logic probe shows 'H' for high, 'L' for low and '-' for floating in the first display location. ; If a pulse is detected (0.5 usec minimum), the second location show a 'P'. ; PB2 change mode, PB1 show mode and save it if was modified ; Tree modes, TTL(tt) L<0.75V,H>2.00V, CMOS 3V (c3.) L<1.00V, H>2.3V, CMOS 5V (c5) L<1.5V, H>3.5V ; Output 1,5V in tt or c3 modes or 2,5V in c5 mode for float detection by 100K, 50uA out. ; Logic Pulser: ; The logic pulser shows the pulse rate (5, 50, 500, 5.0 50.0) in the last 3 locations. ; The first location shows the sensed logic level as a dash in the bottom or top of the digit. ; When PB1 is held down, a series of 1 microsecond pulses are generated in the opposite direction and the center segment is lit. ; Pushing PB2 cycles thru the 5 pulse rates. The selected pulse rate is saved on power down if was used. ; Voltmeter, 0-5.000V: ; The voltmeter uses the 5V logic voltage as reference, 5mV resolution. Use a good regulator (lp2950) to improve precision ~1%. ; Connecting the LM35 temperature can be read. 0.125 = 12.5ºC with 0.5ºC of resolution ; Do not connect the probe to voltages that exceed 5 volts under any conditions. ; Diode Tester: ; 0-5.000V V-drop. ; This is just the voltmeter function with 10k resitor feeding current to the probe tip, 500uA current. ; When a diode or transistor junction is connected from the tip to the ground lead, the drop voltage is displayed. ; When PB1 is held down a 470 ohms resistor is tied to 5V and about 10mA can turn LED on. ; Ohm meter: ; Output 5V by 10K and measure de voltage drop. Very imprecise for R>20K. Useful for short or open circuit test. ; OVER is show if R > 640K ; If defined buzzer PB2 change buzzer mode "norm" "invr" normal or inverted, in normal mode ; buzzer is on with resistors below 100 ohms, in inverted mode buzzer is on with resistors abobe 110 ohms. ; The buzer is latched some millisecons for easy hearing. ; Normal mode is usefull for short circuit and continuity detecion, inverted mode is useful for detection of intermittent open circuits. ; Capacitor Test: ; When a capacitor is connected from the tip to the ground lead, and PB1 is pushed, its value is displayed with a resolution of 100pf. ; Values from .001 uf to about 500 uf are displayed. The larger the capacitor, the longer it takes to measure. ; A value of 100uf takes a couple of seconds. ; Coil Test: ; When an inductorr is connected from the tip to the ground lead. PB1 starts measure and displayed its value with a resolution of 100uH. ; Values from 0.1mH to 999.9 millihenries are displayed. Note: this function assumes that the DC resistance is not more than a few ohms. ; Also, if the unit gets 'stuck' in this mode, short the tip to ground to free it. ; Signal Generator: ; If defined 500 Hz 0.5v pp (100K ; 10K) is generated while PB1 is held down. ; Obsolete, you must use square wave generator with attenuator, more flexible. ; Frequency meter: ; PB1 switches between normal readout or only last 4 digits readout. ; For instance, the display shows '12.57' for a frequency of 12,576 hz. Push PB1 and readout change to '2576' - the lowest 4 digits. ; 2.5V 100K pullup used for high impedance and sensivility. ; If a decimal point shows, the value is in Khz, if the decimal is flashing, the value is in Mhz. ; Hence, a frequency of 42.345,678 hz is displayed as 42.34 with a flashing decimal. Pushing PB1 in this case will display 5678. ; With SCALE switch Freqmeter <-> Rpm meter (freq * 60) with 60 rpm of resolution. In RPM meter mode 10K Ohms to 5V pullup is used. ; Event Counter: ; 8 digit event counter, the display shows the lowest 4 digits. ; PB1 switches between all digits readout or only last 4 digits readout. PB2 resets the count. ; Serial Generator: ; Every time PB1 is pushed, the letters A-Z followed by cr/lf is generated. 8n1. ; Auto polarity sensing. If the signal injection point is orignally high, then normal (zero start bit) ascii is generated. ; Otherwise, the other polarity is done. ; PB2 cycles thru 1200, 2400, 4800, 9600, 19200, 28800, 57600, 115200 baud. ; SCALE switch turn ON/OFF auto baudrate increment. ; Midi Generator: ; Sends note number 60 (middle C) on any of the 16 midi channels. ; Holding PB1 sends 'note on'. Release of PB1 sends 'note off'. ; PB2 cycles thru the 16 channels. The midi channel number is stored. ; r/c servo generator: ; Generates 1ms to 2ms pulse for r/c servos, actually 770us to 2300us, repeated every 20ms (50 Hz) ; PB1 increases pulse, PB2 decreases pulse. Defaults to 1.5 ms (1500 us) every time mode is entered. ; r/c servo measure: ; From 700us to 2300us (and more) 10us resolution. PB1 Show H, signal high time in us. PB2 show L, signal low time in us. ; Button PB1 unlock meter if is locked in half read. ; Square Wave generator: ; Generates 1 - > 32.767 hz squarewave. Resolution and precision about 1Hz. Readout is in Khz: 2.345 = 2.345Khz, 0.010 = 10Hz ; PB1 increases digit under cursor, PB2 change cursor. ; SCALE switch ON/OFF the 10:1 atenuator. Push one time and shows at.on for enable attenuator, then square wave ; generation is disabled. Release for operation. Push other time an shows at.of for disable attenuator. Release again for operation. ; Digital noise: ; Generates 10khz digital pseudorandom series. ; ir.Ou ; Generates ~1 millisecond on and exact 2.5 millisecond off square wave. ; PB2 change the freq. in six steps: 25Khz, 36Khz, 38Khz, 40Khz, 56Khz, 80Khz. On time change with freq. ; When connected to IR LED, used for testing IR reciever modules. ; PWM: ; Generates variable pulse width 3-97 percent of a 6khz (approx) digital signal. ; PB1 decreases pulse width, PB2 increases pulse width. ; STOP: ; Stop watch, start/stop on probe change or PB1. Resolution of 0.001 seconds (1ms) for fast events register. ; Digits are formatted as hhh.mm,ss.mmm "." = fixed decimal point, "," = flashing decimal point. ; When stop watch is stopped PB1 switches between normal readout or only last 4 digits readout. Flashing decimal point ; flash faster when stopped than running. ; If timer have more than ten hours, the less significative digits are not show in "last 4 digits" mode. ; Tested error <1 second in 24 hour ; PB2 reset ; NTSC: ; If defined, generates an NTSC video frame with a white dot pattern. ; ; V 4.2 Ported to Microchip MPASM 5.14, many improvements and new functions ;================================================================================ ; CONFIGURATION: Change this defines with your preferences for your hardware #define VERSION 42 ; Firmmware version in fixed point 2.1 format: 134 is 13.4 ; Common anode displays with transistor: #define COMMON_POL 0 #define SEGMENTS_P 0 #define LED_MPX 1 or 4 ; Common anode displays without transistor: #define COMMON_POL 1 #define SEGMENTS_P 0 #define LED_MPX 1 ; Common cathode displays with transistor: #define COMMON_POL 1 #define SEGMENTS_P 1 #define LED_MPX 1 or 4 ; Common cathode displays without transistor: #define COMMON_POL 0 #define SEGMENTS_P 1 #define LED_MPX 1 ; You must choice between NTSC and BUZZER. #define COMMON_POL 1 ; 1 or 0 Polarity of common outputs #define SEGMENTS_P 0 ; 1 or 0 Polarity of segments outputs, invert it for transistors #define LED_MPX 1 ; 1 or 4 multiplex by 1 (segment by segment 1 of (8*4) 32) or by 4 (4 segments at once 4 of 32(8*4) 3) #define BUZZER 1 ; 1 or 0 Include buzzer code or #define NTSC 0 ; 1 or 0 Include NTSC code, uses the same pin, do not define buzzer and ntsc at once #define SIGNALGEN 1 ; 1 or 0 Include Signalgen in main menu, else use SCALE (HIVIN input) to change attenuator in squaregen ;#define ZERODIV ; Check divition by zero ; END OF CONFIGURATION ;================================================================================ ;================================================================================ ; MPLAB includes list p=16f876A ; Define processor, 876=4pages of ROM, 873=2pages #include ; Register definitions __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _HS_OSC & _LVP_OFF & _DEBUG_OFF & _CPD_OFF errorlevel -302 ; Uncoment for show all 'register not in bank 0' warnings radix dec ; Helper defines ; Special registers bits #define CARRY STATUS, C #define ZERO STATUS, Z ; EEprom register bits #define EE_READ EECON1, 0 #define EE_WRITE EECON1, 1 #define EE_WREN EECON1, 2 #define EE_EEPGD EECON1, 7 ; End of MPLAB includes ;================================================================================ ;================================================================================ ; Some constants #define NDISPLAY 4 ; Digits in display #define CONST_BUZ 11 ; Values < 108 = buzzer ON (10 counts 98 Ohms, 11=108, 12=118 #define CYCLE_BUZ 65 ; Buzzer duty cycle ; Calibration loops for freqmeter, tested with Promax FD915 and daher xtal osc 16.000.000Mhz 10ppm #define FREQ_CALC 42992 ; EEPROM Coarse calibration for 1sec loop in freqmeter 234Hz * unit at 10Mhz #define FREQ_CALF 6 ; EEPROM Fine calibration for 1sec loop in freqmeter 27Hz * unit at 10Mhz #define STOP_CAL 4998 ; Adjust 1ms count in STOP ; Inital values #define INIT_SWG 1000 ; EEPROM Initial freq in Square wave generator #define INIT_SIG 500 ; EEPROM Initial freq in signal generator ; 18 basic modes + optional modes = Total operational modes, () important for negate: -MAXMODE #define MAXMODE (18+NTSC+SIGNALGEN) #define MAXBAUD 8 ; Total baudrates #define MAXPULS 5 ; Pulser rates #define MAXMIDI 15 ; Midi channels #define MAXPROB 3 ; Logic probe modes #define MAXPWM 96 ; Max PWM period: 97% #define MAXIR 6 ; Max ir freq ;================================================================================ ;================================================================================ ; Hardware definitions, LED segments and pins #include "LedSevenSegments.asm" ; Push buttons #define PB1 PORTC,4 ; PB1 pin 15 #define PB2 PORTC,5 ; PB2 pin 16 ; Other input/ output pins #define R20 PORTA,0 ; 20 ohm direct feed out and general input #define R100 PORTA,1 ; 100 ohms, coil measure and ntsc #define R100K PORTA,2 ; 100k used in signalgen (100K;10K), pullup in cap, pulldown in servo measure and freqcount, in SuperProbe original R150 #define Rxxx PORTA,3 ; 150 Ohms in NTSC, Buzzer and reset, in SuperProbe original R100K #define R470 PORTA,4 ; 470 ohms used to test polarity in pulser, and input for TIMER0 #define CLAMP PORTA,5 ; Clamp input to T0 and R470, used only in freqcount and LED #define HIVIN PORTC,6 ; SCALE switch used for HIV detection with 10k pullup, 0=Volt LOW, 1=Volt HIGH #define R10K PORTC,7 ; 10k general pullup ;================================================================================ ;================================================================================ ; List of modes and OpMode code, () optional: ; 0 LogicProbe, 1 LogicPulser, 2 Voltmeter, 3 DiodeTester, 4 OhmMeter, 5 CapMeter, 6 CoilMeter, 7 FreqMeter, ; 8 EventCnt, (9 SignalGen), 10 SerialGen, 11 MidiGen, 12 ServoGen, 13 ServoMeter, 14 SquareGen, 15 NoiseGen, ; 16 IrGen, 17 PwmGen, 18 StopWatch, (19 NtscGen) ;================================================================================ ; EEPROM datas for first start org 2100h #define MODES_EBASE 0 ; Start EEprom address for initial modes de 14 ; Operational mode de 4 ; Pulser rate de 3 ; Midi channel de 2 ; Baud rate de 0 ; Logic probe logic mode de 2 ; Index to IR freq de 48 ; 50 % PWM generator de low INIT_SWG de high INIT_SWG ; Square wave generator initial freq. de low INIT_SIG de high INIT_SIG ; Signal generator initial freq. #define BUZZ_VAL 0x10 ; EEprom address for freqmeter calibration org 0x2100 + BUZZ_VAL de low CONST_BUZ, high CONST_BUZ ; Below this value (raw adc) buzzer is ON #define LCAL_FREQM 0x12 ; EEprom address for freqmeter calibration org 0x2100 + LCAL_FREQM de low FREQ_CALC, high FREQ_CALC ; 1 second delay coarse adjust de FREQ_CALF ; 1 second delay fine adjust #define LCAL_STOPW 0x15 ; EEprom address for stop watch calibration org 0x2100 + LCAL_STOPW de low -STOP_CAL, high (-STOP_CAL&0xffff) ; 1 millisecond counter for stopwatch #define SCALER_EBASE 0x20 ; Start EEprom address for scales table. Format: *h:l /h:l org 0x2100 + SCALER_EBASE #define SCAL_VOLT 0 de low 5000, high 5000, low 1023, high 1023 ; index 0: VolL 5000/1023 #define SCAL_HIVOLT 1 de low 2508, high 2508, low 1023, high 1023 ; index 1: VolH 5,016 divider 1M//249K 2508/1023 #define SCAL_SQUARE 2 de low 10000, high 10000, low 10039, high 10039 ; index 2: Square 10000/10039 #define SCAL_CAP 3 de low 1, high 1, low 1, high 1 ; index 3: Capacitors #define SCAL_COIL 4 de low 1, high 1, low 1, high 1 ; index 4: coils #define IR_DATA_EBASE 0x40 org 0x2100 + IR_DATA_EBASE ; Table of MAXIR*4 elements: cycles, Freq, txt, txt ; 8=84800 9=76900 12=60110 13=56020 14=52630 15=49500 19=39942 ; 20=38020 21=36499 22=34900 28=27900 30=26154 31=25340 32=24780 de 38, 31, LEDS_2, LEDS_5 ; 31=25358 25 Khz dt 38, 21, LEDS_3, LEDS_6 ; 21=36499 36.5 Kh dt 38, 20, LEDS_3, LEDS_8 ; 20=38020 38Khz dt 38, 19, LEDS_4, LEDS_0 ; 19=39942 40Khz dt 38, 13, LEDS_5, LEDS_6 ; 13=56000 56Khz dt 38, 9, LEDS_8, LEDS_0 ; 9=77000 80Khz org 0x2100 + IR_DATA_EBASE+(MAXIR*4) ; End of ERPROM datas ;================================================================================ ;============================================================================= ; Checking configuration MESSG "Compiling SuperProbePlus" #if LED_MPX == 1 MESSG "Multiplex is 1: 1 of 32 segments at once" #else #if LED_MPX == 4 MESSG "Multiplex is 4: 4 of 32 segments at once" #else MESSG "Multiplex Multiplex NOT DEFINED" #endif #endif #if NTSC == 1 MESSG "With NTSC" #endif #if BUZZER == 1 MESSG "With BUZZER" #endif #if SIGNALGEN == 1 MESSG "With signal generator in main menu" #else MESSG "No signalgen, use SCALE to enable attenuator in square wave generator" #endif #if SEGMENTS_P == 1 MESSG "Segments polarity = 1 (POS)" #else MESSG "Segments polarity = 0 (NEG)" #endif #if COMMON_POL == 1 MESSG "Commons polarity = 1 (POS)" #else MESSG "Commons polarity = 0 (NEG)" #endif #if (SEGMENTS_P == COMMON_POL) | LED_MPX == 4 MESSG "Your hardware must have transistors in LED common" #endif #if BUZZER + NTSC > 1 error "Buzzer and Ntsc cannot be used at once, because they uses same pin" #endif ; End of config. check ;================================================================================ ;============================================================================= ; Macro for CALL from PAGE0 to PAGE1 call01 MACRO routine NOEXPAND errorlevel -306 bsf PCLATH,3 call routine bcf PCLATH,3 errorlevel +306 ENDM ;============================================================================= ; Macro for CALL from PAGE1 to PAGE0 call10 MACRO routine NOEXPAND errorlevel -306 bcf PCLATH,3 call routine bsf PCLATH,3 errorlevel +306 ENDM ;============================================================================= ; Variables org 0x20 IVector RES 1 ; Vector for interruption redirection OldW RES 1 ; push W in int OldS RES 1 ; push STATUS in int OldP RES 1 ; push PCLAH in int ; Variables ordered for simple eeprom dump OpMode RES 1 ; Operational mode PulserRate RES 1 ; USed in logic pulser MidiChannel RES 1 ; Midi channel ProbeMode RES 1 ; SubMode in Logic Probe IrFreqIdx RES 1 ; IR index to freq. PwmValue RES 1 ; Duti cicle in PWM generador SquareGenL RES 1 SquareGenH RES 1 ; Used in square wave generator SignalGenL RES 1 SignalGenH RES 1 ; Used in signal generator 0.5 Vpp ; End of ordered variables EEAddress RES 1 ; 8 bit eeprom address Flags0 RES 1 Flags1 RES 1 ; 16 individual bit flags Count RES 1 ; General counter in main routines Temp0 RES 1 ; Used in get texts and ffill, GetWfromBCD, SetWToBCD, and BitTime Temp1 RES 1 ; High part of temp, used in BitTime, servometer, stopwatch, ir.gen OnTime RES 1 ; Used in PWM generator and stopwatch OffTime RES 1 ; Used in PWM generator BaudRate RES 1 ; Baud rate for serial or midi BaudTimerL RES 1 BaudTimerH RES 1 ; Baud Rate timing for serial and midi, and volt level in logicprobe SerialData RES 1 ; Used in serial frame generador Ddata RES 1 ; Temp data used in serialgen, SetWToBCD Cnt0 RES 1 ; Used in BinToBcd routine and math Cnt1 RES 1 ; Used in BinToBcd FirstDig RES 1 ; First digit displayed Display0 RES 1 ; Display 0, segment data for LED, NDISPLAY Display1 RES 1 ; Display 1, segment data for LED Display2 RES 1 ; Display 2, segment data for LED Display3 RES 1 ; Display 3, segment data for LED Bcd0 RES 1 Bcd1 RES 1 Bcd2 RES 1 Bcd3 RES 1 Bcd4 RES 1 ; 10 digit packed bcd buffer SegMask RES 1 ; Segments mask for lowering current in common pin CurDigit RES 1 ; Current digit is being displayed Cursor RES 1 ; Cursor, flashing dash in selected digit DigitUC RES 1 ; Digit under cursor CursorFL RES 1 CursorFH RES 1 ; Counter used for flashing cursor DecPoint RES 1 ; Decimal point pos, and flash, set by GetFormat and used by FormatDP DpFlashL RES 1 DpFlashH RES 1 ; Counter used for flashing decimal point FreqL RES 1 FreqH RES 1 ; Temporal used in square wave generator interrupt amd irgen Timer0 RES 1 ; General timming registers used in wait1sec, serogen, servometer, pulser, ohms etc. Timer1 RES 1 ; hi timer, used in pulser, ohms Rand0 RES 1 Rand1 RES 1 Rand2 RES 1 ; Used in pseudorandom noise genertor Frame RES 1 ; Used in ntsc videogen, serialgen Dela0 RES 1 ; Used in many delay routines Dela1 RES 1 ; Squared and Qubic delay counters Dela2 RES 1 ; Squared and Qubic delay counters ScaleML RES 1 ; Scaler params: output=(input*ScalMH:ScaleML)/ScaleDH:ScaleDL ScaleMH RES 1 ; Scaler params ScaleDL RES 1 ; Scaler params ScaleDH RES 1 ; Scaler params LPFine RES 1 ; Fine calibration of 1 second delay LPCalL RES 1 ; 1 second and 1ms calibration value LPCalH RES 1 ; 1 second and 1ms calibration value lo RES 1 hi RES 1 ; adc data and aux register in math acc0 RES 1 ; acc1 RES 1 acc2 RES 1 acc3 RES 1 ; 32 bit math and counter register xacc0 RES 1 ; xacc1 RES 1 xacc2 RES 1 xacc3 RES 1 ; aux 32 bit math register yacc0 RES 1 ; yacc1 RES 1 yacc2 RES 1 yacc3 RES 1 ; aux2 32 bit math register zacc0 RES 1 ; zacc1 RES 1 zacc2 RES 1 zacc3 RES 1 ; 32 bit Counter register Flo RES 1 Fhi RES 1 ; used in filter adc function FCnt RES 1 ; Auxiliar flags #define LEVEL Flags0,0 ; Previus level for serial and midi. Logic in servometer #define SAVE Flags0,1 ; Config modified, save it #define SWIT Flags0,2 ; Used in PWM generator interrupt #define S_ALT Flags0,3 ; Show alternate data #define HIV Flags0,4 ; Volts scale flag 0=5.00 1=25.00 V #define OHMS Flags0,5 ; 1=Scale flag to Ohms #define OVER Flags0,6 ; 1=Show overange #define CURON Flags0,7 ; Cursor ON ; Mode flags #define SERAU Flags1,0 ; Serial baudrate auto change #define F_RPM Flags1,1 ; Mode RPM un freqmeter #define ATTEN Flags1,3 ; Attenuator 10:1 on #define B_INV Flags1,4 ; Buzzer inverted #define CUR_R Flags1,5 ; Refresh digit under cursor ;============================ 0x00 START CODE =============================== ;============================================================================= org 0x0 ; Reset vector location Reset: goto StartPrg ;=========================== 0x04 INTERRUPT CODE ============================= ;============================================================================= org 0x4 ; Interrupt vector location Interrupt: movwf OldW swapf STATUS,w ; The swapf instruction, unlike the movf, affects NO status bits, which is why it is used here. clrf STATUS ; Point to bank 0 and clear all the flags movwf OldS movf PCLATH,w ; Save PCLATH movwf OldP clrf PCLATH ; This ISR is in page 0 ; movf FSR,w ; FSR not modified in ISR ; movwf OldF movf IVector,w ; Redirect interruption vector addwf PCL,f #define VEC_PWM 0 goto PwmInt #define VEC_SQR 1 goto SqrInt #define VEC_STOP 2 goto StopInt #if BUZZER == 1 ; Only if buzzer hardware exists #define VEC_BUZZ 3 goto Buzzer #endif ;====================== BELOW 0xFF CODE AND TABLES =========================== ;============================================================================= ; Execute routine selected from menu ExecJumps: movf OpMode,w addwf PCL,f goto LogicProbe ; Logic probe goto LogicPulser ; Logic pulser goto Voltmeter ; Voltmeter 0 - 5.00V goto DiodeTester ; Diode measure and tester goto OhmMeter ; Simple ohm meter and continuity test goto CapMeter ; Capacitor meter goto CoilMeter ; Coil meter goto FreqMeter ; Frequency count goto EventCnt ; Event counter #if SIGNALGEN == 1 goto SignalGen ; Signal generator #endif goto SerialGen ; Serial frame generator goto MidiGen ; Midi note generator goto ServoGen ; Servo pulse generator goto ServoMeter ; Servo pulse measure goto SquareGen ; Square wave goto NoiseGen ; Digital noise goto IrGen ; 38 khz test signal goto PwmGen ; Variable pulse width goto StopWatch ; Stop Watch #if NTSC == 1 goto NtscGen ; NTSC generator #endif ;============================================================================= ; Retlw tables dt xxx = retlw xxx ; Convert number in w to LED segments NumberToSegment: addwf PCL,f dt LEDS_0, LEDS_1 dt LEDS_2, LEDS_3 dt LEDS_4, LEDS_5 dt LEDS_6, LEDS_7 dt LEDS_8, LEDS_9 ; Convert number in w to LED common, constant exec time: 6 cycles GetCommon: addwf PCL,f #if COMMON_POL == 0 ; Common active with 0 retlw 0xFE ; 1 inverted retlw 0xFD ; 2 inverted retlw 0xFB ; 4 inverted retlw 0xF7 ; 8 inverted #else ; Common active with 1 retlw 1 retlw 2 retlw 4 retlw 8 #endif ;============================================================================= GetText: movf Temp0,w incf Temp0,f addwf PCL,f retlw LEDS_P ;Prob retlw LEDS_R retlw LEDS_Ol retlw LEDS_B retlw LEDS_P ;PULS retlw LEDS_U retlw LEDS_L retlw LEDS_S retlw LEDS_U ;VoLL retlw LEDS_Ol retlw LEDS_L retlw LEDS_L retlw LEDS_D ;diod retlw LEDS_Il retlw LEDS_Ol retlw LEDS_D retlw LEDS_O ;Ohm retlw LEDS_Hl retlw LEDS_N retlw LEDS_BLANK retlw LEDS_BLANK ; CAP retlw LEDS_C retlw LEDS_A retlw LEDS_P retlw LEDS_C ;CoiL retlw LEDS_Ol retlw LEDS_Il retlw LEDS_L retlw LEDS_F ;FrEq retlw LEDS_R retlw LEDS_E retlw LEDS_Q retlw LEDS_BLANK ; CnT retlw LEDS_C retlw LEDS_N retlw LEDS_T #if SIGNALGEN == 1 retlw LEDS_BLANK ; SIG retlw LEDS_S retlw LEDS_Il retlw LEDS_G #endif retlw LEDS_BLANK ; SEr retlw LEDS_S retlw LEDS_E retlw LEDS_R retlw LEDS_N ;Midi retlw LEDS_Il retlw LEDS_D retlw LEDS_Il retlw LEDS_R ;rc.Ou retlw LEDS_Cl|LEDS_DP retlw LEDS_O retlw LEDS_Ul retlw LEDS_R ;rc.In retlw LEDS_Cl|LEDS_DP retlw LEDS_1 retlw LEDS_N retlw LEDS_BLANK ;[] retlw LEDS_BLANK retlw LEDS_OC retlw LEDS_CC retlw LEDS_N ;noiS retlw LEDS_Ol retlw LEDS_Il retlw LEDS_S retlw LEDS_Il ;ir.ou retlw LEDS_R|LEDS_DP retlw LEDS_Ol retlw LEDS_Ul retlw LEDS_P ;PWM retlw LEDS_W retlw LEDS_M retlw LEDS_BLANK retlw LEDS_S ;STOP retlw LEDS_T retlw LEDS_Ol retlw LEDS_P #if NTSC == 1 retlw LEDS_N ;ntSc retlw LEDS_T retlw LEDS_S retlw LEDS_Cl #endif ; End of modes #define TXT_HELLO MAXMODE retlw LEDS_S ;SuPr retlw LEDS_U retlw LEDS_P retlw LEDS_R retlw LEDS_H ;HELI retlw LEDS_E retlw LEDS_L retlw LEDS_I retlw LEDS_2 ;2012 retlw LEDS_0 retlw LEDS_1 retlw LEDS_2 #define TXT_ATON MAXMODE + 3 retlw LEDS_A ;At.oN retlw LEDS_T|LEDS_DP retlw LEDS_Ol retlw LEDS_N #define TXT_ATOFF MAXMODE + 4 retlw LEDS_A ;At.oF retlw LEDS_T|LEDS_DP retlw LEDS_Ol retlw LEDS_F #define TXT_VOLH MAXMODE + 5 retlw LEDS_U ;VoLH retlw LEDS_Ol retlw LEDS_L retlw LEDS_H #define TXT_OVER MAXMODE + 6 retlw LEDS_O ;OvEr retlw LEDS_V retlw LEDS_E retlw LEDS_R #define TXT_AUTO MAXMODE + 7 retlw LEDS_A ;Auto retlw LEDS_U retlw LEDS_T retlw LEDS_O #define TXT_ALL MAXMODE + 8 retlw LEDS_A ;All retlw LEDS_L retlw LEDS_L retlw LEDS_BLANK #define TXT_LOW MAXMODE + 9 retlw LEDS_L ;LoW retlw LEDS_O retlw LEDS_U retlw LEDS_BLANK #define TXT_RPM MAXMODE + 10 retlw LEDS_R ;rPnn retlw LEDS_P retlw LEDS_N retlw LEDS_N #define TXT_INVR MAXMODE + 11 retlw LEDS_Il ;inv retlw LEDS_N retlw LEDS_V retlw LEDS_BLANK #define TXT_NORM MAXMODE + 12 retlw LEDS_N ;nor retlw LEDS_Ol retlw LEDS_R retlw LEDS_BLANK #define BAUDSTEXT MAXMODE + 13 retlw LEDS_1 ;1200 retlw LEDS_2 retlw LEDS_0 retlw LEDS_0 retlw LEDS_2 ;2400 retlw LEDS_4 retlw LEDS_0 retlw LEDS_0 retlw LEDS_4 ;4800 retlw LEDS_8 retlw LEDS_0 retlw LEDS_0 retlw LEDS_9 ;9600 retlw LEDS_6 retlw LEDS_0 retlw LEDS_0 retlw LEDS_BLANK ; 19.2 (19200) retlw LEDS_1 retlw LEDS_9|LEDS_DP retlw LEDS_2 retlw LEDS_BLANK ; 38.4 (38400) retlw LEDS_3 retlw LEDS_8|LEDS_DP retlw LEDS_4 retlw LEDS_BLANK ; 57.6 (57600) retlw LEDS_5 retlw LEDS_7|LEDS_DP retlw LEDS_6 retlw LEDS_1 ;115.2 (115200) retlw LEDS_1 retlw LEDS_5|LEDS_DP retlw LEDS_2 #define PULSERTEXT BAUDSTEXT+MAXBAUD retlw LEDS_BLANK ; 5 retlw LEDS_BLANK retlw LEDS_BLANK retlw LEDS_5 retlw LEDS_BLANK ; 50 retlw LEDS_BLANK retlw LEDS_5 retlw LEDS_0 retlw LEDS_BLANK ; 500 retlw LEDS_5 retlw LEDS_0 retlw LEDS_0 retlw LEDS_BLANK ; 5.0 retlw LEDS_BLANK retlw LEDS_5|LEDS_DP retlw LEDS_0 retlw LEDS_BLANK ; 50.0 retlw LEDS_5 retlw LEDS_0|LEDS_DP retlw LEDS_0 ;============================================================================= ; Checking if previus tables > 0xFF org 0x100 ;============================================================================= ; PWM interrupt handler, OSC/4 /8 PwmInt: bcf INTCON,T0IF btfss SWIT goto PwmOn bcf SWIT PwmOff: bcf R20 movf OffTime,w movwf TMR0 goto IntReturn PwmOn: bsf SWIT bsf R20 movf OnTime,w movwf TMR0 goto IntReturn ;============================================================================= ; Square wave interrupt handler SqrInt: bcf INTCON,T0IF movf FreqL,w addwf Timer0,f movf FreqH,w skpnc incfsz FreqH,w addwf Timer1,f btfss ATTEN ; Check attenuator flag goto SqrNormal ; Not set: normal signal R20 SqrAtten: btfss Timer1,7 bcf R100K ; Attenuated out R100K btfsc Timer1,7 bsf R100K goto SqrEnd SqrNormal: ; Normal out R20 btfss Timer1,7 bcf R20 btfsc Timer1,7 bsf R20 SqrEnd: movlw -73 ; -73 = 183 addwf TMR0,f goto IntReturn ;============================================================================= ; Stop watch interruption StopInt: movf LPCalL,w addwf TMR1L,f ; Reload the timer, exact count now! movf LPCalH,w movwf TMR1H ; Preset high bcf PIR1,TMR1IF ; Clear interrupt flag. ; Increment the 32bit counter accumulator incfsz zacc0,f goto IntReturn incfsz zacc1,f goto IntReturn incfsz zacc2,f goto IntReturn incf zacc3,f goto IntReturn ;============================================================================= ; Buzzer interrupt handler OSC/4 /16 duty cycle >60% #if BUZZER == 1 errorlevel -207 Buzzer: bcf INTCON,T0IF btfss SWIT goto BuzzOn bcf SWIT BuzzOff: bcf Rxxx movlw -(MAXPWM-(CYCLE_BUZ-2)) ; Off time movwf TMR0 goto IntReturn BuzzOn: bsf SWIT bsf Rxxx movlw -(CYCLE_BUZ-2) ; On time movwf TMR0 goto IntReturn errorlevel +207 #endif IntReturn: ; movf fsr_temp,w ; FSR not modified in ISR ; movwf fsr movf OldP,w movwf PCLATH swapf OldS,w movwf STATUS swapf OldW,f swapf OldW,w retfie ;============================================================================= StartPrg: bsf STATUS,RP0 ; Select TRIS registers (bank 1) movlw B'00111111' ; PORTA all input except RA6-RA7 movwf TRISA movlw B'00000000' ; PORTB LED sgments all outputs movwf TRISB movlw B'10110000' ; PORTC LED commons and RB0-RB3, RB4,RB5 input Buttons, RC6 Rx output, RC7 in Buzzer & power movwf TRISC movlw B'10001000' ; B Pullup OFF and prescaler 1:1 to WDT movwf OPTION_REG bcf STATUS,RP0 ; Select bank 0 clrf Flags1 ; Clear Attenuator, RPM mode and auto baud change #if LED_MPX == 4 movlw 0x0F movwf SegMask #else clrf SegMask bsf SegMask,0 #endif clrf CurDigit call LoadParams ; Restore current modes and initial values call Hello ; Show hello message with version ; Selection of operational modes ModeSelect: call01 SetupPins ; First reset pins and interrupts movf OpMode,w call DisplayText ; Load current mode ModeSelectWPB: call OutputSegments ; Show display movlw 10 call QubicDelay btfsc PB1 ; PB1 down? goto ModeSelect3 ; NO btfss PB2 ; PB2 up? goto ModeSelectWPB ; NO ModeSelect2: ; PB2 pressed call OutputSegments movlw 10 call QubicDelay btfsc PB1 goto ModeSelectExit ; PB1 released: exec btfsc PB2 goto ModeSelect2 ; PB2 released decf OpMode,f ; Decrement mode movlw MAXMODE-1 btfsc OpMode,7 ; underflow? movwf OpMode goto ModeSelect ; Repeat ModeSelect3: ; PB1 pressed call OutputSegments movlw 10 call QubicDelay btfsc PB2 goto ModeSelectExit ; PB2 released: exec btfsc PB1 goto ModeSelect3 incf OpMode,f ; Increment mode movf OpMode,w xorlw MAXMODE skpnz clrf OpMode goto ModeSelect ModeSelectExit: call SaveParams ; Save operational mode goto ExecJumps ; Goto selected mode ;============================= ; Show name, version nr and op mode at startup Hello: movlw TXT_HELLO ; Hello string call DisplayText call ShowFlasing ; Show flashing movf OpMode,w ; Check magic config for Easter egg xorlw 5 ; Magic config step 1: Set OpMode to capacitor meter skpz goto NoEasterEgg movf PulserRate,w ; Check magic config for Easter egg xorlw 4 ; Magic config step 2: Set rate of logic pulser to 50K skpz goto NoEasterEgg movf MidiChannel,w ; Check magic config for Easter egg xorlw 3 ; Magic config step 3: Set midi channel to 4 skpz goto NoEasterEgg movf BaudRate,w ; Check magic config for Easter egg xorlw 2 ; Magic config step 3: Set serial baud rate 4800 skpz goto NoEasterEgg movf ProbeMode,w ; Check magic config for Easter egg xorlw 1 ; Magic config step 3: Set logic probe mode to 1 skpz goto NoEasterEgg movlw TXT_HELLO+1 ; Easter Egg strings call DisplayText call ShowFlasing ; Show flashing movlw TXT_HELLO+2 ; Hello string call DisplayText call ShowFlasing ; Show flashing NoEasterEgg: movlw VERSION call DisplayW ; Prepare version nr #if VERSION < 100 ; If display only xx clear tens digit clrf Display1 #endif bsf Display2,DP_BIT ; Decimal point movlw LEDS_V ; V (version) movwf Display0 call ShowFlasing ; Show flashing version movf OpMode,w call DisplayText goto ShowFlasing ; Show flashing mode ; return ;============================= ; Run display slowly for flashing effect, slow if GIE=1 ShowFlasing: #if LED_MPX == 4 movlw 35 #else movlw 140 ; Timer for show txt deppending flashing rate #endif movwf Count ShowFlasing1: call OutputSegments #if LED_MPX == 4 movlw 24 ; Spent time for flashing depending mpx rate #else movlw 16 #endif call QubicDelay decfsz Count,f goto ShowFlasing1 return ;======================== MAIN FUNCTIONS PAGE 0 ============================== ;============================================================================= ; Logic probe function ; show Low, High, and Float w/ Pulse detection LogicProbe: bsf STATUS,RP0 ; Select TRIS register (Bank 1) movlw B'10101000' ; No prescale on TMR0, input tresholds L<0.8V to H>2.0 V (TTL) movwf OPTION_REG movlw 0x0E ; AN0 on, left justify movwf ADCON1 bcf STATUS,RP0 ; Bank 0 movlw 0x81 ; Read a/d on AN 0 movwf ADCON0 clrf DecPoint ; No decimal movlw 0xFF movwf TMR0 ; Set for single count overflow LpSetMode: movf ProbeMode,w ; Get mode movwf Temp0 bcf CARRY rlf Temp0,f rlf Temp0,f ; *4 call01 ProbeModes ; First char movwf Display2 call01 ProbeModes ; Second char movwf Display3 call01 ProbeModes ; movwf BaudTimerL ; Low voltage value call01 ProbeModes ; Far call to page 1 movwf BaudTimerH ; High voltage value movf ProbeMode,w ; Get mode sublw 2 movlw B'11000010' ; ON, OUT AN2, L, 0010 = 1,5625 V in 100K (AN2) TTL midlevel (1.54) skpnz movlw B'11001000' ; ON, OUT AN2, L, 1000 = 2,5V in 100K (AN2) cmos midlevel (2.46) bsf STATUS,RP0 ; Select TRIS register (Bank 1) ; CVRCON ; H: Bit 5=1: 0 to 0.75 CVRSRC (3.75V), with CVRSRC/24 (0.2083V) step size (VDD*VALUE/24) ; L: Bit 5=0: 0.25 CVRSRC (1.25V) to 0.75 CVRSRC (3.75V), with CVRSRC/32 (0.15625) step size (VDD*VALUE/32 + VDD/4) movwf CVRCON bcf STATUS,RP0 ; Bank 0 LpCycle: btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfsc PB1 goto LpChange ; PB1 no pressed btfss PB2 goto ModeSelect ; Two buttons pressed LpWaitPB: ; Only PB1 pressed movlw 100 call SqDelayWd btfss PB2 goto ModeSelect btfss PB1 goto LpWaitPB call SaveIfFlag ; Save parameters movf Display3,w ; Mode displayed? skpnz ; goto LpSetMode ; Show mode clrf Display2 ; Else clear mode clrf Display3 goto LpMain LpChange: btfsc PB2 ; Only PB2 ? goto LpMain ; No continue LpWaitPB2: ; PB2 pressed movlw 10 call QubicDelay call BlankDisplay ; Blank display while PB2 pressed call OutputSegments btfss PB1 ; exit? goto ModeSelect ; yes btfss PB2 goto LpWaitPB2 incf ProbeMode,f movf ProbeMode,w sublw MAXPROB-1 skpc clrf ProbeMode bsf SAVE ; Pulser rate modified, save it later goto LpSetMode LpMain: call ReadAdc ; Read the input 8 bit value in w and Temp1 ; movf Temp1,w ; 8 bit value subwf BaudTimerL,w ; Low limit skpnc goto Lplow movf Temp1,w ; recall w subwf BaudTimerH,w ; High limit skpc goto Lphi Lpflo: movlw LEDS_Float ;'-' goto Lphl Lphi: movlw LEDS_H ;'H' goto Lphl Lplow: movlw LEDS_L ;'L' Lphl: call DisplayLPchar btfss INTCON,T0IF ; Counter overflow? goto Lpno bcf INTCON,T0IF movlw 0xFF movwf TMR0 ; Set for next time clrf Timer0 movlw LEDS_P ; 'P' movwf Display1 ; Show pulse detected Lpno: call Show1 incf Timer0,f movf Timer0,f skpnz clrf Display1 ; Always P off goto LpCycle ;============================= ; Run display for a short time Show2: clrf Count goto ShowLoop Show1: clrf Count bsf Count,7 ShowLoop: call AddDecPoint ; Add decimal point call OutputSegments ; Show digits movlw 10 call QubicDelay decfsz Count,f goto ShowLoop return ;============================= ; Show w and blank other digits DisplayLPchar: movwf Display0 goto Blank2 BlankDisplay: clrf Display0 clrf Display2 clrf Display3 Blank2: clrf Display1 return ;============================================================================= ; Logic pulser, 1us, 5 rates LogicPulser: bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf STATUS,RP0 ; Select bank 0 LogicPulser2: movf PulserRate,w ; Get rate movwf Temp0 bcf CARRY rlf Temp0,f call01 PulserRates ; Precision timer movwf Timer0 call01 PulserRates ; Hi timer movwf Timer1 movf PulserRate,w ; Get pulser rate text addlw PULSERTEXT call DisplayText goto LogicPulser3 LogicPulser5: btfsc SAVE ; Modified rate? call SaveIfFlag ; Save parameters bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf R20 ; Activate pulse goto $+1 ; 2 nop delay goto $+1 ; 2 nop delay ; Wait 1 us bsf R20 ; Deactivate pulse bcf STATUS,RP0 ; Deselect TRIS (Bank 0) bsf Display0,6 ; Show pulse is active LEDS_Float goto LogicPulser8 LogicPulser3: bsf R20 ; Output High set latch to low. btfsc R470 ; Test level of input which way to pulse? bcf R20 ; Output LOW, then set latch to high. movlw LEDS_Low ; Low level detected? btfsc R20 movlw LEDS_High ; NO, show high movwf Display0 ; In 1st display LogicPulser8: movf Timer0,w call ShortDelay ; Adjust with fine delay movf Timer1,w call SqDelayWd ; Delay with display goto $+1 ; 2 nop delay nop ; Precision delay ; nop btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfsc PB1 goto LogicPulser4 ; no PB1 check PB2 btfsc PB2 goto LogicPulser5 ; PB1 only - do pulses goto ModeSelect ; PB1 and 2 pulsed exit to menu. LogicPulser4: btfsc PB2 ;only PB2 ? goto LogicPulser3 ;no - continue, set pulse direction LogicPulser6: ; PB2 pulsed movlw 10 call QubicDelay call BlankDisplay ; Blank display while PB2 pressed call OutputSegments btfss PB1 ;exit? goto ModeSelect ;yes btfss PB2 goto LogicPulser6 incf PulserRate,f movf PulserRate,w sublw MAXPULS-1 skpc clrf PulserRate bsf SAVE ; Pulser rate modified, save it later goto LogicPulser2 ;============================================================================= ; Ohm meter ; Same as diode drop but with different scale routine OhmMeter: #if BUZZER == 1 bsf STATUS,RP0 ; Select TRIS register (Bank 1) movlw B'10000011' ; B pullup OFF, Internal CLK, Prescaler to Tmr0, 1:16 movwf OPTION_REG ; 32 clock bcf STATUS,RP0 ; Bank 0 movlw VEC_BUZZ movwf IVector movlw BUZZ_VAL ; EEPROM buzzer values call LoadCalibration ; Read calibration parameters bsf INTCON,GIE ; Enable interrupts #endif bsf OHMS ; Scale flag to ohms ;============================================================================= ; Measure diode drops ; Same as voltmeter but with 10K or 470K to 5v feed to probe DiodeTester: movlw SCAL_VOLT ; Index to scale table in eeprom call GetScale ; Get parameters for volt scale bsf R10K ; Ouput 5V to feed resistor. bsf CLAMP ; Ouput 5V to LED resistor. bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf R10K ; Enable output feed resistor goto Volt ; Like voltmeter ;============================================================================= ; Voltmeter function, 0-5.000 volts, ~4 readouts by second Voltmeter: movlw SCAL_VOLT ; Index to scale table in eeprom call GetScale ; Get parameters for volt scale ; Common voltmeter/hi voltmeter/diode/ohm Volt: bsf STATUS,RP0 ; Select TRIS register (Bank 1) movlw 0x8E ; Enable analog 0 right justify, 0x89 for use AN0-AN1 movwf ADCON1 bcf STATUS,RP0 ; Bank 0 movlw 0x81 ; Select analog 0 and enable a/d movwf ADCON0 VoltMain: btfsc HIV goto CheckNoScal ; Goto if HIV=1 CheckScal: btfss HIVIN ; HIV=0, test scale switch goto ContVolt ; Goto if HIVIN=0 VoltmeterHi: movlw SCAL_HIVOLT ; Index to scale table in eeprom call GetScale ; Get parameters for hivolt scale call01 SetupPins ; Reconfigure pins bsf HIV ; Set scale High movlw TXT_VOLH ; VolH string call DisplayText call ShowFlasing ; Show flashing goto Volt ; Jump to common Volt in HIV mode CheckNoScal: btfsc HIVIN ; HIV=1, test scale switch goto ContVolt ; Goto if HIVIN=1 call01 SetupPins ; Reconfigure pins and clear HIV movf OpMode,w ; Load current mode call DisplayText call ShowFlasing ; Show flashing goto ExecJumps ; Goto old mode ContVolt: call ReadFilteredAdc ; Read filtered ADC in hi:lo and unfiltered to Temp0:Temp1 call01 ScaleMeter ; Scale and overange test, return in acc3:acc0 btfss OVER ; Overange test goto NoOverange movlw TXT_OVER ; Display OvEr call DisplayText goto NoFormat NoOverange: ; Display value call BinToBcd call MeterFormat ; Format for ohms, use fixed format if volts NoFormat: movlw 80 ; 1 adc read/scale -> 80 display loops movwf Count VoltDisplayLoop: btfss OVER ; Overange test, no AddDecPoint call AddDecPoint ; Add decimal point call OutputSegments ; Show readout #if BUZZER == 1 btfss OHMS ; Flag set=Ohms mode goto NoOhms ; Jump if clear btfss PB2 call BuzzMode ; If PB2 menu to select buzzer inverted or not call ReadAdc ; Read ADC 10 bits in Temp1:Temp0, w=Temp1 call CheckBuzzer ; Buzzer 80 times faster than readout NoOhms: #endif movlw 6 ; delay 6 * 80 loops call QubicDelay decfsz Count,f goto VoltDisplayLoop btfsc PB1 goto VoltEnd ; Not pressed ; PB1 pressed btfsc OHMS ; Ohms mode, no feed goto VoltNOFeed btfss R10K ; 5V in feed=Diode mode? goto VoltNOFeed bsf STATUS,RP0 bcf CLAMP ; Enable LED resistor bcf STATUS,RP0 VoltNOFeed: btfsc PB2 ; PB1 & PB2 pulsed goto VoltMain ; No bsf STATUS,RP0 ; Yes bsf CLAMP ; Disable LED resistor bcf STATUS,RP0 goto ModeSelect ; Main menu VoltEnd: bsf STATUS,RP0 bsf CLAMP ; Disable LED resistor bcf STATUS,RP0 goto VoltMain ;============================= ; Read 16 bit a/d value in Temp1:Temp0, W=Temp1 ReadAdc: bsf ADCON0,GO goto $+1 ; 2 nop delay CheckADC: btfsc ADCON0,GO ; Check for conversion complete goto CheckADC bsf STATUS,RP0 movf ADRESL,w bcf STATUS,RP0 movwf Temp0 movf ADRESH,w movwf Temp1 return ;============================= ; Read filtered ADC, 16 measures averaging filter, refresh hi:lo each 16 call ; Call every 7ms for good 50hz & 60hz filter ReadFilteredAdc: call ReadAdc ; Temp1:Temp0, w=Temp1 addwf Fhi,f movf Temp0,w addwf Flo,f skpnc incf Fhi,f incf FCnt,f btfss FCnt,4 ; Do average each 16 readouts return Do_Average: rrf Fhi,F ; /16 rrf Flo,F rrf Fhi,F rrf Flo,F rrf Fhi,F rrf Flo,F rrf Fhi,F rrf Flo,F movlw 0x03 andwf Fhi,w ; Adjust to 10 bit and move to W movwf hi movf Flo,w movwf lo clrf Flo ; Start new measure clrf Fhi clrf FCnt return #if BUZZER == 1 errorlevel -207 ;============================= ; Change buzzer mode BuzzMode: call DisplayOff ; blankdisplay only useful if outputsegments BuzzWait: btfss PB1 return btfss PB2 goto BuzzWait btfsc B_INV ; Change buzzer mode goto BuzzNormal movlw TXT_INVR call BuzzerOn ; Buzzer on bsf B_INV ; Set buzzer inverted goto BuzzText BuzzNormal: call BuzzerOff ; Buzzer off bcf B_INV ; Set buzzer normal movlw TXT_NORM BuzzText: call DisplayText goto ShowFlasing ; Show flashing ; return ; Check hilo and ON/OFF buzzer CheckBuzzer: ;if(Temp1:Temp0 Freq btfsc PB1 goto FmeterMeasure btfss PB2 ; Both buttons? goto ModeSelect call SwitchView ; Only PB1: change view mode FmeterMeasure: ; Measure entry point ; btfss PB2 ; PB1 not used ; call SwitchView ; Only PB2: change view mode ; Clear 32bit count and turn on TMR0 bcf CLAMP ; CLAMP to 0 clrf TMR0 ; Clear timer and prescale call ClearAcc ; Clear counter in acc0 - acc3 bsf STATUS,RP0 ; Select TRIS register (Bank 1) bsf CLAMP ; Tristate the clamp bcf STATUS,RP0 ; Bank 0 call Wait1Sec ; Wait one second and show display ; Turn off counter and pulse input to read out prescaler bcf CLAMP bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf CLAMP ; Activate CLAMP bcf STATUS,RP0 ; Bank 0 movf TMR0,w movwf acc1 clrf acc0 FmeterLastLoop: ; Read prescaler not accesible by registers with decf acc0,f bsf CLAMP ; false inputs to TMR0 bcf CLAMP nop ; movf TMR0,w ; actual TMR0 -> w subwf acc1,w skpnz goto FmeterLastLoop goto FmeterMain FreqMMode: btfsc F_RPM ; Change to RPM mode goto FreqNoRpm bsf F_RPM ; Set RPM mode ON goto FreqCont1 FreqNoRpm: bcf F_RPM ; Set RPM mode OFF FreqCont1: call01 FreqMReconfig ; Reconfigure and show flashing FreqModeRepeat: call OutputSegments ; Show text btfsc HIVIN ; Test scale switch goto FreqModeRepeat ; HIVIN flashing text exit ; movf OpMode,w ; Load current mode ; call DisplayText ; call ShowFlasing ; Show flashing goto FreqMeter ;============================================================================= ; Count events using TMR0 directly EventCnt: bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf R10K ; Output in 10k movlw B'10101000' ; No prescale on TMR0 movwf OPTION_REG bcf STATUS,RP0 ; Bank 0 bsf R10K ; Use 10k pullup EvCntReset: clrf TMR0 ; Clear hardware timer call ClearZacc EvCntCount: call XferZacc ; Get count value call FormatData ; Format acc3:acc0 for event counter call AddDecPoint ; Add decimal point call OutputSegments ; Show display ; Update 32bit count from hardware timer movf TMR0,w ; Hardware counter to accum movwf zacc0 btfss INTCON,T0IF ; Overflow goto EvCntEndCnt ; Not yet bcf INTCON,T0IF incfsz zacc1,f goto EvCntEndCnt incfsz zacc2,f goto EvCntEndCnt incf zacc3,f EvCntEndCnt: btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfss PB1 ; PB1 ? goto EvCntPB1 ; No - continue btfss PB2 goto EvCntReset ; Reset counting goto EvCntCount ; Continue count EvCntPB1: btfss PB2 ; Both buttons? goto ModeSelect ; Yes - exit call SwitchView ; Only PB1: change view mode goto EvCntCount ; Continue count ;============================================================================= ; Signal generator (0.5 v square wave at 500 Hz) #if SIGNALGEN == 1 errorlevel -207 ; Warning "Found label after column 1" due indent SignalGen: bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf R10K ; 10k = output bcf R100K ; 100k = output bcf STATUS,RP0 ; Bank 0 bcf R10K ; 10k pull to groun bcf R100K ; 100k for signal gen (100K ; 10K ) 10x divider clrf Display0 ; Display '500' movlw LEDS_5 movwf Display1 movlw LEDS_0 movwf Display2 movwf Display3 SgenMain: btfss Timer0,6 bsf Display0,6 ; Set run indicator segment bsf R100K call SgenDisplay SgenWaitPB: incf Timer0,f btfsc Timer0,6 bcf Display0,6 ; Clear run indicator segment bcf R100K call SgenDisplay btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfsc PB1 goto SgenWaitPB btfsc PB2 goto SgenMain goto ModeSelect SgenDisplay: movlw 32 movwf Count SgenDisplayLoop: call OutputSegments ; Run diplay movlw 15 call ShortDelay decfsz Count,f goto SgenDisplayLoop return errorlevel +207 #endif ;============================================================================= ; Serial signal generator SerialGen: movf BaudRate,w movwf Temp0 bcf CARRY rlf Temp0,f call01 SerialBauds ;get low byte movwf BaudTimerL call01 SerialBauds ;get high byte movwf BaudTimerH movlw BAUDSTEXT addwf BaudRate,w call DisplayText SerialWaitPB: call OutputSegments btfsc HIVIN ; Test scale switch goto SerialMenu ; Goto if HIVIN=1 btfss PB2 goto SerialCheckPB btfsc PB1 goto SerialWaitPB btfss PB2 goto ModeSelect call GetLevel movlw 'A' ; 55h First test char movwf SerialData movlw 26 ; 26 chars A-Z movwf Frame ; Char counter in frame SerialLoop: movf SerialData,w call SerOut incf SerialData,f movlw 43 call SqDelayWd decfsz Frame,f goto SerialLoop movlw 13 ; Output CR call SerOut movlw 100 ; Wait call SqDelayWd movlw 10 ; Outpul LF call SerOut call SaveIfFlag ; Save baudrate if changed movlw 220 ; Wait call SqDelayWd btfsc SERAU ; If auto mode goto SerialIncBRate ; Change baudrate goto SerialGen SerialCheckPB: call DisplayOff ; blank display movlw 20 call QubicDelay btfss PB1 goto ModeSelect btfss PB2 goto SerialCheckPB SerialIncBRate: incf BaudRate,f movf BaudRate,w addlw -MAXBAUD ; limit 0-MAXBAUD (8) skpnc clrf BaudRate btfss SERAU ; if auto mode skip save bsf SAVE ; Baud Rate cahnged, save it later goto SerialGen ; Menu: hivin=1 = menu SerialMenu: btfsc SERAU ; Change auto mode goto SerialNoAuto bsf SERAU ; Set auto mode ON movlw TXT_AUTO ; Autochange baudrate ON string call DisplayText goto SerialCont1 SerialNoAuto: bcf SERAU ; Set auto mode OFF bsf SAVE ; Baud Rate cahnged, save it later ; movlw BAUDSTEXT ; Do nothing, simply flash baud rate ; addwf BaudRate,w ; call DisplayText SerialCont1: call ShowFlasing ; Show flashing SerMenuRepeat: call OutputSegments ; Show text btfsc HIVIN ; Test scale switch goto SerMenuRepeat ; HIVIN flashing text exit movf OpMode,w ; Load current mode call DisplayText call ShowFlasing ; Show flashing goto SerialGen ;============================================================================= MidiGen: movf MidiChannel,w ; Get channel addlw 1 ; Convert to midi channel call DisplayW ; Display it movlw LEDS_C ; Display 'C' movwf Display0 movlw LEDS_H ; Display 'H' movwf Display1 MidiGen1: call OutputSegments ; Show display data btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfss PB2 ; Check PB2 goto MidiUp ; Presed btfsc PB1 ; Check PB1 goto MidiGen1 call GetLevel movlw 45 ; 31250 baud movwf BaudTimerL clrf BaudTimerH call MidiOut1 ; Output channel & note movlw 40 ; Velocity: note on call SerOut MidiWaitPB: movlw 20 ; Wait call SqDelayWd btfss PB2 ; Check mode down goto ModeSelect btfss PB1 ; Wait for button release goto MidiWaitPB call MidiOut1 ; Output channel & note movlw 0 ; Note off call SerOut ; Send note off call SaveIfFlag ; Save channel if marked movlw 250 ; Wait call SqDelayWd goto MidiGen MidiUp: movlw 100 call SqDelayWd btfss PB1 goto ModeSelect btfss PB2 goto MidiUp incf MidiChannel,f ; Next midi channel movf MidiChannel,w addlw -MAXMIDI ; limit 0-MAXMIDI (15) skpnc clrf MidiChannel ; Set first channel 0 bsf SAVE ; Flag for saving new channel goto MidiGen ; Check for current state GetLevel: bcf LEVEL ; Check for resting state btfss R20 ; Read input bsf LEVEL bsf STATUS,RP0 ; Select BANK1 for TRIS register bcf R20 ; Set to output bcf STATUS,RP0 ; Bank 0 return MidiOut1: movf MidiChannel,w iorlw 0x90 ; Channel call SerOut ; Send Channel movlw 60 ; Play note goto SerOut ; Send Note ; return ; Send serial data at current state SerOut: clrf Count ; Bit count bsf Count,3 ; count=8 movwf Ddata call BitZero ; Start bit SerByteLoop: call BitTime btfsc Ddata,0 call BitOne ; Data bits btfss Ddata,0 call BitZero rrf Ddata,f decfsz Count,f goto SerByteLoop call BitTime call BitOne ; Stop bit call BitTime ; Wait 3 bits call BitTime goto BitTime ; return ; Send ZERO bit at current state BitZero: btfss LEVEL ; Test level = high bcf R20 ; out = low btfsc LEVEL ; else bsf R20 ; out = high return ; Send ONE bit at current state BitOne: btfss LEVEL ; Test level = high bsf R20 ; out = high btfsc LEVEL ; else bcf R20 ; out = low return ; Wait bit time at current baud rate BitTime: ; Bit timer for serial gen movf BaudTimerL,w movwf Temp0 movf BaudTimerH,w movwf Temp1 incf Temp0,f incf Temp1,f BitTimeLoop: decfsz Temp0,f goto BitTimeLoop decfsz Temp1,f goto BitTimeLoop return ;============================================================================= ; Servo pulse width generation @20Mhz ServoGen: bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf R20 ; R20 for output bcf STATUS,RP0 ; Bank 0 movlw low 1500 ; Servo center value init every time movwf Timer0 movlw high 1500 movwf Timer1 clrf Temp1 ; Delay in check pushbuttons ServoGMain: movf Timer0,w movwf acc0 movf Timer1,w movwf acc1 clrf acc2 clrf acc3 call BinToBcd call GenFormat ; Nou used DP, need to calibrate loops, do it later ServoGWaitPB: movlw 110 ; count: 110 qubicdelay 6 = exact 20ms movwf Count ServoGShowLoop: call OutputSegments ; addDP, need to calibrate loops, do it later movlw 6 ; 6 call QubicDelay decfsz Count,f goto ServoGShowLoop movf Timer0,w movwf lo movf Timer1,w movwf hi call DisplayOff call ServoGenOut btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 incf Temp1,f btfss Temp1,2 ; Only heck buttons one time of four goto ServoGWaitPB clrf Temp1 btfss PB1 goto ServoGDown btfss PB2 goto ServoGUp goto ServoGWaitPB ServoGUp: btfss PB1 goto ModeSelect ServoGAuxUP: movlw 10 addwf Timer0,f skpnc incf Timer1,f movf Timer1,w xorlw 9 skpnz goto ServoGAuxDown goto ServoGMain ServoGDown: btfss PB2 goto ModeSelect ServoGAuxDown: movlw -10 addwf Timer0,f skpc decf Timer1,f movf Timer1,w xorlw 2 skpnz goto ServoGAuxUP goto ServoGMain ; Precise timming used here ServoGenOut: goto $+1 ; 2 nop delay goto $+1 ; 2 nop delay incf lo,f incf hi,f bsf R20 SGLoopL1: goto $+1 ; 2 nop delay SGLoopH1: decfsz lo,f goto SGLoopL1 decfsz hi,f goto SGLoopH1 bcf R20 ; Wait inverse time for constant delay movf Timer0,w sublw 0 ; low 2300 movwf lo movf Timer1,w sublw 9 ; high 2300 movwf hi nop SGLoopL2: goto $+1 ; 2 nop delay SGLoopH2: decfsz lo,f goto SGLoopL2 decfsz hi,f goto SGLoopH2 nop return ;============================================================================= ; Servo input (pulse width measure) precise machine cycle count used !! ==== ServoMeter: bsf STATUS,RP0 ; Select BANK1 for TRIS register bcf R100K ; Select output in 100k for pulldown bcf STATUS,RP0 ; Select BANK0 bcf R100K ; Pull down lightly bsf LEVEL ; Work with high pulse ServoMeterMain: call Show2 ; Short delay and run display btfsc R20 ; Check direct input goto ServoMeterPB1 ; Wait R20 low ServoMeterDisplay: ; Run display call OutputSegments btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfss PB1 goto ServoMeterPB1 ; Button anti freeze btfss PB2 goto ServoMeterPB1 ; Button anti freeze btfss R20 ; Wait R20 high goto ServoMeterDisplay call ClearAcc ; Clear acc3:acc0 ServoMH: btfss R20 ; Check direct input goto ServoEndMH movlw 10 ; Inc acc0 in 10 units (10us) addwf acc0,f ; Isochronous adder skpc goto $+2 incfsz acc1,f goto $+2 incf acc2,f ; 24 bit count = 16777216 * 10us = 167seg ; goto $+2 ; incf acc3,f ; nop ; nop btfss PB1 goto ServoMeterPB1 ; Button anti freeze movlw 2 ; Adjust delay loop 4+(X*3)-1+30+11 = 50 cycles @ 20(5)Mhz = 10us movwf Temp1 servo_dh: decfsz Temp1,f goto servo_dh call OutputSegments ; Run display constant exec time goto ServoMH ; Total 10us constant ServoEndMH: call ClearZacc ; Clear count at start, Check timing ServoML: btfsc R20 ; Check direct input goto ServoEndML movlw 10 ; Inc acc0 in 10 units (10us) addwf zacc0,f ; Isochronous adder skpc goto $+2 incfsz zacc1,f ; 16 bit counter = 65535 * 10us = 0.66 seg goto $+2 incf zacc2,f ; 24 bit counter = 16777216 * 10us = 167 seg ; goto $+2 ; incf zacc3,f ; nop ; nop btfss PB1 goto ServoMeterPB1 ; Button anti freeze movlw 2 ; Adjust delay loop 4+(X*3)-1+30+11 = 50 cycles @ 20(5)Mhz = 10us movwf Temp1 servo_dl: decfsz Temp1,f goto servo_dl call OutputSegments ; Run display constant exec time goto ServoML ; Total 10us constant ServoEndML: btfsc LEVEL ; Show high or low goto ServoSH ; Show high if set call XferZacc ; Show low if clear ServoSH: call BinToBcd call GenFormat ; Nou used DP, need to calibrate loops, do it later ; call AddDecPoint ; Add decimal point call OutputSegments ; Run display constant exec time ServoMeterPB1: btfsc PB1 goto ServoMeterPB2 ; PB1 bsf LEVEL ; Set show high call BlankDisplay movlw LEDS_H ; 'H' movwf Display3 call OutputSegments btfss PB2 goto ModeSelect ServoMeterPB2: btfsc PB2 goto ServoMeterMain bcf LEVEL ; Set show low call BlankDisplay movlw LEDS_L ; 'L' movwf Display3 call OutputSegments btfss PB1 goto ModeSelect goto ServoMeterMain ;============================================================================= ; Linear squarewave generation @20Mhz SquareGen: bsf STATUS,RP0 ; Select TRIS register (Bank 1) movlw B'10001000' ; Prescaler 1:1 to WDT and B pllup ON movwf OPTION_REG ; bcf STATUS,RP0 ; Bank 0 movlw VEC_SQR ; Redirect interruption movwf IVector bsf INTCON,GIE ; Enable interrupts bsf INTCON,T0IE ; Timer0 interrupt on call01 SquareReconfig ; Set pins an show attenuator mode movlw SCAL_SQUARE ; Index to scale table in eeprom call GetScale ; Get parameters for scale call ClearAcc bsf CURON ; Cursor ON in rigth digit clrf Cursor ; SquareShowFreq: call SquareGetHiLo ; Get freq on acc1:acc0 and hi:lo call BinToBcd ; destroy acc3:acc0 bcf CUR_R ; Refresh digit under cursor call GenFormatWDP ; Format it with decimal point call01 DoScale ; Correct error. uses hi:lo and destroy it movf acc0,w ; Put scaled freq on FreqH:FreqL movwf FreqL movf acc1,w movwf FreqH call SquareGetHiLo ; Get freq in acc1:acc2 again for edit SquareMain: movlw 130 ; va muy rapido, probar ahora quick call QubDelayWD ; Display Loop btfsc HIVIN ; Test scale switch goto SquareMode ; change mode HIVIN=1 btfss PB1 goto SquarePB1 ; PB1 Increment value btfss PB2 goto SquarePB2 ; PB2 Cursor Change goto SquareMain ; Repeat SquarePB1: btfss PB2 goto ModeSelect call XferAccZacc ; Save value movf Cursor,w call GetWFromBCD ; Get BCD value under cursor in w movwf Ddata ; Get data in w call01 SqwLimits ; Get in w limit for this digit subwf Ddata,w ; Roll over limit, data-limit incf Ddata,f skpnc clrf Ddata movf Cursor,w ; w=index, Ddata=value to BCD call01 SetWToBCD ; Set BCD value under cursor call01 BcdToBin16 ; Convert it to bin btfsc acc1,7 ; Value > 32767 call XferZacc ; Get old value call SquareSetHiLo ; Save goto SquareSkip0 SquarePB2: btfss PB1 goto ModeSelect incf Cursor,f call01 SqwLimits ; Get in w limit for this digit iorlw 0 ; Check ZERO skpnz clrf Cursor ; Roll over SquareSkip0: movlw 130 ; 120=Button speed call QubDelayWD ; Display Loop goto SquareShowFreq ; Show new values and repeatt ;============================= ; Change mode if hivin=1 SquareMode: btfsc ATTEN ; Change attenuator status goto SquareAtOff bsf ATTEN ; Set attenuator ON goto SquareCont1 SquareAtOff: bcf ATTEN ; Set attenuator OFF SquareCont1: call01 SquareReconfig SQModeRepeat: call OutputSegments ; Show text btfsc HIVIN ; Test scale switch goto SQModeRepeat ; Exit mode HIVIN=0 movf OpMode,w ; Load current mode call DisplayText bcf INTCON,GIE ; Disable interrupts, else flash too slow call ShowFlasing ; Show flashing bsf INTCON,GIE ; Enable interrupts goto SquareShowFreq ;============================= QubDelayWD: movwf Count QubLoop: call01 AddCursor ; Add cursor call AddDecPoint ; Add decimal point call OutputSegments ; Show freq movlw 8 ; Old value 9, flicker display call QubicDelay decfsz Count,f goto QubLoop return ;============================= SquareSetHiLo: ; Move modified vars movf acc0,w ; save 1 byte btfss ATTEN goto SquareDatas2 movwf SignalGenL ; Put FreqH:FreqL to current freq movf acc1,w movwf SignalGenH return SquareDatas2: movwf SquareGenL ; Put FreqH:FreqL on current freq movf acc1,w movwf SquareGenH return ;============================= SquareGetHiLo: btfss ATTEN goto SquareDatas1 movf SignalGenL,w ; Put current freq on hi:lo movwf lo movwf acc0 movf SignalGenH,w goto SquareExitG1 ; Save 3 bytes ; movwf hi ; movwf acc1 ; return SquareDatas1: movf SquareGenL,w ; Put current freq on hi:lo movwf lo movwf acc0 movf SquareGenH,w SquareExitG1: movwf hi movwf acc1 return ;============================================================================= ; 20 khz digital noise @20Mhz, moved to page 1 NoiseGen: call01 NoiseGenPage1 btfsc HIVIN ; Test scale switch again goto VoltmeterHi ; Goto if HIVIN=1 goto ModeSelect ;============================================================================= ; Generat IR test signal in bursts of 38Khz, moved to page 1 IrGen: call01 IrGenPage1 btfsc HIVIN ; Test scale switch again goto VoltmeterHi ; Goto if HIVIN=1 goto ModeSelect ;============================================================================= ; PWM generator routine 2-97% ~6Khz PwmGen: bsf STATUS,RP0 ; Select TRIS registers (Bank 1) bcf R20 ; R20 for output movlw B'10000010' ; B Pullup off, clock int, prescaler timer0 :8 movwf OPTION_REG bcf STATUS,RP0 ; Select Bank 0 movlw VEC_PWM movwf IVector ; Redirect interruption bsf INTCON,GIE bsf INTCON,T0IE ; Enable timer0 int PwmMain: movf PwmValue,w addlw 2 call DisplayW movf PwmValue,w sublw 0 movwf OnTime movf PwmValue,w sublw MAXPWM sublw 0 movwf OffTime PwmDisplay: movlw 10 call QubicDelay call OutputSegments movlw 10 call QubicDelay call OutputSegments decfsz Timer0,f goto PwmDisplay movlw 25 movwf Timer0 PwmCheckPB: movlw 10 ;5 call QubicDelay call OutputSegments btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfss PB1 goto PwmDec btfss PB2 goto PwmInc clrf Timer0 goto PwmCheckPB PwmDec: btfss PB2 goto ModeSelect decfsz PwmValue,f decf PwmValue,f incf PwmValue,f goto PwmMain PwmInc: btfss PB1 goto ModeSelect incf PwmValue,f movf PwmValue,w xorlw MAXPWM skpnz decf PwmValue,f goto PwmMain ;============================================================================= ; Stopwatch funcion, 24 bit = 4h39m, use 32 bit = 1193h ; Button 1: start/stop/change view, probe change: start/stop ; Button 2: reset StopWatch: movlw VEC_STOP movwf IVector bsf STATUS,RP0 ; Select TRIS register (Bank 1) bsf PIE1,TMR1IE ; Timer1 interrupt on bcf R10K ; 10k output bsf R20 ; Input in RA bcf STATUS,RP0 ; Bank 0 bsf R10K ; 10K Pullup movlw LCAL_STOPW ; Stopwatch EEPROM calibration call LoadCalibration ; Read calibration parameters bsf INTCON,PEIE ; Enable periferal interrupts bsf INTCON,GIE ; Enable interrupts clrf T1CON ; Init TMR1, prescaler 1:1 etc ; Wait for a start condition SWClear: call DisplayOff call ClearZacc ; Clear zacc call SWFormatDatas ; Xfer data, and format it for display call SWTestProbe ; Init the probe tip SWTestButtons: call SWDisplay call SWTestProbe ; Change on tip? skpz goto SWTipStart ; Yes. btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfsc PB1 ; Pressed PB1? goto SWTestPB2 btfsc PB2 ; Pressed PB2? goto SWPB1Start ; no: begin count goto ModeSelect ; Yes return to main menu SWTestPB2: btfsc PB2 ; Only PB2? goto SWTestButtons ; no, repeat test goto SWClear ; yes: reset count SWPB1Start: ; Start with PB1 bsf T1CON,TMR1ON ; Start timer SWWaitNoPB1: call SWWaitPB1Rel ; Wait PB1 released SWTipStart: bsf T1CON,TMR1ON ; Start timer SWRun: ; Started with PB1 or tip, wait stop call SWFormatDatas call01 AddTimeDP ; Add time formatting decimal points call OutputSegments call SWTestProbe ; Change on tip? skpz goto SWEnd ; yes. btfsc PB1 ; hit PB1? goto SWRun ; no, keep running SWEnd: bcf T1CON,TMR1ON ; Stop timer call SWFormatDatas SWEnd1: call SWWaitPB1Rel ; Wait PB1 released SWEnd2: call SWDisplay btfsc HIVIN ; Test scale switch goto VoltmeterHi ; Goto if HIVIN=1 btfss PB1 ; Pressed PB1? goto SWSwitchView ; Yes: change view mode btfsc PB2 ; reset? goto SWEnd2 ; no. goto SWClear ; yes SWSwitchView: btfss PB2 ; PB2? goto ModeSelect ; Yes return to main menu call SwitchView ; Yes: change view mode call SWFormatDatas goto SWEnd2 ; no. ;============================= SWWaitPB1Rel: movlw 12 ; 10 call QubicDelay call SWDisplay ; Debounce PB1 btfss PB2 ; PB2? return ; Yes, return now btfss PB1 ; hit PB1? goto SWWaitPB1Rel ; Wait PB1 released return ;============================= ; Change on probe tip, use Temp0 for store old value SWTestProbe: movf OnTime,w ; last port data, recicle PWM variable movwf Temp0 ; save it movf PORTA,w ; read port a movwf OnTime ; for next time. xorwf Temp0,w ; compare to last value andlw 1 ; ra0 only return ; return nz = change. ;============================= SWFormatDatas: call01 TimeToBcdM call FindFirstD ; 1st non zero in w and count btfss S_ALT ; 0 = normal, 1= show last 4 digits goto SWAllDigits SWLast4Digits: addlw -7 ; Substract digits 0-7 = 1-8 skpc ; Less than 7 digits? movlw 0 ; Yes select only last 4 digits addlw 3 ; No select last digits-8 movwf Count SWAllDigits: call01 TimeFormat ; Get decimal point info for time movwf DecPoint movf Count,w ; Recall 1st non-zero digit call01 GetFormatWDP ; Digits position equal in ohms and time andlw 0x0F ; Strip decimal point info goto FixedFormat ; Fixed Format does return ; return ;============================= SWDisplay: movlw 5 ; Adjust faster flashing DP if not running call QubicDelay call01 AddTimeDP ; Add time formatting decimal points goto OutputSegments #if NTSC == 1 ;============================================================================= ; Video pattern generation, exact timming, not use general display subroutines ; Moved to page 1 errorlevel -207 ; Warning "Found label after column 1" due indent NtscGen: call01 NtscGen1 goto ModeSelect ; Return to main menu errorlevel +207 #endif ;======================== SUBROUTINES IN PAGE 0 ============================== ;============================= ; Put mode text pointed by w on 4 char display DisplayText: movwf Temp0 bcf CARRY rlf Temp0,f rlf Temp0,f call GetText movwf Display0 call GetText movwf Display1 call GetText movwf Display2 call GetText movwf Display3 return ;============================== ; Clear 32bit accumulator acc3:acc0 ClearAcc: clrf acc0 clrf acc1 clrf acc2 clrf acc3 return ;============================================================================= ; Clear 32bit counter Z accumulator zacc3:zacc0 ClearZacc: clrf zacc0 clrf zacc1 clrf zacc2 clrf zacc3 ;TEST 12.34.56.789 hh:mm:ss:mmm ; movlw 0x95 ; movwf zacc0 ; movlw 0x2c ; movwf zacc1 ; movlw 0xB3 ; movwf zacc2 ; movlw 0x02 ; movwf zacc3 return ;============================================================================= ; Move zacc3:zacc0 to acc3:acc0 XferZacc: movf zacc0,w movwf acc0 movf zacc1,w movwf acc1 movf zacc2,w movwf acc2 movf zacc3,w movwf acc3 return ;============================================================================= ; Move to acc3:acc0 zacc3:zacc0 XferAccZacc: movf acc0,w movwf zacc0 movf acc1,w movwf zacc1 movf acc2,w movwf zacc2 movf acc3,w movwf zacc3 return ;============================================================================= ; Open count gate for exactly 1.000000 second while show display Wait1Sec: movf LPCalH,w ; Coarse delay, 234hz/10Mhz por unit movwf Timer1 movf LPCalL,w movwf Timer0 Wait1Loop1: call Wait1Time ; Time filler 55 cycles call AddDecPoint ; Include decimal 17 cycles call OutputSegments ; Show last data, 30 cycles call TmrOV ; Check for TMR0 overflow, 10 cycles decfsz Timer0,f goto $+2 decfsz Timer1,f goto Wait1Loop1 ; loop: (55+17+29+10+5) * timer1:tomer0 ; 4 + loop-1 movf LPFine,w ; Fine delay, ~27 hz/10Mhz por unit movwf Timer0 Wait1Loop3: call TmrOV ; Check for TMR0 overflow, 10 cycles decfsz Timer0,f goto Wait1Loop3 ; loop 2+(10+3*LPFine)-1 =79 return ; 2 ;============================== ; Delay some time (11us) ; Constant execution time 4+(3*16)-1+4=55 cycles with call-return Wait1Time: movlw 16 movwf Temp0 Wait1Loop4: decfsz Temp0,f goto Wait1Loop4 goto $+1 ; 2 nop delay return ;============================== ; Check for TMR0 rollover. ; Constant execution time 10 cycles with call-return TmrOV: btfss INTCON,T0IF ; Overflow? goto NoTmrOV bcf INTCON,T0IF ; Clear the flag incf acc2,f ; Increment next byte skpnz incf acc3,f ; And next on rollover return NoTmrOV: goto $+1 ; 2 nop delay nop return ;============================================================================= ; Subroutine for short delays: 2+1+(8*w)-1+2= 4+(8*w)= 0.8+(1,6*w)us @ 20Mhz ShortDelay: movwf Dela0 ShortLoop: goto $+1 ; 2 nop delay goto $+1 ; 2 nop delay nop decfsz Dela0,f goto ShortLoop return ;============================================================================= ; Squared delay function with display SqDelayWd: movwf Dela2 ; Save w movwf Dela1 Ddelay1: movf Dela2,w ; recall w movwf Dela0 Ddelay0: call OutputSegments ; 30 cycles decfsz Dela0,f ; Inner loop, (33*Dela0)-1 cycles goto Ddelay0 decfsz Dela1,f goto Ddelay1 ; Outer loop, (5+(33*Dela0)*Dela1)-1 return ;============================================================================= ; Qubic delay function w QubicDelay: movwf Dela2 Qdelay2: movwf Dela1 Qdelay1: movwf Dela0 Qdelay0: decfsz Dela0,f goto Qdelay0 ; loop 1 (3*Dela0)-1 decfsz Dela1,f goto Qdelay1 ; Loop 2 (4+Loop1)*Dela1)-1 decfsz Dela2,f goto Qdelay2 ; Loop 2 (4+Loop2)*Dela2)-1 return ;============================================================================= ; Turn off displays DisplayOff: #if COMMON_POL == 0 ; Common active with 0 bsf PORTC,0 ; Outputs off bsf PORTC,1 bsf PORTC,2 bsf PORTC,3 #else ; Common active with 1 bcf PORTC,0 ; Outputs off bcf PORTC,1 bcf PORTC,2 bcf PORTC,3 #endif return ;============================================================================= ; Add decimal point to display, use with OutputSegments ; Constant execution time 17 cycles with call-return AddDecPoint: swapf DecPoint,w andlw 3 ; Bits 5,4 = digit nr. addlw Display0 movwf FSR btfsc DecPoint,7 ; Decimal used? bsf INDF,DP_BIT ; Light decimal point incf DpFlashL,f skpnz incf DpFlashH,f btfss DpFlashH,5 ; btfss DpFlashH,7 ; Lower flash rate goto DPNoFlash btfsc DecPoint,6 ; Flash mode? bcf INDF,DP_BIT ; Flash back off return DPNoFlash: nop return ;============================================================================= ; Cycle thru one or four segment drives on 4 digits ; (constant execution time: 30 cycles with call-return) ; Precise timing for: pwm, rc measure etc OutputSegments: #if COMMON_POL == 0 ; Common active with 0 movlw 0x0F iorwf PORTC,f ; Common pins off (set low 4 bits) #else movlw 0xF0 ; Common active with 1 andwf PORTC,f ; Common pins off (clear low 4 bits) #endif movf CurDigit,w ; Get digit number addlw Display0 ; Base segment data movwf FSR movf INDF,w ; Get the data andwf SegMask,w ; Mask the bit or the nible #if SEGMENTS_P == 0 xorlw 0xFF ; Invert if common anodes: led ON with 0 #else xorlw 0x00 ; NO Invert if common cathode: led ON with 1 #endif movwf PORTB ; One or 4 possible segment line low movf CurDigit,w call GetCommon ; constant exec time: 6 cycles #if COMMON_POL == 0 ; Common active with 0 andwf PORTC,f ; Set to 0 selected common #else ; Common active with 1 iorwf PORTC,f ; Set to 1 selected common #endif #if LED_MPX == 4 swapf SegMask,f ; Swap nible btfsc SegMask,0 ; End of byte? Low side = 0? #else bcf CARRY rlf SegMask,f ; Rotate mask skpnc ; End of byte #endif goto ReloadMask goto $+1 ; 2 nop delay goto $+1 ; 2 nop delay nop #if LED_MPX == 4 nop #endif return ;============================== ReloadMask: #if LED_MPX == 4 movlw 0x0F movwf SegMask ; Reload mask to start in first nible #else bsf SegMask,0 ; Reload mask to start in first bit #endif incf CurDigit,f ; Next digit btfsc CurDigit,2 ; Only 4 digits clrf CurDigit ; Start in first digit return ;============================================================================= ; Switch view mode S_ALT and show flashing message SwitchView: btfss S_ALT ; Check current mode goto SetModeALT ; Clear, goto set bcf S_ALT ; Clear: view most significative digits movlw TXT_ALL goto ShowMsg SetModeALT: movlw TXT_LOW bsf S_ALT ; Set: view low digits ShowMsg: call DisplayText goto ShowFlasing ; Show flashing Trick: ShowFlasing does return ; return ;============================================================================= ; Format data for freqmeter and event counter according S_ALT FormatData: btfss F_RPM ; Check RPM flag goto NoRpm movlw 60 ; Convert to RPM, (Hz=Rps) * 60 = RPM movwf lo clrf hi call01 Mul16by16to32 ; Call multiply, hi:lo*acc1:acc0=acc3:acc0 NoRpm: call BinToBcd ; Destroy acc3:acc0 btfss S_ALT ; 0 = normal, 1= show last 4 digits goto GenFormat ; Use generic format in decimal point etc, trick: genformat does return clrf DecPoint ; No decimal point FormatDataLow: movlw 3 ; Select only last 4 digits goto FixedFormat ; Format trick: FixedFormta does return ; return ;============================================================================= ; Format used for capacitor measurements, each count = 100pf CapFormat: call FindFirstD ; 1st non zero in w and count call01 GetCapFormat ; Get formating info goto DoFormat ;============================================================================= ; Format display digits for meter (volts, diodes and ohms) MeterFormat: btfss OHMS ; If no ohms use fixed format goto VoltsFormat ;============================= ; Format first 4 significant digits on display, ever with decimal point GenFormatWDP: call FindFirstD ; Find 1st non-zero digit call01 GetFormatWDP ; Get formating info for ohms goto DoFormat ;============================================================================= ; Two fixed formats: volt and hi volt VoltsFormat: movlw 0x83 ; Volt scale Format 3.210 btfsc HIV ; If HIV is set change format to movlw 0x93 ; Hi Volt scale Format 32.10 movwf DecPoint goto DoFormat ;============================================================================= ; Show 8 bit w value on display DisplayW: call ClearAcc ; Clear acc0-acc3 movwf acc0 call BinToBcd ; goto GenFormat ; Show 3(4) digits in generic format ;============================= ; Format first 4 significant digits on display, with decimal point if need GenFormat: call FindFirstD ; Find 1st non-zero digit call01 GetFormat ; Get formating info DoFormat: movwf DecPoint andlw 0x0F ;============================= ; Format display digits from specified position in w FixedFormat: movwf Count call GetSegments ; Get segment pattern from BCD and inc. Count movwf Display0 call GetSegments movwf Display1 call GetSegments movwf Display2 call GetSegments movwf Display3 return ;============================================================================= ; Dec Count, Convet to BCD and convert to segments GetSegments: movf Count,w decf Count,f call GetWFromBCD ; Get bcd value goto NumberToSegment ; Convert to segments ; return ;============================================================================= ; Locate in count, FirstDig and w the position of the first non zero digit ; If cursor used, if cursor return cursor FindFirstD: movlw 9 ; Start w/last digit movwf Count FindFLoop: movf Count,w ; Get next digit call GetWFromBCD xorlw 0 ; Set z flag skpz goto FindFExit ; Hit non zero decfsz Count,f goto FindFLoop FindFExit: btfss CURON ; If cursor ON check it goto FindFExit2 movf Cursor,w movwf Count ; then Digits=cursor sublw 3 ; If digits <3 skpc goto FindFExit2 movlw 3 ; Then digits=3 movwf Count FindFExit2: movf Count,w movwf FirstDig ; Save first digit return ;============================================================================= ; Set FSR to BCD(w) buffer and get byte in w GetPointerToBCD: movwf Temp0 ; Save digit nr bcf CARRY rrf Temp0,w ; Find buffer offset addlw Bcd0 ; movwf FSR ; Set pointer movf INDF,w ; Get the byte return ;============================================================================= ; Get in 'w' the bcd digit specified by 'w', Temp0,0 = high/low GetWFromBCD: call GetPointerToBCD ; Set FSR and get byte in w btfsc Temp0,0 ; Low or hi nibble? swapf INDF,w ; High, get again but swapped andlw 0x0F ; Only low return ;============================================================================= ; Convert 32-bit binary number at acc3:acc0 (msb:lsb) into a bcd number ; Uses Mike Keitz's procedure for handling bcd adjust ; Modified Microchip AN526 for 32-bits. About 4+((5*11)+12)*32=2144 cycles! BinToBcd: movlw 32 ; 32-bits movwf Cnt1 ; make cycle counter clrf Bcd0 ; clear result area clrf Bcd1 clrf Bcd2 clrf Bcd3 clrf Bcd4 B2BcdMainLoop: movlw Bcd0 ; make pointer movwf FSR movlw 5 movwf Cnt0 ; Mike's routine: B2BcdInnerLoop: movlw 0x33 addwf INDF,f ; add to both nybbles btfsc INDF,3 ; test if low result > 7 andlw 0xF0 ; low result >7 so take the 3 out btfsc INDF,7 ; test if high result > 7 andlw 0x0F ; high result > 7 so ok subwf INDF,f ; any results <= 7, subtract back incf FSR,f ; point to next decfsz Cnt0,f goto B2BcdInnerLoop rlf acc0,f ; get another bit rlf acc1,f rlf acc2,f rlf acc3,f rlf Bcd0,f ; put it into bcd rlf Bcd1,f rlf Bcd2,f rlf Bcd3,f rlf Bcd4,f decfsz Cnt1,f ; all done? goto B2BcdMainLoop ; no, loop return ;============================================================================= ; Save current seup in eeprom SaveIfFlag: btfss SAVE ; Flagged for save? return ; no SaveParams: movlw MODES_EBASE ; EEPROM base address of parameters movwf EEAddress movf OpMode,w call Eewrite ; Save current mode in eeprom movf PulserRate,w call Eewrite movf MidiChannel,w call Eewrite movf BaudRate,w call Eewrite movf ProbeMode,w call Eewrite movf IrFreqIdx,w call Eewrite bcf SAVE ; Now saved, unmark return ;============================================================================= ; Get setup from eeprom, and adjust limits LoadParams: movlw MODES_EBASE ; EEPROM base address of parameters movwf EEAddress call Eeread movwf OpMode ; Operational mode addlw -MAXMODE skpnc clrf OpMode call Eeread movwf PulserRate ; Pulser rate addlw -MAXPULS skpnc clrf PulserRate call Eeread movwf MidiChannel ; Midi channel addlw -MAXMIDI skpnc clrf MidiChannel call Eeread movwf BaudRate ; Baudrate addlw -MAXBAUD skpnc clrf BaudRate call Eeread movwf ProbeMode ; LogicProbe submode addlw -MAXPROB skpnc clrf ProbeMode call Eeread movwf IrFreqIdx ; Freq index irgen addlw -MAXIR skpnc clrf IrFreqIdx call Eeread movwf PwmValue ; PWM duty cycle addlw -MAXPWM skpnc clrf PwmValue call Eeread movwf SquareGenL ; Square wave gen freq default call Eeread movwf SquareGenH call Eeread movwf SignalGenL ; Signal gen freq default call Eeread movwf SignalGenH return ;============================= ; Load LPCalH:LPCalL with w adressed eeprom data LoadCalibration: movwf EEAddress call Eeread movwf LPCalL call Eeread movwf LPCalH return ;============================================================================= ; Read data 'w' at address EEAddress++ Eeread: movf EEAddress,w ; Get address bsf STATUS,RP1 ; Bank-2 movwf EEADR ; Set address in EEPROM register clrf EEADRH bsf STATUS,RP0 ; Bank-3 bcf EE_EEPGD ; Access data memory bsf EE_READ ; Start the read bcf STATUS,RP0 ; Bank-2 movf EEDATA,w bcf STATUS,RP1 ; Bank 0 incf EEAddress,f ; Next location return ;============================================================================= ; Write data 'w' at address EEaddress++ Eewrite: bsf STATUS,RP1 ; Bank-2 movwf EEDATA ; Set data in EEPROM register bcf STATUS,RP1 ; Bank-0 movf EEAddress,w ; Get Address bsf STATUS,RP1 ; Bank-2 movwf EEADR ; Set address in EEPROM register clrf EEADRH bsf STATUS,RP0 ; Bank-3 bcf EE_EEPGD ; Access data memory bsf EE_WREN ; Start write operation movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EE_WRITE goto $+1 ; 2 nop delay CheckWrite: btfsc EE_WRITE ; Wait for complete goto CheckWrite bcf STATUS,RP0 bcf STATUS,RP1 incf EEAddress,f ; Next location return ;============================================================================= ; Get scale factors ScaleMH:ScaleML and ScaleDH:ScaleDH from eeprom by index w GetScale: movwf EEAddress ; Index in w bcf CARRY rlf EEAddress,f ; make pointer to eeprom datas rlf EEAddress,f movlw SCALER_EBASE ; Add initial address in EEPROM of scaler datas addwf EEAddress,f call Eeread movwf ScaleML ; Low multiplier call Eeread movwf ScaleMH ; High multiplier call Eeread movwf ScaleDL ; Low divider call Eeread movwf ScaleDH ; High divider return ;============================================================================= ; Page 1 code org 0x800 ;=================== BELOW 0x8FF IMPORTANT CODE AND TABLES =================== ;============================================================================= ; ; Type VOH min VOL max VIH min VIL max ; 5V TTL 2.4V 0.5V 2.0V 0.8V Treshold 1.5V ; 3.3V LVTTL 2.4V 0.4V 2.0V 0.8V Treshold 1.5V ; 3.3V LVCMOS 3.0V (VCC-0.3V) 0.5V 2.3V (0.7xVCC) 1.0V (0.3xVCC) Treshold 1.5V ; 5V CMOS 4.7V (VCC-0.3V) 0.5V 3.5V (0.7xVCC) 1.5V (0.3xVCC) Treshold 2.5V ProbeModes: ; 3 modes movf Temp0,w incf Temp0,f addwf PCL,f retlw LEDS_T ; tt TTL 5V/LVTTL 3.3V, Treshold 1.5V retlw LEDS_T retlw 40 ; Low 255/5 * 0.8 = 51 * 0.8 = 40 = 0.78V retlw 102 ; High 255/5 * 2.0 = 51 * 2 = 102= 2.00V retlw LEDS_Cl ; c3. LVCMOS 3.3V, Treshold 1.5V retlw LEDS_3 | LEDS_DP retlw 51 ; Low 255/5 * 0.8 = 51 * 1.0 = 51 = 1.00V retlw 118 ; High 255/5 * 2.0 = 51 * 2.3 = 118= 2.31V retlw LEDS_Cl ; c5 CMOS 5V, Treshold 2.5V retlw LEDS_5 retlw 76 ; Low 255/5 * 0.8 = 51 * 1.5 = 76 = 1.49V retlw 178 ; High 255/5 * 2.0 = 51 * 3.5 = 178= 3.49V ;============================================================================= ; fine, coarse PulserRates: movf Temp0,w incf Temp0,f addwf PCL,f dt 1, 174 ; 5 Hz 1,174 OK dt 1, 55 ; 50 Hz 1,55 OK dt 50, 17 ; 500 Hz 50,17 OK dt 16, 5 ; 5 Khz 16,5 OK dt 4, 1 ; 50 Khz 4,1 OK ;============================================================================= ; 9 bits/byte Aprox: (us_byte-50)/5,4 in Low, High format SerialBauds: movf Temp0,w incf Temp0,f addwf PCL,f dt low 1380, high 1380 ;1200 7500 us/byte dt low 680, high 680 ;2400 3750 us/byte dt low 330, high 330 ;4800 1875 us/byte dt low 160, high 160 ;9600 940 us/byte dt low 78, high 78 ;19200 470 us/byte dt low 33, high 33 ;38400 230 us/byte dt low 20, high 20 ;57600 156 us/byte dt low 5, high 5 ;115200 78 us/byte ;============================================================================= ; Used in format ; bit 7 decimal point ; bit 6 flash decimal ; bit 5,4 decimal point location x.xxx=0, xxxx.=3 ; bit 3-0 starting digit from bcd buffer GetFormat: addwf PCL,f retlw 0x03 ;0b00000011 ; One digit 3210 retlw 0x03 ;0b00000011 ; Two digits 3210 retlw 0x03 ;0b00000011 ; Tree digits 3210 retlw 0x03 ;0b00000011 ; Four digits 3210 retlw 0x94 ;0b10010100 ; Five digits 43.21 retlw 0xA5 ;0b10100101 ; Six digits 543.2 retlw 0xC6 ;0b11000110 ; Seven digits 6,543 retlw 0xD7 ;0b11010111 ; Eight digits 76,54 retlw 0xE8 ;0b11101000 ; Nine digits 876,5 retlw 0xF9 ;0b11111001 ; Ten digits 9876, GetFormatWDP: addwf PCL,f retlw 0x83 ;0b10000011 ; One digit 3.210 retlw 0x83 ;0b10000011 ; Two digits 3.210 retlw 0x83 ;0b10000011 ; Tree digits 3.210 retlw 0x83 ;0b10000011 ; Four digits 3.210 retlw 0x94 ;0b10010100 ; Five digits 43.21 retlw 0xA5 ;0b10100101 ; Six digits 543.2 retlw 0xC6 ;0b11000110 ; Seven digits 6,543 retlw 0xD7 ;0b11010111 ; Eight digits 76,54 retlw 0xE8 ;0b11101000 ; Nine digits 876,5 retlw 0xF9 ;0b11111001 ; Ten digits 9876, GetCapFormat: addwf PCL,f retlw 0x01 ;0b00000001 ; One digit 10--- retlw 0x01 ;0b00000001 ; Two digits 10--- retlw 0x84 ;0b10000100 ; Tree digits 4.321 retlw 0x84 ;0b10000100 ; Four digits 4.321 retlw 0x84 ;0b10000100 ; Five digits 4.321 retlw 0x95 ;0b10010101 ; Six digits 54.32 retlw 0xA6 ;0b10100110 ; Seven digits 654.3 retlw 0xB7 ;0b10110111 ; Eight digits 7654. retlw 0xC8 ;0b11001000 ; Nine digits 8,765 retlw 0xD9 ;0b11011001 ; Ten digits 98,76 ; Different schema ; bit 7 Flashing fast used ; bit 6-4 Flashing fast decimal point location ; bit 3 Flashing normal used ; bit 2-0 Flashing normal decimal point location ; x.xxx=0, xxxx.=3 For buffer start use GetFormat or GetFormatWDP TimeFormat: addwf PCL,f retlw 0x08 ;0b0000 1000 ; One digit 3.210 retlw 0x08 ;0b0000 1000 ; Two digits 3.210 retlw 0x08 ;0b0000 1000 ; Tree digits 3.210 retlw 0x08 ;0b0000 1000 ; Four digits 3.210 retlw 0x09 ;0b0000 1001 ; Five digits 43.21 retlw 0x8A ;0b1000 1010 ; Six digits 5,43.2 retlw 0x9B ;0b1001 1011 ; Seven digits 65,43. retlw 0xA8 ;0b1010 1000 ; Eight digits 7.65,4 retlw 0xB9 ;0b1011 1001 ; Nine digits 87.65, retlw 0x0A ;0b0000 1010 ; Ten digits 987.6 ;============================== ; Limit for each digit, 0 end SqwLimits: movf Cursor,w addwf PCL,f dt 9,9,9,9,3,0 ; Reversed 39999 ;============================== ; Limit for each digit, 0 end TimerLimits: movf Cursor,w addwf PCL,f dt 9,9,9,5,9,5,9,9,0 ; Reversed 99h59m59s99c ;======================== MAIN FUNCTIONS PAGE 1 ============================== ;============================================================================= ; 20 khz digital noise @20Mhz, moved here, no more space in page 0 NoiseGenPage1: bsf STATUS,RP0 bcf R20 bcf STATUS,RP0 movlw 0x55 ; Seed movwf Rand0 NoiseMain: ; Compute noise routine: movf Rand0,w addwf Rand1,w movwf Rand1 ; Rand1=Rand1+Rand0 addwf Rand2,w movwf Rand2 ; Rand2=Rand2+Rand1 bcf CARRY rrf Rand0,f ; do 8 bit rotate (rrf is 9 bit with carry) skpnc bsf Rand0,7 ; inject bit 0 of Rand0 to bit 7 addwf Rand0,f ; add rotated Rand0 with original Rand0 btfsc Rand0,0 ; Output is Bit 0 of Rand bsf R20 btfss Rand0,0 bcf R20 call10 OutputSegments movlw 65 movwf Dela0 NoiseDelay: decfsz Dela0,f goto NoiseDelay btfsc HIVIN ; Test scale switch return ; return with HIVIN=1 btfsc PB2 goto NoiseMain btfsc PB1 goto NoiseMain ; goto ModeSelect return ;============================================================================= ; Generat IR test signal in bursts, moved here, no more space in page 0 IrGenPage1: bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf R20 ; R20 for output bcf STATUS,RP0 ; Bank 0 IrShowData: movlw IR_DATA_EBASE ; EEPROM base address of parameters movwf EEAddress movf IrFreqIdx,w ; Get current rate movwf Temp0 ; bcf CARRY rlf Temp0,f rlf Temp0,w ; Current address*4->w addwf EEAddress,f ; call10 Eeread movwf FreqH ; Burst lenght(in cycles) to FreqH call10 Eeread movwf FreqL ; Freq to FreqL call10 Eeread ; get text movwf Display2 call10 Eeread ; get text movwf Display3 IrMain: call IrBurst movlw 5 movwf Count IrDisplay ; Repeat 5 times call10 OutputSegments movlw 9 call10 QubicDelay decfsz Count,f goto IrDisplay call10 DisplayOff btfsc HIVIN ; Test scale switch return ; return with HIVIN=1 btfsc PB2 goto IrMain btfss PB1 ; goto ModeSelect return call10 DisplayOff ; Bnankdisplay only usefull if outputsegments IRWaitRelease: btfss PB1 return btfss PB2 goto IRWaitRelease incf IrFreqIdx,f movf IrFreqIdx,w sublw MAXIR-1 skpc clrf IrFreqIdx goto IrShowData ;============================= IrBurst: bcf CARRY movf FreqH,w ; Burst lenght movwf Count IrLoop: bsf R20 ; Set output ON movf FreqL,w movwf Dela0 IrDelay1: decfsz Dela0,f goto IrDelay1 goto $+1 ; 2 nop delay nop bcf R20 ; Set output OFF movf FreqL,w movwf Dela0 IrDelay2: decfsz Dela0,f goto IrDelay2 nop decfsz Count,f goto IrLoop return #if NTSC == 1 ;============================================================================= ; Video pattern generator, exact timing, not use general display subroutine ; Moved here from page 0 due space limits errorlevel -207 ; Warning "Found label after column 1" due indent #define BLACK Rxxx #define WHITE R100 NtscGen1: bsf STATUS,RP0 ; Select BANK1 for TRIS register bcf BLACK ; BLACK = output bcf WHITE ; WHITE = output bcf R100 ; R100 = output bcf STATUS,RP0 ; Select BANK0 bcf R100 ; R100 low #if COMMON_POL == 0 ; LEDs ON with 0 in common bsf PORTC,2 ; Clear two last digits bsf PORTC,3 #else bcf PORTC,2 bcf PORTC,3 #endif movlw LEDS_Float ; Swow -- #if SEGMENTS_P == 0 xorlw 0xFF ; Invert #endif movwf PORTB NGMain: call Vsync movlw 40 movwf Count vc1: nop call Bline decfsz Count,f goto vc1 movlw 15 movwf Count vc2: call Dline call Dline4 call Dline4 call Bline4 call Bline4 call Bline4 call Bline4 call Bline4 call Bline4 call Bline4 call Bline4 call Bline4 call Bline4 nop decfsz Count,f goto vc2 movlw 17 movwf Count vc3: call Bline nop decfsz Count,f goto vc3 call Zline4 btfsc HIVIN ; Test scale switch return ; return with HIVIN=1 btfsc PB2 goto NGMain return ; goto ModeSelect ;=========================== Vsync: movlw 6 movwf Count vv2: bcf BLACK call dla10 bsf BLACK movlw 46 call vdela decfsz Count,f goto vv2 movlw 6 movwf Count vv3: bcf BLACK movlw 46 call vdela bsf BLACK call dla8 decfsz Count,f goto vv3 movlw 6 movwf Count vv4: bcf BLACK call dla10 bsf BLACK movlw 46 call vdela decfsz Count,f goto vv4 return ;=========================== Zline4: call dla4 zline: bcf BLACK ;start h-sync call dla16 call dla7 bsf BLACK call vseg movlw 89 call vdela nop return ;=========================== Bline4: call dla4 Bline: bcf BLACK ;start h-sync call dla16 call dla7 bsf BLACK movlw 93 call vdela nop return ;=========================== Dline4: call dla4 Dline: bcf BLACK ;start h-sync call dla16 call dla7 bsf BLACK call dla18 call dla18 movlw 19 movwf Temp0 bd2: goto $+1 ; 2 nop delay goto $+1 ; 2 nop delay goto $+1 ; 2 nop delay bsf WHITE nop bcf WHITE decfsz Temp0,f goto bd2 movlw 5 call vdela return ;=========================== ; Delay in processor cycles (includes call/return) dla18: goto $+1 ; 2 nop delay ; dla17: nop dla16: goto $+1 ; 2 nop delay ; dla15: nop goto $+1 ; 2 nop delay ; dla14: nop ; dla13: nop goto $+1 ; 2 nop delay ; dla12: nop ; dla11: nop dla10: goto $+1 ; 2 nop delay ; dla9: nop dla8: nop dla7: nop goto $+1 ; 2 nop delay ; dla6: nop ; dla5: nop dla4: return ;=========================== vdela: movwf Temp0 vd2: decfsz Temp0,f goto vd2 return ;=========================== ; Light alternate digits (12 cycles) vseg: incf Frame,f ; count frames #if COMMON_POL == 0 bsf PORTC,0 ; LEDS ON with 0 in common bsf PORTC,1 btfss Frame,0 bcf PORTC,0 btfsc Frame,0 bcf PORTC,1 #else ; LEDS ON with 1 in common bcf PORTC,0 bcf PORTC,1 btfss Frame,0 bsf PORTC,0 btfsc Frame,0 bsf PORTC,1 #endif nop return ; End ntsc video gen errorlevel +207 #endif ;======================== SUBROUTINES IN PAGE 1 ============================== ;============================================================================= ; Set all pins to default input mode and disable interrupts SetupPins: bcf INTCON,GIE ; Disable interrupts bcf INTCON,T0IE ; Disable Timer0 interrupt bsf STATUS,RP0 ; Select TRIS registers bcf PIE1,TMR1IE ; Timer1 interrupt off bsf R20 ; All pins are input lines bsf R100 bsf Rxxx bsf R470 bsf R10K bsf R100K bsf HIVIN bsf CLAMP bcf ADCON0,ADON ; AD module off movlw 6 ; All digital on PORTA movwf ADCON1 movlw 0 ; Analog REF OFF, NO OUT movwf CVRCON bcf STATUS,RP0 ; Unselect TRIS bcf OHMS ; Clear scale flag to volts bcf HIV ; Clear scale flag to 5.000V bcf OVER ; Clear overange flag bcf S_ALT ; Clear alt data switch bcf CURON ; Without cursor return ;============================================================================= ; Set the Temp0 in the bcd digit specified by 'w' SetWToBCD: call10 GetPointerToBCD ; Set FSR and get byte in w btfss Temp0,0 ; Low or hi nibble? goto SetLow swapf Ddata,f ; Swap low/high data movlw 0x0F andwf INDF,f ; Clear high buffer movlw 0xF0 goto SetEnd SetLow: movlw 0xF0 andwf INDF,f ; Clear low buffer movlw 0x0F SetEnd: andwf Ddata,w ; Load low data iorwf INDF,f ; Store it return ;====================================================================== SquareReconfig: btfsc ATTEN ; Check attenuator status goto SquareShowON SquareShowOFF bsf STATUS,RP0 ; Select TRIS register (Bank 1) bcf R20 ; R20 output pin bsf R100K ; R100K disabled bsf R10K ; R10K disabled bcf STATUS,RP0 ; Select TRIS register (Bank 0) movlw TXT_ATOFF ; Attenuator OFF string goto SquareShow SquareShowON: bsf STATUS,RP0 ; Select TRIS register (Bank 1) bsf R20 ; R20 disabled bcf R100K ; R100K output bcf R10K ; R10K output bcf STATUS,RP0 ; Select TRIS register (Bank 0) bcf R10K ; R10K low movlw TXT_ATON ; Attenuator ON string SquareShow: call10 DisplayText bcf INTCON,GIE ; Disable interrupts call10 ShowFlasing ; Show flashing bsf INTCON,GIE ; Enable interrupts return ;====================================================================== FreqMReconfig: btfsc F_RPM ; Check RPM status goto FreqMShowON FreqMShowOFF bsf STATUS,RP0 ; Select TRIS register (Bank 1) movlw B'11001000' ; ON, OUT AN2, L, 1000 = 2,5V in 100K (AN2) cmos midlevel (2.46) ; CVRCON ; H: Bit 5=1: 0 to 0.75 CVRSRC (3.75V), with CVRSRC/24 (0.2083V) step size (VDD*VALUE/24) ; L: Bit 5=0: 0.25 CVRSRC (1.25V) to 0.75 CVRSRC (3.75V), with CVRSRC/32 (0.15625) step size (VDD*VALUE/32 + VDD/4) movwf CVRCON bsf R10K ; R10K disabled bcf STATUS,RP0 ; Bank 0 movf OpMode,w ; Load current mode FREQ goto FreqMShow FreqMShowON: bsf STATUS,RP0 ; Select TRIS register (Bank 1) movlw 0 ; Analog REF OFF, NO OUT movwf CVRCON bcf R10K ; R10K output bcf STATUS,RP0 ; Select TRIS register (Bank 0) bsf R10K ; R10K high: pullup movlw TXT_RPM ; Attenuator ON string FreqMShow: call10 DisplayText bcf INTCON,GIE ; Disable interrupts call10 ShowFlasing ; Show flashing bsf INTCON,GIE ; Enable interrupts return ;============================================================================= ; Add decimal point to display, use with OutputSegments, no isochronous ; Used in StopWatch AddTimeDP: movf DecPoint,w andlw 0x03 ; Bits 0-1 = digit nr. addlw Display0 movwf FSR bcf INDF,DP_BIT ; No normal DP btfsc DecPoint,3 ; Used normal DP? bsf INDF,DP_BIT ; Light normal decimal point incf DpFlashL,f ; Decimal Point flash delay skpnz incf DpFlashH,f swapf DecPoint,w btfss DecPoint,7 ; Used fast DP? return andlw 0x03 ; Bits 5-4 swapped = digit nr. addlw Display0 movwf FSR bsf INDF,DP_BIT ; Light decimal point btfsc DpFlashH,2 ; Slow rate bcf INDF,DP_BIT ; Flash back off return ;============================================================================= ; Add cursor to display, use with OutputSegments and after AddTimeDP or AddDecPoint ; Used in SquareGen AddCursor: incf CursorFL,f ; Cursor flash delay skpnz incf CursorFH,f ; movf Cursor,w subwf FirstDig,w ; FirstDigit - Cursor ; sublw 3 ; Invert digit number 0-3 -> 3-0 ; movf FirstDig,w ; subwf Cursor,w ; Cursor - FirstDigit andlw 0x03 addlw Display0 movwf FSR btfsc CUR_R ; Digit under cursor saved? goto AddCNosave movf INDF,w movwf DigitUC ; Save digit under cursor bsf CUR_R ; Remember clear again at cursor or data change AddCNosave: btfss CURON ; Cursor active? return movf DigitUC,w ; Digit under cursor btfsc DpFlashH,0 ; Slow rate movlw LEDS_CURSOR ; Cursor movwf INDF ; Old digit or cursor return ;============================================================================= ; Scale acc3:acc0 to acc3:acc0 with current ScleMH:ScaleML and ScaleDH:ScaleDL DoScale2: movf ScaleML,w movwf lo ; Low movf ScaleMH,w movwf hi ; High goto DoScaleX ;============================================================================= ; Scale hi:lo to acc3:acc0 with current ScleMH:ScaleML and ScaleDH:ScaleDL DoScale: movf ScaleML,w movwf acc0 ; Low movf ScaleMH,w movwf acc1 ; High DoScaleX: call Mul16by16to32 ; Call multiply, hi:lo*acc1:acc0=acc3:acc0 movf ScaleDL,w movwf xacc0 ; Low movf ScaleDH,w movwf xacc1 ; High goto Div32by16to32 ; Call divide, acc3:acc0/xacc1:xacc0=acc3:acc0 ; return ;============================================================================= ; Convert 32 bit xacc3:xacc0 binary to 10 digit packed BCD clock format hh:mm:ss:cc ; first divide by 6.000, rest = for seconds and milliseconds ; then divide by 60, rest = minutes div=hours TimeToBcdC: movlw low 6000 ; First compute seconds and millisecons: divide by 6000 movwf xacc0 movlw high 6000 goto TimToBcdx ;============================================================================= ; Convert 32 bit xacc3:xacc0 binary to 10 digit packed BCD clock format hh:mm:ss:mmm ; first divide by 60.000, rest = for seconds and milliseconds ; then divide by 60, rest = minutes div=hours TimeToBcdM: movlw low 60000 ; First compute seconds and millisecons: divide by 60000 movwf xacc0 movlw high 60000 TimToBcdx: movwf xacc1 call10 XferZacc call Div32by16to32 ; yacc1:yacc0 seconds and milliseconds, acc3:acc0 minutes etc call BinToBcd16 ; Convert yacc1:yacc0 to BCD ss:ccc < 60000 movlw 60 ; Compute minutes: divide by 60 movwf xacc0 ; clrf xacc1 call Div16by8to8 ; acc1:acc0 / W -> acc0 Quot. acc1 rest. ; call Div32by16to32 ; yacc0 minutes, acc0 hours etc ; movf yacc0,w movf acc1,w call BinToBcd8 ; Convert w (yacc0) to BCD in hi:lo swapf lo,w ; move data to high nibble -> w iorwf Bcd2,f ; move to BCD2 high nibble: minutes units movf hi,w movwf Bcd3 ; minutes tens movf acc0,w call BinToBcd8 ; Convert w (acc0) to BCD in hi:lo swapf lo,w iorwf Bcd3,f ; hours units movf hi,w ; hours tens and hundreds movwf Bcd4 ; move to BCD4 high nibble hours tens return ;============================================================================= ; Convert 16-bit binary number at yacc1:yacc0 (msb:lsb) into a bcd number ; Uses Mike Keitz's procedure for handling bcd adjust ; Modified Microchip AN526 for 16-bits. About 4+((3*11)+12)*16=720 cycles! BinToBcd16: movlw 16 ; 016-bits movwf Cnt1 ; make cycle counter clrf Bcd0 ; clear result area clrf Bcd1 clrf Bcd2 clrf Bcd3 clrf Bcd4 B1BcdMainLoop: movlw Bcd0 ; make pointer movwf FSR movlw 3 movwf Cnt0 ; Mike's routine: B1BcdInnerLoop: movlw 0x33 addwf INDF,f ; add to both nybbles btfsc INDF,3 ; test if low result > 7 andlw 0xF0 ; low result >7 so take the 3 out btfsc INDF,7 ; test if high result > 7 andlw 0x0F ; high result > 7 so ok subwf INDF,f ; any results <= 7, subtract back incf FSR,f ; point to next decfsz Cnt0,f goto B1BcdInnerLoop rlf yacc0,f ; get another bit rlf yacc1,f rlf Bcd0,f ; put it into bcd rlf Bcd1,f rlf Bcd2,f decfsz Cnt1,f ; all done? goto B1BcdMainLoop ; no, loop return ;============================================================================= ; 8 bit to 3 digit half-packed BCD, Mike McLaren, K8LH (Jan-09) ;w ; binary input ;hi ; packed BCD 'hundreds' & 'tens' ;lo ; single BCD 'lo' BinToBcd8: clrf hi ; decf hi,f ; preset 'hi' to -1 BTBdiv10: movwf lo ; incf hi,f ; bump 'hi', 0x00..0x25 movlw 6 ; using "packed bcd" format addwf hi,w ; bcd "digit carry"? skpndc ; no, skip, else movwf hi ; fix 'hi' movlw 10 ; lo = lo - 10 subwf lo,w ; borrow? bc BTBdiv10 ; no, branch, else return ;============================================================================= ; ScaleMeter: input= hi:lo output acc3:acc0 ; if OHMS is clear use standar DoScale with current vactors else ; if OHMS is set formula: hi:lo = R1/((Vref/hilo)-1) ;============================================================================= ; Use an apropiate F than do not overflow in registers ; Formula = R2 = R1/((Vref/Vmed)-1) ; Dato=(10000*F)/(((1023*F)/Dato)-1*F); ; Calcule R2 from Vin 5V, Vmasure, R1=10K ; Dato=(449*F)/(((1023*F)/Dato)-1*F); ; Calcule R2 from Vin 5V, Vmasure, R1=449 (470//10K) Ohms #define CONST_MAX 1007 ; Values > 1007 = div. by zero #define CONST_FACTOR 64 ; 64 * 1023 < 65535 (16 bit) #define CONST_R1 (10000*CONST_FACTOR) ; 32 bit constant #define CONST_VREF (1023*CONST_FACTOR) ; 16 bit constant ScaleMeter: btfss OHMS ; Flag clear scale=5V or 25V goto DoScale ; Trick, DoScale does return ; Scale ohms ;if(hilo > CONST_MAX) First check overange by zero div. bcf OVER movf hi,w sublw high CONST_MAX skpc goto Over1 skpz goto ScalOhms2 movf lo,w sublw low CONST_MAX skpc Over1: bsf OVER ScalOhms2: movlw low CONST_VREF movwf acc0 movlw high CONST_VREF movwf acc1 clrf acc2 clrf acc3 ; (hi:lo == 0) ; Prevents Zero div and move hi:lo movf lo,w iorwf hi,w skpnz goto ZeroAcc ; Zero hi-lo, clear acc and return ; (hi:lo -> xacc1:xacc0) == 0 ; Move hi:lo to xacc1:xacc1 movf lo,w movwf xacc0 movf hi,w movwf xacc1 ; CONST_VREF / hi:lo -> acc3:acc0 call Div32by16to32 ; Call divide, acc3:acc0/xacc1:xacc0=acc3:acc0 ; CONST_FACTOR -> xacc3:xacc0 movlw low CONST_FACTOR movwf xacc0 movlw high CONST_FACTOR movwf xacc1 clrf xacc2 clrf xacc3 ; acc3:acc0 - xacc3:xacc0 -> acc3:acc0 call Sub32from32 ; acc1:acc0 -> xacc1:xacc0 movf acc0,w movwf xacc0 movf acc1,w movwf xacc1 ; CONST_R1->acc3:acc0 movlw low (CONST_R1&0xffff) movwf acc0 movlw high (CONST_R1&0xffff) movwf acc1 movlw low (CONST_R1>>16) movwf acc2 movlw high (CONST_R1>>16) movwf acc3 ; CONST_R1 / hi:lo -> acc3:acc0 goto Div32by16to32 ; Call divide, acc3:acc0/xacc1:xacc0=acc3:acc0 ; return ; Div32by16to32 make return ZeroAcc: call10 ClearAcc ; Clear accum return ;============================================================================= ; acc1:acc0 - xacc1:xacc0 -> acc1:acc0, destroy xacc Sub16from16: movf xacc0,w subwf acc0,f movf xacc1,w skpc incfsz xacc1,w subwf acc1,f return ;============================================================================= ; acc3:acc0 - xacc3:xacc0 -> acc3:acc0, destroy xacc Sub32from32: movf xacc0,w subwf acc0,f movf xacc1,w skpc incfsz xacc1,w subwf acc1,f movf xacc2,w skpc incfsz xacc2,w subwf acc2,f movf xacc3,w skpc incfsz xacc3,w subwf acc3,f return ;============================================================================= ; acc1:acc0 + xacc1:xacc0 -> acc1:acc0, destroy xacc Add16to16: movf xacc0,w addwf acc0,f movf xacc1,w skpnc incf xacc1,w addwf acc1,f return ;============================================================================= ; acc3:acc0 + xacc3:xacc0 -> acc3:acc0, destroy xacc Add32to32: movf xacc0,w addwf acc0,f movf xacc1,w skpnc incf xacc1,w addwf acc1,f movf xacc2,w skpnc incf xacc2,w addwf acc2,f movf xacc3,w skpnc incf xacc3,w addwf acc3,f return ;============================================================================= ;malin@onspec.co.uk ;unsigned multiply of acc1:acc0 with hi:lo leaving result in acc3:acc2:yacc1:yacc0 ; These 8 variables need to be defined ; ; Program length 32 line ; time 129 to 228 cycles ; This program looks at the lsb of acc0 to decide whether to add lo to yacc1 ; ; and hi to acc2, with appropriate carrys ; It then looks at the lsb of acc1 to decide whether to add lo to acc2 and ; hi to acc3, again with appropriate carrys. ; The rotates then only have to be done 8 times ; ; This is uses slightly more program but takes a little less time than ; a routine that performs one 16 bit addition per rotate and 16 rotates ; ; Multiple byte addition routine from Microchip AN617 ; Result registers used as loop counter from Bob Fehrenbach & Scott Dattalo ;============================================================================= Mul16by16to32: clrf acc2 clrf acc3 movlw 0x80 movwf yacc0 clrf yacc1 NextBit: rrf acc1,f rrf acc0,f skpc goto NoBitL movf lo,w addwf yacc1,f movf hi, w skpnc incfsz hi, w addwf acc2, f skpnc incf acc3, f bcf CARRY NoBitL: btfss acc0, 7 goto NoBitH movf lo,w addwf acc2,f movf hi, w skpnc incfsz hi, w addwf acc3, f NoBitH: rrf acc3,f rrf acc2,f rrf yacc1,f rrf yacc0,f skpc goto NextBit movf yacc1,w movwf acc1 movf yacc0,w movwf acc0 return ;============================================================================= ; Fast divider, ported from 24 by 16 from unknow source ; Max timing: 4+32*(6+6+4+3+5)-1+3+2=776 cycles (with return) ; Min timing: 4+32*(6+6+5 +5)-1+3+2=712 cycles (with return) ; With zero div. check +7 ; acc3:acc0 / xacc1:xacc2 -> acc3:acc0 ; Remainder -> yacc1:yacc0 ;============================================================================= Div32by16to32: #ifdef ZERODIV movf xacc0,w ; Check zero div. iorwf xacc1,w iorwf xacc2,w iorwf xacc3,w btfsc ZERO return #endif clrf yacc0 clrf yacc1 movlw 32 movwf Cnt0 DivLoop3216: rlf acc0, w ; shift dividend left to move next bit to remainder rlf acc1, f ; rlf acc2, f ; rlf acc3, f ; 32 by 16 update rlf yacc0, f ; shift carry (next dividend bit) into remainder rlf yacc1, f rlf acc0, f ; finish shifting the dividend and save carry in acc0.0, ; since remainder can be 17 bit long in some cases ; (e.g. 0x800000/0xFFFF). This bit will also serve ; as the next result bit. movf xacc0, w ; substract divisor from 16-bit remainder subwf yacc0, f movf xacc1, w skpc incfsz xacc1, w subwf yacc1, f ;here we also need to take into account the 17th bit of remainder, which ;is in acc0.0. If we don't have a borrow after subtracting from lower ;16 bits of remainder, then there is no borrow regardless of 17th bit ;value. But, if we have the borrow, then that will depend on 17th bit ;value. If it is 1, then no final borrow will occur. If it is 0, borrow ;will occur. These values match the borrow flag polarity. skpnc ; if no borrow after 16 bit subtraction bsf acc0, 0 ; then there is no borrow in result. Overwrite ; acc0.0 with 1 to indicate no ; borrow. ; if borrow did occur, acc0.0 already ; holds the final borrow value (0-borrow, ; 1-no borrow) btfsc acc0, 0 ; if no borrow after 17-bit subtraction goto DivSkip ; skip remainder restoration. addwf yacc1, f ; restore higher byte of remainder. (w ; contains the value subtracted from it ; previously) movf xacc0, w ; restore lower byte of remainder addwf yacc0, f DivSkip: decfsz Cnt0, f ; decrement counter goto DivLoop3216 ; and repeat the loop if not zero. #ifdef ZERODIV bcf ZERO ; No zero viv. #endif return ;============================================================================= ; Call w/: Number in acc1:acc0, divisor in xacc0 ; Returns: Quotient in acc0, remainder in acc1. W preserved. ; Notes: Works by left shifted subtraction. ; Size = 29, Speed(w/ call&ret) = 7 cycles if div by zero ; Speed = 94 minimum, 129 maximum cycles Div16by8to8: call DivSkipHiShift movlw 8 movwf Cnt0 movf xacc0,w Div16Loop: call Div16Code decfsz Cnt0,f goto Div16Loop rlf acc0,f ; C << lo << C return ;=============================== Div16Code: rlf acc0, f ; C << lo << C rlf acc1, f ; C << hi << C skpc ; if Carry goto DivSkipHiShift ; subwf acc1, f ; hi-=w bcf CARRY ; ignore carry return ; done ;=============================== DivSkipHiShift: subwf acc1, f ; hi-=w skpnc ; if carry set return ; done addwf acc1, f ; hi+=w bcf CARRY ; clear carry return ;============================================================================= ; Input: packed BCD in bcd2:bcd1:bcd0 (modified!) ; bcd0 = tens:ones ; bcd1 = thou:hund ; bcd2 = 0:tenk ; Output: 16 bit binary in acc1:acc0 ;Temporary: counter ; Size: 23 instructions ; Timing: 2+2+16*(6+12+3)-1+2=341 instruction cycles ; ; Notes: The routine uses BCD division by 2. Each iteration ; the LSB of BCD value (which coincides with correspondent ; binary bit) is shifted to output and BCD value is ; divided by 2. To do the division, BCD is shifted right ; once and corrected. Correction: if MSB of a nibble is set, ; subtract 3 from it. ; ;January 22, 2001 by Nikolai Golovchenko ;============================================================================= BcdToBin16: movlw 16 movwf Cnt0 BcdTobin16Loop: clrc rrf Bcd2,f rrf Bcd1, f rrf Bcd0, f rrf acc1, f rrf acc0, f clrw btfsc Bcd0, 3 iorlw 0x03 btfsc Bcd0, 7 iorlw 0x30 subwf Bcd0, f clrw btfsc Bcd1, 3 iorlw 0x03 btfsc Bcd1, 7 iorlw 0x30 subwf Bcd1, f decfsz Cnt0, f goto BcdTobin16Loop return end