' Frequency counter (0.5 Hz to 40 MHz)
' Counts frequency on P0, sends ASCII to PC at 115200 Baud
' 1.0 Second gate time (gate time will be longer if frequency is < 2 Hz)
'
DEVICE P8X32A, XTAL1, PLL16X
XIN 5_000_000
BAUD CON "T115200" ' Baud rate for PC communications
Signal PIN 0 INPUT ' Input pin for signal
TX PIN 30 HIGH ' Output pin for PC communications
cntStart VAR LONG ' "cnt" value at start of measurement
cntTime VAR LONG ' elapsed "cnt" clocks
sigCnt VAR LONG ' count of how many input pulses have been received
temp VAR LONG ' loop counter
digit VAR LONG ' current digit
digitSum VAR LONG ' Used for leading zero removal
ascii HUB STRING (20) ' Holds ascii representation of frequency
PROGRAM Start ' Start program at label "Start:"
Start:
COUNTERA 80, 0, 0, 1, 0 ' Count positive edges on pin 0
DO
' Wait for a signal pulse to syncronize to the system counter
WAITPNE Signal, Signal
WAITPEQ Signal, Signal
phsa = 0
cntStart = cnt - 4
' Count input pulses until 1.0 seconds have elapsed
DO
WAITPNE Signal, Signal
WAITPEQ Signal, Signal
sigCnt = phsa
cntTime = cnt - cntStart
LOOP UNTIL cntTime > 80_000_000 ' 1.0 Second gate time
cntTime = cntTime - 4 ' To account for "sigCnt = phsa" instruction
' Calculate frequency if >= 0.5 Hz
IF cntTime <= 160_000_000 THEN
' Since cntTime is in 80MHz units, the frequency is sigCnt / (cntTime / 80_000_000) = Hertz
' Rearranged this can be written as (sigCnt * 80_000_000) / cntTime = Hertz
'
' We want the result in milliHertz (1/1000 of a hertz) so we need to use:
' (sigCnt * 80_000_000_000) / cntTime = milliHertz
'
' Okay, now we have a problem, 80_000_000_000 cannot even be represented in 32 bits. Hmmm
' What if we calculate the answer one digit at a time...
'
' The leftmost digit is MegaHertz and that digit can be calculated as:
' (sigCnt * 80) / cntTime = MegaHertz. This we can do easily.
'
' Now the "trick" is how do we get the next digit ?
' We take the remainder from the division, multiply it by 10 and divide again.
' by repeating this process of taking the remainder and multipling by 10 we
' can get as many digits as we need until we run out of precision.
'
digitSum = 0 ' Sum of all digits so far. Used to create replace leading zeros with spaces
ascii = ""
sigCnt = sigCnt * 8 ' Scale signal count to get MegaHertz digit
FOR temp = 0 TO 13 ' 7 digits + decimal point + 3 digits = 11 digits
digit = sigCnt / cntTime ' Calculate this digit of result
sigCnt = __remainder ' Keep remainder for next digit
__temp1 = sigCnt * 2 ' sigCnt = sigCnt * 10
sigCnt = sigCnt * 8 ' Scale signal count for next digit
sigCnt = sigCnt + __temp1
INC digitSum, digit ' Add this digit's value to sum
digit = digit + "0" ' Convert digit to an ascii digit
' If this is a leading zero, make it a space
IF temp < 9 AND ' Only use leading spaces for digits higher than the units digit
digitSum = 0 THEN ' If the sum is zero, then we have only had "0" digits so far
digit = " " ' so use a space instead of a "0" for this digit.
ENDIF
WRBYTE ascii(temp), digit ' Put digit into the result string
' Insert a comma if needed
IF temp = 1 OR
temp = 5 THEN
INC temp
IF digitSum = 0 THEN
WRBYTE ascii(temp), " "
ELSE
WRBYTE ascii(temp), ","
ENDIF
ENDIF
' Insert decimal point at proper place
IF temp = 9 THEN ' If we just processed the units digit,
INC temp ' move pointer to the next character in the result string
WRBYTE ascii(temp), "." ' and make it a decimal point
ENDIF
NEXT ' Process all digits
WRBYTE ascii(14), " ", "H", "z", 13, 0
ELSE
ascii = "Too low (< 0.5Hz)"
ascii = ascii + 13
ENDIF
SEROUT TX, BAUD, ascii ' Send frequency to terminal
LOOP ' Repeat forever
END