In these series of measurement projects with PIC microcontroller, we’re gonna discuss several projects like how to measure DC or AC Voltage, DC or AC Current, Resistance, Capacitance, Frequency, Light, Temperature, Humidity, Pressure, Rain, Soil moisture, Water level, Distance etc.

In this project, we are going to learn how to measure frequency of a signal using a PIC microcontroller and display it on an LCD display.

There are different methods that can be used to measure the frequency of an external signal, this is possibly the simplest method that we’re gonna use in this project, the signal whose frequency is to be measured is connected to the clock input of a microcontroller counter (timer). The counter is enabled for a known time window, and the total count is read. Since each count corresponds to a clock pulse of the external signal, we can easily determine its frequency.

For example, assuming that the time window is set to 1 s and the counter reaches 500 at the end of 1s, then the frequency of the signal is 500Hz. Thus, we can simply display the counter value as the frequency of the signal in hertz. This way, using a 16-bit counter, we can measure frequencies up to 65,535 Hz. If using a 32-bit counter, the maximum frequency that we can measure will be 4,294,967,295 Hz. For the measurement of high frequencies, we can actually use a 16-bit counter and increment a variable each time the counter overflows (65535–0). The total count can then be found by multiplying this variable by 65536 and adding the current counter reading.

The nice thing about using a 1 s time window is that we can directly display the frequency in hertz by simply reading the counter value. This also means that the measurement resolution is 1 Hz, which is acceptable for most measurements. Increasing the time window to 10 s will increase the resolution to 0.1 Hz. On the other hand, decreasing the time window to 0.1 s will reduce the resolution to 10 H z. Figure 1 below shows the circuit diagram of our project.

Figure 1: Digital Frequency Counter Circuit Diagram

In this design, the external signal whose frequency is to be measured will be connected to RA4 (Timer0 clock input: T0CKI ), Timer0 is used in a 16-bit counter mode and the frequency will be displayed on the LCD connected to PORTB of the PIC18F45K22 microcontroller.

Timer1 is used to create the 1 s time window. I t is not possible to generate a 1 s delay using Timer1 since the microcontroller clock frequency is high. Instead, the timer is configured to generate an interrupt every 250 ms, and when 4 interrupts are generated, it is assumed that 1 s has elapsed.

Assuming a prescaler setting of 8, the value to be loaded into Timer1 registers to generate interrupts at 250 ms (250,000 μs) intervals can be calculated from the following:

or

Decimal 3036 is equivalent to 0x0BDC in hexadecimals. Thus, TMR1H =0x0B and TMR1L = 0xDC.

MikroElektonika Timer Calculator can also be used to generate the required code as seen on figure 2 below:

Figure 2: Timer Calculator

Nowadays prototyping PCBs are getting cheaper, a very good news for makers, hobbyists and electronic startups with a limited budget. You can get 10 PCBs (1-2 layers) for only \$5 with PCBWay.com , a China Shenzhen-based manufacturer specializing in PCB prototyping, small-volume production and PCB Assembly service all under one roof.

Before sending Gerber files to manufacture your Printed Circuit Boards, it’s always good practice to check them one more time to make sure that everything is correct with a Gerber viewer to avoid bad surprises. These things do happen. Airwires not properly routed, general layout of your components especially connectors, track width etc.

PCBWay Free Online Gerber Viewer

MikroC Source Code

```/***********************************************************************
Digital Frequency Counter with an LCD Display using PIC Microcontroller
www.studentcompanion.co.za
March 2019
MCU: PC18F45K22
Compiler: mikroC Pro for PIC v7.1.0
***********************************************************************/

// LCD module connections
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;
// End LCD module connections

#define PULSE PORTA.RA4

unsigned int Overflow;
unsigned char Cnt;
//
// Timer interrupt service routine. Program jumps here at every 10ms
//
void interrupt (void)
{
if(INTCON.TMR0IF == 1)                                    // If TIMER0 interrupt
{
Overflow++;                                            // Increment Overflow count
INTCON.TMR0IF = 0;                                     // Clear Timer0 interrupt flag
}
if(PIR1.TMR1IF == 1)                                      // If TIMER1 interrupt
{
TMR1H = 0x0B;                                           	// Re-load timer register
TMR1L = 0xDC;
Cnt++;                                                  	// Increment Cnt
PIR1.TMR1IF = 0;                                         // Clear Timer1 interrupt flag
}
}

void main()
{
unsigned char Txt[11];
unsigned long Elapsed;
unsigned char L_Byte, H_Byte;

ANSELA = 0;                                         	// Configure PORTA as digital
ANSELB = 0;
TRISA.RA4 = 1;                                       	// RA4 is input
Lcd_Init();                                          	// Initialize LCD
Lcd_Cmd(_LCD_CURSOR_OFF);                            	//  Disable cursor

//
// Configure TIMER0 for 16-bit mode, no prescaler, clock provided by external
// signal into pin T0CKI. Timer is not started here
//
T0CON = 0x28;                                      	// TIMER0 16-bit,no prescaler,T0CKI clk
//
// Configure Timer1 for 250ms Overflow. Timer1 is not started here
//
T1CON = 0x36;

PIE1 = 0x01;                                       	// Enable TIMER1 interrupts
PIR1.TMR1IF = 0;                                   	// Clear TIMER1 interrupt flag
INTCON = 0xE0;                                     	// Enable TIMER0 and TIMER1 interrupts

for(;;)                                            	// Wait for interrupts
{
TMR0H = 0;                                       	// Clear Timer0 registers
TMR0L = 0;
TMR1H = 0x0B;                                    	// Load Timer1 registers
TMR1L = 0xDC;
//
Overflow = 0;                                    	// Clear Overflow count
Cnt = 0;                                         	// Clear Cnt
//
// Start TIMER0. It will increment each time an external pulse is detected.
// TIMER0 increments on the rising edge of the external clock
//
T0CON.TMR0ON = 1;
//
// Start Timer1 to count 4x250ms = 1 second
//
T1CON.TMR1ON = 1;
while(Cnt != 4);                           		// Wait until 1 second has elapsed
//
// 1 second has elapsed. Stop both timers
//
T0CON.TMR0ON = 0;                             		// Stop TIMER0
T1CON.TMR1ON = 0;                            		// Stop Timer1
// Get TIMER0 count
L_Byte = TMR0L;
H_Byte = TMR0H;
//
// Store TIMER0 count is variable Elapsed
//
Elapsed = (unsigned long)256*H_Byte + L_Byte;
Elapsed = 65536*Overflow + Elapsed;
//
// Convert into string and display
//
LongWordToStr(Elapsed, Txt);              		 // Long to string conversion
Lcd_Cmd(_LCD_CLEAR);                                	 // Clear LCD
Lcd_Out(2,1,Txt);                                	 // Display measured frequency

Delay_ms(1000);                  			 // Wait 1 second and repeat
}
}```

You can download the full project files (MikroC source code and Proteus Schematic design) below here. All the files are zipped, you will need to unzip them (Download a free version of the Winzip utility to unzip files).

MikroC Source Code: Digital Frequency Counter MikroC

Proteus Schematic Project: Digital Frequency Counter Proteus