there is a PDF:This project proposes a simple frequency reader specifically designed for QRP rigs, also if you may
employ it also as a workbench instrument. In fact it exhibits several very interesting features, like a
maximum working frequency above 40 MHz, a 10 Hz resolution, a low consumption (15 mA) and a
very simple assembly. Moreover it is possible to program an IF value and mode simply by means of
two push buttons.
; ------------------------------------------------------------------------
; 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 <p16f84.inc>
; ------------------------------------------------------------------------
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 <t1234lcd.inc> ; 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 <z4lcd.inc>
; ------------------------------------------------------------------------
; 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
If you read the data sheet it has minimum high time of 30nS and minimum low time of 20nS for TOclock; that's 20MHz.The counter inputs in most PICs will work up to around 50MHz but the upper limit isn't guaranteed. When configured correctly, the input to the pin is sent directly to a hardware counter module so it isn't dependent on the system clock frequency. In all designs I've seen, either timer 0 or timer 1 is used as the counter and their overflow interrupts are used as a 'carry' bit to a software counter to increase their range.
I doubt you will be successful writing it in C, the overhead added by the libraries makes the execution time difficult to predict and therefore compensate for in the frequency calculation. It might work in faster PICs but the 16F84 is a relatively old and slow device by todays standards. Look at the 16F628A as one step up and the 16F1847 as it's up-to-date modern equivalent. they are all pin compatible.
Brian.
Look at the 16F628A as one step up and the 16F1847 as it's up-to-date modern equivalent. they are all pin compatible.
• 1.8V-5.5V Operation – PIC16F1847
• 1.8V-3.6V Operation – PIC16LF1847
That's indeed a good observation. So you say that the best option would be using the new 16F1847 and writing the code in C?
btw, I've found something strange.
The 16F1847 datasheet reads:
is this really correct? The "L" letter means low-votlage only?Code:• 1.8V-5.5V Operation – PIC16F1847 • 1.8V-3.6V Operation – PIC16LF1847
Afair, for the PIC18F2550 and PIC18LF2550 the "L" meant that PIC is for 1.8-5.5V operation, and the version without L was 3.6V-5.5V or something like that.
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?