June
2005, Issue 179
Precision
Frequency Meter
Cypress
PSoC High Integration Challenge 2004 Contest Winner
SOFTWARE
ESSENTIALS
I
wrote most of the code in C language. I used some in-line
assembler code to improve the overall execution speed
and software size. Data memory conservation was a major
problem. Because of the depth of function calls and
ISRs, I had to leave a reasonable amount of RAM for
the stack.
The
software operates a number of threads implemented as
switch statements. The main routine loops through these
threads in an appropriate manner for the operation mode.
The
timing subsystem contains routines to maintain the synchronization
data and calculate the current time from it. A reference
count register holds the clock counts value at a known
hour. The current time can be calculated by subtracting
this reference from the current clock count. There’s
also a clock rate register, which holds the number of
reference clocks that have occurred over a number of
hours and is used for frequency calculations. It’s calculated
by subtracting the last reference count from a new reference
count. The reference clocks count and the number of
hours are then added to the clock rate count and hour
registers. This continues for up to 232 h, at which
point 32 h are removed from the clock rate registers
and the hours count (to keep it to a single byte variable).
Earlier
in the development process, the clock rate was based
on only the last two clock references. But I found that
the system would occasionally reference onto the wrong
pulse of the time pips and therefore would be off by
a few milliseconds. Accumulating the reference clock
counts and hours averages out these abnormalities.
Before
full synchronization has completed (which takes two
time pip signals), the calibration registers are preset
to default values based on the nominal clock frequency.
The display shows smiley and sad faces next to readings
to indicate whether or not they’re calibrated.
The
radio’s audio signal is monitored to detect of time
pips. The time pips consist of six 1-kHz pips on each
second. The first five are 150 ms long. The final pip
is 300 ms long. The exact hour is marked by the start
of the long pip.[1]
The
hardware captures the reference clock count at positive
and negative audio edges. An ISR records these times
(along with the SW high-order bytes). The time sync
processing then goes to work. A time pip is validated
by a silence period (at least 0.6 s) followed by a number
of alternate positive and negative pulses in 500-µs
intervals (for the 1-kHz pip signal), followed by more
silence. The number of 1-kHz pulses is validated to
detect a short or long pip. The reference clock count
at the start of a pip is recorded. After the final pip
is validated, this count synchronizes the system. The
system must receive a short pip followed by a long pip
to generate a valid synchronization. After synchronization,
additional tests check that the time pip occurred when
expected (i.e., close to the top of the hour).
Frequency
measurement counts the number of reference clocks for
a number of input signal cycles over a sample period
of approximately 200 ms. The CountClock is set to capture
on the terminal count of the InputDivider. An input
sample therefore captures the reference clock count
at the start and the end of a sequence of input signal
cycles. It can calculate the number of reference clocks
for the number of input signal cycles counted.
An
initial sample is first performed to get an approximate
indication of the frequency. The InputDivider counter
is then set up appropriately and an accurate sample
is taken. You must obtain the reference clock count
at the start and end of a series of counted input signal
cycles. The input frequency can then be calculated from
this and the calibrated clock rate registers.
Frequency
calculation requires precise mathematics. I used a set
of 64-bit math routines (called UINT64, 64-bit unsigned),
so precision wasn’t lost.
The
data is multiplied by 10, as far as precision allows,
creating decimal places of precision. The error of the
frequency is also calculated by calculating the frequency
for ClockCounts+1. (The difference from the real frequency
calculation is the error.) Decimal precision is then
reduced (divide by 10) so that the final error is reported
as the precision of the last digit. The display then
only shows significant decimal places along with the
error in the last digit. The error calculation doesn’t
take any account of the reference clock accuracy.
The
oven case’s temperature is measured approximately every
second. The heater turns on and off when appropriate.
A simple on/off algorithm turns on the heater if the
temperature drops below 45°C. A full PID control would
generate better oven control, but this simple algorithm
is adequate. The temperature of the crystal oscillator
is also captured, but it’s only for display purposes.
Processing
speed is extremely critical during time synchronization.
The buffer to store audio edge capture data is extremely
small, so the time synchronization routine must not
be significantly delayed. Displaying of the temperature
and time on the LCD is processed step by step in a switch
statement. Each call to the display routine performs
a small step in the process. Full processing and display
in a single hit would take too long.
I
used the debug port extensively during software development.
Debug messages are still in the source code even though
they’re disabled. Log messages are still output through
the debug port. All of these are consistently formatted
so that the data can be easily processed and graphed
(using Excel).