News:

Let's find out together what makes a PIC Tick!

Main Menu

Low to Mid Frequency Measurement on a PIC12F683 device

Started by top204, Mar 24, 2021, 11:09 AM

Previous topic - Next topic

top204

There was a question on the forum about how to measure low frequencies on a PIC12F683 device, so I created this piece of code to show how straightforward it is to create with the Positron8 compiler.

The program uses a Timer0 interrupt to act as the frequency measuring gate, and Timer1 using an external input to count the frequency pulses within that gate period, so it all operates in the background.

The program also has a simple auto calculator to create a re-loading value for the duration of a Timer overflow interrupt without having to use external PC programs to give a value. It bases the calculation on the Declare Xtal value used in the program and the prescaler used for the timer. It uses the powerful, and very useful, preprocessor for the values and calculation, and the error messages if the value is not suitable. The program has many, clear, comments to show exactly what it is doing, so it should give you a better idea of how to alter it to suit your needs, or alter it for a different PIC type.

The program below measures a frequency from pin GPIO.5 (the T1CKI pin) and transmits it to a serial terminal, and it only uses 255 program words and 19 variables! How's that for the Proton8 compiler's assembler code creation?

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Low to Mid frequency measurement on a PIC12F683 device
' The program uses Timer0 as the count window and Timer1 to count the pulses within that window
' It all operates in the background using a Timer0 interrupt
'
' The pin for the frequency signal's input is GPIO.5 (the T1CKI pin)
'
' Written by Les Johnson for the Positron8 BASIC Compiler
'
    Device = 12F683                             ' Tell the compiler the program is for a PIC12F683 device
    Declare Xtal = 8                            ' Tell the compiler the device is operating at 8MHz
  
    On_Hardware_Interrupt GoTo ISR_Handler      ' Point to the interrupt handler
'
' Setup RSout pin and Baud rate
'
    Declare Serial_Baud = 9600
    Declare RSOut_Pin = GPIO.1
'
' Set the device's config fuses to use the internal oscillator and the OSC pins as I/O pins
'  
    Config INTOSCIO, WDT_OFF, PWRTE_ON, CP_OFF, MCLRE_ON
'
' Create some variables
'    
    Dim tCaptureComplete As Bit                 ' True when a frequency capture has occured (must be cleared in the main program)
    Dim wFrequency    As Word Access            ' Holds the frequency value for the main program
    Dim bWindowCount  As Byte Access            ' Holds the counter used for a capture window within the interrupt

    Dim wTimer1 As TMR1L.Word                   ' Create a 16-bit SFR from SFRs TMR1L and TMR1H

$define False 0
$define True 1
 
'----------------------------------------------------------------------------
' Timer0 defines for a PIC12F683 device
'
$define Timer0_Flag() INTCONbits_T0IF                   ' Timer0 Flag
$define Timer0_FlagClear()  Clear Timer0_Flag()         ' Clear the Timer0 interrupt flag
$define Timer0_IntEnable()  Set INTCONbits_T0IE         ' Enable a Timer0 interrupt
$define Timer0_IntDisable() Clear INTCONbits_T0IE       ' Disable a Timer0 interrupt
'
' Meta-Macros to set the prescaler for Timer0 on a PIC12F683 Device
'
$define Timer0_Prescaler256()'
    OPTION_REGbits_PS2 = 1   '
    OPTION_REGbits_PS1 = 1   '
    OPTION_REGbits_PS0 = 1

$define Timer0_Prescaler128()'
    OPTION_REGbits_PS2 = 1   '
    OPTION_REGbits_PS1 = 1   '
    OPTION_REGbits_PS0 = 0

$define Timer0_Prescaler64() '
    OPTION_REGbits_PS2 = 1   '
    OPTION_REGbits_PS1 = 0   '
    OPTION_REGbits_PS0 = 1

$define Timer0_Prescaler32() '
    OPTION_REGbits_PS2 = 1   '
    OPTION_REGbits_PS1 = 0   '
    OPTION_REGbits_PS0 = 0

$define Timer0_Prescaler16() '
    OPTION_REGbits_PS2 = 0   '
    OPTION_REGbits_PS1 = 1   '
    OPTION_REGbits_PS0 = 1
   
$define Timer0_Prescaler8()  '
    OPTION_REGbits_PS2 = 0   '
    OPTION_REGbits_PS1 = 1   '
    OPTION_REGbits_PS0 = 0

$define Timer0_Prescaler4()  '
    OPTION_REGbits_PS2 = 0   '
    OPTION_REGbits_PS1 = 0   '
    OPTION_REGbits_PS0 = 1
   
