• AVR Freaks

AnsweredHot!Delay with interrupts

Author
jvalerio
New Member
  • Total Posts : 27
  • Reward points : 0
  • Joined: 2015/01/07 13:10:37
  • Location: 0
  • Status: offline
2019/11/07 09:35:08 (permalink)
0

Delay with interrupts

I have a program that varies PWM duty cycle from 90 to 50 percent at different delay intervals. I've tried creating a delay with a timer, but the desired output only works if I call the function, that varies the PWM, within the timer ISR. If I call the function outside the ISR, ie: main loop by button press, the output is unstable and the delay is not consistent with what I'm expecting although the PWM does change accordingly.
 
If I comment out everything in the while loop and uncomment SvcMgrPWMOpen(); from the Timer 3 ISR then it works ok.
I want to be able to trigger SvcMgrPWMOpen(); only once when the button is pressed and to be always accurate.
Any advice will be appreciated. 
 
include <xc.h>
#include "targetSetup.h"

int main(void)
{
    targetInit();
     
    while (1) // Run Forever...
    {
        if (PORTBbits.RB12 == 0) //Push button
        {
        
        LATCbits.LATC7 = 0; //LED On
        SvcMgrPWMOpen(); //PWM Out On
        }
        else
         LATCbits.LATC7 = 1; //LED Off
            
    }
    return 0;
}

void _ISR_PSV _T5Interrupt()
{
    IFS1bits.T5IF = 0; //Clear Timer 5 Interrupt Flag
}

void _ISR_PSV _T3Interrupt()
{
    //SvcMgrPWMOpen();
     IFS0bits.T3IF = 0; //Clear Timer 3 Interrupt Flag
}

 
void SvcMgrPWMOpen(void) // msec
{
            OC1_dutyCntrl(90); //PWM 90%
            msDelay(10); //10ms Delay from TMR3
            
            OC1_dutyCntrl(50); //PWM 50%
            msDelay(20); //20ms Delay from TMR3
            
            OC1_dutyCntrl(0); //PWM 0%
            msDelay(5); //5ms Delay from TMR3
               
            OC1_dutyCntrl(90); //PWM 90%
            msDelay(15); //15ms Delay from TMR3
            
            OC1_dutyCntrl(0); //PWM 0%
            msDelay(10); //Pre-Laser Delay
            
}

 
void msDelay(unsigned int mdelay)
{
    TMR3 = 0; //Clear timer 5 register
    PR3 = mdelay * 235; //Number of ticks per mili-second
    IFS0bits.T3IF = 0; //Reset interrupt flag
    T3CONbits.TON = 1; //Dissable timer 3
    while (!IFS0bits.T3IF); //Wait here for timeout
    T1CONbits.TON = 0; //Turn Timer Off
}

post edited by jvalerio - 2019/11/07 09:37:15
#1
crosland
Super Member
  • Total Posts : 1696
  • Reward points : 0
  • Joined: 2005/05/10 10:55:05
  • Location: Warks, UK
  • Status: offline
Re: Delay with interrupts 2019/11/07 10:18:17 (permalink)
0
You haven't shown all of your code. Do you enable T3 interrupts?
 
If so, calling SvcMgrPWMOpen(), that uses T3 to create a delay, from the ISR that handles T3 interrupts, looks wrong. The first time msDelay is called, T3 is turned off: no more interrupts.
 
If not, how do you think the T3 ISR ever runs? How do you think SvcMgrPWMOpen() is called?
 
You need to seriously rethink the code.
 
NEVER call delay routines from an ISR.
 
Set up a 1ms timer (or perhaps shorter) and use it to create a timebase counter. Use the counter value in your main() loop to do the timing, calling SvcMgrPWMOpen() from main() only. Remember that any variables shared between main() and the ISR must be volatile.
 
Oh, and fix the "Dissable timer 3" comment.
#2
jvalerio
New Member
  • Total Posts : 27
  • Reward points : 0
  • Joined: 2015/01/07 13:10:37
  • Location: 0
  • Status: offline
Re: Delay with interrupts 2019/11/07 10:29:12 (permalink)
0
void _ISR_PSV _T3Interrupt()
{
    //SvcMgrPWMOpen();
     IFS0bits.T3IF = 0; //Clear Timer 3 Interrupt Flag
}

 
Is the only interrupt handler I have and I do T3 Interrupt flag, below is T3 settings.
 
