Digital Frequency Meter using Pic Microcontroller

A digital frequency meter with an LCD display can be used to measure the frequency of a square wave. With a little modification in this embedded systems project, you can also measure the frequency of a sine wave. In this digital frequency meter, the PIC16F877A microcontroller is used to measure the frequency with the help of an external interrupt. Additionally, a 16 * 2 LCD is used to display the frequency digitally.

Digital Frequency Meter Applications

Many applications use digital frequency meters. Many sensors do not output in voltage form, so it is necessary to perform signal conditioning to interface these sensors with a digital system. For instance, in capacitive sensors, an additional circuit is used to convert changes in capacitance to another physical parameter that a digital system can easily read. Capacitive sensors typically convert the change in frequency with respect to the input into a proportional frequency square wave using additional circuitry. An example of such a sensor is a humidity sensor.

There are many other examples of a digital frequency meter, such as:

Components

Complete list of components you need to make a digital frequency meter:

Resistors,"R1",4k,
Capacitors,"C1",22pF,
Capacitors,"C2",22pF,
Integrated Circuits,"U1",PIC16F877A,
LCD,"LCD1",LM016L,
CRYSTAL,"X1",CRYSTAL 20Mhz,

How to Measure Frequency with Pic Microcontroller

Frequency and time period are inversely proportional to each other.

Frequency = 1 / time period

If one knows the time period of a wave, the frequency can also be easily calculated using the above relation. We can’t measure the frequency directly with this method. In this project, the time period of a square wave is measured first, which is then converted into frequency using the above relationship between frequency and time period.

Circuit Diagram

The PIC16F8877A microcontroller uses external interrupts at rising edges and timers to measure the time period of a square wave. It develops an algorithm to convert the time period into frequency and displays the measured frequency digitally using a 16×2 LCD.

Digital frequency meter
Digital frequency meter circuit diagram

In the above circuit diagram, a pulse generator is used to generate a square wave of any frequency. This is for simulation purposes only. You can connect any device to this pin to measure its frequency. We have connected a 6 kHz square wave with the interrupt pin to measure its frequency. However, the LCD is showing 5.99 kHz. To remove this error, read the frequency 5-10 times and display the average on the LCD.

Pic Microcontroller Code

The code for this project is written in the MIKROC compiler. If you do not know how to use MikroC for Pic, you can refer to these tutorials:

The provided code is for a digital frequency meter using a microcontroller, specifically for a PIC16F877A. The purpose of this code is to measure the frequency of an input signal using an interrupt-driven approach and display the calculated frequency on an LCD screen.

// Digital Frequency Meter using Interrupts

sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;

int on = 0;         // Unused variable, likely for future functionality
int value, freq;    // Variables to store Timer1 value and calculated frequency
char text[7];       // Character array to hold the frequency as a string

// Interrupt Service Routine for external interrupt
void interrupt(void)
{
    // Check if Timer1 is not running
    if (T1CON.TMR1ON == 0)
    {
        T1CON.TMR1ON = 1;   // Start Timer1
        INTCON.INTF = 0;    // Clear the external interrupt flag
    }
    else if (T1CON.TMR1ON == 1)
    {
        T1CON.TMR1ON = 0;   // Stop Timer1
        value = (TMR1H << 8) | (TMR1L); // Combine Timer1 high and low bytes
        INTCON.INTE = 0;    // Disable external interrupt
        
        freq = 5007999 / value; // Calculate frequency
        
        IntToStr(freq, text);   // Convert frequency to a string
        
        Lcd_Init();             // Initialize LCD
        lcd_out(1, 1, "frequency:"); // Display label on LCD
        lcd_out(2, 1, text);    // Display frequency on LCD
        delay_ms(1000);         // Delay for one second
        
        TMR1H = 0;              // Reset Timer1 high byte
        TMR1L = 0;              // Reset Timer1 low byte
        
        INTCON.INTE = 1;        // Re-enable external interrupt
        INTCON.INTF = 0;        // Clear the interrupt flag
    }
}

void main(void)
{
    TRISB.f0 = 1;            // Set RB0 as input for external interrupt
    T1CON = 0X00;            // Timer1 configuration
    TMR1H = 0;               // Initialize Timer1 high byte
    TMR1L = 0;               // Initialize Timer1 low byte
    INTCON.GIE = 1;          // Enable Global Interrupt
    INTCON.INTE = 1;         // Enable RB0/INT external Interrupt
    OPTION_REG.INTEDG = 0;   // Interrupt on falling edge

    do
    {
        // Wait here for interrupts
    } while(1);
}

How Does the Code Work?

sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;

These lines of code define the LCD control pins (RS, EN) and data pins (D4-D7) using the sbit keyword with PORTB of PIC16F877A microcontroller.

sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;

Define the direction of pic microcontroller LCD control pins and data pins as output using the sbit keyword.

int on=0,value,freq;
char text[7];

These lines of code declare variables on, value, freq, and text as integer and character arrays respectively.

