frequency measurement using PIC

Frequency Measurement using the PIC16F88 PDF Print E-mail


"[The PIC16F88 contains] a rather daunting feature set for someone new to embedded development and Microchip controllers to master. However, a good C compiler can handle the bank switching and other idiosyncrasies of the part while still allowing the user to configure the many peripheral parts and interrupts on the chip. "

Frequency Measurement using the PIC16F88

By
Karl Tubalkain, P.E.


Purpose of the Application Note


This application note examines how to set up the capture compare register and the interrupt routine in a PIC16F88 to measure the period of an input sig

Architecture

Microchip microprocessors use a Harvard architecture, which means that the data and program spaces are separate.  The 16F88 has 14-bit instructions that map into a 4Kx14 flash memory space.  The product selector guide lists the program memory as 7168 bytes (4096 x 14 / 8 = 7168). The main features are listed below:

Program Memory (Bytes)        7168
EEPROM Data Memory Bytes    256
RAM Bytes                368
I/O Pins                16
Analog                7x10-bit
Comparators                2
Timers/WDT                1-16 bit, 2-8 bit, 1-WDT
Serial I/O                AUSART, I2C/SPI
Max Speed                20 MHz
Internal Oscillator            8 MHz
Brown Out Detection
ICD Breakpoints            1
Capture/Compare/PWM        1

Looking at the above list, it is curious that there are 368 RAM bytes.  The reason for this is that there are actually 4 banks of 128 RAM bytes, but these banks of RAM also include Special Function Registers, leaving 368 user registers or bytes of RAM.  The Microchip assembler requires the user to keep track of the current RAM bank.  Other features of the chip include a 13-bit program counter and an 8 level stack.  The chip currently retails for $4.75, qty 1,  at Digi-Key.




Software Example:  The Capture Compare Register

The CCP register can be configured to capture the value of the 16-bit Timer1 register when an event occurs.  An event can be configured to be:

·    Every falling edge
·    Every rising edge
·    Every 4th rising edge
·    Every 16th rising edge

In this example, it is desired to detect a frequency between 1400 Hz and 2500 Hz.  This could be done with a bandpass filter, or the period of the signal could be measured directly using the capture compare register.

/*****************************************************************************
* File:    main.c
* Date:     12/03/2005
* By:        Karl Tubalkain
* dba:    karlanda.c
*
* (C) Copyright karlanda.c
*
* Set RB1 if RB0 > 1400 Hz and RB0 < 2500 Hz
****************************************************************************/
#include <pic.h>
#include “cdef.h”

extern void init(void);

unsigned int capture_time, new_capture_time, old_capture_time;

void main(void)
{
    // initialize I/O
    init();

    while (1)
        ;
}

static void interrupt isr(void)
{
    if (CCP1IF)               // A TMR1 register capture occurred
    {
        old_capture_time = new_capture_time;
        new_capture_time = 256*CCPR1H + CCPR1L;

        // Only compute the capture time if the new capture time is greater
        // than the old capture time. This avoids the problem of TMR1
        // overflow. Capture time units are in micro-seconds.

        if (new_capture_time > old_capture_time)
        {
            capture_time = new_capture_time - old_capture_time;

            if ((capture_time > P2) && (capture_time < P1))
                RB1 = 1;
            else
                RB1 = 0;
        }

        CCP1IF = 0;         // clear interrupt flag
    } 
}

As in most programs, much of the work is done by defining constants.  In this case the internal oscillator frequency is 2 MHz.  The internal clock  is derived from the oscillator by dividing by 4.  The capture mode is set to trigger an interrupt every 16th rising edge, and the Timer1 prescale is set to 8.  In this case,

P1 = (2000000/4)*16/(8*1400) = 714
P2 = (2000000/4)*16/(8*2500) = 400

/****************************************************************************
* File:    cdef.h
* Date:    12/03/2005
* By:         Karl Tubalkain
* dba:     karlanda.c
*
* (C) Copyright karlanda.c
*
****************************************************************************/

#define FOSC    (2000000L)
#define ICLK    (FOSC/4)
#define CAPTURE    16
#define PRESCALE    8

#define F1        1400
#define F2        2500

