It uses the simpler method of direct frequency measurement which means that the input event (for which you want to obtain a frequency reading) is used to directly increment a counter (inside the PIC).
The other way of making a frequency counter is reciprocal counting which uses a rising edge event to trigger a counter and stops the counter on a falling edge.
The direct counting method means that the number of digits displayed depends on the input so for a 1Hz input, the digit 1 is displayed indicating a frequency of 1Hz. Reciprocal counting displays more digits for more resolution at lower frequency i.e. it can display fractions of a Hertz. However, for basic use the frequency counter shown here is extremely useful.
In the same way as the LCD counter project this frequency counter circuit uses TMR1 in 16 bit counter mode to count the input signal edges. Counter overflows are accumulated to give the total count in multiples of 65536. Adding the current value of the counter at the end gives the total count. In addition Timer 0 is used to obtain an accurate period of 1 second by counting 1e6 clock edges (Fsoc/4=1MHz) where the main clock frequency is set by a crystal resonant at 4MHz.
The other problem with 7 segment displays is that they must be updated approximately every 20ms for your eye to be fooled into seeing a continuous display so part of the Timer 0 counting goes into doing 7 segment display refresh. Normally you would use a TTL chip that does the refreshing for you (75LS47 or CMOS 4511) but the design here does it all.
The complicated bit is that the interrupt routine must be small so that the count is captured correctly while still allowing display refresh.
|Max frequency||~50MHz (limited by input pin characteristics).|
|Input signal level||TTL|
To create a capture time of 1 second requires counting the internal clock (Fosc/4=4e6/4), and as before we use Timer0. To do this we need to use the interrupts generated when an overflow of the counter occurs.
Main clock processing period time = Fosc/4 = (1/(4e6/4)) = 1e-6 = 1us
To get to 1 second we need 1/(Fosc/4) counts = 1/ 1e-6 = 1e6
Timer 0 counter overflows every 256 counts.
Since the overflow occurs every time that the counter passes 256 we need to count 1e6/256 overflows1e6/256 = 3906.25
The extra 0.25 indicates 64 cycles are required before the last interrupt fires see the
lcd project calculations as it is exactly the same in operation.
Just as in the LCD project the measurement time is 1 second the final count is actually the frequency of the input signal and again using the 1 second measurement time gives a frequency resolution of 1 Hz.
Note: The exact maximum operating frequency is determined by the PIC input pin characteristic.
|Compiler||Mikroelectronika MikroC Compiler Free!|
|Target||16F877A (retargetable to other PICs that have TMR1)|
|Software notes||Constant time code.|
|Hardware notes||Seven segment multiplexing.|
your details to get the Download Link
and get the microcontroller newsletter:
(Note: Your email is safe it will never
be sold or rented).
Once constructed you can test the wiring out using the following two files e.g. if the 1st download file does not appear to work:
These tests will also test out a system using individual 7 segment displays.
|Frequency counter circuit:Test wiring 1||Download here.|
|Frequency counter circuit:Test wiring 2||Download here.|
Test wiring 1 : Outputs constant digits to the display and reads "12345678" from left to right. You can use this test to see if PORTD and each transistor driver is connected correctly.
Test wiring 2 : Outputs a shifted digit set from 1-8 to test PORTD
connections to each 7 segment. Just observe each digit and make sure it goes
through each number 1 to 8.
For a tutorial on compiling these files click here.
You can recompile the frequency counter circuit files if you want examine code operation (using the built in simulator) or change the source code. Note the hex file is contained in the download.
For the general theory of operation of this circuit and notes on frequency counting for this frequency counter circuit click here.
The main circuit blocks of the frequency counter circuit are shown in the diagram below.
The 8 seven segment displays are multiplexed using a Johnson counter (4017) that activates a single output after each clock pulse. Port A drives the reset line and clock signal to the 4017 and transistors at the outputs of the 4017 are connected to the common cathode of each seven segment display. This lets the micro turn on each display sequentially. Port D drives the segment enable lines to control the character displayed.
The crystal oscillator is simply a crystal and two capacitors connected to the PIC oscillator port at OSC1 and OSC2. The capacitors can both be fixed unless you want to tune it using a frequency reference. If you don't have an accurate reference then use fixed capacitors.
The PIC micro can be any type that has Timer 0 and Timer 1 hardware and and has enough memory to hold the program and enough ports to drive the 4017 and the segment enable lines (8 bits).
The LED is toggled at the end of every gate time to indicate that the processor is alive - so if there is no input signal you can tell that the software is working.
You can program the PIC in circuit through the ICSP connector.
Compiler project files
C Source files.
All other header files contain prototypes.
This file contains the port initialization, interrupt_initialisation, main and interrupt functions.
After initialisation the code enters an endless loop where it continuously waits for update flags that are set in the interrupt routine. So in actual fact the main part of the system is not contained in "main" but in the interrupt() code.
init_ports() - Sets up port directions
init_interrupts() - Sets up Timer 0 and Timer 1 controls.
start_timer_count() - Restarts Timer 0 and 1 counts from zero and starts interrupts again.
interrupt() - Accepts and acts on interrupt events.
main() - Initialises code and enters an endless loop reacting to update flags.
This routine is the entry point for the program and calls all initialisation functions then starts the interrupt routine and enters an endless loop waiting the the flag: 'update_display'.
When the flag is set it takes the stored timer values and calculates the total stored time. If this is non-zero it is converted to a string placed into array 'op'. This array is used by the sevensegment.c code in updating the 7 segment displays.
If the calculated value is zero then zeros are put into array 'op' for display instead.
This routine contains the main operation of the counter. As discussed Timer 1 counts events by increasing TMR1_counter for every input event (edge)'
Timer 0 counts time increasing every Fosc/4 edge.
Interrupts are generated for Timer 0 overflow (256 * (1/(Fosc/4.))) and as discussed previously the number of Timer 0 overflows that gets close to 1 second is 3906.
At this point the flag do_TMR0_end_count is set indicating to the Timer 0 interrupt service routine that the next count will be the last. At this point Timer 0 is adjusted for an overflow count of 64 Fosc/4 periods and when the interrupt fires variables are updated (prefixed with the letters 'st_'). At this point the complete 1 second time has expired and update of the display is required. This is achieved by setting the flag update_display. The routine main then reacts to this flag after the interrupt has finished.
The last thing the Timer 0 interrupt does is to call the display refresh routine display_str_8seg7 every 19 Timer 0 overflows (see sevensegment.c for details).
This file has the 8 digit seven segment display driver.
The first output from the 4017 is not connected so this acts as the reset state. At every call the next digit is output on port D and the 4017 is advanced one bit by strobing the clock. In this way after each call the next digit is displayed.
Function: init_display_str_8seg7 -
This routine initialises the hardware and index counter to a known state.
This means that the routine is not taking too long so that other interrupts can be serviced.
The actual call rate in the interrupt is when Timer 0 has overflowed 19 times and it is also the last thing that the interrupt code looks at since it is not as important as capturing frequency events or counting time. This number is:
19*256*Fosc/4 = 19*256*1e-6 = 0.0049s ~ 5ms
So all 8 digits are updated at about 5ms*8 = 40ms which is 25Hz.
It was found by experiment but is the repeat rate that you would expect .
This is a public domain conversion of a long word to ascii output.
It could be made more efficient but allows use of any number base.
This contains macros for bit manipulation which should be compiler independent.
End of code description for the frequency counter circuit.
Jump from frequency counter circuit
to Best Microcontroller Projects Home Page.
Find out how to use the AD9833 to generate sine, square and triangle waves at 0.1Hz resolution.
Arduino Nano ISP: How to program an ATmega328P using an Arduino Nano as the ISP programmmer. One common problem: Programming a sketch into the chip without a reset control - solved here.
Arduino Voltage reference: Both the Uno and Nano have a built-in voltage reference: Find out how to make it more accurate. In addition find out how to use it to measure the supply voltage 1V8 ~ 5V5
Arduino EEPROM: How to use it and How to preserve the life of EEPROM. Two examples sketches to save multiple values to EEPROM.
A tutorial on using the ADS1115 precision 16 bit ADC for low power use.
Learn how to use the TP4056 properly. There's a right, and a wrong way, to use it to safely charge Lithium Ion batteries.