Making a frequency meter using PIC16F628A in CCS PICC

In electronic signal the frequency is total number of pulses in a unit of time (second). There’s many ways to measure frequency such as measuring the period of input signal or reading the number of pulse per second. Measuring the total number of pulse per second is very effective and much more easier.

In this project I use PIC16f628A as a CPU. In PIC16F628A I use two timer: Timer0 and Timer1.

  • I use timer0 interrupt to make a one-second timer tick to harvest the total input pulses every one second.
  • Timer1 is uses for capturing every rising edge of the input square wave pulses. It’s 16-bit wide. If it’s overflow it will generate interrupt and continuous counting.

This frequency meter could measure up to 50MHz square wave signal that I have tested. If you have a higher frequency generator you can try you own.

Some PIC users implement a ready-to-use CCS PICC example, to create this stuff work. They made the same to this one, but the code implementation is a little bit different from here. I didn’t use the method like them. I made my own code writing.

sch
The frequency meter schematic run at 4MHz internal crystal oscillator and 16×2 LCD Display

Image galleries:

source archive:

https://github.com/BongPeav/CCS-PICC/raw/master/Frequency_Counter_LCD.rar

There’s a mistake of the period of the frequency in this code. When it measures the frequency of 0Hz, the period shows 0 second. However I don’t have any time to modify them again. The code project attached here is opened. The user can modify this code to the correct one’s.

The PCB project I list above was made by hands. For some professional requirements, we can order a professional PCB service such as PCB Way at a reasonable price. I have made this meter for a few local companies that require electronic measurement.

Order this kit with PCBWay:

PCB from PCBWay

CCS PICC code for this example project:

/* THE CPU RUN BY INTERNAL 4MHz CLOCK
*  WE USE TIMER 0 TO MAKE A ONE SECOND DURATION
*  WE USE TIMER 1 TO ACCUMULATE THE TOTAL INPUT PULSE */

#include <16F628A.h>
#fuses INTRC_IO,NOLVP,MCLR
//#fuses WDT 
#use delay(clock=4M)

#define ONE_SEC_TICKS 3907
//3907
#define LCD_ENABLE_PIN  PIN_B2                                    
#define LCD_RS_PIN      PIN_B0                                    
#define LCD_RW_PIN      PIN_B1                                    
#define LCD_DATA4       PIN_B3                                    
#define LCD_DATA5       PIN_B4                                    
#define LCD_DATA6       PIN_B5                                    
#define LCD_DATA7       PIN_B7                                    
#include<LCD.c>

volatile unsigned int16 T1=0;
volatile unsigned int16 T0_INT_CNT=0;
volatile unsigned int16 T1_INT_CNT=0;
volatile unsigned int32  FREQ=0;
volatile float  PRD=0;

void main()
{  
   LCD_INIT();
   //setup_wdt(WDT_2304MS);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1);
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_TIMER0);
   enable_interrupts(INT_TIMER1);
   clear_interrupt(INT_TIMER0);
   clear_interrupt(INT_TIMER1);
   printf(LCD_PUTC," FREQUENCY METER");
   printf(LCD_PUTC,"\n   Please Wait");
   set_timer1(0);
   set_timer0(0);
   while(TRUE){
      //restart_wdt();
   }
}

#INT_TIMER0
void TMR0_ISR(){
   T0_INT_CNT++;
   if(T0_INT_CNT==ONE_SEC_TICKS){
      T0_INT_CNT=0;
      T1=get_timer1();
      FREQ=make32(T1_INT_CNT,T1);   // make 32 bit value of input pulses 
      PRD=(1000000.0/FREQ)*1000;    // nanoseconds   range
      
      ///////////////////////////////////////////////////////
      printf(LCD_PUTC,"\ff : %Lu Hz\n",FREQ);
     
      if(PRD<1000)
         printf(LCD_PUTC,"T : %0.3f nS",PRD);
      else if(PRD>1000&&PRD<1000000){
         PRD/=1000;
         printf(LCD_PUTC,"T : %0.3f uS",PRD);
      }
      else {
         PRD/=1000000;
         printf(LCD_PUTC,"T : %0.3f mS",PRD);
      }
       
     /////////////////////////////////////////////////////////////
     
      
      T1_INT_CNT=0;
     
   }
   clear_interrupt(INT_TIMER0);
}