void timer3init (void)
{
    T3CONbits.TON = 0; //Stops 16-bit Timer
    
    T3CONbits.TCS = 0; //Select Internal clock (FP)
    T3CONbits.TCKPS = 0b11; //Select 1:256 Prescaler
    T3CONbits.TGATE = 0; //Disable gated timer
    T3CONbits.TSIDL = 0; //Discontinues module operation when device enters Idle mode
     
    TMR3 = 0x00; //Clear timer 3 register
    PR3 = 0; //Set period of timer
    
    IPC2bits.T3IP = 0x01; //Set Timer 3 Interrupt Priority Level
    IFS0bits.T3IF = 0; //Clear Timer 3 Interrupt Flag
    IEC0bits.T3IE = 1; //Enable Timer 3 interrupt
    
    T3CONbits.TON = 1; //Enable timer 3
}

 
234 into PR3 sets T3 for 1ms, then I was multiplying this to generate a longer delay from the delay function, maybe thats whe're I got it all wrong and trying to figure it out.
#3
Aussie Susan
Super Member
  • Total Posts : 3638
  • Reward points : 0
  • Joined: 2008/08/18 22:20:40
  • Location: Melbourne, Australia
  • Status: offline
Re: Delay with interrupts 2019/11/10 19:10:51 (permalink) ☄ Helpfulby jvalerio 2019/11/12 07:19:24
5 (1)
Looking at this code you seem to be mixing polling and using an interrupt for the T3 timer - use one or the other. Trying to use both will cause all sorts of problems.
If you don't need to do anything else with the MCU while it is waiting for the timer to expire, then how you have written the 'msDelay' function in your first post is fine: just do not set the IE bit and delete the ISR.
If you do want to do other things then use the interrupt approach and either use the ISR to set a flag that you can check in your main loop or, depending on exactly what you need to do, you *might* be able to do the work directly in the ISR.
As a general comment I have almost never come across a situation where I've needed to play with the interrupt priorities - leave them at the default unless you fund that you absolutely must have nested interrupts.
(By the way, the reason your your original code is wrong is that your polling and ISR are fighting each other. When the timer hardware sets the IF flag, the MCU hardware will complete its current instruction and then call the ISR. The ISR then clears the IF flag and returns to your code. If you are sitting n the 'while' loop testing the IF bit, then whether you see it or not depends on exactly what code is generated by the C compiler (which will also depend on the optimisation level etc.). Of course there is a chance that the instruction that is being executed is the one that tests the IF bit but is probably one of the other instructions that implement the loop in which case you will never see it set.)
Susan
#4
jvalerio
New Member
  • Total Posts : 27
  • Reward points : 0
  • Joined: 2015/01/07 13:10:37
  • Location: 0
  • Status: offline
Re: Delay with interrupts 2019/11/11 11:02:00 (permalink)
0
Thanks Susan, this makes complete sense.I would like the MCU to do other things. Do you have a reference to an app note or example code how I can accomplish this using the interrupts approach with ISR? I've never worked with interrupts before and basically just getting familiar with them.
#5
mpgmike
Super Member
  • Total Posts : 332
  • Reward points : 0
  • Joined: 2014/01/23 17:27:06
  • Location: NJ
  • Status: offline
Re: Delay with interrupts 2019/11/11 17:10:05 (permalink)
3.5 (2)
Most folks start by playing with 8-bit PIC offerings to get their feet wet.  There are many functions that are a tad simpler (and in some cases, much simpler) than the 16- or 32-bit versions.  May I ask why you are learning about interrupts with a 16-bit processor?  What is your experience?
 
As a side bar, Microchip has an online "course" where you can work step-by-step through lessons on how to use the tools, processors, and their special functions.  Check out:
 
https://microchipdeveloper.com/training-self:start

I don't need the world to know my name, but I want to live a life so all my great-grandchildren proudly remember me.
#6
Aussie Susan
Super Member
  • Total Posts : 3638
  • Reward points : 0
  • Joined: 2008/08/18 22:20:40
  • Location: Melbourne, Australia
  • Status: offline
