Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

delay using interrupts & timer

Status
Not open for further replies.

drkidd22

Member
I'm trying to get a grasp of creating delays with interrupt and timers.
Below is an example of what I have so far, basically in the while loop I'm trying to replace the built in __delay_ms[x] function with interrupts, but have not been successful in understanding how to implement that. If I moved the if...else from the while loop into the T4 ISR then it works fine. It will also work if I did:

Code:
     while(1)
     {
         if(ton >= 4)
          {
            LATAbits.LATA1 = ~LATAbits.LATA1; //Toggle LED [this works]
            ton = 0;
           }
     }


Code:
/*
 * File:   main.c
 * Author: DRkidd22
 * Created on August 16, 2019, 12:17 AM
 */

#include <xc.h>
#include "device_config.h"


volatile unsigned int tick_count = 0x00;
volatile unsigned int ton = 0;

void interrupt T4Interrupt (void)
{
    if(PIR4bits.TMR4IF)  //if timer flag is set
    {           
                PIR4bits.TMR4IF = 0;        //Clear the interrupt flag
                ++tick_count;               //[25ms increments]

        if(tick_count >= 4)
        {
        
        ton++; //[100ms increments]
        tick_count = 0;

        }
                
    }
}

void main() {
    
    //INTERRUPT CONTROL REGISTER
    INTCONbits.GIE = 1;     //Enable Global Interrupts
    INTCONbits.PEIE = 1;    //Enable peripheral interrupts
    
    //PERIPHERAL INTERRUPT REQUEST REGISTER 4
    PIR4bits.TMR4IF = 0;    //Clear IF flag before enabling the interrupt.
    
    //PERIPHERAL INTERRUPT ENABLE REGISTER 4
    PIE4bits.TMR4IE = 1;    //Enable timer4 interrupts
    
    //Configure the timer
    //CLOCK SELECTION REGISTER
    T4CLKCONbits.CS = 0x05;      //T4CS MFINTOSC 31.25khz
    
    //TIMER4 HARDWARE LIMIT CONTROL REGISTER
    T4HLTbits.MODE = 0x00;      //T4MODE Software control
    T4HLTbits.CKSYNC = 0x00;    //T4CKSYNC Not Synchronized
    T4HLTbits.CKPOL = 0x00;     //T4CKPOL Rising Edge;
    T4HLTbits.PSYNC = 0x00;     //T4PSYNC Not Synchronized

   //T4RST EXTERNAL RESET SIGNAL SELECTION REGISTER
    T4RSTbits.RSEL = 0x00;      //T4RSEL selected by T4CKIPPS pin

    T4PR = 0x01;                //Timer4 Module Period Register [25ms period]
    T4TMR = 0x00;               //Holding Register for the 8-bit TMR4 Register

    //TIMER 4 CONTROL REGISTER
    T4CONbits.CKPS = 0x06;      //T4CKPS 1:64, Prescale Select bits
    T4CONbits.OUTPS =  0x0B;    //T4OUTPS 1:12 Postscale
    T4CONbits.ON =  0x01;       //TIMER On 
    
    
    SYSTEM_Init();
    
    //RA0 is the output LED D2
    //RA5 is the input button S2 (pulled hi)
    
    //Configure pin direction
    TRISAbits.TRISA0 = 0;    //Set RA0 as output
    TRISAbits.TRISA1 = 0;    //Set RA1 as output
    TRISAbits.TRISA2 = 0;    //Set RA2 as output
    TRISAbits.TRISA3 = 0;    //Set RA3 as output
    
    TRISAbits.TRISA5 = 1;    //Set RA5 as input
    
    //Configure Analog pins to Digital
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    
    LATA = 0;
      
while(1)
    {
       if(ton >= 4)

                {

                    LATAbits.LATA1 = 1;

                     ton = 0; //reset counter
                }

        else
              {

            LATAbits.LATA1 = 0;

                }
    }
}

//64/31.25kHz = 0.002048
//2.048 * 34 = .069632
//.069632 * 12 = .835584
//31.25kHz / 64 = 488
//488Hz / 12 = 41
//1/41 = ~25ms period
 
Might help if you told us what chip this is. TMR4 would be the 5th timer in some chip (0-4). While I never used a timer up that high (high end chip?), one would need to know the chip to research the timer itself.
For example, the PIC18F46K22 chip has the TMR4IF flag in PIR5....
 
Last edited:
A lot of times it makes sense to create a periodic timer. e.g 10 mS. Say you needed 200 mS and 300 mS timers. They are both multiples of 10 ms. The ISR essentially counts the number of 10 mS intervals and sets another global variable that the main code needs to act on,

You may or may not need to use semaphores.
 
ok i understand the logic and what needs to be done, but im having difficulty when trying to program it.
With the code at the bottom I can blink 2 LEDs at different rates using the timer and counter variables and it's accurate.
One of the LEDs is blinked directly from the ISR and the second from within the while loop.

