chipKIT® Development Platform

Inspired by Arduino™

Rapid interrupt causes board freeze

Created Wed, 24 Aug 2011 19:45:51 +0000 by Alan Chatham


Alan Chatham

Wed, 24 Aug 2011 19:45:51 +0000

So, I'll be the first to say, I know nothing about PIC uCs; I'm an AVR guy myself. Here's my issue - I'm trying to develop something that will turn on different lights based on if it's receiving one of 6 different specific frequency square waves (44khz, 48khz, 76khz, 88khz, 176khz, and 192khz). To do this, I went ahead and made an interrupt that detects a falling edge and uses that to increment a counter, then every so often I compare that counter against how long it's been since the last time I measured, measuring the frequency that way. This works, more or less on the Arduino, but moving the code over to the chipKit, all the sudden it started stalling on me. So I cut things down and came up with this simplest of routines on the chipKit:

void setup(){
  attachInterrupt(0,countUp,FALLING);
  pinMode(13, OUTPUT);//LED pin
}

volatile int counter = 0;
void countUp(){
  counter++;
}

void loop(){
  delay(500);
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
}

and fed in an 80khz square wave. The board locks up (i.e., the LED stops blinking), even when the signal is removed.

Any suggestions to what's going on here? Any hope of solving this problem?


dc101

Thu, 25 Aug 2011 02:20:15 +0000

Are you using the Max or the Uno board? I tried to duplicate your code with my Max32 but I couldn't get it to break.

I did modify your test slightly, but it shouldn't change the resullts. Since I don't have a function generator I took the output of pin13 and fed it into the interrupt. To make sure the interrupt was actually working, I set LED8 to high on the interrupt.

The results show that LED8 illuminates and the waveform continues on the scope. With the code below, I'm showing 266 kHz on my scope from pin13 (without pin13 hooked to the board it's 654 kHz).

One thing I did note was that using the Serial.print(counter) function inside the countUp() function caused the board to lockup. Not really sure what to make of that, I haven't experimented too much with other functions. I did try a delay(1); in there and that cause it to lock up as well.

void setup(){
  attachInterrupt(0,countUp,FALLING);
  pinMode(13, OUTPUT);//clock
  pinMode(77, OUTPUT);
}

volatile int counter = 0;
void countUp(){
  counter++;
  digitalWrite(77, HIGH);
  //delay(1);
}

void loop(){
  digitalWrite(13, HIGH);
  digitalWrite(13, LOW);
}

GeneApperson

Thu, 25 Aug 2011 03:51:13 +0000

I wouldn't use an interrupt to do this. I would use an input capture. Unfortunately, the Arduino API doesn't support input captures, so you would to bypass the abstraction layer and write directly to the PIC32 peripheral registers.

To use an input capture, you program timer 2 or timer 3 to count at a particular frequency by selecting which pre-scaler value to use (a divider between the 80Mhz peripheral bus frequency and the clock input to the timer). You then associate whichever input capture you are using to work with the timer you choose. There is a timer select bit in the input capture control register to tell it to record timer 2 or timer 3.

Bring the signal from whichever input capture you are using into the appropriate pin for that input capture (IC1, IC2, etc). The input capture can be programmed to trigger on a rising edge, falling edge, both edges, and a few other options. If you program it for rising edge, then on each rising edge of the input signal, the input capture will record the count value of the timer.

You wait for a capture event, read the value and remember it, then wait for another capture event, read the value and subtract the first value. This gives you the period of the signal in cycles of the clock frequency of the timer.

Ideally, you would use interrupts to receive the input capture events, but the current system (based on what Arduino supports) does't support input capture niterrupts, so you would have to use polling to see the capture events and read the capture values. You poll by reading the input capture control register to see when data is available.

If you are interrested in trying this, refer to Section 14, Timers and Section 15, Input Capture of the PIC32 Family Reference Guide that can be downloaded from the Microchip web site.

Gene Apperson Digilent


hairymnstr

Thu, 25 Aug 2011 09:47:09 +0000

I would guess the interrupt is locking up because it takes too long and the flag clearing (presumably done automatically by the attachinterrupt() boilerplate) is being done at the start, so the CPU goes into a permanently interrupted mode and depending on how the delay() and Serial.print() work regarding interrupts, they may never complete either because the custom interrupt is higher priority.


GeneApperson

Thu, 25 Aug 2011 14:37:36 +0000