Re: Delay with interrupts 2019/11/11 18:34:39 (permalink) ☼ Best Answerby jvalerio 2019/11/12 07:48:34
5 (1)
Jose - you basically have everything set up for the timer interrupt already. You initialise the timer, set the IE bit and then turn the timer on when you are ready. You also have the ISR written and it already clears the IF bit.
What I suggest you do it to start with a new app that only contains the setup for T3 and the main loop plus the T3 ISR. In the ISR you can toggle a LAT register bit that corresponds to a pin with a LED on it.
You don't say what MCU you are using but some of the timers are 32-bit and so you can set them up to interrupt every (say) second. Even the 16-bit timers can include a counter in the ISR so they interrupt every (say) 1mSec, count to (say) 500 and then toggle the LAT register bit to turn the LED on or off every 1/2 second.
That should give you a basic understanding of the timer interrupt (and hence most interrupts).
As a 2nd step, you can create a 'volatile' variable that you can set within the ISR (instead of toggling the LAT register bit). Then within the main loop you can test for the variable to be set and, if it is then toggle the LED and clear the variable again. This will give you the basic idea of how to trigger off more extensive processing from an interrupt (remembering that you make ISR code execute as rapidly as possible so no delays and long code processes which is what Crosland was alluding to in post #2).
Susan
#7
jvalerio
New Member
  • Total Posts : 27
  • Reward points : 0
  • Joined: 2015/01/07 13:10:37
  • Location: 0
  • Status: offline
Re: Delay with interrupts 2019/11/12 07:48:06 (permalink)
0
Thanks again Susan, I think I got a better understanding now and was able to get it working with below code.
 
 
/* 
 * File: main.c
 * Comments: Pic24 Interrupts
 * Date: November 12, 2019
 * Device: PIC24EP256GP204
 */

#include <xc.h>
#define _ISR_PSV __attribute__((__interrupt__, __auto_psv__))

void timer5init (void);
volatile int count = 0;

int main(void)
{  
    // Fcy = 60MHz (Instruction Clock)
    // Fosc = 120MHz System Clock
    // Configure Oscillator to operate the device at 60MIPS (Fcy) from Internal FRC
    // Fosc= Fin*[M/(N1*N2)], Fcy=Fp=Fosc/2
    // Fosc= 7.37*[65/(2*2)]~=119.7625Mhz for FRC(7.37MHz) input clock
    // Configure PLL prescaler, PLL postscaler, PLL divisor
    //OSC2, Pin31 is configured as clock output to verify clock is setup correctly.
    
    PLLFBD = 63; // M=65
    CLKDIVbits.PLLPOST = 0; // N2=2
    CLKDIVbits.PLLPRE = 0; // N1=2
    
    RCONbits.SWDTEN = 0; // Disable Watch Dog Timer
     
    // Initiate Clock Switch to FRC oscillator with PLL (NOSC=0b001)
    __builtin_write_OSCCONH(0x01);
    __builtin_write_OSCCONL(OSCCON | 0x01);
    
    // Wait for Clock switch to occur
    while (OSCCONbits.COSC != 0b001);
    // Wait for PLL to lock
    while (OSCCONbits.LOCK != 1);
 
    TRISCbits.TRISC7 = 0; //RC7 as output
    LATCbits.LATC7 = 1; //Turn off the led/RC7 (inverted)
    
    timer5init();

    while (1) //Run Forever...
    {
            if (count == 5) //Timer interrupt is set for 250ms (5 * 250m) = 1.25Sec
            {
            LATCbits.LATC7 = ~LATCbits.LATC7; //Toggle RC7 pin
            count = 0;                                      //Reset the counter
            }
    }

    return 0;
}

void timer5init (void)
{
    T5CONbits.TON = 0; //Stops 16-bit Timer
    
    T5CONbits.TCS = 0; //Select Internal clock (FP)
    T5CONbits.TCKPS = 0b11; //Select 1:256 Prescaler
    T5CONbits.TGATE = 0; //Disable gated timer
    T5CONbits.TSIDL = 0; //Discontinues module operation when device enters Idle mode
     
    TMR5 = 0x00; //Clear timer5 register
    PR5 = 58593; //Set period of timer [250ms]
    
    IPC7bits.T5IP = 0x01; //Set Timer 5 Interrupt Priority Level
    IFS1bits.T5IF = 0; //Clear Timer 5 Interrupt Flag
    IEC1bits.T5IE = 1; //Enable Timer5 interrupt
    
    T5CONbits.TON = 1; //Enable timer5
}

void _ISR_PSV _T5Interrupt()
{
    if (IFS1bits.T5IF == 1)
        {
            count++; //Increment count ever 250ms
        }
    IFS1bits.T5IF = 0; //Clear Timer 5 Interrupt Flag
}

#8
Nikolay_Po
Super Member
  • Total Posts : 1932
  • Reward points : 0
  • Joined: 2012/04/01 13:49:27
  • Location: Russia, Novorossiysk
  • Status: offline
Re: Delay with interrupts 2019/11/12 14:30:03 (permalink)
5 (1)
Jose, good example!
 
You may omit "if (IFS1bits.T5IF == 1)" check inside of timer #5 ISR. In PIC24/dsPICs interrupt controller will not enter certain interrupt without setting this flag.
 
Also I recommending to think what if your main loop cycle will be slower than one tick of a counter (after you'll add more code in a loop)? In such case your test for exact value "if (count == 5)" will fail even if the timer already passed that number.
The code below is somewhat larger and slower but is less glitch-prone (I think):
    while (1) //Run Forever...
    {
            if (count >= 5) //Timer interrupt is set for 250ms (5 * 250m) = 1.25Sec
            {
                 LATCbits.LATC7 = ~LATCbits.LATC7; //Toggle RC7 pin
                 count -= 5; //Reset the counter. Keep event frequency even with loop delays longer than one counter tick
            }
    }

 
HTH!
Nikolay.
post edited by Nikolay_Po - 2019/11/12 14:32:07
#9
Jump to:
© 2019 APG vNext Commercial Version 4.5