; ------------------------------------------------------------------------ ; FILE : LCD4CNTR.ASM - variation with 4 bite LCD communication * ; (4-bites, high nibble, RB4..RB7 data, RA0..RA2 control) * ; CONTENTS : Simple low-cost 7-digit frequency meter using a PIC16F84 * ; COPYRIGHT: Peter Halicky OM3CPH * ; AUTHOR : Peter Halicky OM3CPH & Peter Halicky Jr., OM2PH ex OM2APH * ; PCB : Tibor Madarasz OM2ATM * ;-------------------------------------------------------------------------- ; E-Mail: peter@halicky.sk or om3cph@oe3xbs.aut.eu ; ; Bratislava, Slovakia, December 1998, revised & debugged February, 2000 ;-------------------------------------------------------------------------- ; This is 7-digit frequency meter counting up to 35 MHz. The decimal point ; is after MHz digit, but can be at any position. ; ; Hardware is very simple: ; ; It contains : PIC 16F84 ; 1 NPN low power HF Si transistor, ; 16 character (2x8) in 1 Line LCD display, ; Xtal 1..10 MHz, ; some resistors, capacitors and 2 Si switching diodes... ; (see schematic) ; Note: ; LCD display is 16 character in 1 line LCD display PVC160101PTN which ; seems to be compatible with TWO LINES HITACHI LCD display, exept ; that that one has only 8 characters in 1 line. ; ; The counter uses internal prescaler of PIC as low byte of counter, ; TMR0 as middle byte and some register as high byte of counter. ; ; Some ideas were taken from "Simple low-cost digital frequency meter ; using a PIC 16C54" (frqmeter.asm) ; written by James Hutchby, MadLab Ltd. 1996 ; ; LCD interfacing was completly taken from Norm Cramer's LCD.ASM ; ------------------------------------------------------------------------ ; ; This software is free for private usage. It was created for HAM radio ; community members. Commercial exploatation is allowed only with permission ; of authors. ; ; ------------------------------------------------------------------------ ; ; The measuring period is 100 000 us. ; Procesor cycle is T = 4/Fx [us,MHz], Fx is Xtal frequency ; ; Number of procesor cycles per measuring period: ; ; N = 100 000/T procesor cycles ; N = Fx * 100 000/4 = 25 000 x Fx ; ; The main steps of measuring period: ; ; 1. decode 3-byte value into 7 decimal numbers, ; 2. decode decimal value of digit to chars, ; 3. set decimal point if needed, ; 4. output to PORTB (LCD), ; 5. start measurement, ; 6. test TMR0 overflow bite, if YES increase TimerH, ; 7. goto 5 until measuring period is done, ; 8. stop measurement, ; 9. shift out precounter content, ; 10. Add/substract RF according signal from optocoupler, ; 11. goto 1 ; ; ------------------------------------------------------------------------ ; ; Total timing formula: N = 25 000 * Fx = ((9*T1+4)*T2+4)*T3+5+9*T4+Z ; ; N = 25 000 * Fx [MHz] ; ; Example: Fx = 4 MHz ; ; N = 25 000 * 4 = 100 000 ; N = 25 000 * Fx = ((9*T1+4)*T2+4)*T3+5+9*T4+Z ; ; ------------------------------------------------------------------------ include ; ------------------------------------------------------------------------ Index equ 0Ch ; dummy register Count equ 0Dh ; inkremental register Help equ 0Eh ; dummy register LED0 equ 0Fh LED1 equ 010h LED2 equ 011h LED3 equ 012h LED4 equ 013h LED5 equ 014h LED6 equ 015h CHAR equ 016h ; LCD subroutines internal use TimerH equ 017h ; the highest byte of SW counter LowB equ 018h ; low byte of resulted frequency MidB equ 019h ; middle byte of resulted frequency HigB equ 01Ah ; high byte of resulted frequency TEMP equ 01Bh ; temporary register HIndex equ 01Ch ; index register LEDIndex equ 01Dh ; LED pointer R1 equ 01Eh ; Timing counters R2 equ 01Fh R3 equ 020h ; ------------------------------------------------------------------------ ; LCD variables ; ------------------------------------------------------------------------ ;Xtal equ 8 ; MHz DELAY15000 equ .39 ; 1+15000*Xtal/4/770 DELAY4100 equ .11 ; 1+4100*Xtal/4/770 DELAY100 equ 1 ; 1+100*Xtal/4/770 LINE0 equ 0 LINE1 equ 040h ; PORTB bits E equ 2 ; LCD Enable control line RA2 R_W equ 0 ; LCD Read/Write control line RA0 RS equ 1 ; LCD Register-Select control line RA1 ; LCD Data are sent through RB4 - RB7 ; ------------------------------------------------------------------------ include ; timing loop values ; ------------------------------------------------------------------------ org 0 Start clrf STATUS ; Do initialization, Select bank 0 clrf INTCON ; Clear int-flags, Disable interrupts clrf PCLATH ; Keep in lower 2KByte clrf PORTA ; ALL PORT output should output Low. clrf PORTB clrf Index clrf LEDIndex clrf LED0 clrf LED1 clrf LED2 clrf LED3 clrf LED4 clrf LED5 clrf LED6 clrf LowB clrf MidB clrf HigB bsf STATUS,RP0 movlw b'00010000' ; RA0..RA3 outputs movwf TRISA ; RA4 input movlw 0xF0 ; RB0..RB3 outputs, rest inputs movwf TRISB bsf OPTION_REG,NOT_RBPU ; Disable PORTB pull-ups clrwdt movlw b'10100111' ; Prescaler -> TMR0, movwf OPTION_REG ; 1:256, rising edge bcf STATUS,RP0 ; ; Initilize LC-Display Module ; Busy-flag is not yet valid clrf PORTA ; ALL PORT output should output Low. ; Initilize the LCD Display Module clrf PORTB ; ALL PORT output should output Low bcf PORTA,E ; Clear all controll lines bcf PORTA,RS bcf PORTA,R_W movlw DELAY15000 ; Wait for 15ms for LCD to get powered up movwf R1 clrf R2 LCycle decfsz R2,F goto LCycle ; 3*256 decfsz R1,F ; 3*256+1 goto LCycle ;(3*256+2)*R1=770*R1 in procesor cycles ;****************************************************************************** ; Initialization of LCD display ;****************************************************************************** movlw 0x0F andwf PORTB,F ; Clear the upper nibble movlw 0x030 ; Command for 4-bit interface high nibble iorwf PORTB ; Send data to LCD bsf STATUS,RP0 ; Select Register page 1 movlw 0x0F andwf TRISB,W movwf TRISB ; Set Port for output bcf STATUS,RP0 ; Select Register page 0 bsf PORTA,E ; Clock the initalize command to LCD module bcf PORTA,E movlw DELAY4100 ; Delay for at least 4.1ms before continuing movwf R1 clrf R2 LCycle2 decfsz R2,F goto LCycle2 ; 3*256 decfsz R1,F ; 3*256+1 goto LCycle2 ;(3*256+2)*R1=770*R1 in procesor cycles bsf PORTA,E ; Clock the initalize command to LCD module bcf PORTA,E movlw DELAY100 ; Wait for 100 us movwf R1 clrf R2 LCycle3 decfsz R2,F goto LCycle3 ; 3*256 decfsz R1,F ; 3*256+1 goto LCycle3 ;(3*256+2)*R1=770*R1 in procesor cycles movlw 0x0F andwf PORTB,F ; Clear the upper nibble movlw 020h ; Command for 4-bit interface high nibble iorwf PORTB ; Send data to LCD bsf PORTA,E ; Clock the initalize command to LCD module bcf PORTA,E movlw 0x028 ; 4 bits, 2 lines, 5x7 Font call PutCMD movlw B'00001000'; disp.off, curs.off, no-blink call PutCMD movlw 1 ; LCD clear call PutCMD movlw B'00001100'; disp.on, curs.off call PutCMD movlw B'00000110'; auto-inc (shift-cursor) call PutCMD goto Go ; to line 493 ;************************************************************************ ; LCD Module Subroutines ;======================================================================== ; Busy: Returns when LCD busy-flag is inactive ; PORTA returns as RA0..RA2 output, RA3,RA4 input ;************************************************************************ Busy bsf STATUS,RP0 ; Select Register page 1 movlw 0xF0 ; Set port to input iorwf TRISB,W ; Only set upper half of port movwf TRISB movlw b'00011000' ; PORTA should be set RA0..RA2 output movwf TRISA ; RA3,RA4 input bcf STATUS,RP0 ; Select Register page 0 bcf PORTA,RS ; Set LCD for Command mode bsf PORTA,R_W ; Setup to read busy flag bsf PORTA,E ; Set E high bcf PORTA,E ; Set E low movf PORTB,W ; Read upper nibble busy flag, DDRam address andlw 0xF0 ; Mask out lower nibble movwf TEMP bsf PORTA,E ; Toggle E to get lower nibble bcf PORTA,E swapf PORTB,W ; Read lower nibble busy flag, DDRam address andlw 0x0F ; Mask out upper nibble iorwf TEMP,W ; Combine nibbles btfsc TEMP,7 ; Check busy flag, high = busy goto Busy ; If busy, check again bcf PORTA,R_W bsf STATUS,RP0 ; Select Register page 1 movlw 0x0F andwf TRISB,W movwf TRISB ; Set Port for output bcf STATUS,RP0 ; Select Register page 0 return ;======================================================================== ; PUTCHAR Sends character to LCD, Required character must be in W ;======================================================================== PutCHAR movwf CHAR ; Character to be sent is from W saved call Busy ; Wait for LCD to be ready ; Busy routine sets PORTB adequately movlw 0x0F andwf PORTB,F ; Clear the upper nibble movf CHAR,W andlw 0xF0 ; Get upper nibble iorwf PORTB,F ; Send data to LCD bcf PORTA,R_W ; Set LCD to write bsf PORTA,RS ; Set LCD to data mode bsf PORTA,E ; toggle E for LCD bcf PORTA,E movlw 0x0F andwf PORTB,F ; Clear the upper nibble swapf CHAR,W andlw 0xF0 ; Get lower nibble iorwf PORTB,F ; Send data to LCD bsf PORTA,E ; toggle E for LCD bcf PORTA,E return ;======================================================================== ; PutCMD Sends command to LCD, Required command must be in W ;======================================================================== PutCMD movwf CHAR ; Command to be sent is from W saved call Busy ; Wait for LCD to be ready movlw 0x0F andwf PORTB,F ; Clear the upper nibble movf CHAR,W andlw 0xF0 ; Get upper nibble iorwf PORTB,F ; Send data to LCD bcf PORTA,R_W ; Set LCD to write bcf PORTA,RS ; Set LCD to command mode bsf PORTA,E ; toggle E for LCD bcf PORTA,E movlw 0x0F andwf PORTB,F ; Clear the upper nibble swapf CHAR,W andlw 0xF0 ; Get lower nibble iorwf PORTB,F ; Send data to LCD bsf PORTA,E ; toggle E for LCD bcf PORTA,E return ;************************************************************************ ; End of LCD Module Subroutines ;************************************************************************ ; Numeric routines ;------------------------------------------------------------------------ ; 3 byte substraction of the constant from the table which sets carry if ; result is negative ;------------------------------------------------------------------------ Subc24 clrf TEMP ; it will TEMPorary save C movf Index,W ; pointer to low byte of constant movwf HIndex ; W -> HIndex call DecTable ; W returned with low byte of constant bsf STATUS,C ; set C subwf LowB,F ; LowB - W -> LowB ; if underflow -> C=0 btfsc STATUS,C goto Step1 bsf STATUS,C movlw 1 subwf MidB,F ; decrement MidB ; if underflow -> C=0 btfsc STATUS,C goto Step1 bsf STATUS,C movlw 1 subwf HigB,F ; decrement HigB btfsc STATUS,C ; if underflow -> C=0 goto Step1 bsf TEMP,C ; set C Step1 decf HIndex,F movf HIndex,W ; pointer to middle byte of const call DecTable bsf STATUS,C subwf MidB,F ; MidB - W -> MidB btfsc STATUS,C ; if underflow -> C=0 goto Step2 bsf STATUS,C movlw 1 subwf HigB,1 ; decrement HigB btfsc STATUS,C ; if underflow -> C=0 goto Step2 bsf TEMP,C ; set C Step2 decf HIndex,F movf HIndex,W ; pointer to middle byte of constatnt call DecTable bsf STATUS,C subwf HigB,F ; HigB - W -> HigB btfsc STATUS,C ; if underflow -> C=0 goto ClearCF bsf STATUS,C goto SubEnd ClearCF rrf TEMP,C ; C -> STATUS SubEnd retlw 0 ; ------------------------------------------------------------------------ ; 3 byte addition of the constant from the table which sets carry if ; result overflows ; ------------------------------------------------------------------------ Addc24 clrf TEMP ; register for TEMPorary storage of C movf Index,W ; pointer to lower byte of const into W movwf HIndex ; save it into HIndex call DecTable ; W contains low byte of const bcf STATUS,C ; clear C addwf LowB,1 ; W + LowB -> LowB btfss STATUS,C ; test overflow goto Add2 bcf STATUS,C ; clear C movlw 1 addwf MidB,F ; increment MidB btfss STATUS,C goto Add2 bcf STATUS,C movlw 1 addwf HigB,F ; increment HigB btfss STATUS,C ; test overflow goto Add2 bsf TEMP,C ; store C Add2 decf HIndex,F ; pointer to middle byte into W movf HIndex,W call DecTable bcf STATUS,C addwf MidB,1 ; W + MidB -> MidB btfss STATUS,C goto Add3 bcf STATUS,C ; clear C movlw 1 addwf HigB,1 ; increment HigB btfss STATUS,C goto Add3 bsf TEMP,C Add3 decf HIndex,F ; pointer to higher byte into W movf HIndex,W call DecTable bsf STATUS,C addwf HigB,F ; W + HigB -> HigB, btfss STATUS,C goto ClarCF bsf STATUS,C goto AddEnd ClarCF rrf TEMP,C ; C -> STATUS AddEnd retlw 0 ;------------------------------------------------------------------------ ; Tables for 3 byte constants ;------------------------------------------------------------------------ ; Table of decades ;------------------------------------------------------------------------ DecTable addwf PCL,F ; W + PCL -> PCL retlw 0 ; 10 retlw 0 ; retlw 0Ah ; retlw 0 ; 100 retlw 0 ; retlw 064h ; retlw 0 ; 1 000 retlw 03h ; retlw 0E8h ; retlw 0 ; 10 000 retlw 027h ; retlw 010h ; retlw 01h ; 100 000 retlw 086h ; retlw 0A0h ; retlw 0Fh ; 1 000 000 retlw 042h ; retlw 040h ; ;************************************************************************ ; Entry point for main cycle ;------------------------------------------------------------------------ ; Routine for the conversion of 3 byte number into 7 decimal numbers ;************************************************************************ Go movlw 6*3-1 ; pointer to dec. table movwf Index ; 6*3-1 -> Index movlw 9 ; maximum of substractions movwf Count ; 9 -> Count clrf Help movlw 6 movwf LEDIndex Divide call Subc24 ; substract untill result is negative, btfsc STATUS,C ; add last substracted number goto Add24 ; next digit incf Help,F decf Count,F btfss STATUS,Z goto Divide movlw 3 subwf Index,F goto Next Add24 call Addc24 movlw 03h subwf Index,F Next movlw 9 movwf Count movlw LED1 ; LED1 -> W addwf LEDIndex,W ; LED1 + LEDIndex -> W movwf TEMP decf TEMP,F ; LEDIndex+LED1-1 -> TEMP movf TEMP,W movwf FSR ; W -> FSR movf Help,W ; Help -> W clrf Help ; save result at LEDx movwf INDF ; W -> LED(6..1) decf LEDIndex,F movlw 1 addwf Index,W btfss STATUS,Z goto Divide movf LowB,W movwf LED0 ; the rest -> LED0 ;------------------------------------------------------------------------- ; registers LED0..LED6 are filled with values - ready to be displayed ;------------------------------------------------------------------------- movlw 6 movwf LEDIndex movlw LINE0 iorlw 080h ; Position cursor leftmost on first line call PutCMD LEDCycle movlw LED0 ; LED0 -> W addwf LEDIndex,W ; LED1 + LEDIndex -> W movwf FSR ; W -> FSR movf INDF,W ; LED(0..6) -> W iorlw 030h call PutCHAR ; Display character movlw 5 ; test for decimal point bsf STATUS,Z subwf LEDIndex,W btfss STATUS,Z goto NoDot movlw '.' ; this can be ' ' or ',' ...... call PutCHAR ; Display character NoDot decfsz LEDIndex,F goto LEDCycle ; continue with next number movlw LED0 ; LED0 -> W addwf LEDIndex,W ; LED0 + LEDIndex -> W movwf FSR ; W -> FSR movf INDF,W ; [FSR] -> W iorlw 030h call PutCHAR ; Display character movlw LINE1 ; continue at right half of display iorlw 080h ; Function set call PutCMD ; Position cursor leftmost on first line movlw ' ' call PutCHAR ; Display character movlw 'M' call PutCHAR ; Display character movlw 'H' call PutCHAR ; Display character movlw 'z' call PutCHAR ; Display character movlw LINE0 iorlw 080h ; Function set call PutCMD ;------------------------------------------------------------------------- ; It is time to prepare new measuring cycle ;------------------------------------------------------------------------- clrf TimerH clrf TMR0 nop ; it is SUGGESTED... nop clrf LEDIndex movlw T1 ; set initial counter values movwf R1 movlw T2 movwf R2 movlw T3 movwf R3 clrf INTCON ; global INT disable, TMR0 INT disable ; clear TMR0 overflow bite ; ------------------------------------------------------------------------ ; Start measurement: RA3 + RA4 set input ; ------------------------------------------------------------------------ movlw b'00010000' ; all ports set L, RA4 set H movwf PORTA bsf STATUS,RP0 movlw b'00011111' ; RA0..RA4 input movwf TRISA bcf STATUS,RP0 ; ------------------------------------------------------------------------- ; It is opened now... ; ------------------------------------------------------------------------- Cycle btfss INTCON,2 ; 1 Test for TMR0 overflow goto Nothing ; 3 incf TimerH,F ; 3 bcf INTCON,2 ; 4 goto Nxt ; 6 Nothing nop ; 4 nop ; 5 nop ; 6 Nxt decfsz R1,F ; 7 goto Cycle ; 9 movlw T1 ; 9*T1 movwf R1 ; 9*T1+1 decfsz R2,F ; 9*T1+2 goto Cycle ; 9*T1+4 movlw T2 ;(9*T1+4)*T2 movwf R2 ;(9*T1+4)*T2+1 decfsz R3,F ;(9*T1+4)*T2+2 goto Cycle ;(9*T1+4)*T2+4 ; ------------------------------------------------------------------------ ; Final test for TMR0 overflow ; ------------------------------------------------------------------------ movlw T4 ;((9*T1+4)*T2+4)*T3 movwf Help ;((9*T1+4)*T2+4)*T3+1 Cycle2 btfss INTCON,2 ; 1 goto Not2Do ; 3 incf TimerH,F ; 3 bcf INTCON,2 ; 4 goto Nx ; 6 Not2Do nop ; 4 nop ; 5 nop ; 6 Nx decfsz Help,F ; 7 goto Cycle2 ; 9 nop ; ((9*T1+4)*T2+4)*T3+1+9*T4+Z ; nop ; Z times fine tuning nops include ; ------------------------------------------------------------------------ ; Stop the measurement ; ------------------------------------------------------------------------ clrw ; 1 movwf PORTB ; 2 movlw b'00010000' ; 3 RA0..RA3 = 0 movwf PORTA ; 4 W -> PORTA ; ((9*T1+4)*T2+4)*T3+1+9*T4+Z+4 bsf STATUS,RP0 ; movlw b'00010111' ; RA3 output movwf TRISA ; RA0..RA2,RA4 input bcf STATUS,RP0 ; ; ------------------------------------------------------------------------ ; Analyse precounter and store counted value in registers ; ------------------------------------------------------------------------ movf TMR0,W movwf MidB ; TMR0 -> MidB movf TimerH,W movwf HigB ; TimerH -> HigB clrf TEMP CountIt incf TEMP,F bsf PORTA,3 ; _| false impulz bcf PORTA,3 ; |_ bcf INTCON,2 movf TMR0,W ; actual TMR0 -> W bcf STATUS,Z subwf MidB,W btfsc STATUS,Z goto CountIt incf TEMP,F comf TEMP,F incf TEMP,F incf TEMP,W movwf LowB goto Go ; start new cycle - line 434 ; ------------------------------------------------------------------------ end