Simple low-cost digital frequency meter
This article presents a design for a simple, low-cost digital frequency meter with the following features:
The circuit is built around a member of the PIC family of microcontrollers from Arizona Microchip. In fact the most junior member of that family - the 16C54. In this article we hope to give some insight into the methodology of software design for microcontrollers, and in particular an insight into programming the PIC. The challenge was to achieve a solution with the minimum of hardware by moving the functionality into software. Basing the circuit on a microcontroller, rather than opting for a more conventional electronic design, gives a greater degree of flexibility. Software is more adaptable than hardware, it is much easier to change a line or two in the source code than to add another track to a pcb.
The PIC is an excellent microcontroller for this type of project. It is robust, simple to interface to the outside world, and relatively simple to program (or at least no harder than any other microcontroller or microprocessor that we’ve come across). We’ve been using this chip for several years now and have never seen one damaged by static when being handled (less worry about earthing straps or whether one’s clothes have synthetic fibres in them). We’ve seen them survive being placed into sockets backwards and power applied. We’ve even seen them pulled out and put back into their sockets with the battery connected without damage! We don't wish to encourage this kind of treatment, especially as static damage to chips can show up after a lengthy period of use, but it is a tribute to their robustness.
All processors have peculiarities in their instruction sets and the PIC is no exception. With the PIC at least there are only 33 instructions to learn so the situation is not too bad. One of the most confusing is the subtract instruction, which is actually an add with the negative of the number. This reverses the carry flag from what one would expect!
Individual instructions are represented by mnemonics which are easier to remember than the binary codes that the processor actually understands. A mnemonic gives an indication of what an instruction does. For example the instruction
The PIC 16C54 has 13 input/output (I/O) pins of which 12 are general purpose. These pins are used to drive four 7-segment LEDs that display the measured frequency. The remaining I/O pin is connected to an internal register in the PIC called the RTCC (real time clock/counter). This register can count either internal instructions or external pulses. In this instance we are using its ability to count pulses. The RTCC pin is connected to an external probe for the meter via some circuitry to condition the input signal. The RTCC can trigger on a rising or a falling edge, in this design we have conventionally selected triggering on rising edges. There is also a prescaler associated with the RTCC which can prescale the input to the counter from 1:2 to 1:256.
The desired accuracy of ± 1Hz rules out using an RC oscillator to drive the microcontroller. A crystal or ceramic resonator must be used. The frequency meter must measure up to an 8MHz input signal so the processor needs to be fast. Resonator versions of the chip run up to 4MHz, crystal versions up to 20MHz. Therefore the high speed version running with a 20MHz crystal was chosen. Each PIC instruction takes 4 clock cycles to execute, so a 20MHz PIC has a performance of 5 million instructions per second or MIPS! (although each instruction doesn't actually do very much by itself it is still a very impressive figure).
How does one measure the frequency of a signal ? Simply by counting the total number of pulses over a fixed period of time, typically 1 second. This will always give a reading accurate to ± 1Hz. For high frequencies (above 10kHz) the meter can be made more responsive by timing over a shorter period, say 1/8 s. This reduces the accuracy to ± 8Hz but because only 4 significant digits are displayed anyway that doesn’t matter.
The RTCC can’t count more than one pulse per instruction cycle (per 4 clock cycles). With a 20MHz crystal it can count a maximum of 5 million pulses per second. For signal frequencies above 5MHz therefore the prescaler must be used. This has the effect of dividing down the input frequency to the counter. With prescaling of 2 the frequency meter can cope with up to 10MHz signals.
One of the design goals was to dispense with range switches or the equivalent. Consequently the software must be adaptive to whatever input signal frequency it is fed with, from a few tens of Hz to MHz - a range of 6 orders of magnitude! The first problem to be solved was how to display such a range of frequencies using just four digits (i.e. without being able to display the units, whether Hz, kHz or MHz). The solution was to always display the signal frequency in kHz with the position of the decimal point effectively indicating the units. Table 1 gives the displayed readings for the range of frequencies. A more sophisticated (and expensive) approach would be to use an alphanumeric LCD display which could display the units as well as the digits.
Underflows (frequencies less than 1Hz) or no signal at all are displayed as a single zero, and overflows (frequencies greater than 8MHz) are displayed as the letter ‘E’ (for error).
Table 1 - How the frequency meter displays different frequencies
One of the arts of circuit design using small microcontrollers is to make the I/O pins on the chips do a number of different jobs each (as there are so few of them). In this design the same set of 8 pins drives four 7-segment displays, each of which could be displaying a different digit at the same time. How can this be done you might ask ? The answer is a technique known as multiplexing whereby only one of the 7-segment displays is actually switched on at any one time. The software very quickly displays a digit on each of the four displays in turn, but because this happens very fast (a thousand times a second in fact!) you don’t notice any flicker. This phenomenon is known as ‘persistence of vision’ and is the same technique that is exploited in televisions. Any flicker faster then 72Hz can’t be perceived by the human eye. The other 4 I/O pins control which 7-segment display is currently enabled.
This technique complicates the software somewhat because the display multiplexing needs to happen all the time the software is counting pulses. The two processes must be interleaved and because of the need to know the exact number of instructions executed this all requires careful planning.
As mentioned above, the RTCC register of the PIC can either count internal instructions or external pulses. If it is used to count internal instructions then it is effectively acting as a clock (and so can measure time). In this case however we are using it to count pulses. This means we must use some other method for measuring time - specifically, by manually counting the number of instructions that are executed. This is a laborious process but is simplified because the PIC is what is known as a reduced instruction set computer or RISC. Basically this means that it is lean and fast. It has a small number of simple instructions (no fancy addressing modes here) but because they are simple the processor can decode and execute them very quickly. The advantage from our point of view of the PIC being a RISC is that all instructions execute in the same amount of time (or more accurately all instructions that don’t cause a program branch execute in 4 clock cycles, all branch instructions in 8 clock cycles). This means that to work out how long a fragment of code takes to execute is simply a matter of counting the number of instructions.
The number of clock cycles in a loop gives the timing resolution of a process. For example, suppose some code is timing how long it takes for an event to occur. If the loop which polls the state of the event takes 10 µs to execute, then when the event happens the worst case is that the software becomes aware of that fact 10 µs later. So it is important to make timing loops short and efficient.
A key concept here is the idea of ‘equal path lengths’. Basically this means that whatever path the processor takes through a piece of code (and remember that a processor usually jumps about all over the place within its loops and subroutines), the total number of clock cycles elapsed must be the same whatever the different conditions that prevail. For example, consider the fragment of code below that increments a 16-bit counter. The 16-bit counter is implemented from two 8-bit registers because the PIC doesn’t have any 16-bit registers. Let’s call these two registers
The first instruction adds one to the low byte. If the result of this operation is not zero (i.e. the register has not rolled over) then the instruction to increment the high byte is skipped. The
This technique is applied to the main loop of the program. The code that multiplexes the display and polls the RTCC always takes a constant (and known) number of clock cycles to execute and thus can form the basis for measuring time.
The timing loop for the frequency meter should be of a length such that a second can be conveniently timed using a 16-bit loop counter. A timing loop of 20 µs needs to be iterated 50 000 times per second. This value fits nicely into 16 bits (and is also a multiple of 8 which is a requirement for timing 1/8 s with the same loop).
The RTCC register is 8 bits wide and can only count up to 255 pulses. We need to be able to count many more pulses than that in a second. The register is extended by software into a 24-bit counter which can count up to 16 million, enough for our purposes. The timing loop looks at the current value of RTCC and compares it to the previous value. If the previous value had its most significant (left-most) bit set and this bit is now clear then the counter must have rolled over (for example going from 255, which is 11111111 in binary, to 0). When the RTCC rolls over the high 16 bits of the 24-bit counter need to be incremented. The low 8 bits are simply a copy of the RTCC register.
If more than 128 pulses occur between polls of RTCC then pulses will be lost. This sets the upper limit to the frequencies that can be measured. A timing loop of 20 µs is equivalent to a frequency of about 6MHz. This is a little too low for the specification so use is made of the RTCC prescaler at high frequencies. Prescaling of 1:2 effectively halves the input signal frequency and takes us safely above 8MHz. The prescaling must be compensated for in the software by an appropriate multiplication.
The subroutine that drives the 7-segment displays uses a ‘lookup’ table. This contains the patterns of light for the individual LED segments that represent the decimal digits 0 to 9. The PIC uses a rather strange way of implementing a lookup table. A list of return-from-subroutine (
The first macro is used at the head of a lookup table and gives it a name. The instruction
A complete lookup table is therefore constructed as follows :-
In this design four different lookup tables are used for the four 7-segment displays. This is to simplify the layout of the pcb at the expense of a marginal increase in the complexity of the software.
You may have noticed that we haven’t given a flow chart for the software. It is more instructive to study the actual source code for the program which is documented more comprehensively than a flow chart would be.
The circuit of the frequency meter can be divided into three main parts - the power supply, the input amplifier, and the digital circuitry.
The meter is intended to be portable and can be powered by a 9V alkaline battery. A voltage regulator is used to stabilise the supply rail to 5V. It is important for accuracy that the microcontroller has a good power supply and the regulator does this very well.
The digital rail is expected to be noisy due to the multiplexing of the LED displays and the switching of relatively high currents (40mA). Components R7 and C2 form a low-pass filter of around 100Hz and isolate the voltage supplied to the input amplifier from the noisy digital rail.
Note that if the meter is powered by a battery then it must be a good quality alkaline battery, such as a Duracell.
The input amplifier is the interface between the input signal and the PIC microcontroller, and its function is to condition the signal before it passes to the RTCC pin. It will convert any periodic signal (sine, triangular, square wave etc.) with a peak to peak amplitude of at least 50mV into a clean 5V TTL-level square wave as required by the RTCC input. The amplifier has a high input impedance to minimise its effect on the input signal.
The first stage of the input amplifier is based around a FET (TR2) which uses constant current biasing. Constant current biasing has the advantage that the drain current does not depend on the characteristics of the FET which can vary widely. This means that the dc levels throughout the circuit can be accurately predicted making the design work easier.
The design starts with the choice of FET. A BF244 was selected because of its low and narrow range of drain current IDSS (2 - 6.5mA). A value of 1mA was chosen for the constant current biasing (below IDSS min) and this ensures that all BF244 FETs will have the same dc operating point regardless of their variation in transconductance.
Resistors R3 and R5 form a voltage divider and provide a fixed voltage of 0.9V at the base of TR3. The emitter is therefore at 0.9 - 0.65 = 0.25V. This voltage falls across the 220R emitter resistor causing TR3 to provide a constant current of (0.25/220) = ~ 1mA. The drain resistor R1 has a value of 1k and causes a voltage drop of 1.25V. The voltage at the drain of the FET is therefore fixed at 3.75V. Capacitor C5 provides an ac path to ground, bypassing the constant current source, and allows ac voltages to develop across the drain resistor R1.
The voltage gain of the FET stage is not predictable since it depends on the transconductance of the FET and this parameter has a spread of values. Measured values of voltage gain at the drain of TR2 were between 1 and 2.
The output impedance of the FET stage is ~ 1k and an emitter follower is used to connect the next stage which is a common emitter amplifier. Each stage is dc coupled and so the base of TR4 sits at 3.75 - 0.65 = 3.1V. The emitter of TR4 is at 3.1 - 0.65 = 2.45V. The emitter resistor R12 has a value of 560R giving an emitter current of ~ 4.4mA. The collector resistor R8 has a value of 220R and gives a voltage drop of ~ 1V.
The gain of this stage is R8/re where re is the intrinsic emitter resistance. The voltage gain is about 40.
The signal from the collector of TR4 is converted to a 5V square wave using the switching transistor TR5. Capacitor C6 provides ac coupling to the base resistor R10 and diode D4 clamps the signal to the ground rail.
The voltage gain of the input stage is up to 80 and has a flat response from 100Hz to 10MHz. Resistor R4 determines the input impedance of the amplifier. A value of 1M was chosen and this produces a fixed impedance up to 30kHz. The impedance then reduces by 10 for each decade the frequency is increased, i.e. at 300kHz it is 100k, and at 3MHz is 10k.
Input protection is provided by R2, D1 and D2. D1 is a silicon diode and clamps the input to ground (-0.6V). Resistor R2 limits the current during an over-voltage condition.
A standard power-on reset circuit is provided for the PIC. Components C8 and R13 hold the voltage at MCLR near zero volts until the PIC's oscillator has stabilised. Resistor R14 protects against C8 discharging through the MCLR pin, and diode D3 enables the capacitor to discharge quickly when power is removed.
The microcontroller clock circuitry, consisting of C9, C10, R15 and X1, is also very standard and is straight from the Microchip data sheets.
The four 7-segment displays share driver resistors (R16 - R23), one per LED segment (8 in total including the decimal point). The displays don’t have to be wired like to like - in other words all the A segments, all the decimal points etc. don’t have to be wired together. It is easier to lay out the tracks on the pcb without this constraint and it is not hard to have a different lookup table for each display in the software. The displays are common cathode and are activated by pulling the common pin low and then writing 1’s or 0’s to the individual segments. If the common pin is high then no current can flow. In this way just one display can be activated at a time.
Construction is straightforward. First fit and solder the resistors and trim their legs. Identify the resistors by the coloured stripes on the body. Then fit and solder the capacitors, paying attention to the polarity of the electrolytics (negative is marked by a stripe on the side of the body). The ceramic and polyester capacitors can be fitted either way around.
Next fit the diodes, transistors and the regulator. The positive end of the diodes is marked by a line on the body of the component. Note that the Schottky diode may not be marked with a type number. The symbols on the pcb silk indicate the orientation of the transistors and regulator (flat side of the component against the flat side of the symbol).
Next fit the IC socket (matching the notch in the socket against the notch in the symbol on the board) and the dual 7-segment displays (note the decimal points on the pcb symbol). Care should be taken when soldering these components to avoid solder bridges between the pins. It is not recommended that the PIC is soldered directly to the pcb. Finally fit the crystal, slide switch, BNC socket, and battery connector. The latter has support holes drilled on the pcb. Feed the battery snap leads up through the support holes from the track side of the board and then down the solder holes. Red is positive and black is negative.
Don't fit the chip into its socket until you have thoroughly checked your construction. A good visual inspection should be the first stage. Check that all the components have been inserted correctly and that there are no dry joints and no solder bridges between tracks. Then follow the procedure in the Circuit Testing section below.
With regard to casing the project, basically any kind of box will do that has a cut-out for the display. Personally we feel that these kinds of projects don't especially need cases. It is often more attractive for the workings to be on show. Not using a case also encourages one to be neater with one’s construction. If the meter is not cased then rubber feet can be attached to the corners.
However if a case is required then select one that is slightly larger than the pcb. Mount the pcb in the bottom part of the case using spacing pillars at the four corners of the board (fitting 4mm holes). Cut a hole in the side of the case for the BNC socket. You can run a short screened wire from the socket to the pcb or solder the socket to the pcb and use the socket’s nut to hold the pcb firmly to the case. Cut out a section in the top part of the case directly above and slightly larger than the 7-segment displays. Glue a piece of red cellophane or gel on the inside of the case over the cut-out. Finally mount an on-off toggle switch on the case and connect wires from the switch to the pcb (S1).
Without the PIC in place the analogue circuitry can be tested. Apply power to the board and check for the following dc voltages:
Apply a test signal to the input (about 100mV at 1kHz). Look for the presence of the signal at the output of the amplifier (the collector or top leg of TR5) as a clean square wave.
If the board checks out as above then fit the chip into its socket. Match the notch in the chip against the notch in the socket. The legs of the chip may need bending a little, do this carefully with your fingers. No special static precautions are necessary.
The software includes a power-on self-test. All the LED segments are turned on for about a second. If you see this then you know the power supply is ok, the processor is oscillating, and the multiplexing is working. After the self-test the display is blanked and then, if no input signal is present, 1 second later a single zero is displayed.
Insert the test signal again. If everything is working properly the meter will display the signal frequency.
A standard oscilloscope-type probe can be connected to the BNC socket. These probes generally have a crocodile clip attached. This should be connected to an earth point (0V) on the device to be tested.
The meter should be turned off when not being used as the LEDs draw a fairly large current. It could be connected to a mains power supply for bench use (supplying between 5 and 24V dc). The current drain is normally about 50mA.
D2 can be left out entirely and this improves the performance of the meter by increasing its input impedance but at the cost of losing the input protection.
A useful addition to the circuit at high frequencies (above 1MHz) is a 100pF ceramic capacitor in parallel with resistor R10. This help TR5 to switch on and off more quickly. The capacitor can be soldered directly across R10.
If the meter is being powered by a mains power supply the display can be made brighter by reducing the value of the driver resistors R16 - R23, to 220R. The suggested value of 330R is designed not to draw too much current from a battery. A cheaper regulator such as a 78L05 can be used if the meter is to be exclusively mains powered.
A good use of the frequency meter is to calibrate other equipment. For example a signal generator that doesn't have its own digital display. The meter will show the exact frequency being produced by the signal generator.
Texas Instruments manufacture a series of light sensors (e.g. TSL220) which produce an output frequency dependent on light intensity. They are very easy to use requiring just one external capacitor. A light meter combining one of these sensors with the frequency meter could accurately measure irradiance over a 3 decade range.
Attach a crystal microphone to the frequency meter and you have a guitar tuner. The meter will display the exact frequency being played on a musical instrument. You can easily tune the instrument with reference to a table of note frequencies.
Resistors (all 1/4W 5% carbon film) R1,R9 1k (brown, black, red, gold) R2,R10,R11 470R (yellow, purple, brown, gold) R3,R13 10k (brown, black, orange, gold) R4 1M (brown, black, green, gold) R5 2k2 (red, red, red, gold) R6,R8,R15 220R (red, red, brown, gold) R7 10R (brown, black, black, gold) R12 560R (green, blue, brown, gold) R14 100R (brown, black, brown, gold) R16-R23 330R (orange, orange, brown, gold) Capacitors (electrolytics 2.5mm spacing, others 5mm) C1,C4,C6 100u electrolytic 16V C2,C5,C7 220u electrolytic 16V C3 100n miniature polyester C8,C11 100n ceramic C9,C10 22p ceramic Semiconductors TR1,TR3,TR4 BC547B npn transistor (or BC548B, BC549B) TR2 BF244A or B N-channel JFET TR5 ZTX313 npn switching transistor D1-D3 1N4148 silicon diode D4 BAT42 Schottky diode REG1 LP2950 or 78L05 5V regulator IC1 18-pin DIL socket + PIC16C54 HS/P microcontroller DISPLAY1,2 0.56" dual 7-segment LED display, common cathode Miscellaneous X1 20MHz crystal, HC-49/U case S1 miniature pcb vertical slide switch, SPDT INPUT BCN pcb socket BATTERY PP3 battery snap 4 x rubber feet
This article first appeared in Electronics Today International Volume 26 No. 2.