I would guess the interrupt is locking up because it takes too long and the flag clearing (presumably done automatically by the attachinterrupt() boilerplate) is being done at the start, ...

Correct. The external interrupt service routines are clearing the interrupt flag before calling the application function. This is arguably a bug and should probably be changed for the next release.

Thanks,

Gene Apperson Digilent


Ivan

Fri, 26 Aug 2011 08:02:35 +0000

Hi to all. I'm new. This is a big Bug !

Can the problem is in the file WInterrupts.c ?

This is the old code:

//************************************************************************ // INT1 ISR void __ISR(_EXTERNAL_1_VECTOR, ipl4) ExtInt1Handler(void) {

**IFS0bits.INT1IF	=	0;**
if (intFunc[1] != 0)
{
	(*intFunc[1])();
}

}

I think it should be ???

//************************************************************************ // INT1 ISR void __ISR(_EXTERNAL_1_VECTOR, ipl4) ExtInt1Handler(void) { if (intFunc[1] != 0) { (*intFunc[1])(); } IFS0bits.INT1IF = 0; }

or: ???

//************************************************************************ // INT1 ISR void __ISR(_EXTERNAL_1_VECTOR, ipl4) ExtInt1Handler(void) { if (intFunc[1] != 0) { (*intFunc[1])(); IFS0bits.INT1IF = 0; }

}

Alan Chatham, can You test my first solution ?

Kind Regards


Alan Chatham

Fri, 26 Aug 2011 17:35:50 +0000

Thanks, Gene, for the tip! I've got things up and running with the input capture now; I really appreciate the advice!

For reference, I was doing the interrupt stuff on a chipKit Uno32.


GeneApperson

Fri, 26 Aug 2011 22:44:18 +0000

@Ivan,

Your first case is correct. You want the interrupt flag to always be cleared on every pass through the ISR. The case where you have clearing the interrupt flag inside the if statement could result in a situation where the CPU is locked up being interrupted constantly.

Gene Apperson Digilent


Ivan

Mon, 29 Aug 2011 09:07:26 +0000

@Gene

Thank Gene for Your time and the fast response. Now me too, I will start to do some experiments with my chipkit uno32 card & interrupts routines....

Bye


GeneApperson

Wed, 31 Aug 2011 20:02:38 +0000

Correct. The external interrupt service routines are clearing the interrupt flag before calling the application function. This is arguably a bug and should probably be changed for the next release.

This has been logged as an issue on github and I have submitted a patch to correct it. The patch hasn't been integrated into the source tree yet, but it should be up there in a few days and will be incorporated into the next release.

Gene Apperson Digilent


serveurperso

Tue, 13 Sep 2011 05:07:32 +0000

Hello,

