Did you find this site useful ? Please
to help www.micro-examples.com

If you need a coder or a freelance programmer, submit your project to me


Simple Frequency Meter

Printer-friendly version | Forums | FAQs |

This section will show you how to build this f-meter :

  • PIC16F84 microcontroller
  • RS232 text output
  • one output per second
  • basic frequency range from 0 to 83 KHz with 20 Mhz crystal
  • 1 Hz accuracy
  • no extra component !
  • can be build without MikroC compiler licence !

and make it run on a EasyPic development circuit board.

The C language source code is freely provided !

 

This is the source code for building a very simple, but efficient frequency-meter, you can test it directly with an EasyPic board with no extra component.

The program counts the pulses on the RB0 pin during 1 second, and send the result to a software emulated serial communication line on RB1 and RB2.

Each level transition from 0V to +5V on the RB0 pin triggers a software interrupt, which increments the cntr counter.

We only have to wait 1 second to know how much times the counter has been increased, this will give us directly the signal frequency.

A simple delay loop with Delay_ms(1000) will not be accurate enough. When interrupts are enabled, like in our case, the delay function lasts longer than expected, that's why we will have to find another way to wait 1 second :

A timer is started at the beginning of each mesure, its overflow will produce another software interrupt, which will increment the ovrflw counter.

When the counter rises to the number corresponding to one second delay, the interrupts are disabled and the cntr counter is converted to string and written to the serial communication line. A new reading cycle is then started.

This simple frequency meter will give a 1 Hz accuracy in the range from 1 Hz to 67 KHz approximately with a 16 MHz crystal.

The maximum frequency depends on the speed of the microcontroller : with a 20 Mhz clocked pic, the maximum frequency range should be around 83 KHz.

You can also make it run with a high-speed PIC : 40 MHz or 48 MHz, which should allow more than 160 KHz.

The limitation comes from the interrupt routine : have a look on the asm file produced by the MikroC compiler, you will see that around 60 clock cycles are needed to complete the call. If the transitions on the RB0 pin are too fast, they will overlap, and the cntr counter will not be incremented at each edge.

If you need higher frequency range, just add a cheap decade counter like the 4017 : it will multiply by 10 the maximum frequency range, but you will lost the last significiant digit.

Add another divider if you want to multiply it by 100, or a programmable divider : you have enough free I/O pins on the pic to control it.

Here is the ultra simple code for MikroC compiler :


 

/*
 * SIMPLE SERIAL FREQUENCY METER
 *
 * this program writes one time per second to the RS232 communication line,
 * the frequency of the input signal on the RB0 pin
 *
 * PIC16F84A
 * 16 Mhz crystal, HS clock
 *
 * PORTB.0, in : counter input
 * PORTB.1, out : RS232 tx
 * PORTB.2, in : RS232 rx
 *
 * Author : Bruno Gavand, november 2005
 * see more details on www.micro-examples.com
 *
 */

/*
 * RAM variables
 */
unsigned long   cntr ;          // number of RB0 transition
unsigned int    ovrflw ;        // number of timer0 overflows
unsigned char   str[10] ;       // display result string

/*
 * constant strings
 */
const unsigned char welcome[] = "\r\rRS232 Frequency Meter Ready\rGo to www.micro-examples.com for details\r" ;
const unsigned char unit[] = " Hz\r" ;

/*
 * write the s ram string to RS232
 */
void    Comm_Write(unsigned char *s)
        {
        while(*s)
                {
                Soft_Uart_Write(*s) ;
                s++ ;
                }
        }

/*
 * write the s constant string to RS232
 */
void    Comm_WriteConst(const unsigned char *s)
        {
        while(*s)
                {
                Soft_Uart_Write(*s) ;
                s++ ;
                }
        }

/*
 * convert the cnrt long value to string
 */
void    Long2str(void)
        {
        unsigned char   i, j ;

        if(cntr == 0)
                {
                str[0] = '0' ;
                str[1] = 0 ;
                }
        else
                {
                str[0] = 0 ;
                i = 0 ;
                while(cntr > 0)
                        {
                        for(j = i + 1 ; j > 0 ; j--)
                                {
                                str[j] = str[j - 1] ;
                                }
                        str[0] = cntr % 10 ;
                        str[0] += '0' ;
                        i++ ;
                        cntr /= 10 ;
                        }
                }
        }

/*
 * interrupt routine called 4000000/256 times by seconds :
 * the timer TMR0 is increased each 4 clock cycles (quartz frequency is 16 Mhz),
 * and overflows when reseting from 255 to 0,
 * calling the interrupt procedure with bit T0IF set
 *
 * also called on each RBO transition, with bit INTF set
 */
void    interrupt(void)
        {
        if(INTCON.INTF)
                {
                /*
                 * RB0 interrupt
                 */
                cntr++ ;                // inc. transition counter
                INTCON.INTF = 0 ;       // clear interrupt flag to enable next call
                }
        else if(INTCON.T0IF)
                {
                /*
                 * TIMER 0 overflow
                 */
                ovrflw++ ;              // inc. overflow counter
                INTCON.T0IF = 0 ;       // clear interrupt flag to enable next call on overflow
                }
        }

/*
 * entry point
 */
main()
        {
        Soft_Uart_Init(PORTB, 1, 2, 38400, 0) ;    // RS232 on PORTB, bits 1 & 2, 38400 bauds
        Comm_WriteConst(welcome) ;              // write welcome message

        TRISB.F0 = 1 ;                  // RB0 interrupt pin as input

        OPTION_REG = 0b11011000 ;       // no prescaler

        /*
         * main loop
         */
        for(;;)
                {
                cntr = 0 ;              // clear counters
                ovrflw = 0 ;

                INTCON = 0b10110000 ;           // T0IF, INTF and GIE enabled

                while(ovrflw < 15626) ;         // wait 1 second : 15626 = 16 000 000 / 4 / 256, rounded up

                INTCON.GIE = 0 ;                // stop all interrupts

                Long2Str() ;                    // convert counter to string
                Comm_Write(str) ;               // write string
                Comm_WriteConst(unit) ;         // write unit
                }
        }

//


Don't forget to plug the RS232 jumpers to RB1 and RB2 on your Easypic board !

Set your RS232 terminal configuration to 38400 bauds, 8 bits, no parity, 1 stop, no flow control.

Power up the board : the frequency meter starts counting !

The code is less than 900 bytes, it will fit within the MikroC demo limit.

You can post your questions and comments in the forum

All trademarks and registered trademarks are the property of their respective owners