;**************************************************************************** ;* Filename: PROBE.ASM ;**************************************************************************** ;* Author: Voja Antonic ;* Company: PC Press ;* Revision: RevA1 ;* Date: 07-09-98 ;* Assembled using MPASM rev 01.50 ;**************************************************************************** ;* Include files: ;* p16f84.inc ;**************************************************************************** ;* This is the program for multi-purpose laboratory instrument which ;* consists of logic probe, single-channel logic state analyzer, ;* serial code receiver and frequency counter. As this is single-chip ;* instrument, all functions are supported by software. ;* ;* LCD module used is Hitachi's LM032L with 2 lines of 20 columns. ;* ;* Note: The code is optimized for code space, and for that reason the ;* most of code could not be written in modular format. For the same ;* reason a lot of subroutines have more than one entry point and some ;* of them are terminated by GOTO instead of RETURN. ;* ;* I/O port usage (all PORTA bits are inputs, all PORTB bits outputs) ;* (note: bits 0, 5, 6 and 7 in port B have two functions each): ;* ;* PORTA,0 (input) probe input ;* PORTA,1 (input) voltage monitor (high if battery voltage < 4 V) ;* PORTA,2 (input) left key (key 1) (low = key pressed) ;* PORTA,3 (input) right key (key 2) (low = key pressed) ;* PORTA,4 (input) probe input ;* PORTB,0 (output) eneble LCD (high = select LCD), discharge (high = on) ;* PORTB,1 (output) register select LCD (low = instruction, high = data) ;* PORTB,2 (output) hi-imp output for probe pin ;* PORTB,3 (output) current hold for MCU supply (low = off) ;* PORTB,4 (output) LCD module D4 ;* PORTB,5 (output) LCD module D5, LED P (high = on) ;* PORTB,6 (output) LCD module D6, LED H (high = on) ;* PORTB,7 (output) LCD module D7, LED L (high = on) ;* ;* External Clock Frequency: 10 MHz ;* Config Bit Settings: CP=OFF, PWRTE=ON, WDT=OFF, OSC=HS ;* Program Memory Usage: 1023 words ;* Data RAM Usage: 68 bytes ;* Data EEPROM Usage: 61 bytes ;* Note: This is read-only data, so the Data EEPROM must be programmed ;* before the unit is ready to use. MCU will not affect data EEPROM contents. ;**************************************************************************** list p=16f84, f=inhx8m, n=0 include "p16f84.inc" FLAG equ 0ch ; 1 by flag register RXBITS equ 0dh ; 1 by bit0=parity, bit 1=7/8 bits, bit 2=inverse DJNZ equ 0eh ; 1 by general purpose, e.g. loop counter SCRATCH equ 0fh ; 1 by general purpose scratchpad PCOUNT equ 10h ; 1 by timing count for led P (monostable simulator) SUBMODE equ 11h ; 1 by submode (cursor horizontal position) DEBO1 equ 12h ; 1 by rotor for key 1 debouncing DEBO2 equ 13h ; 1 by rotor for key 2 debouncing COUNT equ 14h ; 1 by general purpose counter RATE equ 15h ; 1 by analyzer sample rate, 0...15 CHARCOU equ 16h ; 1 by char counter for fixed format display SHOWCOU equ 17h ; 1 by 1-4, which group of 60 samples is displayed DELAYL equ 18h ; 1 by delay for led P on when led L is on DELAYH equ 19h ; 1 by delay for led P on when led H is on PRESC equ 1ah ; 1 by prescaler rate for frequency counter TIMOUTL equ 1bh ; 1 by timeout counter lo, for auto power off TIMOUTH equ 1ch ; 1 by timeout counter hi, for auto power off RXRATE equ 1dh ; 1 by rx baud rate, 0...7 BIN4 equ 1eh ; 4 by arith buf bin value, 4 by, lo byte first CMP4 equ 22h ; 4 by arith buf for comparing, 4 by, lo byte first BUFFER equ 26h ; 42 by 42 by receive buffer for analyzer and RX REL equ 1 ; =1 to place cursor on 1st char of command (default) ; =0 to place cursor before the command ; Bits definitions for FLAG register (bit 0 not used): DP equ 1 ; decimal point in 3-digit bin2dec conv PTIP equ 2 ; prev.state of probe input (for edge detect) RIPPLE equ 3 ; zero blanking bit XTOX equ 4 ; analyzer start at: 1=rising, 0=falling edge LEDP equ 5 ; led Pulse, 1=on LEDH equ 6 ; led High, 1=on LEDL equ 7 ; led Low, 1=on ;***************************************************************************** ;* Reset vector ;***************************************************************************** goto Start ;***************************************************************************** ;* Get1MHz ;* This subroutine fetches 307 samples (last 7 will be ignored) from PORTA.0 ;* rotating throughu CARRY at 1 MHz rate - 2.5 instruction cycles for each ;* sample, realized mostly as as 2 and 3 cycles alternatively, at the ;* following order: ;* ;* 4t-2t-2t (not in main loop, executed only once), and then ;* ;* 2t-2t-3t-2t-3t-2t-3t-2t-3t-2t-3t-2t-3t-2t-2t-4t (repeated 19 times) ;* ;* Call Common initializes loop counter (COUNT) to make 19 cycles before ;* exiting (16 samples are fetched at each pass), and FSR to point to ;* BUFFER. It also presets T0SE bit depended on XTOX bit (in FLAG register) ;* to enable proper edge detecting, as it will affect TMR0 state. ;* State of key 2 (Break) is tested while waiting for starting condition. ;* Write pointer FSR is incremented after every 8 samples. COUNT initial ;* value is 01101101 and, after ANDing 0c0h and subtracting 33h from it, ;* makes 0dh, even if COUNT is incremented 18 times. After 19 passes, ;* COUNT is incremented to b'10000000', which after AND 0c0h and ;* SUB 33h makes 4dh. Those jumps are location sensitive, and it makes the ;* whole subroutine unrelocateable. ;* Between this subroutine and the instruction goto Finished (below), which ;* must be at location 4dh, there are 25 free locations. They are used for ;* tables DecTab and CurTab, which causes that those tables must have the ;* fixed length. If anything is relocated here, take care not to affect ;* location of instruction goto Finished. ;* ;* Input variables: None ;* Output variables: None ;***************************************************************************** org 1 ; this subroutine must start at address 1 Get1MHz ; 2.5 t read cycle movlw 80h-.19 ; loop end after 19 cycles (38 by=304 samples) call Common ; initialize COUNT, FSR, hi-imp out... ; ...bit XTOX and T0SE bit GetEdge btfss PORTA,3 ; test status of key 2 and... goto Break ; ...jump to Break handling routine if pressed movf TMR0,W ; TMR0 = logic level edge detector rrf PORTA,F ; <--- ; the first sample is a little earlier ; ... to compensate starting delay btfsc STATUS,Z ; test if there was egde... goto GetEdge ; ...and loop if not rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C ; movwf PCL will jump here at 18 passes ; ...@addr 0100 0000 (40h) - 33h = 0dh rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte incf COUNT,F ; COUNT = loop counter rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte incf FSR,F ; must be exactly 8 read cycles distance ; ... between FSR incrementing rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte movf COUNT,W ; COUNT = loop counter rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte andlw 0c0h ; this will make first 18 jumps to 0dh, ; ...and the 19th one to 4dh rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte addlw -33h ; this will make first 18 jumps to 0dh, ; ...and the 19th one to 4dh rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte incf FSR,F ; must be exactly 8 read cycles distance ; ... between FSR incrementing rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C rrf INDF,F ; rotate bit into destination byte rrf PORTA,F ; <--- get bit from input to C movwf PCL ; jumps to 0dh in first 18 passes ; jumps to 4dh at 19th pass ;***************************************************************************** ;* This table is used for bin2ascii (4-byte to 8-digit) conversion ;***************************************************************************** DecTab dt 098h,096h,080h ; decimal 10 000 000 dt 00fh,042h,040h ; decimal 1 000 000 dt 001h,086h,0a0h ; decimal 100 000 dt 000h,027h,010h ; decimal 10 000 dt 000h,003h,0e8h ; decimal 1 000 dt 000h,000h,064h ; decimal 100 dt 000h,000h,00ah ; decimal 10 ;***************************************************************************** ;* Cursor position table for all SUBMODEs in mode 4 (battery manager) ;***************************************************************************** CurTab4 dt 0d2h+REL,0c0h,0c3h+REL,0cah+REL ;***************************************************************************** ;* This is exit point for subroutine Get1MHz. Do not move this ;* instruction, it must be at address 4dh! ;***************************************************************************** org 80h-33h ; addr 1000 0000 (80h) - 33h = 4dh ; Get1MHz jumps here goto Finished ;***************************************************************************** ;* Table for LCD module character generator redefinition, to enable ;* pseudographic representation of bit samples in analyzer mode. It defines ;* first 8 characters, which are stored in LCD module's RAM. ;***************************************************************************** Graphs ; ... ..* .*. .** *.. *.* **. *** dt 00h, 01h, 04h, 05h, 10h, 11h, 14h, 15h ;***************************************************************************** ;* Cursor position table for all SUBMODEs in mode 1 (Analyzer) ;***************************************************************************** CurTab1 dt 0d2h+REL,0c0h,0cch+REL,0ceh+REL,0d0h+REL ;***************************************************************************** ;* Cursor position table for all SUBMODEs in mode 2 (Serial rcvr) ;***************************************************************************** CurTab2 dt 0d2h+REL,0c7h+REL,0cch+REL,0ceh+REL,0d0h+REL ;***************************************************************************** ;* Prescaler table for resolution display in mode 3 (freq counter) ;***************************************************************************** PrescTab dt .4, .8, .16, .32 ;***************************************************************************** ;* Range table for max. frequency display in mode 3 (freq counter) ;***************************************************************************** RangeTab dt .5, .10, .20, .40 ;***************************************************************************** ;* Timing constants for serial code receiver ;***************************************************************************** BaudRate dt .188, .94, .46, .23, .11, .5, .3, .1 ;***************************************************************************** ;* Text strings (terminator = last character with bit 7 set) ;***************************************************************************** TxtHz dt "MHz",'/'+80h DisTxt dt "Off Disch. Charg",'e'+80h Head1 dt "Analyze",'r'+80h Head2 dt "Seria",'l'+80h Head3 dt "Frequenc",'y'+80h Head4 dt "Batter",'y'+80h BrkMes dt "Brea",'k'+80h ; ;******* ;******* START ;******* ;***************************************************************************** ;* Power Up sequence: I/O port B defined as all outputs, PORTB,3 set to ;* switch power supply on and the internal Data Ram is cleared ;***************************************************************************** Start bsf STATUS,RP0 clrf TRISB ; port b: all bits outputs, port a: inputs bcf STATUS,RP0 movlw 0ch ; start of RAM clear, also PORTB output byte movwf PORTB ; switch power supply ON (set PORTB,3) call ClrRam ; clear internal RAM and wait 33.8 ms ;***************************************************************************** ;* LCD module initialization. 4-bit mode selected, display data RAM cleared, ;* cursor set to blink mode, and the pseudographics character for ;* character set 00h-07h preset from table Graphs. ;***************************************************************************** bcf PORTB,1 ; rs lo (instruction) movlw 2 ; 4-bit mode call Nibble ; write 4-bit mode command movlw 28h ; function set: 4 bit mode, 2 lines, 5*7 dots call WrComL ; write command and wait 130 us movlw 06h ; mode set: cursor direction right, no shift call WrComL ; write command and wait 130 us movlw 0dh ; display on, no cursor, blink cursor pos call WrComL ; write command and wait 130 us movlw 40h ; cg ram addr 0 call WrComL ; write address and wait 130 us movlw Graphs ; start address of graph set for lcd disp movwf SCRATCH ; move start address to pointer movlw .8*.4 ; 8 special characters to define ; CHARCOU is decremented in Char routine, ; ...that is why here is 8*4 movwf CHARCOU ; loop counter for 8 special characters GoGraph movlw .5 ; five rows are equal movwf COUNT ; counter for 5 rows FiveRows ; inner loop: 5 rows are equal call PclSub1 ; move SCRATCH to PCL call CharNCC ; rows 1-5 from the table decfsz COUNT,F ; five passes over? goto FiveRows ; no, loop movlw 15h ; 15h = b'10101' = dot-space-dot-space-dot call CharBl ; row 6: all dots set, row 7: all dots cleared call Blank ; row 8: all dots cleared incf SCRATCH,F ; inc ptr decfsz CHARCOU,F ; 8 characters defined? goto GoGraph ; no, loop 8 x ; ;******* ;******* USER INTERFACE ;******* ;***************************************************************************** ;* This is home point for mode 1 (Analyzer): prints text "Analyzer" and ;* command line in line 2, with cursor placed depended on variable SUBMODE. ;* Then the keyboard routine is called, where it will wait for key to be ;* pressed. ;* Break entry point prints message Break in line 1 and readraws line 2 ;***************************************************************************** Mode1 ; Mode 1: Analyzer movlw Head1-1 ; start address of string -1 call Headline ; print "Analyzer" goto Farm1 ; avoid "Break" message Break ; Break entry pt (if Break pressed in Mode 1) call PrintBrk ; print "Break" Farm1 call PrintM1 ; print string in line 2 Farm1B movlw CurTab1 ; get address of cursor table in analyzer mode call CurPosKb ; place cursor on proper position ; test keys and probe input, service leds ; return if key pressed (C:key1, NC:key2) ;***************************************************************************** ;* If key 1 pressed (C), SUBMODE is advanced (range 0...4, then wrap to 0 ;* If key 2 pressed (NC), program vectors to corresponding routine ;* (except if SUBMODE=1, then the sample rate is advanced and displayed) ;***************************************************************************** btfsc STATUS,C ; test which key was pressed goto Key1A ; jump if key 1 ; continue if key 2 pressed call Range5 ; advance variable SUBMODE in range 0...4 goto Farm1B ; go wait next key Key1A ; key 1 pressed decfsz SUBMODE,W ; test SUBMODE (set Z if SUBMODE=1) goto NoRate ; jump if SUBMODE <>1 ; continue if SUBMODE=1 incf RATE,F ; advance sample rate bcf RATE,4 ; RATE range 0...15 call ClrRow1 ; prepare line 1 to print sample rate No. incf RATE,W ; readjust RATE from 0...15 to 1...16 call Print255 ; print serial number of sample rate 1...16 goto Farm1 ; go redraw row 2 and wait for next command EdgeSet ; Change start condition (L-to-H or H-to-L) movlw 10h ; bit 4 is flag XTOX xorwf FLAG,F ; change flag goto Farm1 ; go redraw row 2 and wait for next command NoRate btfsc SUBMODE,0 ; bit 0 will be set here only if SUBMODE=3 goto Mode1Go ; if SUBMODE=3 btfsc SUBMODE,1 ; bit 1 will be set here only if SUBMODE=2 goto EdgeSet ; if SUBMODE=2 btfsc SUBMODE,2 ; bit 2 will be set here only if SUBMODE=4 goto Mode1Show ; if SUBMODE=4 ; if SUBMODE=0 then program drops to Mode2... ;***************************************************************************** ;* This is home point for mode 2 (RS232 receiver): prints text "Serial" and ;* command line in line 2, with cursor placed depended on variable SUBMODE. ;* BrkRS entry point prints message Break in line 1 and readraws line 2 ;* if no bytes are received, and displays first 7 bytes if any byte received ;***************************************************************************** Mode2 ; mode 2: Serial receiver movlw Head2-1 ; start address of string -1 call Headline ; print "Serial" goto Farm2 ; avoid "Break" message BrkRS ; Break entry pt (if Break pressed in mode 2) movf FSR,W ; FSR points where to write next rcvd byte xorlw BUFFER ; if FSR = literal BUFFER the no bytes rcvd btfss STATUS,Z ; test if FSR = literal BUFFER goto Show2 ; no - then some bytes received, show them call PrintBrk ; yes - no bytes received, print "Break" ;***************************************************************************** ;* Prints baud rate in KBaud on LCD ;* ;* Input variables: RXRATE in range 0...7 ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** Farm2 movlw 0c8h ; baud rate position on LCD call WrComL ; move cursor command bcf FLAG,DP ; no decimal point printing if RATE=0 clrf BIN4+1 ; BIN4+1 is high byte for baud rate display incf RXRATE,W ; move RXRATE from range 0...7 to 1...8 movwf DJNZ ; DJNZ = RXRATE+1 movlw .115 ; case RXRATE=7: then Baud rate=115 Kbaud btfsc DJNZ,3 ; test if DJNZ=8 (same as RXRATE=7) goto Lth256 ; yes, go case 115.2 (RXRATE=7) bsf FLAG,DP ; for rete 0...6 there is a decimal point bsf BIN4+1,1 ; case RXRATE=6: this is hi byte for 57.6 movlw .576-.512 ; case RXRATE=6: this is lo byte for 57.6 incf DJNZ,F ; DJNZ=RXRATE+2 btfsc DJNZ,3 ; test if DJNZ=8 (same as RXRATE=6) goto Lth256 ; yes, go case 57.6 (RXRATE=6) clrf BIN4+1 ; for rates 0...5 hi byte is zero movlw .3 ; constant for rates 0...5 movwf BIN4 ; BIN4 will be rotated (multiplied by 2) ; RXRATE+2 times to get 1.2 - 2.4 - 4.8 ; - 9.6 - 19.2 - 38.4 X2Loop bcf STATUS,C ; clear bit C to get multiplying by 2 rlf BIN4,F ; multiply low byte rlf BIN4+1,F ; multiply hi byte, that is 16-bit rotate decfsz DJNZ,F ; test if RXRATE+2 times multiplied goto X2Loop ; no, loop movf BIN4,W ; yes, get result to print it Lth256 call PrintBR ; print baud rate, including decimal point call Blank ; to delete last numeric from previous rate ;***************************************************************************** ;* Prints number of bits to be received (7 or 8), with suffix "p" if parity ;* bit will be received (not written to RAM!), and with prefix "i" if ;* inverse input polarity is expected. Input variable RXBITS, bit 0 set if ;* parity bit expected, bit 1 set if 8-bit word and bit 2 set if inverse ;* polarity (low start bit, inverse data bits and high stop bit) ;***************************************************************************** movlw 0cch ; pos for bit No. (7/8/7p/8p/i7/i8/i7p/i8p) -1 call WrComL ; write command movlw 'R' ; space: true polarity (R = RS232) btfsc RXBITS,2 ; let it be space if RXBITS,2 cleared movlw 'T' ; "i": inverse polarity (T = TTL) call Char ; print blank or "i" ("R" or "T") movlw '7' ; "7": 7 bits btfss RXBITS,1 ; let it be 7 if RXBITS,1 set movlw '8' ; "8": 8 bits call Char ; print "7" or "8" (bits) movlw ' ' ; space: no parity btfsc RXBITS,0 ; let it be space if RXBITS,0 cleared movlw 'p' ; "p": parity bit exists call Char ; print "p" (parity bit present) or blank ;***************************************************************************** ;* This call prints number of group displayed and "*" (execution) symbol ;***************************************************************************** call KaoAna ; print rest of line - it is the same as ; on mode 1 (analyzer) ;***************************************************************************** ;* Places cursor on proper position (input variable SUBMODE) and calls ;* keyboard subroutine, where it will wait for key to be pressed ;***************************************************************************** movlw CurTab2 ; table with cursor positions call CurPosKb ; place cursor on proper pos ; test keys and probe input, service leds ; return if key pressed (C:key1, NC:key2) ;***************************************************************************** ;* If key 1 pressed (C), SUBMODE is advanced (range 0...4, then wrap to 0 ;* If key 2 pressed (NC), program vectors to corresponding routine ;* (except if SUBMODE=1, then the Baud rate is advanced and displayed) ;***************************************************************************** btfsc STATUS,C ; test which key was pressed goto Key1B ; jump if C set, it means that key 1 pressed ; key 2 pressed call Range5 ; increment SUBMODE in range 0...4 goto Farm2 ; go redraw row 2 and wait for next command Key1B ; key 1 pressed btfsc SUBMODE,2 ; bit 2 is set only if SUBMODE = 4 goto Mode2Show ; jump if SUBMODE = 4 btfss SUBMODE,1 ; bit 1 is cleared only if SUBMODE = 0 or 1 goto Sub01 ; jump if SUBMODE = 0 or SUBMODE = 1 btfsc SUBMODE,0 ; bit 0 is set here only if SUBMODE = 3 goto Mode2Go ; jump if SUBMODE = 3 ; drops here if SUBMODE = 2 incf RXBITS,F ; advance RXBITS (command) bcf RXBITS,3 ; RXBITS cycle in range = 0...7 goto Farm2 ; go redraw row 2 and wait for next command Sub01 btfss SUBMODE,0 ; bit 0 is set here only if SUBMODE = 0 goto FreqEp ; if SUBMODE = 0 then goto frequency entry pt incf RXRATE,F ; if SUBMODE = 1 then advance RXRATE bcf RXRATE,3 ; RXRATE cycle in range 0...7 goto Farm2 ; go redraw row 2 and wait for next command ;***************************************************************************** ;* This subroutine increments variable SUBMODE, and if the result is >4 it ;* wraps to 0 ;***************************************************************************** Range5 ; increment SUBMODE in range 0...4 incf SUBMODE,F ; advance SUBMODE btfsc SUBMODE,2 ; if SUBMODE,2 cleared then no overflow btfss SUBMODE,0 ; if SUBMODE,0 cleared then no overflow return ; no overflow: return clrf SUBMODE ; SUBMODE cycle in range 0...4 return ; SUBMODE wrapped to 0, return ;***************************************************************************** ;* Mode4 is home point for mode 4 (off/discharge/charge): prints text ;* "Battery" and command line in line 2, with cursor placed depended on ;* variable SUBMODE. Then the keyboard routine is called, where it will ;* wait for key to be pressed ;* Break4 entry point prints message Break in line 1 and readraws line 2 ;* ExitDis is the entry point if key 2 is pressed during discharging ;***************************************************************************** ExitDis ; exit discharge entry point (if disch Break) call DisEna30 ; switch off PORTB,0 for discharge transistor Break4 ; exit Charge entry point (if Charge Break) call PrintBrk ; print "Break" goto Contm4 ; avoid headline printing Mode4 ; mode 4: discharge/charge call Headline2 ; print "Battery" Contm4 call Row2 ; move cursor to line 2 movlw DisTxt-1 ; point to message -1 call Write ; print Off Disch Charge Farm4 movlw CurTab4 ; point to cursor table for mode 4 call CurPosKb ; place cursor @ Off/Disch/Charge/--> ; test keys and probe input, service leds ; return if key pressed (C:key1, NC:key2) ;***************************************************************************** ;* If key 1 pressed (C), SUBMODE is advanced (range 0...3, then wrap to 0 ;* If key 2 pressed (NC), program jumps to corresponding routine ;***************************************************************************** btfsc STATUS,C ; test which key was pressed goto Key1D ; C set: key 1 pressed ; key 2 pressed incf SUBMODE,F ; advance SUBMODE bcf SUBMODE,2 ; SUBMODE cycle in range 0...3 goto Farm4 ; go redraw row 2 and wait for next command Key1D ; key 1 pressed btfsc SUBMODE,1 ; bit 1 set only if Charge or Disch. submode goto ChargDis ; goto Charge or Discharge process ; depended on bit 0 in variable SUBMODE btfss SUBMODE,0 ; SUBMODE,0 cleared here if SUBMODE=0 goto Mode1 ; shortcut to mode 1 (Analyzer) goto Suicide ; manual power off ; ;******* ;******* CHARGE/DISCHARGE ;******* ;***************************************************************************** ;* Charge/Discharge entry point. If bit SUBMODE,0 set, then go to Charge ;* SUBMODE,1 is set in this point. (SUBMODE=2 or 3) ;***************************************************************************** ChargDis call ClrRow1 ; in both cases row 1 must be cleared btfsc SUBMODE,0 ; test if SUBMODE,0 set, if so... goto Charge ; ...jump to Charge (SUBMODE=3) ; ...else continue to discharge (SUBMODE=2) ;***************************************************************************** ;* Discharge starts here (SUBMODE=2) ;* Cursor moved to line 1 under text "Disch." ;* Then command 02h (Home Cursor) is issued to LCD controller, but this is ;* dummy command - the sense is to freeze it after first nibble, and thus to ;* leave PORTB,0 (ENA) in high state as long as discharging lasts. After the ;* discharging termination (if voltage monitor detects <4V or key 2 pressed), ;* the command for LCD controller will be completed, switching ;* discharging transistor off. ;* If discharging is broken by key, program returns to user interface for ;* mode 4, if terminated by voltage monitor, charging takes place ;***************************************************************************** bcf PORTB,1 ; pull LCD Register Select low (=instruction) clrw ; high nibble of instruction 02h = 0h call Hinib_B ; output W,4-7 to 4-bit LCD data bus call EnaLCD ; generate Enable signal (1200us) for LCD movlw 20h ; command 02h = home cursor (nibbles swapped) call Hinib_B ; output W,4-7 to 4-bit LCD data bus bsf PORTB,0 ; ENA activated (the command won't be ; finished until Break or voltage < 4V) DisLoop btfss PORTA,3 ; test key 2 status... goto ExitDis ; if low,discharging is manually broken by key btfss PORTA,1 ; test voltage monitor... goto DisLoop ; if still >=4V, loop ; discharging terminated here (voltage < 4V) call DisEna30 ; switch off discahge transistor ;***************************************************************************** ;* Charging starts here (by command or after successful discharging) ;* Minute and hour counters are initialized and counting process starts. ;* Clock (in format HH:MM) is displayed in line 1 under text "Charge". ;* If charging broken by key, program returns to user interface for ;* mode 4, if terminated by timeout (14 hours), the unit jumps to SUICIDE ;* (switches off the unit forcing the output PORTB,3 low). ;***************************************************************************** Charge ; Charge entry point clrf TIMOUTL ; initialize minute counter 0...59 clrf TIMOUTH ; initialize hour counter 0...13 ChLoop movlw 8bh ; position of digital clock on LCD call WrComL ; cursor to digital clock pos movf TIMOUTH,W ; TIMOUTH=hours in binary format call Print255 ; print hour in format HH movlw ':' ; ":" = separator call PrintTL ; print ":" and minute in format MM movlw .228 ; 228 x 263270.4 us = 60 sec movwf SCRATCH ; high byte loop counter for 1 minute loop Min1 call GoLoop ; 1283t (513.2us) inclusive ; 1283t call GoLoop ; total 1026.4us ; 1283t btfss PORTA,3 ; 2t test status of key 2... goto Break4 ; - ... if low,charging manually terminated decfsz COUNT,F ; 1t low byte loop counter goto Min1 ; 2t inner pass 1028.4 us decfsz SCRATCH,F ; high byte loop counter for 1 minute goto Min1 ; one pass 263270.4 us incf TIMOUTL,F ; advance minute counter movf TIMOUTL,W ; TIMOUTL = minute up counter addlw -.60 ; test if 60 minutes of charging passed... btfss STATUS,C ; if 60 minutes passed, C should be set goto NotHour ; not yet hour advance clrf TIMOUTL ; if 60 minutes passed, clear minute counter incf TIMOUTH,F ; ...and advance TIMOUTH = hour up counter NotHour movf TIMOUTH,W ; TIMOUTH = hour up counter addlw -.14 ; test if 14 hours of charging btfss STATUS,C ; if 14 hours passed, C should be set goto ChLoop ; ...if not yet 14 hours, loop goto Suicide ; charging terminated after 14 h ; ;******* ;******* KEYBOARD AND PROBE ;******* ;***************************************************************************** ;* CurPosKb ;* This subroutine places cursor in line 2 at position taken from the ;* lookup table: table offset is addressed by W at input, and the table read ;* location by variable SUBMODE. ;* High timeout counter (TIMOUTH) is initialized. This sets automatic ;* Power Off timing to about 8 minutes. TIMOUTL is of minor importance ;* here (it affects less than 2 seconds of timing), so it was not worth ;* waisting one instruction word. ;* Bit DEBO,0 is set to disable false recognizing of PORTA,3 low level as ;* falling edge (as if key 2 was just pressed). This could happen if some ;* function was broken by pressing key 2, as those are simple port tests ;* without affecting debouncer. ;* This subroutine continues to keyboard scan subroutine. ;* ;* Input variables: SUBMODE, affects cursor position ;* Output variables: TIMOUTH=0, high timeout counter ;***************************************************************************** CurPosKb addwf SUBMODE,W ; add SUBMODE to lookup table offset call PclSub ; get cursor position from table call WrComL ; write new cursor position to LCD clrf TIMOUTH bsf DEBO1,0 ; set any bit in both debouncers... bsf DEBO2,0 ; ...to disable false recognizing of low... ; ...level as falling edge ;***************************************************************************** ;* GoKbd ;* This is the main keyboard subroutine, in which program loops all the time ;* except in frequency counter mode, or while waiting for start condition or ;* executing some command. Program exits this subroutine only if some key is ;* just pressed (not if continuosly pressed), or when timeout counter ;* (TIMOUTH, TIMOUTL) after 8 min reaches zero. If key 1 was pressed, flag ;* STATUS,C will be reset at exit, if key 2 was pressed, flag STATUS,C will ;* be set. If timeout detected, the routine SUICIDE is executed (the unit ;* switched off forcing the output PORTB,3 low). ;* During keyboard scan, LEDs L, H and P are serviced. Logic state at ;* PORTA,4 affects LEDs L and H directly, and LED P is under control of down ;* counter PCOUNT. This counter is initialized at every logic level ;* transition at PORTA,4, and while counting down, if PCOUNT>0, LED P is ;* switched on. ;* Loop labeled "Unstable" adds the extra delay which timing is not ;* constant, but changes from 3 to 49t. This minimizes the interference ;* between the input scan and tested signal frequency. ;* Counter TMR0 is used for detecting of short pulses. At each ;* transition detected, TMR0 is cleared, and then periodicaly tested if ;* counter state was incremented. If so, PCOUNT is initialized and LED P ;* turned on. ;* Register DJNZ is used as a freerunning counter, which divides loop count ;* by 256 and slows down PCOUNT countdown and keys scanning. Keys are ;* debounced and falling edge (pressing moment) detected by rotating ;* registers DEBO1 and DEBO2, and testing them if the key was unpressed at ;* least at 7 passes and then pressed at 1 pass. ;* ;* Input variables: none ;* Output variables: Bit STATUS,C reset if key 1 was pressed, set if key 2 ;***************************************************************************** GoKbd movf DJNZ,W ; 1t to avoid interference, total avg 29t iorlw 0f0h ; 1t extra timing range between -.16 and -1 movwf SCRATCH ; 1t here SCRATCH varies from 0f0h to 0ffh UnStable incfsz SCRATCH,F ; 1-17t (avg .9) advance extra timing count goto UnStable ; 2-32t (avg .17) loop loosing extra time movf TMR0,W ; TMR0 = hardware transition detector btfsc STATUS,Z ; test if there was transition at PORTA,4... goto NoIniP ; ...if not, do not affect LED P status bsf PCOUNT,3 ; initialize PCOUNT for LED P timing clrf TMR0 ; reinitialize hardware transition detector NoIniP movlw 28h ; bit 4 (TOSE) RESET: low-to-hi transition btfsc PORTA,4 ; if probe tip low,leave TOSE reset... movlw 38h ; if high, set TOSE: hi-to-low transition bsf STATUS,RP0 ; select bank 1 of registers movwf OPTION_REG ; set/reset TOSE bcf STATUS,RP0 ; reselect bank 0 bcf DELAYH,0 ; initialize input bit in delay HI rotor bcf DELAYL,0 ; initialize input bit in delay LOW rotor movlw 4 ; value 4 = bit 2 set xorwf PORTB,F ; changes state of PORTB.2 (square wave... ; generation for probe tip hi-impedance output btfsc PORTA,4 ; test probe input logic level... goto InputHi ; ...and jump if case 2: input high ; ...or continue if case 1: input low btfss PORTB,2 ; test state of hi-impedance output and... bsf DELAYL,0 ; ...switch on led L because hi-ipm out was lo bcf FLAG,PTIP ; reset flag to notify that previoust state... ; ...of probe tip was low goto ContInpX ; jump to skip case 2 InputHi ; entry point for case 2: input high btfsc PORTB,2 ; test state of hi-impedance output and... bsf DELAYH,0 ; ...switch on led L because PORTB,2 was high bsf FLAG,PTIP ; set flag to notify that previoust state... ; ...of probe tip was high ContInpX ; moves LHP leds from FLAG to PORTB bcf FLAG,LEDL ; initialize input bit for led L delay rotor movf DELAYL,F ; test DEALYL status... btfss STATUS,Z ; ...and skip if DELAYL=0 bsf FLAG,LEDL ; turn on led L if DELAYL rotor > 0 rlf DELAYL,F ; propagate bit thru DELAYL rotor bcf FLAG,LEDH ; initialize input bit for led H delay rotor movf DELAYH,F ; test DEALYH status... btfss STATUS,Z ; ...and skip if DELAYH=0 bsf FLAG,LEDH ; turn on led H if DELAYH rotor > 0 rlf DELAYH,F ; propagate bit thru DELAYH rotor call MoveLESd ; transfer leds status from flag bits to PORTB incfsz DJNZ,F ; test if this is 256th pass... goto GoKbd ; ...if not, loop ;------------------------------ passes here at each 256. pass (about 8.9 ms) bcf FLAG,LEDP ; reset led P flag (will be set if PCOUNT>0) movf PCOUNT,F ; test PCOUNT status... btfsc STATUS,Z ; ...and skip jump if PCOUNT>0 goto PCount0 ; ...else jump if PCOUNT = 0 decf PCOUNT,F ; if PCOUNT>0, then decrement it movf DELAYL,W ; W>0 if led L is on iorwf DELAYH,W ; W>0 if led L or led H is on btfss STATUS,Z ; ...skip if both L and H leds are off bsf FLAG,LEDP ; if PCOUNT>0 then dec PCOUNT, and set led P PCount0 ; ----------- key 2 test (right key) swapf PORTA,W ; let key 1 and 2 status move to bits 6 and 7 movwf SCRATCH ; SCRATCH,7=key2, SCARTCH,6=key1 comf SCRATCH,F ; complement to set bit if key pressed rlf SCRATCH,F ; set C if key 2 pressed rlf DEBO2,F ; propagate key 2 bit thru rotor bcf STATUS,C ; reset C to notify at exit that key 2 pressed decf DEBO2,W ; DEBO2 = b'00000001' if just pressed btfsc STATUS,Z ; skip return if key 2 was not just pressed return ; *** exit 1: key 2 just pressed (NC) ; ----------- key 1 test (left key) rlf SCRATCH,F ; set C if key 1 pressed rlf DEBO1,F ; propagate key 1 bit thru rotor bsf STATUS,C ; set C to notify at exit that key 1 pressed decf DEBO1,W ; DEBO1 = b'00000001' if just pressed btfsc STATUS,Z ; skip return if key 1 was not just pressed return ; *** exit 2: key 1 just pressed (C) decfsz TIMOUTL,F ; timeout lo counter... goto GoKbd ; ... not yet zero: loop decfsz TIMOUTH,F ; timeout hi counter... goto GoKbd ; ... not yet zero: loop ; *** exit 3: continue with timeout process ;***************************************************************************** ;* Power Off entry point ;* Wait until both keys off for 34 ms, and then switch power off. ;* PORTB,3, when low, switches the unit off. ;***************************************************************************** Suicide call KeysOff ; test keys off to avoud re-trigering clrf PORTB ; pull PORTB,3 low to switch power off goto $ ; loop until power off ;***************************************************************************** ;* KeysOff ;* Loop until both keys off for 34 ms, then exit ;* ;* Input variables: none ;* Output variables: TIMOUTH is cleared to 0 ;***************************************************************************** KeysOff clrf TIMOUTH ; initialize pointer BothOff btfsc PORTA,2 ; skip if key 1 on btfss PORTA,3 ; do not skip if key 2 on goto KeysOff ; reinitialize ptr if any key on call loop130 ; loop 130us decfsz TIMOUTH,F ; test pointer goto BothOff ; loop 256 times to verify both keys off return ; both keys are off for at least 34 ms ; ;******* ;******* ANALYZER ;******* ;***************************************************************************** ;* Pointer2 ;* Writes two measuring points for analyzer reference. First is TIMOUTL*10+5, ;* then TIMOUTL is incremented by 2 and the second one is TIMOUTL*10+0 ;* ;* Input variables: TIMOUTL will first be printed as TIMOUTL*10+5 ;* Output variables: TIMOUTL is incremented by 3 (pointer advanced by 30) ;***************************************************************************** Pointer2 call Pointer1 ; first two digits movlw '5' ; third digit is 5 for odd pointer incf TIMOUTL,F ; each incrementing advances pointer by 10 call AdvToCh ; advance ptr and print "5" call Pointer1 ; first two digits movlw 'O' ; third digit is 0 for even pointer AdvToCh incf TIMOUTL,F ; each incrementing advances pointer by 10 goto Char ; print 0 or 5 ;***************************************************************************** ;* Pointer1 ;* Writes blank, then symbol "^" and then converts TIMOUTL and prints ;* as 2 digits. ;* ;* Input variables: TIMOUTL, bin value which will be printed as 2-digits ;* Output variables: none ;***************************************************************************** Pointer1 call Blank ; skip first 3 samples (one character) movlw '^' ; "^" (pointing tool) PrintTL call Char ; print "^" movf TIMOUTL,W ; TIMOUTL is the main pointer for... goto Print255 ; ...first two digits ;***************************************************************************** ;* Mode1Show ;* Write measuring points at row 2, and wait until keys are released. ;* Then increment SHOWCOU in range 0...4 and continue to Draw subroutine ;* This command, which executes when key 2 is pressed in analyzer mode, ;* while the cursor is on the number of 60-sample group, advances the ;* pointer. ;* It continues to Draw routine. ;***************************************************************************** Mode1Show call Row2 ; pointers are in row 2 bcf STATUS,C ; prepare for multiplying by 2 rlf SHOWCOU,W ; W=SHOWCOU*2 movwf TIMOUTL ; TIMOUTL=SHOWCOU*2 rlf TIMOUTL,F ; TIMOUTL-SHOWCOU*4 addwf TIMOUTL,F ; TIMOUTL-SHOWCOU*6 call Pointer2 ; print 1st and 2nd pointer call Pointer2 ; print 3rd and 4th pointer call KeysOff ; wait until key off call PrintM1 ; restore normal row 2 incf SHOWCOU,F ; advance number of groups displayed btfsc SHOWCOU,2 ; test if SHOWCOU=5: first test bit 2... btfss SHOWCOU,0 ; test if SHOWCOU=5: ...then test bit 0 goto Draw ; skip wrapping if SHOWCOU<5 clrf SHOWCOU ; SHOWCOU cycle in range 0...4 ;***************************************************************************** ;* Draw ;* This subroutine writes 20 pseudographic characters in line 1 in analyzer ;* mode. First the whole buffer is rotated 0/60/120/180/240 bit places to ;* the right (if SHOWCOU=0/1/2/3/4, respectively) to adjust the sequence ;* to be displayed to the start of the buffer. Then the string of 20 ;* 3-bit groups is rotated right, and displayed as 20 special characters ;* (codes 0-7), defined at program setup (at loop labeled GoGraph). Then ;* the buffer is rotated again, to the total of 304 bit places, so the ;* buffer contents is unmodified on exit. ;* ;* Input variables: ;* SHOWCOU, denotes which group of 60 samples will be displayed ;* Output variables: none ;***************************************************************************** Draw movf SHOWCOU,W ; prepare to rotate buffer SHOWCOU*60 times btfss STATUS,Z ; avoid rotating if SHOWCOU=0 call Carusel ; rotate buffer SHOWCOU*60 times call Row1 ; samples must be written in row 1 movlw .20 ; .20 characters to write movwf CHARCOU ; CHARCOU is the main character counter Go20Chars clrf SCRATCH ; register for 3-bit code generation (0...7) call RRBuf ; bit from buffer in C... rlf SCRATCH,F ; ...bit from C in code register call RRBuf ; bit from buffer in C... rlf SCRATCH,F ; ...bit from C in code register call RRBuf ; bit from buffer in C... rlf SCRATCH,W ; ...bit from C in code register and in W call CharNCC ; draw one char = 3 samples decfsz CHARCOU,F ; 20 characters written? goto Go20Chars ; no, loop movf SHOWCOU,W ; prepare to rotate to total of 304 bits sublw .4 ; W=4-SHOWCOU btfss STATUS,Z ; avoid rotating if SHOWCOU=0 call Carusel ; rotate buffer again to restore it call RRBuf4 ; four more times to get 304 times goto Farm1 ; done, go back to user interface ;***************************************************************************** ;* Carusel ;* This subroutine rotates BUFFER right W*60 times ;* Note: if W=0, rotating will be performed 1024 times ;* ;* Input variables: ;* W, how many (*60) times the buffer will be rotated right (W>0) ;* Output variables: none ;***************************************************************************** Carusel movwf SCRATCH ; SCRATCH=W swapf SCRATCH,F ; SCRATCH=W*16 subwf SCRATCH,F ; SCRATCH=W*15 RRLoop call RRBuf4 ; 4 rotates in every pass decfsz SCRATCH,F ; done W*15*4 times? goto RRLoop ; no, continue rotating BUFFER return ; done ;***************************************************************************** ;* RRBuf4 executes RRBuf 4 times ;* RRBuf rotates buffer (38 bytes=304 bits) right for one bit position. Bit ;* STATUS,C is first loaded from the first bit in buffer, so rotating will ;* be completely performed at 304 bits, not through C. ;* ;* Input variables: none ;* Output variables: none ;***************************************************************************** RRBuf4 ; 4 times rotates right 38 bytes call RRBuf2 ; rotate BUFFER 2* and then again 2* RRBuf2 call RRBuf ; rotate BUFFER once and then again once RRBuf ; rotates right 38 bytes, bit in C at exit movlw BUFFER+.37 ; start with last byte to be rotated movwf FSR ; FSR = pointing register for rotating movlw .38 ; 38 bytes total buffer movwf COUNT ; byte counter bcf STATUS,C ; C will be rotated into BUFFER+.37, so it... btfsc BUFFER,0 ; ...must be equal to bit BUFFER+0,0, to... bsf STATUS,C ; ...perform non-destructive rotating ByteLoop rrf INDF,F ; byte rotated here decf FSR,F ; let FSR point to next byte decfsz COUNT,F ; COUNT is byte counter, done? goto ByteLoop ; no, loop return ; here the output bit is in STATUS,C ;***************************************************************************** ;* Mode1Go ;* This is entry point for analyzer start command (symbol * ). After clearing ;* line 1 of LCD, the program vectors to routines which handle different ;* sampling rates. Three highest rates (1 MHz, 500 KHz and 228 KHz are ;* sampled at individual routines (Get1MHz, Get500KHz and Get228KHz), and ;* the remining rates at routine GetSlowClk. All those routines (except ;* Get1MHz, which is location-sensitive (so it is at the very beginning of ;* the program), are listed here. ;* ;* Input variables: none ;* Output variables: none ;***************************************************************************** Mode1Go ; *** ept: analyzer start call ClrRow1 ; clear LCD line 1 and turn LEDs off movf RATE,W ; RATE = sample rate in range 0...15 btfsc STATUS,Z ; skip if sample rate>0 goto Get1MHz ; jump to individual routine if RATE=0 ; --- try 500 KHz rate movwf SCRATCH ; SCRATCH = RATE decfsz SCRATCH,F ; test if RATE=1 goto Try228KHz ; if not RATE=1, then try if RATE=2 ; if RATE=1, then drop to Get500KHz ;***************************************************************************** ;* Get500KHz ;* This subroutine fetches 304 samples at 2us rate (5 instruction cycles ;* timing). ;* Call Common initializes loop counter (COUNT) to 38*8=304 samples ;* and FSR to point to BUFFER. It also presets T0SE bit depended on ;* XTOX bit (in FLAG register)to enable proper edge detecting, as it ;* will affect TMR0 state. ;* State of key 2 (Break) is tested while waiting for starting condition. ;* ;* Input variables: none ;* Output variables: none ;***************************************************************************** Get500KHz ; 5 t read cycle movlw .38 ; 38 bytes * 8 bits = 304 samples call Common ; initialize COUNT, FSR, hi-imp out... ; ...bit XTOX and T0SE bit Edge500 btfss PORTA,3 ; test status of key 2 and... goto Break ; ...jump to Break handling routine if pressed movf TMR0,W ; TMR0 = logic level edge detector btfsc STATUS,Z ; test if there was egde... goto Edge500 ; ...and loop if not decf FSR,F ; adjust pointer as it will be advanced... ; ... before data write Get500Loop rrf PORTA,F ; <-- move input status to C incf FSR,F ; advance write pointer rrf INDF,F ; write bit C in destination rotor movlw .6 ; initialize count for 6 bits movwf DJNZ ; DJNZ = bit counter Go6Bits rrf PORTA,F ; <-- 6* move input status to C rrf INDF,F ; write bit C in destination rotor decfsz DJNZ,F ; DJNZ = bit counter goto Go6Bits ; loop if not yet 6 bits fetched rrf PORTA,F ; <-- move input status to C rrf INDF,F ; write bit C in destination rotor decfsz COUNT,F ; COUNT = byte counter goto Get500Loop ; loop if not yet 38 bytes fetched goto Finished ; all bits fetched; go display them ;***************************************************************************** ;* Test if register SCRATCH reaches 0 after decrementing (this happens ;* if RATE = 2), if so drop to Get228KHz else go to GetSlowClk ;***************************************************************************** Try228KHz decfsz SCRATCH,F ; test RATE status (SCRATCH=RATE-1) goto GetSlowClk ; jump if not RATE=2 ;***************************************************************************** ;* Get228KHz ;* This subroutine fetches 304 samples at 4.4us rate (11 instruction ;* cycles timing). ;* Call Common304 initializes loop counter (COUNT) to 304 samples ;* and FSR to point to BUFFER. It also presets T0SE bit depended on ;* XTOX bit (in FLAG register)to enable proper edge detecting, as it ;* will affect TMR0 state. ;* State of key 2 (Break) is tested while waiting for starting condition. ;* ;* Input variables: none ;* Output variables: none ;***************************************************************************** Get228KHz ; 11 t read cycle call Common304 ; initialize COUNT(lo), TIMOUTH(hi byte)... ; ...FSR, hi-imp outbit XTOX and T0SE bit Edge228 btfss PORTA,3 ; test status of key 2 and... goto Break ; ...jump to Break handling routine if pressed movf TMR0,W ; TMR0 = logic level edge detector btfsc STATUS,Z ; test if there was egde... goto Edge228 ; ...and loop if not Go228 goto $+1 ; 2 two extra cycles to make 11t Go228B rrf PORTA,F ; <--- ; 1 move input bit to C rrf INDF,F ; 1 write bit C in destination rotor decf COUNT,W ; 1 COUNT = bit counter andlw 7 ; 1 test if 8th pass... btfsc STATUS,Z ; 1 (2) ...and skip if not incf FSR,F ; 1 (0) ...else advance pointer decfsz COUNT,F ; 1 COUNT = (lo byte) bit counter goto Go228 ; 2 loop if not yet = 0 decfsz TIMOUTH,F ; TIMOUTH = (hi byte) bit counter goto Go228B ; this does not add extra cycles, as it ; ...jumps after goto $+1 goto Finished ; all bits fetched; go display them ;***************************************************************************** ;* GetSlowClk ;* This subroutine fetches 304 samples at variable rate, depended on ;* RATE (SCRATCH=RATE-3). Timing constant is loaded from lookup table ;* located at DATA EEPROM (locations 30h-3ch). ;* ;* Call Common304 initializes 16-bit loop counter to 304 samples ;* (lo byte: COUNT=.304-.256, hi byte: TIMOUTH=.1) ;* and FSR to point to BUFFER. It also presets T0SE bit depended on ;* XTOX bit (in FLAG register)to enable proper edge detecting, as it ;* will affect TMR0 state. ;* ;* Rates 3-11 (100KHz-2.4KHz) have loop period of factor took from EEPROM ;* table multiplied by 5 instruction cycles and adding 20 instruction ;* cycles (Factor*5T+20T), and rates 12-15 (1.2KHz-40Hz) multilied the ;* factor by 417 instruction cycles and adding 415 instruction cycle ;* (Factor*417T+415T) ;* ;* RATE = 3, factor: 1, T/cycle: 25, Freq: 100 KHz ;* RATE = 4, factor: 6, T/cycle: 50, Freq: 50 KHz ;* RATE = 5, factor: 9, T/cycle: 65, Freq: 38.4 KHz ;* RATE = 6, factor: 16, T/cycle: 100, Freq: 25 KHz ;* RATE = 7, factor: 22, T/cycle: 130, Freq: 19.2 KHz ;* RATE = 8, factor: 46, T/cycle: 250, Freq: 10 KHz ;* RATE = 9, factor: 48, T/cycle: 260, Freq: 9.6 KHz ;* RATE = 10, factor: 100, T/cycle: 520, Freq: 4.8 KHz ;* RATE = 11, factor: 204, T/cycle: 1040, Freq: 2.4 KHz ;* RATE = 12, factor: 5, T/cycle: 2500, Freq: 1 KHz ;* RATE = 13, factor: 14, T/cycle: 6253, Freq: 400 Hz ;* RATE = 14, factor: 59, T/cycle: 25018, Freq: 100 Hz ;* RATE = 15, factor: 149, T/cycle: 62548, Freq: 40 Hz ;* ;* State of key 2 (Break) is tested while waiting for starting condition. ;* LED P is turned on while sampling, to indicate sample period at slower ;* rates, in which it appears to be visible. ;* ;* Input variables: RATE, affects timing factor ;* Output variables: none ;***************************************************************************** GetSlowClk decf SCRATCH,W ; W = RATE-3 addlw .48 ; rate table in data eeprom starts at addr .48 call AGet_EE ; get time constant from table in data eeprom movwf SCRATCH ; time constant for rates 3-15 move to SCRATCH call Common304 ; initialize COUNT(lo), TIMOUTH(hi byte)... ; ...FSR, hi-imp outbit XTOX and T0SE bit EdgeSlow btfss PORTA,3 ; test status of key 2 and... goto Break ; ...jump to Break handling routine if pressed movf TMR0,W ; TMR0 = logic level edge detector btfsc STATUS,Z ; test if there was egde... goto EdgeSlow ; ...and loop if not bsf PORTB,5 ; turn led P on to notify samplig in progress GoSlow rrf PORTA,F ; <--- ; 1 move input bit to C rrf INDF,F ; 1 write bit C in destination rotor decf COUNT,W ; 1 COUNT = bit counter andlw 7 ; 1 test if 8th pass... btfsc STATUS,Z ; 1 (2) ...and skip if not incf FSR,F ; 1 (0) ...else advance pointer btfsc RATE,3 ; 1 1 if bits 2 and 3 cleared, that... btfss RATE,2 ; 2 1 ...means that rate<12... goto Not417 ; - 2 ...if so, jump to short timing ; ---------- 417 (RATE 12-15, bits 2,3 set) movf SCRATCH,W ; 1 SCRATCH = timing constant movwf CHARCOU ; 1 CHARCOU = loop counter Loop417 ; \ movlw .82 ; 1 \ movwf DJNZ ; 1 \ call Loop7 ; 411 > total cycles here 417*SCRATCH - 1 nop ; 1 / decfsz CHARCOU,F ; 1 / 2t at exit only goto Loop417 ; 2 / 0t at exit only movf PORTB,W ; 1 andlw 0dfh ; 1 reset bit 5 (LED P) btfss COUNT,4 ; 1 2 iorlw 20h ; 1 0 set bit 5 (LED P) if COUNT,4 = 0 movwf PORTB ; 1 blink LED P while sampling ea 16th pass movlw .78 ; 1 constant for long timing goto SameAs5 ; 2 skip short timing Not417 ; ---------- 5 (RATE 3-11) goto $+1 ; 2 waist 2 cycles movf SCRATCH,W ; 1 SCRATCH = timing constant SameAs5 ; ---------- 417 and 5 (RATE 3-15) movwf DJNZ ; 1 DJNZ = loop counter call GoLoop ; short timing: 3T+5T*SCRATCH, long: 393T decfsz COUNT,F ; 1 COUNT = (lo byte) bit counter goto GoSlow ; 2 loop if not yet = 0 decfsz TIMOUTH,F ; TIMOUTH = (hi byte) bit counter goto GoSlow ; this adds 2T extra only once,after 48th pass Finished clrf SHOWCOU ; reset pointer to 1st group of 60 samples goto Draw ; all bits fetched; display them ;***************************************************************************** ;* Common304 ;* This subroutine initializes low byte loop counter (COUNT) to 304 samples ;* (lo byte: COUNT=.304-.256, hi byte: TIMOUTH=.1+1) ;* and FSR to point to BUFFER. It also presets T0SE bit depended on ;* XTOX bit (in FLAG register)to enable proper edge detecting, as it ;* will affect TMR0 state. ;* Entry point Common allows subroutines Get1MHz and GetSlowClk to preset ;* COUNT to another values. ;* ;* Input variables: ;* For entry point COMMON, register W is placed in COUNT ;* Output variables: ;* COUNT is initialized to number of loop passes (W or low byte of 304) ;* TMR0 is cleared ;* T0SE and PORTB,2 are copied from FLAG,XTOX ;***************************************************************************** Common304 movlw .2 ; hi byte = 2 for regular lo byte value... ; ...plus extra 256 passes movwf TIMOUTH ; hi byte loop counter for 304 passes movlw .304-.256 ; lo byte value for 304 passes Common movwf COUNT ; COUNT = loop counter movlw BUFFER ; first byte of destination movwf FSR ; FSR = destination pointer movlw 28h ; initialize T0SE fot low-to-hi transition bcf PORTB,2 ; clear hi-imp out if expecting rising edge btfsc FLAG,XTOX ; test which edge was slctd as start condition goto ToOption ; and skip falling edge if rising selected movlw 38h ; initialize T0SE fot hi-to-low transition bsf PORTB,2 ; set hi-imp out if expecting falling edge ToOption bsf STATUS,RP0 ; select bank 1 of registers movwf OPTION_REG ; set/reset TOSE bcf STATUS,RP0 ; reselect bank 0 clrf TMR0 ; initialize TMR0 as edge detector return ; finished ;***************************************************************************** ;* PrintM1 ;* Print string at line 2 in analyzer mode. ;* At pos 0, rate in format XX[[.]X][M|K]Hz/XX[[.]X][u|m]s is printed. Those ;* values are picked from table located in Data EEPROM, locations 0-2Fh. ;* Register CHARCOU is used to fill blanks up to pos 13 in line 2, to ;* disable phantom characters appearance when changing rate from some ;* long-string to short-string value. ;* Symbol for starting or rising edge for starting event is written at pos ;* 13, symbol "*" for start command at pos 15 and the number of group ;* displayed at pos. 17. ;* ;* Input variables: ;* RATE will be printed in pos 0, row2 ;* bit FLAG,XTOX affects the printed symbol of rising/falling edge ;* SHOWCOU (number of group displayed) is printed as numeric (+1) ;* Output variables: none ;***************************************************************************** PrintM1 call Row2 ; move cursor to row 2 movlw .13 ; initialize counter for 13 char fix format movwf CHARCOU ; CHARCOU = character counter movf RATE,W ; W = RATE addwf RATE,W ; W = 2 * RATE addwf RATE,W ; W = 3 * RATE (ea rate has 3 bytes in table) call AGet_EE ; get first byte from table in data eeprom movwf TIMOUTL ; TIMOUTL = 1st byte from table swapf TIMOUTL,W ; move to bits 0-3 as bits for freq are 4-7 call Get_EE ; get second byte from table in data eeprom btfsc TIMOUTL,6 ; bit 6 = decimal point for frequency bsf FLAG,DP ; set decimal point bit if bit 6 set call Print3 ; display sampling frequency movlw 'M' ; for "MHz" display movf RATE,F ; if RATE=0... btfss STATUS,Z ; ...then let it be MHz movlw 'K' ; for "KHz" display btfsc TIMOUTL,7 ; bit 7 = KHz or MHz, skip 1st char if cleared call Char ; print "M" or "K" if bit 7 set movlw TxtHz-1+1 ; for "Hz" display call Write ; print "Hz" movf TIMOUTL,W ; TIMOUTL = 1st byte from table call Get_EE ; get second byte from table in data eeprom btfsc TIMOUTL,2 ; bit 2 = decimal point for period bsf FLAG,DP ; set decimal point bit if bit 2 set call Print3 ; display digits for period movlw 0e4h ; Greek "micro" btfss TIMOUTL,3 ; if bit 3 set, let it be "micro" movlw 'm' ; ...if not, convert to "milli" (m) call Char ; print "micro" or "m" movlw 's' ; s stands for seconds call Char ; print "s" XtraChar ; adds extra (CHARCOU) blanks movlw ' ' ; print blank to overprint previous string call CharNCC ; print blank without affecting CHARCOU decfsz CHARCOU,F ; CHARCOU=character counter goto XtraChar ; loop if not yet pos 13 ; ----- here follows starting condition symbol movlw 1 ; symbol of rising edge btfss FLAG,XTOX ; let it be rising if XTOX set movlw 4 ; symbol of falling edge call CharBl ; print symbol and blank KaoAna ; ----- here follows "start" ("*") symbol movlw '*' ; "start" symbol call CharBl ; print "start" symbol and blank ; ---- here follows number of 60 samples group incf SHOWCOU,W ; SHOWCOU = number of 60 samples group call Num ; print it (incremented by 1) call Blank ; print blank Arrow movlw 7eh ; 7Eh = right arrow in LM032L character set goto Char ; print arrow in rightmost pos ;***************************************************************************** ;* AGet_EE ;* Get_EE ;* This is routine for reading from Data EEPROM. Writing to BIN4+0 and ;* BIN4+1 is also integrated here, as those variables are used for bin to ;* decimal convertsion. ;* ;* Input variables: W, data address at AGet_EE ;* Output variables: BIN4, binary data for rate display from DATA EEPROM ;***************************************************************************** AGet_EE movwf EEADR ; initialize eeprom address pointer Get_EE andlw 3 ; hi byte BIN4 for freq display movwf BIN4+1 ; BIN4+1 = hi byte for range 0...999 bsf STATUS,RP0 ; select bank 1 of registers bsf EECON1,RD ; set handshaking bit for data eeprom read bcf STATUS,RP0 ; reselect bank 0 movf EEDATA,W ; reading from data eeprom incf EEADR,F ; advance address pointer for future reading movwf BIN4 ; lo byte BIN4 for freq display bcf FLAG,DP ; clear decimal point flag return ; finished ;***************************************************************************** ;* ClrRow1 ;* SameAs20 ;* This subroutine clears line 1 of LCD and switches off LEDs. Entry point ;* SameAs20 allows clearing some other number of character positions ;* starting from line 1 of LCD. Alll LEDs are turned off, flags for LEDs ;* also Cursor pointer of LCD is restored to first pos of line 1 at exit of ;* subroutine. ;* ;* Input variables: ;* entry point ClrRow1: none ;* entry point SameAs20: W=number of characters to be cleared ;* Output variables: ;* CHARCOU is decrementeted by the number of cleared characters ;* SHOWCOU is cleared to 0 ;***************************************************************************** ;* Row1 ;* This subroutine moves cursor to pos 0 of row 1 on LCD. ;* ;* Input variables: none ;* Output variables: none ;***************************************************************************** ClrRow1 movlw .20 ; 20 spaces (one row) to write SameAs20 movwf SHOWCOU ; SHOWCOU = space counter call Row1 ; move cursor to row 1 LoopClD call Blank ; print one space decfsz SHOWCOU,F ; SHOWCOU = space counter goto LoopClD ; loop if not yet 20 spaces movlw 1fh ; bits 7,6,5 (LED flags) reset andwf FLAG,F ; turn LED flags off Row1 movlw 080h ; command for line 1 goto WrComL ; go write command ; ;******* ;******* SERIAL CODE RECEIVER ;******* ;***************************************************************************** ;* Mode2Show ;* This subroutine advances SHOWCOU in range 0...5, and then continues to ;* subroutine Show2 ;***************************************************************************** Mode2Show incf SHOWCOU,F ; advance SHOWCOU btfsc SHOWCOU,2 ; bits 1 and 2 will be set if SHOWCOU... btfss SHOWCOU,1 ; ...is equal to 5... goto Show2 ; ...if not, skip clearing clrf SHOWCOU ; cycle show counter in range 0...5 ;***************************************************************************** ;* Show2 ;* This subroutine prints the 7 bytes of Buffer (+0, +7, +14, +21, +28 or ;* +35) in hex mode at line 1, and the same bytes in ASCII at line 2. ASCII ;* representation is with bit 7 reset, and non-printables (<20h) are printed ;* as dots ;* ;* Input variables: ;* SHOWCOU, denotes which group of 7 bytes will be displayed ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** Show2 movlw BUFFER-1 ; source pointer for reading -1 btfsc SHOWCOU,2 ; if bit 2 of SHOWCOU set... addlw .28 ; ...then add 28 (4 groups) to pointer btfsc SHOWCOU,1 ; if bit 1 of SHOWCOU set... addlw .14 ; ...then add 14 (2 groups) to pointer btfsc SHOWCOU,0 ; if bit 0 of SHOWCOU set... addlw .7 ; ...then add 7 to pointer movwf FSR ; FSR on (1st byte pos)-1 to display call Row1 ; move cursor to row 1 movlw .7 ; bytes to display movwf SCRATCH ; SCRATCH = byte display counter Hex7 incf FSR,F ; advance pointer (it was x-1 at beginning) swapf INDF,W ; move hi nibble first to display 1. hex digit call HexDigit ; display 1st digit in hex mode movf INDF,W ; move lo nibble to display second hex digit call HexDigit ; display 2nd digit in hex mode call Blank ; blank after hex number decfsz SCRATCH,F ; SCRATCH = byte counter goto Hex7 ; loop if not yet 7 bytes movf FSR,W ; FSR = read pointer addlw -.7 ; restore it for ASCII mode printing movwf FSR ; FSR on (1st byte pos)-1 to display call Row2 ; move cursor to row 2 movlw .7 ; bytes to display movwf SCRATCH ; SCRATCH = byte display counter Ascii7 incf FSR,F ; advance pointer (it was x-1 at beginning) movf INDF,W ; read byte andlw 7fh ; reduce ascii representation to 7 bits addlw -20h ; test if byte < 20h addlw 20h ; restore previous value btfsc STATUS,C ; C is set if byte < 20h movlw 0a5h ; represent non-printables as dots (0a5h=dot) call Char ; display ascii char decfsz SCRATCH,F ; SCRATCH = byte counter goto Ascii7 ; loop if not yet 7 bytes goto Farm2 ; go back to user interface ;***************************************************************************** ;* Mode2Go ;* This is entry point for Start command in mode 2 (serial code receiver) ;* Line 1 and first 7 positions (ASCII chars) of line 2 on LCD is cleared. ;* Here the buffer is cleared and a sequence of 42 bytes are received and ;* written to buffer. Manual break (key 2) jumps to Break handling point. ;* This protocol is used: High start bit, 7 or 8 data (true) bits, 0 or ;* 1 parity bit (not written to memory) and 1 low stop bit (not tested ;* for validity). If RXBITS,2 is set then the input is inverted. ;* Baud rates 1200-115200 are supported. ;* Note: no receive errors are detected nor indicated. ;* ;* Input variables: ;* RXRATE (range 0...7), which affects timing loaded from table BaudRate ;* RXBITS, bits 0-2 significant: bit0=parity, bit 1=7/8 bits, bit 2=inverse ;* ;* Output variables: ;* Buffer (42 bytes) loaded with bytes received, all unreceived bytes ;* represented as 00s. ;***************************************************************************** Mode2Go bcf PORTB,2 ; clear hi-imp probe output... btfsc RXBITS,2 ; ...let it be low if polarity bit cleared bsf PORTB,2 ; set hi-imp output if polarity bit set call ClrBuf ; clear wholw buffer movlw .47 ; .47 blanks to clear displayed values call SameAs20 ; clear displayed HEX and ASCII values movlw BUFFER ; start of buffer... movwf FSR ; ...assigned to destination pointer movf RXRATE,W ; RXRATE = selected rate in range 0...7 addlw BaudRate ; add to timing constant table offset call PclSub ; get rate to W RX42Bytes movwf DJNZ ; baud rate timing factor to timing counter clrf COUNT ; this is to preset bit counter to 8 and... bsf COUNT,3 ; ...not to disturb W btfss RXBITS,0 ; RXBITS,0 is set if 7 bits selected btfss RXBITS,1 ; RXBITS,1 is set if parity bit selected incf COUNT,F ; if not(RXBITS and 3 = 2) then COUNT=9 btfsc RXBITS,0 ; RXBITS,0 is set if 7 bits selected btfsc RXBITS,1 ; RXBITS,1 is set if parity bit selected decf COUNT,F ; if not(RXBITS and 3 = 1) reduce to 8 or 7 btfss RXBITS,2 ; RXBITS,2 is set if inverse polarity slctd goto GetSp2 ; jump to true polarity if RXBITS,2 is cleared GetSp1 ; ----- inverse rx btfss PORTA,4 ; test input status... goto GetSp1 ; ...and loop if still low GetStart1 btfss PORTA,3 ; test status of key 2... goto BrkRS ; ...and jump to Break if presseed btfsc PORTA,4 ; test input status... goto GetStart1 ; ...and loop if still high goto StartFound ; falling edge detected: start reception GetSp2 ; ----- true rx btfsc PORTA,4 ; test input status... goto GetSp2 ; ...and loop if still high GetStart2 btfss PORTA,3 ; test status of key 2... goto BrkRS ; ...and jump to Break if presseed btfss PORTA,4 ; test input status... goto GetStart2 ; ...and loop if still low ; rising edge detected: start reception StartFound ; 2-9 t from starting edge HalfBit bsf PORTB,5 ; 1*W led P on nop ; 1*W waist one cycle decfsz DJNZ,F ; 1*W DJNZ = timing constant counter goto HalfBit ; 2*W loop if not yet half bit timing passed RX8Bits movwf DJNZ ; 1 move timing constant to counter OneBit call Waist8T ; 8 8 * W waist 8 t decfsz DJNZ,F ; 1 2 * W DJNZ = timing constant counter goto OneBit ; 2 - * W loop if not yet 1 bit time passed call Waist6T ; 6 waist 6 t rrf PORTA,F ; 1 <--- move input status to C rrf INDF,F ; 1 place C in input rotor decfsz COUNT,F ; 1 COUNT = bit counter goto RX8Bits ; 2 total 22t + (w-1) * 11 ;-------------------- received byte @ INDF btfsc RXBITS,0 ; test if parity bit selected... rlf INDF,F ; ...and discard parity bit if received btfss RXBITS,2 ; test if inverse polarity selected... comf INDF,F ; ...and complement if true parity bcf STATUS,C ; clear 8th bit for 7-bit mode btfsc RXBITS,1 ; test if 7-bit mode selected... rrf INDF,F ; ...and rotate 8th (zero) bit if 7 bits slctd incf FSR,F ; advance destination pointer btfsc FSR,6 ; bits 4 and 6 will both be set if... btfss FSR,4 ; ... end of buffer+1 reached goto RX42Bytes ; loop if not yet FSR=50h goto Show2 ; over: go show received bytes ; ;******* ;******* FREQUENCY COUNTER ;******* ;***************************************************************************** ;* GoPresc ;* Prescaler factor (variable PRESC, in range 0...3) is advanced ;* (executes when key 2 is pressed in frequency counter mode) ;***************************************************************************** GoPresc incf PRESC,F ; advance prescaler bcf PRESC,2 ; and cycle prescaler in range 0...3 ;***************************************************************************** ;* FreqEp ;* Frequency counter entry point. ;* Subroutine WrParam does this: Displays message "Frequency" in row 1, ;* counter range (taken from table RangeTab) and resolution (taken from ;* table PrescTab) in row2. ;* LEDs are turned OFF, and the main counter (BIN4, 4 bytes) cleared. The ;* following is used to count pulses: ;* State of TMR0 is written to BIN4+0 and sequencialy tested, and when the ;* bit 7 of current value detected to be 0 and the previous one was 1, the ;* overflow is considerd. In that case, the state of BIN4+1 is advanced, ;* extended to BIN4+2. After 500 ms, the 32- bit value of BIN4 is shifted ;* left in a total of PRESC+2 times, to get multiplication by 4, 8, 16 or ;* 32. Then BIN+4 (4 bytes) is converted to ASCII and printed on LCD. ;* This routine does not call keyboard routine, as the accurate timing of ;* 500 ms must be generated for counting (high count register is CHARCOU, ;* low count SHOWCOU). Instead of this, there is the individual routine for ;* key 1 and key 2 test, and also the countdown timer for automatic power ;* off (registers TIMOUTL, TIMOUTH). ;* If key 1 is pressed, mode 1 (analyzer) is entered. If key 2 is ;* pressed, prescaler value is advanced. ;* ;* Note: code from label "Loop500A" to comment "; 500 ms timeout" is real ;* time code. If the number of cycles is changed, then the literals ;* 0f4h+1 and 24h+1 written to CHARCOU and SHOWCOU must be readjusted. ;* ;* Input variables: PRESC (affects prescaler factor) ;* Output variables: none ;***************************************************************************** FreqEp ; mode 3: frequency counter call WrParam ; print "Frequency" and "xxMHz/Rxx" call KeysOff ; test both keys off for 34 ms and ; initialize 8 min auto off sequence Count500 movlw 0d2h+REL ; right arrow position call WrComL ; move cursor on right arrow clrf BIN4+1 ; clear next counter byte clrf BIN4+2 ; clear next counter byte clrf BIN4+3 ; clear next counter byte clrf SCRATCH ; initialize TMR0 overflow detector movlw 0f4h+1 ; high loop counter for 500 ms movwf CHARCOU ; CHARCOU = hi byte counter movlw 24h+1 ; 0f424h = .62500 cycles = 1250000 T = 500 ms movwf SHOWCOU ; SHOWCOU = lo byte counter movf PRESC,W ; PRESC = prescaler factor selected addlw 20h ; for PRESC 0,1,2,3 w = 20h,21h,22h,23h call ToOption ; here is clrf TMR0 also Loop500A btfss PORTA,3 ; 2 test key 2 status goto GoPresc ; - and jump to "prescaler change" if hit Loop500 movf TMR0,W ; 1 1 1 the only place where TMR0 is read movwf BIN4 ; 1 1 1 rtcc ---> freq0 rlf BIN4,W ; 1 1 1 carry <--- TMR0.7 rlf SCRATCH,F ; 1 1 1 rotor <--- carry movf SCRATCH,W ; 1 1 1 SCARTCH = TMR0 overflow detector andlw 3 ; 1 1 1 isolate first 2 bits for edge detect xorlw 2 ; 1 1 1 00000000 if 1 <--- 0 btfss STATUS,Z ; 1 2 2 | skip if TMR0 overflow goto NotOvf1 ; 2 - - | if nz ; | 5T any case incf BIN4+1,F ; - 1 1 | nsb NotOvf1 btfsc STATUS,Z ; 2 2 1 | skip MSB advane if not overflow incf BIN4+2,F ; - - 1 | msb movlw Head4-1 ; 1 initialize "Battery" message btfss PORTA,2 ; 2 test key 1 status and... goto Mode4 ; - got shortcut to mode 4 if pressed decfsz SHOWCOU,F ; 1 (2) lo loop counter goto Loop500A ; 2 (-) 20T total decfsz CHARCOU,F ; (1) hi loop counter goto Loop500 ; (2) 20T total ; --- 500 ms timeout here incf PRESC,W ; prepare for x2 multiply: PRESC incremented movwf CHARCOU ; CHARCOU = multiply factor counter incf CHARCOU,F ; and prescaler constant incremented again ShLoop ; BIN4 = BIN4 * 2 total (PRESC+2) times bcf STATUS,C ; clear C to allow clean x2 multiply rlf BIN4, F ; low byte x2 multiply rlf BIN4+1,F ; next byte x2 multiply rlf BIN4+2,F ; next byte x2 multiply rlf BIN4+3,F ; highest byte x2 multiply decfsz CHARCOU,F ; CHARCOU = multiply factor counter goto ShLoop ; loop if not yet PRESC*2 times multiplied movlw 8ah ; frequency display position call WrComL ; cursor to freq display position call Print8 ; print the frequency in 8-digit ASCII decfsz TIMOUTL,F ; TIMOUTL = lo byte auto power off counter goto Count500 ; inner loop bsf TIMOUTL,2 ; TIMOUTL is initialized to 4 passes instead ; of 256, 4*256*500ms=512s=8.5min approx decfsz TIMOUTH,F ; TIMOUTH = hi byte auto power off counter goto Count500 ; loop if not yet 8.5 min goto Suicide ; 8.5 min timeout - go switch power off ; ;******* ;******* BINARY TO ASCII CONVERSION ;******* ;***************************************************************************** ;* Headline ;* Clears SHOWCOU (Headline2 skips this), Clears LCD, prints right ;* arrow @ last pos of row 2 and prints message addresed by W+1 at page 0. ;* Terminator is last character with bit 7 set. ;* ;* Input variables: W+1 addresses string (on page 0) to be printed ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** Headline clrf SHOWCOU ; initialize show group counter Headline2 movwf SCRATCH ; move input parameter to SCARTCH movlw .59 ; .59 characters to clear call SameAs20 ; clear all but right arrow goto GoWrite ; print headline message on LCD ;***************************************************************************** ;* WrParam ;* Prints message "Frequency" in line 1 and parameters for frequency counter ;* in line 2. ;* Text XXMHz/RYY is printed, where XX is taken from RangeTab, and YY from ;* PrescTab. ;* ;* Input variables: ;* PRESC, affects displayed frequency range and resolution ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** ;* Print255 ;* Entry point Print255 converts 8-bit binary value (<100) in BIN4 to ;* 2-digit ASCII and prints it on LCD, without decimal point. Leading zeros ;* are printed. ;* ;* Input variables: W, binary number (0-99) to be converted and printed ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** ;* Print3 ;* Entry point Print3 converts 16-bit binary value (<1000) in BIN4 to 2- or ;* 3-digit ASCII and prints it on LCD. Leading zero is skipped only if value ;* is <100. Decimal point is printed between tens and ones if FLAG,DP is ;* set, otherwise decimal point is omited. ;* ;* Input variables: BIN4 (2 bytes, LSB first, in range 0-999) binary number ;* to be converted and printed ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** ;* PrintBR ;* Entry point PrintBR does the same as PRINT3, but the low byte value is ;* in W instead in BIN4+0. This is used for baud rate display. ;* ;* Input variables: W, BIN+1 (2 bytes, LSB in W, MSB in BIN+1, in range ;* 0-999) binary number to be converted and printed ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** WrParam ; print xxMHz/Rxx movlw Head3-1 ; address of message "Frequency" call Headline2 ; print message call Row2 ; move cursor to row 2 movlw RangeTab ; offset of max frequency range table call Presc255 ; print max frequency range movlw TxtHz-1 ; address of message "MHz/" call Write ; print message movlw 'R' ; "R" stands for "Resolution" call Char ; print "R" movlw PrescTab ; offset of resolution table Presc255 addwf PRESC,W ; add PRESC to offset call PclSub ; read value from table Print255 bcf FLAG,DP ; clear decimal point enable flag clrf BIN4+1 ; clear hi byte (allow range 00-99) PrintBR movwf BIN4 ; allow W as input parameter Print3 ; convert BIN4(16) and print 3 decimal digits clrf BIN4+3 ; clear hi byte clrf BIN4+2 ; clear next byte clrf CMP4+3 ; clear hi byte of temporary register clrf CMP4+2 ; clear next byte of temporary register clrf CMP4+1 ; clear next byte of temporary register movlw .100 ; first digit constant call Times ; how many times it goes in BIN4+1 and BIN4? btfss STATUS,Z ; skip printing if it goes zero times call Num ; print digit (hundreds) if W>0 movlw .10 ; second digit constant call Times ; how many times it goes in BIN4? call Num ; print digit (tens) movlw '.' ; decimal point btfsc FLAG,DP ; test decimal point flag and skip if reset call Char ; print decimal point if DP set goto NumBin4 ; print last digit (ones) ;***************************************************************************** ;* Times ;* Counts how many times CMP4 (32-bit value) "goes" in BIN4 (32-bit value). ;* BIN4 is sequentialy subtracted by CMP4 and counter COUNT advanced. When ;* borrow is detected, BIN4 is restored to the last positiv value (by ADDing ;* CMP4 again), COUNTer decremented and written to W. ;* ;* Input variables: CMP4 (32-bit value), BIN4 (32-bit value) ;* Output variables: ;* BIN4 (32-bit value) modified to mod(CMP4) ;* W (in range 0...9) = BIN4 (32-bit value) / CMP4 (32-bit value) ;***************************************************************************** Times movwf CMP4 ; place input parameter in CMP4 for comparing clrf COUNT ; clear result counter GoTD incf COUNT,F ; advance result counter call Sub4 ; BIN4=BIN4-CMP4 nc if result <0 btfsc STATUS,C ; test did it "go"? goto GoTD ; loop if so call Add4 ; BIN4=BIN4+CMP4 c set if ovf decf COUNT,W ; W = how many times CMP4 goes in BIN4 (32bit) return ; result in W ;***************************************************************************** ;* Print8 ;* This subroutine converts 32-bit value in BIN4 (low byte first), to ;* 8-digit ASCII and prints it on LCD. Leading zeros are printed as blanks. ;* Table DecTab (21 words, must be at page 0 if PCLATH=0) is used in conversion. ;* ;* Input variables: BIN4 (32-bit value, <.100,000,000) ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** Print8 ; bin2dec conv BIN4(32), print 8 decimal digits movlw DecTab-1 ; inici tab ptr movwf SCRATCH ; SCRATCH = tab ptr bcf FLAG,RIPPLE ; zeros initialy printed as blanks, until ; ...first non-zero appears Cif7 clrf CMP4+3 ; clear CMP4+3, it is =0 in all cases call PclSub2 ; get constant from table movwf CMP4+2 ; load decimal const from table in CMP4+2 call PclSub2 ; get constant from table movwf CMP4+1 ; load decimal const from table in CMP4+1 call PclSub2 ; get constant from table call Times ; how many times CMP4 goes in BIN4? call NZNum ; print if w>0 or RIPPLE=1, else blank movf SCRATCH,W ; SCRATCH = table pointer addlw .237-DecTab ; test if end of table btfss STATUS,C ; C set if end of table goto Cif7 ; if not yet end of table loop (will be 7x) NumBin4 movf BIN4,W ; last digit is in BIN4 goto Num ; last digit must be printed unconditionally ;***************************************************************************** ;* Sub4 ;* Subtract CMP4 (32-byte value) from 32-bit value in BIN4 (low byte first) ;* This is performed as adding of negative value of CMP4. Negating is ;* performed as comlementing and incrementing by 1. ;* Note: Incrementing by 1 is performed on least significant byte only, ;* without 32-bit extension, for code space saving. This will not cause ;* error in this case, as the number of all possible values for CMP4+0 is ;* limited and none of them is equal to 0FFh before incrementing ;* (all possible values are taken from table DecTab, and are: 0ah, 64h, ;* 0e8h, 10h, 0a0h, 40h and 80h, and their negative values). ;* However, this is valid if this subroutine is used for decimal ;* conversion only, and if it is used for some other application, ;* extension to 32-bit should be added after incrementing. ;* ;* Input variables: BIN4 (32-bit value), CMP4 (32-bit value) ;* Output variables: ;* BIN4 (32-bit value) ;* STATUS,C denotes the sign of result: if cleared, output value ;* is negative (there is borrow) ;* ;* Note: Entry points Waist8T and Waist6T are used only by some real-time ;* routines, in that case the instructions are dummy ;***************************************************************************** Sub4 ; 32-bit sub: BIN4 = BIN4 - CMP4 ; NC if result<0 call NegCmp ; negate CMP (32 bits) first call Add4 ; add as negative value NegCmp comf CMP4+0,F ; complement low byte Waist8T comf CMP4+1,F ; complement next byte comf CMP4+2,F ; complement next byte Waist6T comf CMP4+3,F ; complement high byte incf CMP4+0,F ; neg = complement + 1 ; (no need to test overflow here, it will ; ...never reach 0 after incrementing) return ; finished ;***************************************************************************** ;* Add4 ;* Add CMP4 (32-byte value) to BIN4 (32-byte value). Four-instruction groups ;* (movf-btfsc-incfsz-addwf) is used istead of non-existing ADD WITH CARRY. ;* Input variables: BIN4 (32-bit value), CMP4 (32-bit value) ;* Output variables: BIN4 (32-bit value), 33th bit in STATUS,C ;***************************************************************************** Add4 ; 32-bit add: BIN4 = BIN4 + CMP4 movf CMP4,W ; low byte addwf BIN4,F ; low byte add movf CMP4+1,W ; next byte btfsc STATUS,C ; skip to simple add if C was reset incfsz CMP4+1,W ; add C if it was set addwf BIN4+1,F ; next byte add if NZ movf CMP4+2,W ; next byte btfsc STATUS,C ; skip to simple add if C was reset incfsz CMP4+2,W ; add C if it was set addwf BIN4+2,F ; next byte add if NZ movf CMP4+3,W ; high byte btfsc STATUS,C ; skip to simple add if C was reset incfsz CMP4+3,W ; add C if it was set addwf BIN4+3,F ; high byte add if NZ return ; finished ;***************************************************************************** ;* PclSub is used for indirect addressing ;* PclSub1 uses SCRATCH instead of W as input parameter ;* PclSub2 advances pointer SCRATCH before executong ;* ;* Note: PCLATH=0 in all cases. So all tables pointed by this routine ;* are on page 0 ;***************************************************************************** PclSub2 incf SCRATCH,F ; advance table pointer PclSub1 movf SCRATCH,W ; move table pointer to W PclSub movwf PCL ; jump to address pointed by PCLATH,W ;***************************************************************************** ;* ClrBuf ;* ClrRam ;* Subroutine ClrBuf clears BUFFER (42 bytes) ;* Entry point ClrRam allows some other start point for claring. It clears ;* internal RAM from address in W to the location 7Fh. ;* (locations 50h-7Fh, which do not exist in 16F84, are dummy). ;* ;* Both entry points continue to disabling Enable signal for LCD and ;* 33.8 ms timing loop ;* ;* Input variables: ;* W is the start address if area to be cleared (ClrRam entry point only) ;* Output variables: none ;***************************************************************************** ClrBuf movlw BUFFER ; get start address of buffer ClrRam movwf FSR ; FSR = destination pointer for clearing Zeros clrf INDF ; clear one byte incf FSR,F ; advance dest pointer btfss FSR,7 ; test if end of RAM... goto Zeros ; ...if not, loop - else move LEDs ;***************************************************************************** ;* Entry point DisEna30: Remove enable and discharge signal, and ;* refresh LEDs. Then loop 33.8 ms ;* Entry point Wait30: Loop 33.8 ms ;* Input variables: none ;* Output variables: none ;***************************************************************************** DisEna30 call DisEna ; disable discharging output signal Wait30 clrf COUNT ; COUNT = time loop counter, to waist 33.8 ms GoWait30 call loop130 ; waist 130 us decfsz COUNT,F ; COUNT = time loop counter goto GoWait30 ; loop if not yet 256 passes return ; timing over ;***************************************************************************** ;* NZNum ;* Num ;* Print numeric value in W (in range 0...9) on LCD. If FLAG,RIPPLE is ;* cleared, 0 is printed as blank. If non-zero numeric is printed, it ;* automatically sets FLAG,RIPPLE. ;* 0 (30h) is printed as capital O (4Fh), for improved readibility, as 0 ;* may easily be substituted by 8 on LCD. This changing 0 to O is not ;* performed only in ASCII representation of recorded bytes in serial code ;* receiver. ;* Entry point NUM prints numeric unconditionally, undependently of bit ;* FLAG,RIPPLE. ;* ;* Input variables: ;* W (0...9), number to be printed at current cursor position of LCD ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** NZNum ; same aso Num, only blank instead of 0 btfsc FLAG,RIPPLE ; test if RIPPLE bit set... goto Num ; ...if RIPPLE set, no more blanks addlw 0 ; set Z flag if W=0 btfsc STATUS,Z ; is it = 0 ? goto Blank ; if so, jump to space routine bsf FLAG,RIPPLE ; if >0, clear RIPPLE bit, for no more blanks Num andlw 0fh ; isolate low nibble btfsc STATUS,Z ; is it = 0 ? movlw 'O'-30h ; if so, initialize capital O addlw 30h ; adjust ASCII for numeric goto Char ; print digit ; ;******* ;******* LCD ROUTINES ;******* ;***************************************************************************** ;* All these entry ponts of this subroutine are used in program: ;* ;* Row2: Issues command to move cursor to row 2 of LCD, and loops 130 us, ;* to allow time for LCD controller to execute the command ;* WrComl: Issues command in W to the LCD, and loops 130 us, to allow time ;* to LCD controller to execute the command ;* loop130: Loops 130 us including call and return ;* GoLoop: Loops W*2 us ;* Loop7: Same as GoLoop, only 2t shorter (used in sample rate routine) ;***************************************************************************** Row2 movlw 0c0h ; command for line 2 WrComL ; issues command in W call WrCom ; write command in LCD loop130 ; * waist 130 us clrf DJNZ ; this is to initialize DJNZ to 40h and... bsf DJNZ,6 ; not to disturb W GoLoop goto $+1 ; 2 waist 2 t Loop7 decfsz DJNZ,F ; 1 DJNZ = timing loop counter goto GoLoop ; 2 64x5=320t (128 us) return ; 2 finished ;***************************************************************************** ;* All these entry ponts of this subroutine are used in program: ;* ;* CharBl: print character in W and then blank on LCD and decrement CHARCOU ;* Blank: print blank (32h) on LCD and decrement CHARCOU ;* Char: print character in W on LCD and decrement CHARCOU ;* CharNCC: print character in W on LCD without affecting CHARCOU ;* ;* Note: CHARCOU is used to print fixed format message on LCD, as the ;* calling routine will add N-CHARCOU blanks to fill area N characters long ;* ;* Input variables: ;* all entry points except Blank: W = character to be printed ;* Output variables: ;* all entry points except CharNCC: CHARCOU is decremented by one ;***************************************************************************** CharBl ; print char, then blank call Char ; print char first Blank ; print blank movlw ' ' ; print blank Char ; print W decf CHARCOU,F ; decrement character counter CharNCC ; print W without affecting CHARCOU bsf PORTB,1 ; pull RS hi (data register select) goto Skr1 ; continue 4-bit mode writing to LCD ;***************************************************************************** ;* PrintBrk ;* Print message "Break" in row 1, pos 0 ;* ;* Input variables: none ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** ;* Write ;* Print message addressed by W+1 in line 1 ;* Note: Terminator is last character in string with bit 7 set ;* ;* Input variables: W points to string (RETLWs) decremented by 1 ;* Output variables: ;* CHARCOU is decremented by the number of characters printed ;***************************************************************************** PrintBrk call Row1 ; move cursor to row 1 movlw BrkMes-1 ; message "Break" address-1 Write ; write string addressed by W, terminator 0 movwf SCRATCH ; SCRATCH = source pointer GoWrite ; write string at (SCRATCH+1), terminator 0 call PclSub2 ; advance pointer and read pointed byte addlw 80h ; this is to test if bit 7 was set... btfsc STATUS,C ; ...if so, C will be set goto Char ; last character was with bit 7 set andlw 7fh ; restore initial character value call Char ; print one character goto GoWrite ; loop ;***************************************************************************** ;* Wrcom ;* Write command in W to LCD, then loop 130 us ;* Skr1 ;* Allows data write to LCD, if PORTB,1 is set previously ;* Nibble ;* Write one nibble (W,0-3) to LCD data bus ;* ;* Note: all entry ponits are terminated by 130us timing loop, to allow ;* LCD controller to execute accepted command/data. ;* ;* Input variables: command in W ;* Output variables: none ;***************************************************************************** WrCom bcf PORTB,1 ; rs lo (command) Skr1 movwf DJNZ ; save W in DJNZ for lo nibble writing call Hinib_B ; outputs w,4-7 to PORTB,4-7 call EnaLCD ; generate enable signal for hi nibble Nibble swapf DJNZ,W ; restore initial value of W and swap it call Hinib_B ; outputs w,0-3 to PORTB,4-7 call EnaLCD ; generate enable signal for low nibble goto loop130 ; waist 130 to allow LCD to crunch command ;***************************************************************************** ;* Generate Enable signal (1200us) for LCD controller, and refresh LEDs. ;* Entry point DisEna: Remove enable and discharge signal, and refresh LEDs. ;***************************************************************************** EnaLCD bsf PORTB,0 ; enable LCD controller goto $+1 ; waist 2 cycles, to make signal 1.2us long DisEna bcf PORTB,0 ; terminate enable signal LCD controller ;***************************************************************************** ;* MoveLEDs ;* Entry point MoveLEDs: transfer from FLAG,LEDP FLAG,LEDH and FLAG,LEDL to ;* PORTB,5 PORTB,6 and PORTB,7 to service LEDs. ;* ;* Input variables: ;* FLAG, bits LEDP, LEDH, LEDH will affect LED1, LED2, LED3 respectively ;* Output variables: none ;***************************************************************************** ;* Hinib_B ;* Entry point Hinib_B: output W,4-7 to 4-bit LCD data bus ;* ;* Input variables: ;* hi nibble of W is copied to LCD data bus ;* Output variables: none ;***************************************************************************** MoveLESd ; writes states for LEDs L,H,P to PORTB,5-7 movf FLAG,W ; FLAG bits 5,6,7 are LED bits Hinib_B ; outputs w,4-7 to PORTB,4-7 bcf PORTB,4 ; clear high nibble of PORTB bcf PORTB,5 ; clear high nibble of PORTB bcf PORTB,6 ; clear high nibble of PORTB bcf PORTB,7 ; clear high nibble of PORTB andlw 0f0h ; mask for hi nibble of W iorwf PORTB,F ; write hi nibble of W in hi nibble of PORTB return ; finished ;***************************************************************************** ;* HexDigit ;* This subroutine prints low nibble of W on LCD as hexadecimal digit. Zero ;* (30h) is printed as capital O (7Fh) ;* ;* Input variables: W in range 0...0fh, hex number to be printed ;* Output variables: CHARCOU is decremented by two ;***************************************************************************** HexDigit ; hex W (lo nibble) to LCD, change 0... ; ...to capital O andlw 0fh ; isolate low nibble of W... movwf BIN4 ; ...and put it in BIN4 addlw -0ah ; test if input number > 9 btfss STATUS,C ; Is it > 9 ? goto NumBin4 ; ...if not, just print it as-is addlw .7+3ah ; 3ah...3fh to 'A'...'F' correction goto Char ; print ASCII adjusted hex value a...f ; ;******* ;******* DATA EEPROM ;******* ;***************************************************************************** ;* This table is located in data eeprom ;* It contains numerical data for 16 sample frequencies period display ;* for analyzer. Last 13 bytes are timing constants used by subroutine ;* GetSlowClk to generate internal timings (three fastest rates need no ;* constants from the table, as they are treated as special cases) ;***************************************************************************** ;* ----- TABLE 1 (00h-2Fh): Rate display table for analyzer ;* ;* **** First byte: Flags. Bits in this byte have the following functions: ;* bit 7 = 0: Frequency in Hz ;* = 1: Frequency in Mhz or Khz ;* bit 6 = 0: Frequency does not contain decimal point ;* = 1: Frequency contains decimal point before last digit ;* bits 5,4: Bits 9 and 8 for frequency display, respectively ;* bit 3 = 0: Period in us (microseconds) ;* = 1: Period in ms (miliseconds) ;* bit 2 = 0: Period does not contain decimal point ;* = 1: Period contains decimal point before last digit ;* bits 1,0: Bits 9 and 8 for period display, respectively ;* **** Second byte: low significant byte for frequency ;* **** Third byte: low significant byte for period ;***************************************************************************** ;* ----- TABLE 2 (30h-3Ch): Timing constant table for analyzer ;* ;* Timing constant factors for all sample rates generated by subroutine ;* GetSlowClk (all except 1MHz, 500KHz and 228KHz) ;***************************************************************************** ;* Note: This is read-only data, so the Data EEPROM must be programmed ;* before the unit is ready to use. MCU will not affect data EEPROM ;* contents. If your programmer does not support automatic loading of ;* Data EEPROM contents from the HEX file, it must be loaded manualy. ;* This will help in that case (all values are hexadecimal): ;* ;* addr 00-07: 88 01 01 98 F4 02 8C E4 ;* addr 08-0f: 2C 88 64 0A 88 32 14 D8 ;* addr 10-17: 80 1A 88 19 28 C8 C0 34 ;* addr 18-1f: 88 0A 64 C8 60 68 C8 30 ;* addr 20-27: D0 C9 18 A1 80 01 01 14 ;* addr 28-2f: 90 19 00 64 0A 00 28 19 ;* addr 30-37: 01 06 09 10 16 2E 30 64 ;* addr 38-3c: CC 05 0E 3B 95 ;* ;* Total bytes used in Data EEPROM: 61 (the last three bytes don't care) ;***************************************************************************** org 2100h ; constant T/sample Hz s RATE de b'10001000', .1, .1 ; - 2.5 1M 1u 0 de b'10011000', .244, .2 ; - 5 500K 2u 1 de b'10001100', .228, .44 ; - 11 228K 4.4u 2 de b'10001000', .100, .10 ; 1 25 100K 10u 3 de b'10001000', .50, .20 ; 6 50 50K 20u 4 de b'11011000', .128, .26 ; 9 65 38.4K 26u 5 de b'10001000', .25, .40 ; 16 100 25K 40u 6 de b'11001000', .192, .52 ; 22 130 19.2K 52u 7 de b'10001000', .10, .100 ; 46 250 10K 100u 8 de b'11001000', .96, .104 ; 48 260 9.6K 104u 9 de b'11001000', .48, .208 ; 100 520 4.8K 208u 10 de b'11001001', .24, .161 ; 204 1040 2.4K 417u 11 de b'10000000', .1, .1 ; 5 2500 1K 1m 12 de b'00010100', .144, .25 ; 14 6253 400 2.5m 13 de b'00000000', .100, .10 ; 59 25018 100 10m 14 de b'00000000', .40, .25 ; 149 62548 40 25m 15 ; timing constants table de .001, .006, .009, .016, .022, .046, .048 de .100, .204, .005, .014, .059, .149 end