#define P1        ICLK * CAPTURE / (PRESCALE * F1)
#define P2        ICLK * CAPTURE / (PRESCALE * F2)


Specifying SFRs


I once asked a colleague how many special function registers had to be specified for Microchip processors, and he replied “All of them.”  If you want to be safe, follow his advice.

In this example I have only specified the pertinent registers.  Note that it is good practice to initialize outputs before setting the direction (input or output) of a port to eliminate startup glitches.  I have included information from the data sheet in comments.  Notice that the include file doesn’t specify the PIC16F88 directly.

The Hi-Tech C Compiler is tightly integrated with MPLAB, and picks up the processor type from a user selection when setting up the project.  The file “pic.h” then includes the selected processor special function definitions.

/****************************************************************************
* File:    init.c
* Date:     12/03/2005
* By:          Karl Tubalkain
* dba:    karlanda.c
*
* (C) Copyright karlanda.c
****************************************************************************/
#include <pic.h>

void init(void);

void init(void)
{
/**************************************************************************
* OSCCON REGISTER
*
* bit 7     Unimplemented:
*
* bit 6-4   IRCF<2:0>: Internal RC Oscillator Frequency Select bits
*           000 = 31.25 kHz
*           001 = 125 kHz
*           010 = 250 kHz
*           011 = 500 kHz
*           100 = 1 MHz
*           101 = 2 MHz
*           110 = 4 MHz
*           111 = 8 MHz
*
* bit 3     OSTS: Oscillator Start-up Time-out Status bit
*           1 = Device is running from the primary system clock
*           0 = Device is running from T1OSC or INTRC as a secondary system
*               clock
*
* bit 2     IOFS: INTOSC Frequency Stable bit
*           1 = Frequency is stable
*           0 = Frequency is not stable
*
* bit 1-0   SCS<1:0>: Oscillator Mode Select bits
*           00 = Oscillator mode defined by Fosc<2:0>
*           01 = T1OSC is used for system clock
*           10 = Internal RC is used for system clock
*           11 = Reserved
**************************************************************************/

    // 2 MHz Internal Clock
    IRCF2 = 1;
    IRCF1 = 0;
    IRCF0 = 1;

    // Use Internal RC for system clock
    SCS1 = 1;
    SCS0 = 0;
    /**************************************************************************
* PORT INITIALIZATION
*
* First initialize I/O, then set data direction.
**************************************************************************/

    PORTA = 0B00000000;
    TRISA = 0B00000000;

    PORTB = 0B00000000;
    TRISB = 0B00000001;
    /**************************************************************************
* T1CON: TIMER1 CONTROL REGISTER
*
* bit 7     Unimplemented
*
* bit 6     T1RUN: Timer1 System Clock Status bit
*           1 = System clock is derived from Timer1 oscillator
*           0 = System clock is derived from another source
*
* bit 5-4   T1CKPS<1:0>: Timer1 Input Clock Prescale Select bits
*           11 = 1:8 Prescale value
*           10 = 1:4 Prescale value
*           01 = 1:2 Prescale value
*           00 = 1:1 Prescale value
*
* bit 3     T1OSCEN: Timer1 Oscillator Enable Control bit
*           1 = Oscillator is enabled
*           0 = Oscillator is shut off
*
* bit 2     T1SYNC\: Timer1 External Clock Input Sychronization Control bit
*           TMR1CS = 1:
*           1 = Do not synchronize the external clock input
*           0 = Synchronize external clock input
*           TMR1CS = 0:
*           This bit is ignored.
*
* bit 1     TMR1CS: Timer1 Clock Source Select bit
*           1 = External clock from pin ...
*           0 = Internal clock (Fosc/4)
*
* bit 0     TMR1ON: Timer1 On bit
*           1 = Enables Timer1
*           0 = Stops Timer1
**************************************************************************/

    // 1:8 Prescale value
    T1CKPS1 = 1;
    T1CKPS0 = 1;

    T1OSCEN = 0;

    TMR1CS = 0;
    TMR1ON = 1;     // Enable Timer 1

/**************************************************************************
* CCP1CON: CAPTURE/COMPARE/PWM CONTROL REGISTER
*
* bit 7-6   Unimplemented
*
* bit 5-4   CCP1X:CCP1Y: PWM Least Significant bits
*
* bit 3-0   CCP1M<3:0>: CCP1 Mode Select bits
*           0000 = Capture/Compare/PWM disabled
*           0100 = Capture mode, every falling edge
*           0101 = Capture mode, every rising edge
*           0110 = Capture mode, every 4th rising edge
*           0111 = Capture mode, every 16th rising edge
*           1000 = Compare mode, set output on match (CCP1IF bit is set)
*           1001 = Compare mode, clear output on match (CCP1IF bit is set)
*           1010 = Compare mode, generate software interrupt ...
*           1011 = Compare mode, trigger special event ...
*           11xx = PWM mode
**************************************************************************/

    CCP1CON = 0B00000111;   // Capture every 16th rising edge
    /**************************************************************************
* ANSEL REGISTER PIC16F88 DEVICES ONLY
*
* bit 7     Unimplemented
*
* bit 6-0   ANS<6:0>: Analog Input Select bits
*           1 = Analog I/O
*           0 = Digital I/O
**************************************************************************/

    ANSEL = 0B00000000;

/**************************************************************************
* INTCON: INTERRUPT CONTROL REGISTER
*
* bit 7     GIE: Global Interrupt Enable bit
*           1 = Enables all unmasked interrupts
*           0 = Disables all interrupts
*
* bit 6     PEIE: Peripheral Enable Interrupt Enable bit
*           1 = Enables all unmasked peripheral interrupts
*           0 = Disables all peripheral interrupts
*
* bit 5     TMR0IE: TMR0 Overflow Interrupt Enable bit
*           1 = Enables the TMR0 interrupt
*           0 = Disables the TMR0 interrupt
*
* bit 4     INTE: RB0/INT External Interrupt Enable bit
*           1 = Enables the RB0/INT external interrupt
*           0 = Disables the RB0/INT external interrupt
*
* bit 3     RBIE: RB Port Change Interrupt Enable bit
*           1 = Enables the RB port change interrrupt
*           0 = Disables the RB port change interrrupt
*
* bit 2     TMR0IF: TMR0 Overflow Interrupt Flag bit
*           1 = TMR0 register has overflowed (must be cleared in software)
*           0 = TMR0 register did not overfow
*
* bit 1     INTF: RB0/INT External Interrupt Flag bit
*           1 = The RB0/INT external interrupt occurred (must be cleared in
*               software)
*           0 = The RB0/INT external interrupt did not occur
*
* bit 0     RBIF: RB Port Change Interrupt Flag bit
*           A mismatch condition will continue to set flag bit RBIF. Reading
*           PORTB will end the mismatch condition and allow the flag bit
*           RBIF to be cleared.
*           1 = At least one of the RB7:RB4 pins changed state (must be
*               cleared in software)
*           0 = None of the RB7:RB4 pins have changed state.
**************************************************************************/

    TMR0IE = 0;     // Disable interrupt on TMR0 overflow
    PEIE = 1;       // Enable peripheral interrupts
    INT0IE = 0;     // Disable the RB0/INT external interrupt. Note the
                    // difference between the databook and the Hi-Tech
                    // compiler.
    GIE = 1;        // Global interrupt enable
    /**************************************************************************
* PIE1: PERIPHERAL INTERRUPT ENABLE REGISTER 1
*
* bit 7     Unimplemented:
*
* bit 6     ADIE: A/D Converter Interrupt Enable bit
*           1 = Enabled
*           0 = Disabled
*
* bit 5     RCIE: USART Receive Interrupt Enable bit
*           1 = Enabled
*           0 = Disabled
*
* bit 4     TXIE: USART Transmit Interrupt Enable bit
*           1 = Enabled
*           0 = Disabled
*
* bit 3     SSPIE: Synchronous Serial Port (SSP) Interrupt Enable bit
*           1 = Enabled
*           0 = Disabled
*
* bit 2     CCP1IE: CCP1 Interrupt Enable bit
*           1 = Enabled
*           0 = Disabled
*
* bit 1     TMR2IE: TMR2 to PR2 Match Interrupt Enable bit
*           1 = Enabled
*           0 = Disabled
*
* bit 0     TMR1IE: TMR1 Overflow Interrupt Enable bit
*           1 = Enabled
*           0 = Disabled
**************************************************************************/

    CCP1IE = 1;     // Enable capture/compare interrupt

/**************************************************************************
* PIR1: PERIPHERAL INTERRUPT STATUS REGISTER 1
*
* bit 7     Unimplemented:
*
* bit 6     ADIF: A/D Converter Interrupt Flag bit
*           1 = A/D conversion completed (must be cleared in software)
*           0 = A/D conversion is not complete
*
* bit 5     RCIF: USART Receive Interrupt Flag bit
*           1 = The USART receive buffer is full (cleared by reading RCREG)
*           0 = The USART receive buffer is not full
*
* bit 4     TXIF: USART Transmit Interrupt Flag bit
*           1 = The USART transmit buffer is empty (cleared by writing to
*               TXREG)
*           0 = The USART transmit buffer is not empty
*
* bit 3     SSPIF: Synchronous Serial Port (SSP) Interrupt Flag bit
*           1 = The transmission/reception is complete (must be cleared in
*               software)
*           0 = Waiting to transmit/receive
*
* bit 2     CCP1IF: CCP1 Interrupt Flag bit
*           Capture Mode
*           1 = A TMR1 register capture occurred (must be cleared in
*               software)
*           0 = No TMR1 register capture occurred
*           Compare Mode
*           1 = A TMR1 register compare match occurred (must be cleared in
*               software)
*           0 = No TMR1 register ccompare match occurred
*
* bit 1     TMR2IF: TMR2 to PR2 Match Interrupt Flag bit
*           1 = A TMR2 to PR2 match occurred (must be cleared in software)
*           0 = No TMR2 to PR2 match occurred
*
* bit 0     TMR1IF: TMR1 Overflow Interrupt Flag bit
*           1 = The TMR1 register overflowed (must be cleared in software)
*           0 = The TMR1 register did not overflow
**************************************************************************/

    CCP1IF = 0;     // Clear CCP1IF interrupt flag

}