$define Timer0_Prescaler2()  '
    OPTION_REGbits_PS2 = 0   '
    OPTION_REGbits_PS1 = 0   '
    OPTION_REGbits_PS0 = 0
'
' Calculate the value to place into the TMR0 register in order to achieve a certain interrupt rate (in us)
'
$define cPrescalerValue 256                         ' The prescaler value used for the timer (must be the same prescaler value as used for the timer's setup)
$define cMicroSeconds 10000                         ' Interrupt rate (in uS)
$define cTweekValue 1                               ' Holds a tweak value for the timer calculation

$define cTimer_Value $eval ((256 + cTweekValue) - ((cMicroSeconds / cPrescalerValue) * (_xtal / 4)))

$if cTimer_Value > 255
    $error "cTimer_Value is too large for the interrupt duration"
$elseif cTimer_Value = 0
    $error "cTimer_Value is too small for the interrupt duration"
$endif

'----------------------------------------------------------------------------
' The main program starts here
' Measure, and display, a low to mid frequency signal on the T1CKI pin (GPIO.5)
'
Main:
    Setup()                                             ' Setup the program and peripherals
'
' Create a loop to display the frequency on a serial terminal
'
    Do                                                  ' Create a loop
        If tCaptureComplete = True Then                 ' Is there a frequency value ready?
            tCaptureComplete = False                    ' Yes. So reset the event flag used within the interrupt
            RsOutLn "Frequency = ", Dec wFrequency, "Hz" ' Display the frequency on a serial terminal
        EndIf

        DelayMS 512                                     ' Create a large delay to show that the frequency is still captured correctly, for the demo
    Loop                                                ' Do it forever
   
'----------------------------------------------------------------------------
' Setup variables and peripherals
' Input     : None
' Output    : None
' Notes     : The PIC12F683 device must be setup for internal oscillator, so the T1CKI pin is exposed for use
'
Proc Setup()
    OSCCON = %01110000                          ' Setup the microcontroller to operate on its internal oscillator running at 8MHz
    bWindowCount = 0                            ' Reset the interrupt, window, counter
    tCaptureComplete = False                    ' Reset the frequency ready flag 
    Timer1_Init()                               ' Setup Timer1
    Timer0_Init()                               ' Setup Timer0
   
    Timer0_IntEnable()                          ' Enable a Timer0 interrupt
    Set INTCONbits_GIE                          ' Enable global interrupts
EndProc

'--------------------------------------------------------------------------
' Setup Timer0
' Input     : None
' Output    : None
' Notes     : None
'
Proc Timer0_Init()
    OPTION_REGbits_T0CS = 0                     ' Set Timer0 for internal instruction cycle clock (FOSC/4)
    OPTION_REGbits_PSA = 0                      ' Prescaler is assigned to the Timer0 module
    Timer0_Prescaler256()                       ' Set Timer0 prescaler to 1:256
    TMR0 = cTimer_Value                         ' Load TMR0 with the value that will trigger the interrupt for a correct time duration
    Timer0_FlagClear()                          ' Clear the Timer0 interrupt flag
EndProc
  
'--------------------------------------------------------------------------
' Setup Timer1
' Input     : None
' Output    : None
' Notes     : None
'
Proc Timer1_Init()
    wTimer1 = 0                                 ' Clear Timer1
    T1CON = %00000111                           ' Set Timer1 for external clock and enable it
EndProc

'---------------------------------------------------------------------------------
' Interrupt handler
' Count pulses on Timer1 using Timer0 for the capture window
' Input     : None
' Output    : wFrequency holds the pulses counted in Timer1
' Notes     : Change the value compared with bWindowCount to alter the time gate window period
'
ISR_Handler:
    Context Save                                    ' Save any compiler system variables and SFRs used
'
' Service a Timer0 interrupt
'
    If Timer0_Flag() = True Then                    ' Was it Timer0 that triggered the interrupt?
        Inc bWindowCount                            ' Yes. So increment the counter to give a longer window period before Timer1 is read
        If bWindowCount > 100 Then                  ' Is it time to take a reading from Timer1?
            wFrequency = wTimer1                    ' Yes. So transfer the value from Timer1 into wFrequency
            wTimer1 = 0                             ' Clear Timer1
            bWindowCount = 0                        ' Reset the window counter
            tCaptureComplete = True                 ' Signal that a pulse rate capture is complete       
        EndIf
        TMR0 = cTimer_Value                         ' Load TMR0 with the value that will trigger the interrupt for a correct time duration
        Timer0_FlagClear()                          ' Clear the Timer0 interrupt flag
    EndIf

    Context Restore                                 ' Restore any compiler system variables and SFRs used, and exit the interrupt

Below is a simulation of the above program running in Isis.Low to Mid Frequency Counter using a PIC12F683.jpg