#INT_TIMER1
void TMR1_ISR(){
   T1_INT_CNT++;
   clear_interrupt(INT_TIMER1);
}


Any suggestion relates to this post could be sent to the author using the contact form below.

A temperature display using PIC16F716 and LM35 with MikroC and Proteus 8

LM35 is an analog temperature sensor. The output temperature data is +10mV per degree Celsius. Typically It come with TO-92 transistor-like package.

TO92
LM35 temperature sensor with TO-92 Package

We commonly use it on board with microcontroller to measure temperature for example for controlling DC fan.

In this project I make temperature display board using LM35 and PIC16F716 along with seven segment display (SSD). There are many popular PIC device with built-in ADC, but I posses only PIC16F716. It has an 8-bit ADC module inside. I use 2.5V voltage reference adjusted from a POT to decrease the ADC step size for 8-bit ADC.

The SSD is multiplexed together due to I/O pins limited.

Schematic:

SSD

The CPU run at the speed 11.0592Mhz that I posses.  The ADC conversion run very fast so it will consume a lot energy and the display will become rough. I use timer0 interrupt to schedule the ADC reading to 1 time per second.

Source from GitHub:

https://github.com/BongPeav/MikroC-8-bit-PIC/raw/master/16F716_LM35_SSD.rar

MikroC source code:

// PIC16F716 LM35 SSD
// CLOCK 11.0592MHz
// FMC 2764800 MIPS
// TMC 361 ns
// TIMER0 RATE 1:256 = 92416 ns = 92.416 us
// TIMER0 INTERRUPT : 92.416 us * 256 = 23658 us = 23.658 ms
// ADC CLOCK = INTERNAL RC
// ADC CHANNEL = RA0
// VREF = RA3 2.6V

unsigned short cnt=0;
unsigned  temperature=0;
unsigned D1=0,D0=0;
bit read;
unsigned char SSD_MAP[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90} ;

unsigned short get_lm35(){
  ADCON0.GO_DONE=1; // start read
  while(ADCON0.GO_DONE);
  return ADRES;
}

void SSD() {

     temperature=100*(ADRES)*2.5/256;
     D1=temperature/10;
     D0=temperature%10;
     PORTB=SSD_MAP[D1];
     PORTA=0x02;
     delay_ms(10);
     PORTA=0x00;
     
     PORTB=SSD_MAP[D0];
     PORTA=0x04;
     delay_ms(10);
     PORTA=0x00;
     
     PORTB=0x9C;
     PORTA=0x10;
     delay_ms(5);
     PORTA=0x00;

     PORTB=0xC6;
     PORTB|=0x80;
     delay_ms(5);
     PORTB&=0x7F;
}

void interrupt(){
     if(INTCON.T0IF) {
        cnt++;
        if(cnt==50)
        { cnt=0;
          get_lm35();
        }
     }
     INTCON.T0IF=0;
}

   
void main() {

     PORTB=0x00; // CLEAR PORTB
     PORTA=0x00; // CLEAR PORTA
     TRISA=0x09; // RA0 AND RA4 AS INPUT
     TRISB=0x00; // PORTB AS OUTPUT
     OPTION_REG.T0CS=0;   // SELECT TIMER0
     OPTION_REG.PSA=0;    // PRESCALER ASSIGNED TO TIMER0
     OPTION_REG&=0x07;    // SELECT 1:256
     INTCON.GIE=1;        // ENABLE GLOBAL INTERRUPT
     INTCON.T0IE=1;       // TIMER0 INTERRUPT ENABLE
     ADCON1=0x05; // RA0 ANALOG RA3 VREF
     ADCON0.ADCS0=1;
     ADCON0.ADCS1=1; // SELECT INTERNAL RC
     ADCON0.CHS0=0;
     ADCON0.CHS1=0;
     ADCON0.CHS2=0;  // SELECT CHANNEL 0
     ADCON0.ADON=1; // ENABLE ADC
     INTCON.T0IF=0;    // CLEAR FLAG
     get_lm35();
     TMR0=0;
     while(1)
     SSD();

}

See also:

Any suggestion relates to this post could be sent to the author using the contact form below.