Memory Map


Memory Usage Map:
Program ROM   $0000 - $004C  $004D (    77) words
Program ROM   $07DA - $07FF  $0026 (    38) words                            
                                                 $0073 (   115) words total

Program ROM
Bank 0 RAM    $0020 - $0027  $0008 (     8) bytes
Bank 0 RAM    $0070 - $0070  $0001 (     1) bytes                           
                                              $0009 (     9) bytes total Bank 0 RAM

Program statistics:

Total ROM used      115 words (2.8%)
Total RAM used        9 bytes (2.4%)
Loaded C:\Consulting\Articles\Capture\CCP.cof.BUILD SUCCEEDED: Sun Dec 04 10:25:37 2005

Conclusion

This example demonstrates the advantage of using C versus Assembly Language.  As shown in the memory map, the entire program only uses 9 bytes of RAM and 115 words of ROM.  The program is easily portable among different PIC microcontrollers.  In fact, because the software is written in C, it is possible to port it to other compilers or microcontrollers.  Assembly language code is non-portable and tightly coupled to the microcontroller architecture.

Author Notes

Karl Tubalkain is a software consultant for karlanda.c.   He lives in southern New Hampshire and specializes in embedded software design.  He welcomes your comments, and may be reached by email at karlanda@verizon.net

This article is the joint property of Taylor River Real Time, LLC and karlanda.c.   Re-distribution (via printed, web, email, or any other medium) may be done only by karlanda.c. or Taylor River Real Time, LLC. This source code, however, may be used in any project (commercial or otherwise), as long as the copyright and contact information are left in the source code, without modification. By using this source code, the User agrees to waive any and all claims of any nature, it may hold against either karlanda.c or Taylor River Real Time, LLC. The User further agrees to hold harmless and indemnify both karlanda.c and Taylor River Real Time, LLC from any and all claims by third parties relative to said use.

Links

1.    http://www.taylorriver.com
2.    http://www.digikey.com/
3.    http://www.microchip.com/
4.    http://www.htsoft.com/

Post a Comment

Copyright © Rough Record. Designed by OddThemes