0
\$\begingroup\$

I'm implementing timer routines using timer0. The goal is to have a 1ms timer routine so I can use it for general proposes. The problem is that I'm getting a 13kHz waveform in RA6 and I intended to have a 1kHz waveform. Compiler is C18.

Relevant part of the circuit is this: Circuit

Here is the code:

testTimer.c

#include "processor.h"
#include "defs.h"

#include "clock.h"
#include "timer.h"

// PIC18F26J50 Configuration Bit Settings
// 'C' source line config statements
#include <p18F26J50.h>

// CONFIG1L
#pragma config WDTEN = OFF // Watchdog Timer (Disabled - Controlled by SWDTEN bit)
#pragma config PLLDIV = 2 // PLL Prescaler Selection bits (Divide by 2 (8 MHz oscillator input))
#pragma config STVREN = OFF // Stack Overflow/Underflow Reset (Disabled)
#pragma config XINST = OFF // Extended Instruction Set (Disabled)

// CONFIG1H
#pragma config CPUDIV = OSC1 // CPU System Clock Postscaler (No CPU system clock divide)
#pragma config CP0 = OFF // Code Protect (Program memory is not code-protected)

// CONFIG2L
#pragma config OSC = INTOSCPLL // Oscillator (INTOSCPLL)
#pragma config T1DIG = OFF // T1OSCEN Enforcement (Secondary Oscillator clock source may not be selected)
#pragma config LPT1OSC = OFF // Low-Power Timer1 Oscillator (High-power operation)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor (Disabled)
#pragma config IESO = OFF // Internal External Oscillator Switch Over Mode (Disabled)

// CONFIG2H
#pragma config WDTPS = 32768 // Watchdog Postscaler (1:32768)

// CONFIG3L
#pragma config DSWDTOSC = INTOSCREF// DSWDT Clock Select (DSWDT uses INTRC)
#pragma config RTCOSC = INTOSCREF// RTCC Clock Select (RTCC uses INTRC)
#pragma config DSBOREN = OFF // Deep Sleep BOR (Disabled)
#pragma config DSWDTEN = OFF // Deep Sleep Watchdog Timer (Disabled)
#pragma config DSWDTPS = G2 // Deep Sleep Watchdog Postscaler (1:2,147,483,648 (25.7 days))

// CONFIG3H
#pragma config IOL1WAY = OFF // IOLOCK One-Way Set Enable bit (The IOLOCK bit (PPSCON<0>) can be set and cleared as needed)
#pragma config MSSP7B_EN = MSK7 // MSSP address masking (7 Bit address masking mode)

// CONFIG4L
#pragma config WPFP = PAGE_63 // Write/Erase Protect Page Start/End Location (Write Protect Program Flash Page 63)
#pragma config WPEND = PAGE_WPFP// Write/Erase Protect Region Select (valid when WPDIS = 0) (Page WPFP<5:0> through Configuration Words erase/write protected)
#pragma config WPCFG = OFF // Write/Erase Protect Configuration Region (Configuration Words page not erase/write-protected)

// CONFIG4H
#pragma config WPDIS = OFF // Write Protect Disable bit (WPFP<5:0>/WPEND region ignored)

// running this program will make
// a 1MHz signal avaliable
// on RA6 to be checked with a
// oscilloscope

VOID main( VOID )
{
    clockInit();
    timerInit();

    // set RA6 as digital output
    TRISAbits.TRISA6 = DIGITAL_OUTPUT;

    // turn led off
    LATAbits.LATA6 = 0;

    while(1);
}

timer.h

#ifndef TIMER_H
#define TIMER_H

#ifdef __cplusplus
extern "C" {
#endif

VOID timerInit( VOID );
VOID timerReloadRegister( VOID );

#ifdef __cplusplus
}
#endif

#endif /* TIMER1US_H */

timer.c

#include "defs.h"
#include "processor.h"
#include "interrupts.h"
#include <p18cxxx.h>

PRIVATE UINT16 lastTimerRegisterValue_ = 0;

/*----------------------------------------------------------------------------*/
PRIVATE VOID writeTMR0Register_( const UINT16 regValue )
{
    UINT16_VAL value;
    value.Val = regValue;

    TMR0H = value.byte.HB;
    TMR0L = value.byte.LB;

    lastTimerRegisterValue_ = regValue;
}

/*----------------------------------------------------------------------------*/
VOID timerReloadRegister( VOID )
{
    writeTMR0Register_( lastTimerRegisterValue_ );
}


/*----------------------------------------------------------------------------*/
VOID timerInit( VOID )
{
    UINT16 timerRegValue;
    UINT32 clockTimerFreq = 0;
    UINT32 nTicks = 0;

    const UINT32 clockSystemFreq = CLOCK; // CLOCK = 48000000
    const UINT16 prescalerValue = 256;
    const UINT16 resolution = 65536;
    const UINT16 timerPeriodInMs = 1;

    interruptsDisableGlobal();

    //! configure the timer 0 for 16 bit mode and prescaler of 256
    T0CONbits.TMR0ON = 0; //! turn off
    T0CONbits.T08BIT = 0; //! select the 16 bit mode
    T0CONbits.T0CS = 0; //! internal clock
    T0CONbits.T0SE = 0; //! src edge select bit (0->1)
    T0CONbits.T0PS = 0b111; //! 1/256 prescaler
    T0CONbits.PSA = 0; //! prescaler is assigned

    clockTimerFreq = (UINT32)(clockSystemFreq / ( 4 * prescalerValue ));
    nTicks = (UINT32)((timerPeriodInMs * clockTimerFreq) / 1000);
    timerRegValue = (resolution - nTicks + 1);

    writeTMR0Register_( timerRegValue );

    INTCONbits.TMR0IF = 0; //! clear the interrupt flag
    INTCONbits.TMR0IE = 1; //! enable timer0 interrupt

    T0CONbits.TMR0ON = 1; //! turn on

    interruptsEnableGlobal();
}

