Interrupt infinite loop

Discussion in 'Embedded Systems and Microcontrollers' started by zbblanton, Jan 14, 2014.

  1. zbblanton

    Thread Starter New Member

    Jan 14, 2014
    5
    0
    So I'm working on controller servos with TLC5940 LED Driver and this is my first time using interrupts. The interrupt is on Timer 2 match with PR2. I know the interrupt is working because of some testing blinking an LED but it appears that the interrupt is called over and over again.

    All my while loop does at the end of main is blink an LED. The LED turns on but never off, I assume this is when the first interrupt happens but it seems like every time the interrupt returns to the main it seems to be called again.

    Code ( (Unknown Language)):
    1.  
    2. //Microcontroller: PIC18F4520
    3. //8MHz internal clock
    4. //Complier: xc8 v1.20
    5. #include <xc.h>
    6. #include "tlc5940.h"
    7.  
    8. #define _XTAL_FREQ 8000000
    9. #define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
    10. #define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
    11.  
    12. #define OSCCON = 0b01110000
    13. #define ANSEL = 0b00000000
    14. #pragma config LVP = OFF
    15.  
    16. #pragma config OSC = HS, WDT=OFF, PWRT=OFF, BOREN=OFF, CP0=OFF,CP1=OFF,CP2=OFF,CP3=OFF, MCLRE=ON
    17.  
    18. volatile int count;
    19.  
    20. void delay_sec() //actually, half a second
    21. {
    22.     for(int i = 0; i < 50; i++)
    23.     {
    24.         __delay_ms(10);
    25.     }
    26. }
    27.  
    28. void interrupt blank_pulse(void)
    29. {
    30.     if(PIE1bits.TMR2IE && PIR1bits.TMR2IF)
    31.     {
    32.         if(count >= 520) //572
    33.         {
    34.             tlc_blank = 1;
    35.             __delay_us(1);
    36.             tlc_blank = 0;
    37.             count = 0; //Reset count
    38.         }
    39.         count++;
    40.         PIR1bits.TMR2IF = 0;
    41. //        if(PIR1bits.TMR2IF == 0)//Testing if there is a loop happening
    42. //        {
    43. //            LATDbits.LD3 = 1;
    44. //            delay_sec();
    45. //            LATDbits.LD3 = 0;
    46. //            delay_sec();
    47. //        }
    48.     }
    49. }
    50.  
    51. void spi_init()
    52. {
    53.     SSPSTATbits.SMP = 0; //Input data sampled at middle of data output time
    54.     SSPSTATbits.CKE = 1; //Transmit on transition from active to Idle clock state
    55.     SSPCON1bits.CKP = 0; //Idle state for clock is a low level
    56.     SSPCON1bits.SSPM0 = 0;//Sets SPI in master mode and sets clock to FOSC/4
    57.     SSPCON1bits.SSPM1 = 0;
    58.     SSPCON1bits.SSPM2 = 0;
    59.     SSPCON1bits.SSPM3 = 0;
    60.     SSPCON1bits.SSPEN = 1; //Enables the serial port
    61.  
    62.     TRISCbits.RC5 = 0; //Set SDO to ouput
    63.     TRISCbits.RC3 = 0; //Set SCK to output
    64. }
    65.  
    66. int main()
    67. {
    68.     init_TMR2();
    69.     count = 0;
    70.  
    71.     PIE1bits.TMR2IE = 1;
    72.     INTCONbits.PEIE = 1;
    73.     INTCONbits.GIE = 1;
    74.     T2CONbits.TMR2ON = 0; //Turn Timer on
    75.  
    76.     TRISD = 0b00000000;
    77.     PORTD = 0b00000000;
    78.     TRISB = 0b00100000;
    79.     PORTB = 0b00000000;
    80.  
    81.     TRISA = 0b00001111;
    82.     PORTA = 0b00001111;
    83.     TRISE = 0b00001000;
    84.     PORTE = 0b00000000;
    85.  
    86.     SSPSTATbits.SMP = 0; //Input data sampled at middle of data output time
    87.     SSPSTATbits.CKE = 1; //Transmit on transition from active to Idle clock state
    88.     SSPCON1bits.CKP = 0; //Idle state for clock is a low level
    89.     SSPCON1bits.SSPM0 = 0;//Sets SPI in master mode and sets clock to FOSC/4
    90.     SSPCON1bits.SSPM1 = 0;
    91.     SSPCON1bits.SSPM2 = 0;
    92.     SSPCON1bits.SSPM3 = 0;
    93.     SSPCON1bits.SSPEN = 1; //Enables the serial port
    94.  
    95.  
    96.     T2CONbits.TMR2ON = 0; //Turn Timer on
    97.  
    98.     //dot correction
    99.     //delay_sec();
    100.     init_dot_correction();
    101.     //delay_sec();
    102.    
    103.     //delay_sec();
    104.     init_grayscale();
    105.     //delay_sec();
    106.     update_grayscale();
    107.  
    108.     T2CONbits.TMR2ON = 1; //Turn Timer on
    109. //    while(1) //Old function before new interrupt
    110. //    {
    111. //        while(counter < 572) //Wait until ~40 ticks have passed //was 5 888
    112. //        {
    113. //            if(TMR2 == PR2)
    114. //            {
    115. //                counter = counter + 1;
    116. //            }
    117. //        }
    118. //        tlc_blank = 1;
    119. //        __delay_us(1);
    120. //        tlc_blank = 0;
    121. //        counter = 0; //Reset counter
    122. //    }
    123.     while(1)
    124.     {
    125.         LATDbits.LD3 = 1;
    126.         delay_sec();
    127.         LATDbits.LD3 = 0;
    128.         delay_sec();
    129.  
    130.     }
    131. }
    132.  
     
  2. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    764
    Have you tried to simulate it?

    I asume you have available MPSIM. If you proceed step by step, every register that has changed will have the value in red. If you expect a change in a certain register, and it did not happen, the value in that one will remain in black. Basic but useful.
     
    Last edited: Jan 14, 2014
    zbblanton likes this.
  3. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,394
    1,606
    IF you do not "PIR1bits.TMR2IF = 0;" whenever "PIE1bits.TMR2IE == 1" then you cannot leave the ISR, it will retrigger if you try to leave. It seems you may have that part correct but it is hard to tell with so much of the code commented out, not matching comments, or irrelevant to the problem.

    Try cleaning up your code ("T2CONbits.TMR2ON = 0;" and "T2CONbits.TMR2ON = 1;" cannot both "//Turn Timer on") ("void delay_sec() //actually, half a second" ORLY?), and slim it down to the smallest piece that can reproduce the "problem."

    Post that, along with a very clear description of the problem and someone else may be able to help.

    To help yourself learn how to use a debugger. PICkits can do in-circuit debugging of live code as it runs in your PIC. If not, there is always the simulator in MPLAB(X) which will do a fine job of simulating timer interrupts.
     
    zbblanton likes this.
  4. t06afre

    AAC Fanatic!

    May 11, 2009
    5,939
    1,222
    Could it be that simple. I can not see any return in your ISR.
    Example
    Code ( (Unknown Language)):
    1.  
    2. [LEFT]void interrupt tc_int(void)
    3. {
    4. if (TMR0IE && TMR0IF) {
    5. TMR0IF=0;
    6. ++tick_count;
    7. [COLOR=red]return;[/COLOR]
    8. }[/LEFT]
    9.  
    10. }
    11.  
    I also HIGHLY agree with others. Recommending learning to use the debugging options you have at hand. The software simulator is a very good friend then it comes to learning PIC programming
     
    zbblanton likes this.
  5. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    You would not use a 'return' at the end of an interrupt routine. The compiler will generate 'retfie' after restoring context.

    Delays should not be used in an interrupt routine. It slows the system down and the delay functions are likely not re-entrant (although XC8 may generate copies for each context - don't know).

    What does your init_TMR2() code look like?
    Be sure that the interrupt priority setup agrees with your interrupt vector.
     
    Last edited: Jan 14, 2014
    zbblanton likes this.
  6. tshuck

    Well-Known Member

    Oct 18, 2012
    3,531
    675
    The delay in your interrupt (after clearing the interrupt flag) is very likely causing the timer to overflow again, before returning from interrupt context.

    This is why special attention must be given to interrupt sequencing. Clearing the flag should be one of the last things you do.
     
    Last edited: Jan 14, 2014
    zbblanton likes this.
  7. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    That delay is commented out but that would be a concern.

    I ran it up in MPSIM after adding a TMR2 init and commenting out the tlc_stuff. It toggles the LED in the main-while loop so the basic interrupt is there.

    FWIW: looking at the disassembly listing, having delay_sec() in both contexts (main and interrupt) gets two copies of delay_sec() to avoid re-entrancy problems.
     
    Last edited: Jan 14, 2014
    zbblanton likes this.
  8. tshuck

    Well-Known Member

    Oct 18, 2012
    3,531
    675
    Ah, I didn't scroll all the way to the left...:(

    Double check that you are generating a hex file and that the generated hex file is what is making it to the PIC, considering John's working simulation...
     
    Last edited: Jan 14, 2014
    zbblanton likes this.
  9. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    I have a two hour jump on you in the morning coffee dept.

    Plus, MPLABX grays out the commented lines... ;)
     
    zbblanton and tshuck like this.
  10. zbblanton

    Thread Starter New Member

    Jan 14, 2014
    5
    0
    Wow was not expecting such feedback :)

    Yes I'm aware its only half second ;). It was a placeholder that just hasn't been cleaned up yet.

    I can clean the code up once I get off work and repost it, not a problem. Sorry for that messy code.

    Yes I really need to take full advantage of my PICKIT 3.

    Thanks for the advice from both of you on the delay in interrupt that makes sense.

    Sorry I should have posted my timer function, ill post that once I get off work as well.

    So it ran ok for you in the sim? I'll have to see if I can do that and then track through my code.

    To me it just seems like it exits the interrupt correctly but is just immediately called back.

    Would the two dalays cause a problem?

    Yes it is for sure the generated hex file. Tested it by applying some changes to the main and saw change in the LED.
     
  11. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    It did the same for me with the first quickie TMR2 setup. By the time it was done with the IRQ service, TMR2 had counted up to PR2. I added (lots of) time using the pre/postscalers and it worked. Perhaps your TMR2 is running too fast?

    Before you put it on the chip, be sure to do the rest of the CONFIG regs (XC8 will complain until you do). The easy way to do this is to select Configuration Bits on the lower view window. That will bring up a nice list of configs with drop-down options. Set up ALL of the options then click Generate Source Code to Output. You will get a window full of wonderful #pragmas. Copy/paste to your source and you are ready to go.

    FWIW here's the code I stepped in MPSIM - attached zip is the same. If you set breakpoints on each LED transition in main and monitor LATD in the SFR window, you'll see the port bit change on every breakpoint.
    Have fun!

    Code ( (Unknown Language)):
    1. /*
    2.  * File:   zzblanton.c
    3.  * Author: John
    4.  *
    5.  * Created on January 14, 2014, 9:54 AM
    6.  */
    7.  
    8. //Microcontroller: PIC18F4520
    9. //8MHz internal clock
    10. //Complier: xc8 v1.20
    11. #include <xc.h>
    12. //#include "tlc5940.h"
    13.  
    14. #define _XTAL_FREQ 8000000
    15. #define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
    16. #define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
    17.  
    18. #define OSCCON = 0b01110000
    19. #define ANSEL = 0b00000000
    20. #pragma config LVP = OFF
    21.  
    22. #pragma config OSC = HS, WDT=OFF, PWRT=OFF, BOREN=OFF, CP0=OFF,CP1=OFF,CP2=OFF,CP3=OFF, MCLRE=ON
    23.  
    24. volatile int count;
    25.  
    26. void init_TMR2(void)
    27. {
    28.     T2CON = 0b01111010; // quick and dirty init
    29.     PR2 = 160;   //
    30.     TMR2 = 0;
    31.  
    32. }
    33. void delay_sec() //actually, half a second
    34. {
    35.     for(int i = 0; i < 50; i++)
    36.     {
    37.         __delay_ms(10);
    38.     }
    39. }
    40.  
    41. void interrupt blank_pulse(void)
    42. {
    43.     if(PIE1bits.TMR2IE && PIR1bits.TMR2IF)
    44.     {
    45.         if(count >= 520) //572
    46.         {
    47.     //        tlc_blank = 1;
    48.             __delay_us(1);
    49.             delay_sec();
    50.      //       tlc_blank = 0;
    51.             count = 0; //Reset count
    52.         }
    53.         count++;
    54.         PIR1bits.TMR2IF = 0;
    55. //        if(PIR1bits.TMR2IF == 0)//Testing if there is a loop happening
    56. //        {
    57. //            LATDbits.LD3 = 1;
    58. //            delay_sec();
    59. //            LATDbits.LD3 = 0;
    60. //            delay_sec();
    61. //        }
    62.     }
    63. }
    64.  
    65. void spi_init()
    66. {
    67.     SSPSTATbits.SMP = 0; //Input data sampled at middle of data output time
    68.     SSPSTATbits.CKE = 1; //Transmit on transition from active to Idle clock state
    69.     SSPCON1bits.CKP = 0; //Idle state for clock is a low level
    70.     SSPCON1bits.SSPM0 = 0;//Sets SPI in master mode and sets clock to FOSC/4
    71.     SSPCON1bits.SSPM1 = 0;
    72.     SSPCON1bits.SSPM2 = 0;
    73.     SSPCON1bits.SSPM3 = 0;
    74.     SSPCON1bits.SSPEN = 1; //Enables the serial port
    75.  
    76.     TRISCbits.RC5 = 0; //Set SDO to ouput
    77.     TRISCbits.RC3 = 0; //Set SCK to output
    78. }
    79.  
    80. int main()
    81. {
    82.     init_TMR2();
    83.     count = 0;
    84.  
    85.     PIE1bits.TMR2IE = 1;
    86.     INTCONbits.PEIE = 1;
    87.     INTCONbits.GIE = 1;
    88.     T2CONbits.TMR2ON = 0; //Turn Timer on
    89.  
    90.     TRISD = 0b00000000;
    91.     PORTD = 0b00000000;
    92.     TRISB = 0b00100000;
    93.     PORTB = 0b00000000;
    94.  
    95.     TRISA = 0b00001111;
    96.     PORTA = 0b00001111;
    97.     TRISE = 0b00001000;
    98.     PORTE = 0b00000000;
    99.  
    100.     SSPSTATbits.SMP = 0; //Input data sampled at middle of data output time
    101.     SSPSTATbits.CKE = 1; //Transmit on transition from active to Idle clock state
    102.     SSPCON1bits.CKP = 0; //Idle state for clock is a low level
    103.     SSPCON1bits.SSPM0 = 0;//Sets SPI in master mode and sets clock to FOSC/4
    104.     SSPCON1bits.SSPM1 = 0;
    105.     SSPCON1bits.SSPM2 = 0;
    106.     SSPCON1bits.SSPM3 = 0;
    107.     SSPCON1bits.SSPEN = 1; //Enables the serial port
    108.  
    109.  
    110.     T2CONbits.TMR2ON = 0; //Turn Timer on
    111.  
    112.     //dot correction
    113.     //delay_sec();
    114. //    init_dot_correction();
    115.     //delay_sec();
    116.  
    117.     //delay_sec();
    118.   //  init_grayscale();
    119.     //delay_sec();
    120.   //  update_grayscale();
    121.  
    122.     T2CONbits.TMR2ON = 1; //Turn Timer on
    123. //    while(1) //Old function before new interrupt
    124. //    {
    125. //        while(counter < 572) //Wait until ~40 ticks have passed //was 5 888
    126. //        {
    127. //            if(TMR2 == PR2)
    128. //            {
    129. //                counter = counter + 1;
    130. //            }
    131. //        }
    132. //        tlc_blank = 1;
    133. //        __delay_us(1);
    134. //        tlc_blank = 0;
    135. //        counter = 0; //Reset counter
    136. //    }
    137.     while(1)
    138.     {
    139.         LATDbits.LD3 = 1;
    140.         delay_sec();
    141.         LATDbits.LD3 = 0;
    142.         delay_sec();
    143.  
    144.     }
    145. }
    146.  
     
    zbblanton likes this.
  12. zbblanton

    Thread Starter New Member

    Jan 14, 2014
    5
    0
    Ok so I ran through it through the simulator and it loops in the interrupt.

    I tried to remove everything except the PIR1bits.TMR2IF = 0 and it still would loop.

    All of you guys said it work for you, but... you all set up the timer. So maybe there is something I'm doing wrong with it.

    I think your right, I think the timer is running to fast.

    Here is a clean up code with the timer function

    Code ( (Unknown Language)):
    1.  
    2. //Microcontroller: PIC18F4520
    3. //8MHz internal clock
    4. //Complier: xc8 v1.20
    5. #include <xc.h>
    6. #include "tlc5940.h"
    7.  
    8. #define _XTAL_FREQ 8000000
    9. #define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
    10. #define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
    11.  
    12. #define OSCCON = 0b01110000
    13. #define ANSEL = 0b00000000
    14. #pragma config LVP = OFF
    15.  
    16. #pragma config OSC = HS, WDT=OFF, PWRT=OFF, BOREN=OFF, CP0=OFF,CP1=OFF,CP2=OFF,CP3=OFF, MCLRE=ON
    17.  
    18. volatile int count;
    19.  
    20. void delay_sec() //actually, half a second
    21. {
    22.     for(int i = 0; i < 50; i++)
    23.     {
    24.         __delay_ms(10);
    25.     }
    26. }
    27.  
    28. void interrupt blank_pulse(void)
    29. {
    30.     if(PIE1bits.TMR2IE && PIR1bits.TMR2IF)
    31.     {
    32.         if(count >= 520) //572
    33.         {
    34.             tlc_blank = 1;
    35.             __delay_us(1);
    36.             tlc_blank = 0;
    37.             count = 0; //Reset count
    38.         }
    39.         count++;
    40.         PIR1bits.TMR2IF = 0;
    41.     }
    42. }
    43.  
    44. void spi_init()
    45. {
    46.     SSPSTATbits.SMP = 0; //Input data sampled at middle of data output time
    47.     SSPSTATbits.CKE = 1; //Transmit on transition from active to Idle clock state
    48.     SSPCON1bits.CKP = 0; //Idle state for clock is a low level
    49.     SSPCON1bits.SSPM0 = 0;//Sets SPI in master mode and sets clock to FOSC/4
    50.     SSPCON1bits.SSPM1 = 0;
    51.     SSPCON1bits.SSPM2 = 0;
    52.     SSPCON1bits.SSPM3 = 0;
    53.     SSPCON1bits.SSPEN = 1; //Enables the serial port
    54.  
    55.     TRISCbits.RC5 = 0; //Set SDO to ouput
    56.     TRISCbits.RC3 = 0; //Set SCK to output
    57. }
    58.  
    59. void TMR2_init()
    60. {  
    61.     //Set to 100,000hz with 50% duty cycle. Prescaler is 4.
    62.     PR2 = 0b00000100 ;
    63.     T2CON = 0b00000101 ;
    64.     CCPR1L = 0b00000010 ;
    65.     CCP1CON = 0b00101100 ;
    66.     TRISC = 0b00000000;
    67.     PORTC = 0b00000000;
    68.     T2CONbits.TMR2ON = 0; //Turn Timer off
    69. }
    70.  
    71. int main()
    72. {
    73.     TRISD = 0b00000000;
    74.     PORTD = 0b00000000;
    75.     TRISB = 0b00100000;
    76.     PORTB = 0b00000000;
    77.     TRISA = 0b00001111;
    78.     PORTA = 0b00001111;
    79.     TRISE = 0b00001000;
    80.     PORTE = 0b00000000;
    81.  
    82.     TMR2_init();
    83.     count = 0;
    84.  
    85.     PIE1bits.TMR2IE = 1;
    86.     INTCONbits.PEIE = 1;
    87.     INTCONbits.GIE = 1;
    88.     T2CONbits.TMR2ON = 0; //Turn Timer on
    89.  
    90.     spi_init();
    91.     init_dot_correction();
    92.     init_grayscale();
    93.     update_grayscale();
    94.  
    95.     T2CONbits.TMR2ON = 1; //Turn Timer on
    96.     while(1)
    97.     {
    98.         LATDbits.LD3 = 1;
    99.         delay_sec();
    100.         LATDbits.LD3 = 0;
    101.         delay_sec();
    102.  
    103.     }
    104. }
    105.  
     
  13. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    Don't forget that it takes instructions to get into/out of an interrupt with context saving etc.

    Timer 2 is interrupting every 32 Tcyc (prescaler=4 * PR2=8), that's not a lot of instructions and probably is why you are stuck in the interrupt routine. When I sim'd your new code, it eventually broke at the port output but it took a long time to do it.

    So.. you want a 50% duty cycle PWM with a 100KHz (10us) period. Doable but why the interrupt so often? Could you factor the 520 into the postscaler? See FIG 13-1 in the datasheet.

    When figuring out the duty cycle don't forget to include CCPxCON <5:4>. They are the LSBits in the calculations.

    BTW when you init T2CON, you are turning TMR2 on. Leave it off until its ready to run.

    EDIT: and actually, with Prescaler=4, PR2=8, 8MHz, the Lil' Professor says the period is 62.5KHz so I guess we got some 'splainin' to do.
     
    Last edited: Jan 14, 2014
    zbblanton likes this.
  14. zbblanton

    Thread Starter New Member

    Jan 14, 2014
    5
    0
    Not a 100% sure what you mean on the period. I ended up using an online calculator for it.

    It has to be that fast, but yes I can try the postscaler out and ill let you guys know how it works.
     
  15. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    That's what I understood your comment to be..

    What are you actually trying to do? Maybe I'm misunderstanding some things here. The PWM period is how many Tcyc (Tosc/4 = 500ns) TMR2/PR2 counts. The duty cycle set in CCP1 is how many of those counts makes 50%.

    For yours: 100KHz is a period of 10us. With an input clock of 500ns period, you count to 20 for 10us. 4Prescaler * (4+1)* PR2 = 20 total count. For a 50% duty cycle, CCP1 is set to .5*20 = 10 counts. The CCP1 hardware output will run at 100KHz 50% duty cycle.

    The interrupt rate is 10us * the POSTscaler (which doesn't affect the PWM). Set the postscaler to some multiple of 10us i.e. set to 10 for 100us etc. Rather than count to 520*10us in the interrupt routine can you count to 52*100us?

    The upshot is that 10us only gives you 20 instruction cycles to work with. If you took a look at the disassembled output (the generated code from the C) you would find that the interrupt routine takes longer than that. For a 4520 with the errata handling that XC8 adds it takes 7 Tcycs just to vector to the interrupt, fix the errata and retfie. Add context save and you are way over 10us.

    That's why it seems to spend all of its time in the interrupt.
     
    zbblanton likes this.
  16. zbblanton

    Thread Starter New Member

    Jan 14, 2014
    5
    0
    The TLC5940 LED Driver has a blank pin that you must toggle high to reset its grayscale counter. So that is what the interrupt is performing. Its really the only downside of this controller.

    That did it! Set the post scale to 4 and now is running perfectly.


    Thank you all for your help. I learned more then I even came here for :)
     
Loading...