My Max32 always freeze:(

with mpide-0022-windows-20110822 and mpide-0022-windows-20110907-test (/w updated WInterrupts.c)

Rapid interrupt test source is a rotary encoder without capacitors. I do not have this problem with Arduino ATmega238 and 2560.

I tested RISING. Is it possible, in the future, to implement the CHANGE trigger ?

I really appreciate your help edited : sorry for me english Pascal


GeneApperson

Tue, 13 Sep 2011 17:20:34 +0000

Pascal,

The PIC32 microcontrollers only support rising and falling edge triggered external interrupts. They don't support level triggering or interrupt on change. It isn't possible to correctly implement CHANGE on these devices.

The PIC32 does support pin change interrupts. Currently the Arduino abstraction layer doesn't have support for pin change interrupts, but I am planning to add support for it in a future version.

Gene Apperson Digilnet


serveurperso

Thu, 22 Sep 2011 10:03:47 +0000

There is no solution to prevent the freeze for rising and falling edge triggered external interrupts ?


hairymnstr

Thu, 22 Sep 2011 10:25:29 +0000

I'm not sure what you're actually doing, I've used external interrupts with quadrature encoders with no freezes on the Uno32, can you post the code that's causing the problem?


serveurperso

Sat, 24 Sep 2011 16:38:23 +0000

I'm not sure what you're actually doing, I've used external interrupts with quadrature encoders with no freezes on the Uno32, can you post the code that's causing the problem?

I use a Quadrature encoder only as quick and dirty random noise generator.

This random medium/high frequency freeze my MAX32. An Arduino AVR don't freeze in same configuration.

Pascal


hairymnstr

Mon, 26 Sep 2011 12:21:49 +0000

Without seeing the actual code and knowing what version of mpide you're using I can't really say what's going wrong. I have very high frequency interrupts working just fine.


serveurperso

Thu, 29 Sep 2011 07:45:38 +0000

Without seeing the actual code and knowing what version of mpide you're using I can't really say what's going wrong. I have very high frequency interrupts working just fine.

I used this minimalist attachInterrupt() code : On the last stable version + the last testing (mpide-0022-windows-20110907-test).

#define testPin 2

volatile int32_t testCounter = 0;

void setup() {
 pinMode(testInterrupt, INPUT);

 attachInterrupt(0, testInterrupt, RISING);

 Serial.begin(115200);
}

void loop() {
 Serial.println(testCounter, DEC);

 delay(10);
}

void testInterrupt() {
 testCounter++;
}

Ivan

Fri, 14 Oct 2011 12:18:20 +0000

Hi

Same problem for me. My uno32 freeze with rapid interrupts. I can post the code, but it's very similar to the code in the last post. I have a random pulse generator that trigger the pin 7 (int 2) and a main loop that writes a counter to a graphic display. With no interrupts, uno32 works well (10 minutes or more) and the counter increments. If I generate pulse on pin 2, after 1/2 minute the main loop() freeze!. The interrupt code instead seems still working! (I have put in the interrupt routine a digitalWrite (43,1) to see if it freeze completely). I have tried the "patch" that I have posted in this thread, but nothing, there is no system for having interrupts correct working... Can You help me ? King Regards.

P.S. this was the patch:

This is the old code: //************************************************************************ // INT1 ISR void __ISR(_EXTERNAL_1_VECTOR, ipl4) ExtInt1Handler(void) } I think it should be ??? //************************************************************************ // INT1 ISR void __ISR(_EXTERNAL_1_VECTOR, ipl4) ExtInt1Handler(void) { if (intFunc[1] != 0) { (*intFunc[1])(); } IFS0bits.INT1IF = 0; }


jasonk

Fri, 21 Oct 2011 04:25:20 +0000

You might try __ISR(_EXTERNAL_1_VECTOR, IPL7SRS) and then change the interrupt source to use IPL7 rather than IPL4. IPL7 uses shadow registers for context saving rather than pushing the registers to the stack.


jasonk

Fri, 21 Oct 2011 04:34:31 +0000

I'm interested to see if Gene's fix for this issue https://github.com/chipKIT32/chipKIT32-MAX/issues/124 also fixes the freeze due to rapid interrupts. If you have an interrupt of priority > IPL2 rapidly firing, it could starve the core timer interrupt. That would cause the next call to delay() to appear to "freeze" for an extended period.


Ivan

Fri, 28 Oct 2011 09:21:46 +0000

Hi

I have changed the interrupt priority to IPL7 (not IPL7SRS as you suggested), and I have seen an improvement. Now the board supports a higher interrupt frequency. I also realized that the problem is related to the main loop where I used the delay() function. In fact, the interrupt routine continues to run, but the loop() cycle seems to stop. I put a check in the interrupt routine by making 1 and then 0 an output pin and with an oscilloscope, and I still see the square wave even when the board apparently freezes. I came to the conclusion that the problem must be in the Timer Interrupt routine. If I remove the delay() function, from my main loop() the problem for my is resolved ! (I still use in the interrupt routine the delayMicroseconds() routine ! without problems). I have also tried to download the Gene's fixed wiring.c, but for me, doesn't work still well. The delay() function works good for the first 53sec, then one seconds (delay(1000)) begins to last for much more! (maybe 10 seconds ???). My Interrupt routine probably lasts about 0,8-0,9 ms.


Jacob Christ

Fri, 28 Oct 2011 20:35:23 +0000

Your first case is correct. You want the interrupt flag to always be cleared on every pass through the ISR.

I haven't really looked at how the PIC32 interrupts work but if they are like a PIC16 or PIC 18 (where you service multiple interrupts in a single ISR) it seem like you might miss interrupts if you always clear the interrupt flag.

Does each interrupt on the PIC32 have its own vector?

Jacob


q66

Mon, 07 Nov 2011 06:55:21 +0000

:( it compiles, it works on an arduino but as soon as i attach the external interrupt on the max32... bammM!

HELP! HELP! HELP!

volatile uint16_t rcValue[8] = {1500,1500,1500,1500,1500,1500,1500,1500}; // interval [1000;2000]
static void rxInt() {
  uint16_t now,diff;
  static uint16_t last = 0;
  static uint8_t chan = 0;

  now = micros();
  diff = now - last;
  last = now;
  if(diff>3000) { 
    chan = 0;
  }
  else {
    if( 900 < diff && diff < 2200 && chan < 8 ) {
	  rcValue[chan] = diff;
	}
    chan++;
  }
}


int output_pin = 10;

static void outputPPMframe() {
  unsigned long start_frame = micros();
  for (int i = 0; i < 6; i++) { 
    //Serial.println(rcValue[i]);
    digitalWrite(output_pin, LOW);
    delayMicroseconds(300);
    digitalWrite(output_pin, HIGH); 
    delayMicroseconds(rcValue[i] - 300); 
  }
  digitalWrite(output_pin, LOW);   
  delayMicroseconds(300); 
  digitalWrite(output_pin, HIGH);
  delayMicroseconds(20000 - (micros() - start_frame));  
}

void setup() {
   //Serial.begin(9600);
  pinMode(output_pin, OUTPUT);
  digitalWrite(output_pin, LOW);
  attachInterrupt(0, rxInt, RISING); //PIN 2 ON DUEMILANOVE, 3 ON MAX32
}

void loop() {
  outputPPMframe();
}

IRobot

Fri, 30 Dec 2011 20:22:49 +0000

Hi there, I guess I am just getting to the point that some of you have already reached. I have two motors with quadrature encoders connected to INT2 and INT3 on a ChipKitUno32. The loop() function calls methods to implement a PID controller for both motors based on updates to counters that are incremented/decremented by the interrupt handlers for the quadrature inputs. Everything runs for a variable amount of time, but usually after a few tens of seconds, the loop() function stops (I flash an LED on/off in the loop) and the only way out is a reset. Setting a constant output value for the motors (disabling PID controller) shows the same problem. Disabling the PID and the attachInterrupt() calls in the setup() method and everything works fine. I have a delay() call in the loop, but the board still locks up after removing this. I looked at the suggested solutions, but I don't really want to start hacking the platform code. Has this problem been confirmed by Digilent? Can anyone there provide a solution? Otherwise, I will have to do the quadrature decode in hardware which makes the purchase of the Uno32 a bit redundant (I have already switched from an Arduino board because it couldn't do this).


IRobot

Sat, 31 Dec 2011 20:28:28 +0000

I think I have solved the problem I was having. After reading some more about the delay/millis problem, I switched to using a call to delayMicroseconds() in my loop function (without any delay in the loop, it seems to cause other timing-related problems - possibly timer starvation?). This change seems to fix things for me as the loop no longer crashes. Is it possible that the implementation of the millis/delay methods is not robust enough to be usable? I also see that the delayMicroseconds code states it will fail every 71 minutes (presumably due to a counter rolling over). Has anyone thought about detecting this condition and handling it correctly? It would be nice not to have to restart every hour :)