interrupts.h

#ifndef INTERRUPTS_H
#define INTERRUPTS_H

#ifdef __cplusplus
extern "C" {
#endif

VOID interruptsDisableGlobal( VOID );
VOID interruptsEnableGlobal( VOID );

VOID interruptsEnablePeripheral( VOID );
VOID interruptsDisablePeripheral( VOID );

#ifdef __cplusplus
}
#endif

#endif /* INTERRUPTS_H */

interrupts.c

#include <usart.h>
#include "defs.h"
#include "processor.h"
#include "interrupts.h"
#include "timer.h"


// prototype needed
VOID high_isr (VOID);

/*
 * For PIC18 devices the high interrupt vector is found at
 * 00000008h. The following code will branch to the
 * high_isr function to handle interrupts that occur at
 * the high vector.
 */
#pragma code high_vector=0x08
VOID interrupt_at_high_vector( VOID )
{
  _asm GOTO high_isr _endasm
}

#pragma code /* return to the default code section */

#pragma interrupt high_isr
VOID high_isr (VOID)
{
    // USART1 interrupt handling code
    if( PIR1bits.RC1IF )
    {
        /* Clear the RX interrupt flag for USART1*/
        PIR1bits.RC1IF = 0;
    }

    // timer 0 interrupt handling code
    if( INTCONbits.TMR0IF )
    {
        // actual interrupt handling
        if( CLEAR == PORTAbits.RA6 )
        {
            LATAbits.LATA6 = SET;
        }
        else if( SET == PORTAbits.RA6 )
        {
            LATAbits.LATA6 = CLEAR;
        }

        // reloads last timer register value
        timerReloadRegister();

        // re-enable interrupt
        INTCONbits.TMR0IF = 1;
    }
}

VOID interruptsDisableGlobal( VOID )
{
    // disables all interrupts
    INTCONbits.GIEH = 0;
}

VOID interruptsEnableGlobal( VOID )
{
    // enables all interrupts
    INTCONbits.GIEH = 1;
}

VOID interruptsEnablePeripheral( VOID )
{
    // enables peripheral interrupts
    INTCONbits.PEIE = 1;
}

VOID interruptsDisablePeripheral( VOID )
{
    // enables peripheral interrupts
    INTCONbits.PEIE = 0;
} 

Can anyone please, help me figure out what I'm doing wrong?

\$\endgroup\$
4
  • \$\begingroup\$ Please explain what is wrong, you've explained what you want, and posted a wall of code. What isn't working? \$\endgroup\$
    – Voltage Spike
    Commented Oct 8, 2019 at 21:21
  • \$\begingroup\$ I intended to have a 1kHz waveform on RA6 but I got a 13kHz with the scope. Likely I'm setting up the timer wrong but I can't find the error. \$\endgroup\$ Commented Oct 8, 2019 at 21:29
  • \$\begingroup\$ Not familiar with C18 but in out compiler, the math function provides a result with resolution related to the operands. You appear to be re-casting the result to UINT32 after this result is obtained. As a quick check, try declaring all of your variables as UINT32 so all of the integer math is done in this resolution. \$\endgroup\$ Commented Oct 8, 2019 at 21:47
  • \$\begingroup\$ I fixed things the way @JohnBirckhead told and also fixed a obvious bug in clearing the interrupt bit. Now I'm getting a 512Hz waveform. \$\endgroup\$ Commented Oct 8, 2019 at 23:39

1 Answer 1

1
\$\begingroup\$

I figured that I made a number of mistakes:

  1. I was supposed to expect a 500Hz waveform for a 1ms timer because the pin will toogle every edge (raising and falling);
  2. There is an obvious bug in the interrupt routine that clears the interrupt flag: the TMR0IF bit should be set to 0, not to 1
  3. The calculations for the timer register are here for reference. This code is correct for me:
VOID timerInit( VOID )
{
    UINT16 timerRegValue;
    UINT32 timerClock = 0;
    UINT32 nTicks = 0;

    const UINT32 prescalerValue = 256;
    const UINT32 resolution = 65536;
    const UINT32 timerPeriodInMs = 1;

    interruptsDisableGlobal();

    //! configure the timer 0 for 16 bit mode and prescaler of 256
    T0CONbits.TMR0ON    = 0;                //! turn off
    T0CONbits.T08BIT    = 0;                //! select the 16 bit mode
    T0CONbits.T0CS      = 0;                //! internal clock 
    T0CONbits.T0SE      = 0;                //! src edge select bit (0->1)     
    T0CONbits.T0PS      = 0b111;            //! 1/256 prescaler
    T0CONbits.PSA       = 0;                //! prescaler is assigned   

    timerClock = (CLOCK / ( 4 * prescalerValue ));
    nTicks = ((timerPeriodInMs * timerClock) / 1000);
    timerRegValue = (UINT16)(resolution - nTicks);

    writeTMR0Register_( timerRegValue );

    INTCONbits.TMR0IF   = 0;            //! clear the interrupt flag
    INTCONbits.TMR0IE   = 1;            //! enable timer0 interrupt        

    T0CONbits.TMR0ON    = 1;            //! turn on

    interruptsEnableGlobal();    
}

Now, I'm a getting a shiny 501Hz waveform in the scope! Thank you all for the tips.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.