void interrupt(void)
{
    if (T1CON.TMR1ON == 0)
    {
        // Positive Edge Trigger: Start Timer1 on the positive edge of input signal
        T1CON.TMR1ON = 1;    // Start Timer1
        INTCON.INTF = 0;     // Clear external interrupt flag
    }
    else if (T1CON.TMR1ON == 1)
    {
        // Negative Edge Trigger: Stop Timer1 on the negative edge of input signal
        T1CON.TMR1ON = 0;    // Stop Timer1
        value = (TMR1H << 8) | (TMR1L); // Combine Timer1 high and low bytes to form a 16-bit value
        INTCON.INTE = 0;     // Disable external interrupt
        
        // Calculate frequency using a predefined constant and Timer1 value
        freq = (5007999 / value);
        
        // Convert the frequency value to a string
        IntToStr(freq, text);
        
        // Initialize and set up an LCD display
        Lcd_Init();
        
        // Display "frequency:" on the first line of the LCD
        lcd_out(1, 1, "frequency:");
        
        // Display the calculated frequency on the second line of the LCD
        lcd_out(2, 1, text);
        
        // Wait for a short duration (1 second)
        delay_ms(1000);
        
        // Reset Timer1 values
        TMR1H = 0;
        TMR1L = 0;
        
        // Enable external interrupt for the next cycle
        INTCON.INTE = 1;
        
        // Clear external interrupt flag
        INTCON.INTF = 0;
    }
}

This interrupt function serves as an interrupt service routine (ISR) for an external interrupt triggered by a positive edge and a subsequent negative edge of an input signal. When the positive edge occurs, denoting the start of a cycle, the code checks if Timer1 is not already running. If it isn’t, Timer1 is initiated, and the external interrupt flag is cleared. This marks the beginning of measuring a time interval.

Upon encountering the negative edge, indicating the end of a cycle, the code halts Timer1, combines its high and low bytes to form a 16-bit value, and temporarily disables the external interrupt. This value is then used to calculate the frequency of the input signal by applying a predefined constant and a formula. The calculated frequency is converted into a textual representation and displayed on a two-line LCD screen, along with an introductory label. After a brief one-second delay, Timer1 is reset to zero, external interrupt functionality is re-enabled, and the interrupt flag is cleared to prepare for the next cycle. In essence, this code captures the time between consecutive positive and negative edges of an input signal, calculates its frequency, displays it on an LCD, and then readies the system for the next signal cycle.

void main(void)
{
    TRISB.f0 = 1;            // Set RB0 as input for external interrupt
    T1CON = 0X00;            // Timer1 configuration
    TMR1H = 0;               // Initialize Timer1 high byte
    TMR1L = 0;               // Initialize Timer1 low byte
    INTCON.GIE = 1;          // Enable Global Interrupt
    INTCON.INTE = 1;         // Enable RB0/INT external Interrupt
    OPTION_REG.INTEDG = 0;   // Interrupt on falling edge

    do
    {
        // Wait here for interrupts
    } while(1);
}

This is the main function. It configures the RB0 pin of the PIC16F877A microcontroller as an input pin, initializes the timer and interrupt settings, and enters an infinite loop.

Demonstration

Sure, here’s the corrected version:

By making a slight modification to the aforementioned circuit, you can also measure the frequency of a sine wave. All you need is a small external circuitry that can convert the sine wave into a square wave with the same frequency. This can be easily achieved using an operational amplifier. The operational amplifier can be used as a comparator for this purpose, and you can also utilize a zero-detection circuit. I will also provide an article on measuring the frequency of a sine wave and an example of a digital frequency meter using a humidity sensor.

You may also like to read:

60 thoughts on “Digital Frequency Meter using Pic Microcontroller”

  1. Hello Bilal,
    I am a fresh-woman. I am interested to work Proteus. I tried but I couldn’t run. Is it possible, please send Proteus file for me?
    Thank you.

    Reply
  2. i thin, as the given measured frequency increases the measurement accuracy will decrease .whats the maximum frequency which can be measured say with 1 or 2 percent of error.also the micro controller be run at 4 – 16 Mhz what will be the range of measurement when mcu is run at its max speed.

    Reply
  3. Thank You very much Mr. Malik… I am a student in power electronics doing my project. Your posts are really worthy and you prove that knowledge grows by sharing.

    Reply
  4. Hi Malik, appreciate your work. May I ask how do you get this number in your coding?
    “freq=(5007999/value);”. That 5007999 number, it is obviously frequency=1/T but can you explain how you did the calculation to get that number?
    Thank you so much

    Reply
  5. hi , your projects are so sucsessful , everything is easily understandable.

    thank you so much , your information is grateful for me.

    the other people who wants the”full code” are stupid because all they need is at your article 🙂

    Reply
  6. hi!,I like your writing so much! share we communicate
    more approximately your post on AOL? I require an expert in this
    house to solve my problem. May be that is you!

    Taking a look ahead to look you.

    Reply
  7. Could you please send full code and simulation to me at kuhnsterry@hotmail.com? Also, how would you modify code to read 3 separate variable frequencies input to the chip, A, B and C and then add A and B and subtract result from C ie. (X = C – (A + B)) and display X on the LCD…,. Thanks…Terry

    Reply
  8. Hello!
    What if I have more than one source of frequency generator and I need to measure their frequencies simultaneously?
    Please help.
    Thank you

    Reply
  9. I am happy and serious when I watched this program
    Can you help me with a program similar to this if I told you about the details
    Thank you so much

    Reply
  10. Respected Mr Malik,
    Your project is appreciable. Kindly elaborate the frequency calculation formula to enable novice like me to understand.Thanks a lot sir.

    Reply
  11. Respected Mr Malik,
    Thanks a lot Sir for this beautiful article. I would like to know that you have used external interrupt in square wave frequency measurement but ISR in code is quite big, does PIC 16 F 877 MCU contains enough memory space in Interrupt vector table ? Kindly elaborate. It will be a great help for the novice like me.

    Reply
  12. The Code is complete and 100% working
    i have two questions which are still unclear to me
    1. How we get 5007999?
    2. why the LCD display 5.99k instead of 6K. (not cleared from your video)

    Reply
  13. What is the figure 5007999 in the code. I know that frequency is the inverse of time, and time period is stored in the value variable , then what is the need for this figure.

    Reply

Leave a Comment