GeneApperson

Fri, 06 Jan 2012 00:55:33 +0000

There is a known bug in the core timer interrupt routine. There have been various attempts to fix it. The version of the core timer interrupt routine in the 20111221 release should fix it.

The problem symptom was that if a core timer interrupt was ever missed, the compare register didn't get updated correctly, and the system millisecond counter wouldn't get updated again until the core time wrapped around and hit the old value in the compare register. It takes 107 seconds for the core timer to wrap. In this case, any call to the delay function would delay 107 seconds plus the delay time asked for.

This problem would manifest under various circumstances: Interrupts disabled for more than 1ms. EEPROM page erase, which takes 20ms, and so on.

Gene Apperson Digilent


GVillalon

Thu, 23 Feb 2012 17:08:56 +0000

Hi, I'm having the same problem. Currently using 20111221 release and waited much more than 107 seconds and it doesn't return. Trying with a Max32 and an encoder.


woerr

Thu, 18 Feb 2016 13:48:41 +0000

Edit, double post


woerr

Thu, 18 Feb 2016 13:54:45 +0000

Hi, Im having the same problem.

program was working perfectly until I added an external interrupt that is being triggered at only 34 hz. my program is quite long and complcated. I am using a picadillo 35t. The interupt just does a count++ and then in my loop I taking the total and printing it to the screen. There are no delays in my program at all.

Some assistance would be much appreciated. I am using Uecide

Edit* Freeze occurs when interrupt is triggered while items are busy rendering on screen.