PASM waitpeq to measure the period of a frequency
StefanL38
Posts: 2,292
Hi,
I would like to know if the following code is measuring the period of a frequency with maximum possible precision.
The cog is stopped at the waitpeq-cmd until the specified bit matches the specified value.
If it does match - the time it takes to execute the command
move, firstpulse , cnt
is the same as with
move, secondpulse, cnt
so that after calculating the period-time in clockticks with
the period-time of the pulse is EXACTLY within the precision of clockticks (=12,5 nanoseconds at 80 MHz)?
If this is not exact- is there another way to get in measured with a resolution of one (or maybe 4 clockticks)?
best regards
Stefan
I would like to know if the following code is measuring the period of a frequency with maximum possible precision.
mov _InputStateM, #0 'load BitPattern ZERO
WaitIRPinLow waitpeq _InputStateM, _IR_Recv_Pin2 'wait until specified bit "_IR_Recv_Pin" is ZERO
'which means pulsetrain starts
mov _FirstPulse, cnt 'take snapshot of free running systemcounter
mov _InputStateM, #8 'load bitpattern that should be matched
WaitIRPinHigh waitpeq _InputStateM, _IR_Recv_Pin2 'wait until INA-registerbit specified in _IR_Recv_Pin2
'has the same state as in _InputStateM
mov _InputStateM, #0 'load BitPattern ZERO
WaitIRPinLow2 waitpeq _InputStateM, _IR_Recv_Pin2 'wait until specified bit "_IR_Recv_Pin" is ZERO
mov _SecondPulse, cnt 'take another snapshot of free running systemcounter
mov _Period, _SecondPulse #calculate difference of systemcounter-snapshots
sub _Period, _FirstPulse 'calculate clockticks of pulsedistance (=carrier-frequency)
I assume if the frequency is not near the maximum that could be measured this way.The cog is stopped at the waitpeq-cmd until the specified bit matches the specified value.
If it does match - the time it takes to execute the command
move, firstpulse , cnt
is the same as with
move, secondpulse, cnt
so that after calculating the period-time in clockticks with
mov _Period, _SecondPulse #calculate difference of systemcounter-snapshots
sub _Period, _FirstPulse 'calculate clockticks of pulsedistance (=carrier-
the period-time of the pulse is EXACTLY within the precision of clockticks (=12,5 nanoseconds at 80 MHz)?
If this is not exact- is there another way to get in measured with a resolution of one (or maybe 4 clockticks)?
best regards
Stefan

Comments
Here is a frequency counter I wrote in PropBasic. It is well commented and should help.
' 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 10 MegaHertz and that digit can be calculated as: ' (sigCnt * 8) / cntTime = x0 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 ' Scale signal count for next digit __temp1 = sigCnt * 2 ' sigCnt = sigCnt * 10 using powers of two sigCnt = sigCnt * 8 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 ENDBean
Are you sure you are trying to measure the period? Period of a frequency is 1/frequency. From the comments in your code it seems like you want to measure the time between two waveforms or pulse trains. Measuring frequency can be done with the counters. I'm pretty sure there's an example in the AN001 App Note. But if my suspicion is correct, that won't help you. Are you timing the arrival of consecutive pulse trains, or simply measuring the period of a consecutive pulses?
I think the solutions are very different- the latter being much easier.
Peter
I think one of the things you are asking is "what is the resolution/accuracy of waitpeq/waitpne" - yes its 1 system clock (12.5ns if 80MHz).
And the other thing "is this the most accurate way to measure frequency?" - to which the answer is a resounding NO....
The accurate way is to measure the time for _many_ periods and divide this by the number of periods timed. So if measuring 100kHz the above way the resolution of 12.5ns out of a period of 10us is 0.125%. Measuring 100,000 periods gives a precision of 0.00000125% (beyond the accuracy of your crystal!).