But if I replaced my code in the while loop with:
Code:
        if (ton >= 3)
        {
        LATAbits.LATA2 = 1;
        ton = 0;
        }
        
        else
        {
            LATAbits.LATA2 = 0;
        }
I was expecting the same result if I added the above code to the while loop.

Essentially trying to have something in the form of:

LATAbits.LATA2 = 1;
__delay_ms(50);
LATAbits.LATA2 = 0;
__delay_ms(50);

but with timer and interrupts.

The below works ok.
Code:
#include <xc.h>
#include "device_config.h"

volatile unsigned int tick_count = 0;
volatile unsigned int ton = 0;

void interrupt T4Interrupt (void)
{
    if(PIR4bits.TMR4IF)  //if timer flag is set
    {           
                PIR4bits.TMR4IF = 0;        //Clear the interrupt flag
                ++tick_count;               //[25ms increments]             
    }
    
    if (tick_count >= 5)
    {
        LATAbits.LATA1 = ~LATAbits.LATA1;
        ton++;              //increment ton every 125ms
        tick_count = 0;     //reset tick_count
        
    }
    PIR4bits.TMR4IF = 0;
    
}

void main() {
    
    //INTERRUPT CONTROL REGISTER
    INTCONbits.GIE = 1;     //Enable Global Interrupts
    INTCONbits.PEIE = 1;    //Enable peripheral interrupts
    
    //PERIPHERAL INTERRUPT REQUEST REGISTER 4
    PIR4bits.TMR4IF = 0;    //Clear IF flag before enabling the interrupt.
    
    //PERIPHERAL INTERRUPT ENABLE REGISTER 4
    PIE4bits.TMR4IE = 1;    //Enable timer4 interrupts
    
    //Configure the timer
    //CLOCK SELECTION REGISTER
    T4CLKCONbits.CS = 0x05;      //T4CS MFINTOSC 31.25khz
    
    //TIMER4 HARDWARE LIMIT CONTROL REGISTER
    T4HLTbits.MODE = 0x00;      //T4MODE Software control
    T4HLTbits.CKSYNC = 0x00;    //T4CKSYNC Not Synchronized
    T4HLTbits.CKPOL = 0x00;     //T4CKPOL Rising Edge;
    T4HLTbits.PSYNC = 0x00;     //T4PSYNC Not Synchronized

   //T4RST EXTERNAL RESET SIGNAL SELECTION REGISTER
    T4RSTbits.RSEL = 0x00;      //T4RSEL selected by T4CKIPPS pin

    T4PR = 0x00;                //Timer4 Module Period Register [25ms period]
    T4TMR = 0x00;               //Holding Register for the 8-bit TMR4 Register

    //TIMER 4 CONTROL REGISTER
    T4CONbits.CKPS = 0x06;      //T4CKPS 1:64, Prescale Select bits
    T4CONbits.OUTPS =  0x0B;    //T4OUTPS 1:12 Postscale
    T4CONbits.ON =  0x01;       //TIMER On 
    
    
    SYSTEM_Init();
    
    //RA0 is the output LED D2
    //RA5 is the input button S2 (pulled hi)
    
    //Configure pin direction
    TRISAbits.TRISA0 = 0;    //Set RA0 as output
    TRISAbits.TRISA1 = 0;    //Set RA1 as output
    TRISAbits.TRISA2 = 0;    //Set RA2 as output
    TRISAbits.TRISA3 = 0;    //Set RA3 as output
    
    TRISAbits.TRISA5 = 1;    //Set RA5 as input
    
    //Configure Analog pins to Digital
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    
    LATA = 0;
      
    while(1)
    {
        if (ton >= 3)
        {
        LATAbits.LATA2 = ~LATAbits.LATA2;
        ton = 0;
        }
    }

}

//64/31.25kHz = 0.002048
//2.048 * 34 = .069632
//.069632 * 12 = .835584
//31.25kHz / 64 = 488
//488Hz / 12 = 41
//1/41 = ~25ms period
 
I normally set up a high frequency interrupt then use one or more stages of [software] counters like your tick_count to scale it to the lowest frequency needed, possibly RTC levels of seconds - hours or more.

Any stuff that needs checking at regular intervals like serial comms, goes in the appropriate stage of the division sequence.

If you need any "floating" counters for use elsewhere, test each for zero and decrement if greater.


Then for triggering lower speed external events, set a bit when the appropriate counter overflows, which can be seen by the main program. It can be cleared by the async main routine after being acted on.


For your test code, try having tick_count reset to zero if >= 9 and also within the interrupt, separately test it for >= 5 to set the LED output, else clear it.
 
I tend to setup a 1mS interrupt to do all timing. In the interrupt I increment a 16 bit (unsigned) variable (ticker) and then use,
Code:
    if((ticker-oldticker)>5){
        oldticker=ticker;
       //this will be executed every 5mS
    }
Mike.
Edit, you can, of course, have as many oldticker variables so you can have many different time periods.
Edit2, if you need any delays > 65 seconds then use 32 bit variables.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top