Interrupts not always served on Teensy 3.1

Status
Not open for further replies.

roberthh

Active member
I'm testing the Interrupt reponse times for Teensy 2.0 and Teensy 3.1 in preparation of a new project. Doing this, I wrote a very simple program, which activates an interrupt if an external event happens, and simply toggles an output line once the interrupt arrives. Th eocde is attached. Th eexternal event is genearted by a pulse generator, which fires a 200 ns pulse each ms. With Teensy 2.0 this works fine, besides some jitter, since the timer interrupt is still enabled. With Teens 3.1, this does not work reliable, saying that only about every third interrupt it catched. The behabiour does not depend on the lelngt of the pulse (1 tried it up to 10 µs) or the frequency of the pulses. Does someona have any clue what is to be changed?

--------------- Code -----------

void setup()
{
// Initialize the digital pin as an output.

pinMode(10, OUTPUT);
pinMode(2, INPUT);
attachInterrupt(2, flagIsr, RISING);

}

volatile int intarrived = 1;

void loop()
{
// Main code loop

while (intarrived) {
}
digitalWrite(10, HIGH);
delayMicroseconds(5);
digitalWrite(10, LOW);
intarrived = 1;
}

/// --------------------------
/// Custom ISR Routine
/// --------------------------
void flagIsr()
{
intarrived = 0;
digitalWrite(10, HIGH);
digitalWrite(10, LOW);
}
 
Just a question... The HIGH/LOW seems to happen twice, once in the loop and once in the isr routine. Is that intended? Why?

That looks like the loop makes Pin10 blink all the time with a 5us on-time as long as no interrupt occurs, but when one occurs, there will be one single shorter(?) blink. digitalWrite() is relatively slow. Why not use digitalWriteFast()?
 
Hello Tehermingenieur, thanks for the reply.

a) There are intentionally two pulses, one to see how fast the ISR receives the signal, and the second to see how fast the main loop is notified.
b) There is the tiny 'while (intarrived) {}' loop (negative logic, sorry) which blocks the main program from running. And when there is not input pulse, there is no output pulse. As said, it works fine with Teensy 2.0 (or Arduino x), but not with Teensy 3.1.
c) Thanks for the hint to digitalWriteFast(). Where is that documentet?
Best Regards
 
I saw the digitalWriteFast() - like most else - on the forum and I ended up with this handy tool to toggle a pin:
Code:
#define qBlink() (digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN) ))

Also to paste code use the "#" on advanced or hit the 'Quote Bubble' button and change QUOTE to CODE on both ends to preserve indentation.

I'm wondering if it is somehow running too fast? put a 'delayMicroseconds(1);' or 'yield();' in your empty 'while (intarrived)'.

Try a compile at 24mHz - it should have no trouble keeping up if a T2 can do it.

Add this to setup: 'pinMode(LED_BUILTIN, OUTPUT);' then call above qBlink() between the HIGH&LOW writes in the flagIst() and watch for the LED to glow or blink steadily at lower freq.
 
Hello all, the problem disappeared. I cannot say it was solved, so it's a little bit strange. The first reliable operation was trying to reduce clock speed. But then it worked at every clock speed. Thank you all for your suggestions. I learned a few new tricks. The hint about digitalFastWrite was helpful. About digitalFastWrite(): A code sequence like:

while (1) {
digitalWriteFast(pin, HIGH);
digitalWriteFast(pin, LOW);
}

does not always produce a square output. if it does, the frequency at 96 MHz clock is like 6.5 MHz. With my test code above, the shortest pulse detected is like 25 ns. The only thing to add for this first feasibility run is temporily switching of the timer interrupt.
 
More on timings

I shoudl add, that I played around with generatung suqare waves as inicated above, Two observations, which the 'Senior' in this form probably know:

digitalreadFast() (and its counterpart) generate the equivalent Assembler statement, when called with constant parameters. so:

digitalWriteFast(10, HIGH); is equivalent to CORE_PIN10_PORTSET = CORE_PIN10_BITMASK;

but much better readable. This short loop gave the fastest square wave at 96 MHZ I could produce:

while (1) {
digitalWriteFast(10, HIGH);
__asm__ volatile("DSB");
digitalWriteFast(10, LOW);
}


Without the __asm__ volatile("DSB"); instruction, the output would not go high the full way up. Cycle time is 73 ns (7 Clocks)
Regards
 
Faster? - is it more squared? If not optimized away there should always be a jump
Code:
FASTRUN
void BlinkForever()
{
  goto OnEntry;
OffNow:
  digitalWriteFast(10, LOW);
  // __asm__ volatile("DSB");
  goto OnNow;
OnEntry:
  goto OffNow;
OnNow:
  digitalWriteFast(10, HIGH);
  // __asm__ volatile("DSB");
  goto OffNow;
}
 
Hello defragster, thank you for your commets. I tested that. The square wave of my code above has no 50% duty cycle, and yes, you see the rise/fall times. The initial code you suggest has a cycle time of 52 ns (like 5 clock cycles), and 60% durty cycle. I added a load to the output (330Ohm) to get an almost full voltage swing. I did no further testign on the influence of the probe tip. When I uncomment the __asm__ statements, I get a full level swing w/o load(as expected). But even then, no 50% duty cycle. The duty cycle is 60% high, 40% low, period of 100ns. If I drop the __asm__ Statement after setting the output HIGH, the durty cycle is 40%, and the period 73ns. All in 96MHz/optimized settings of Teensuino.
Best Regards
 
Something optimized or not symmetric in the goto/jumps.

This using the qBlink should more easily give uniform cycles and you can adjust the duty. You'd not have periodic oddity if you disabled interrupts.

Code:
#define qBlink10() (digitalWriteFast(10, !digitalReadFast(10) ))

{
  while (1)   {
    qBlink10();
    delayMicroseconds(500000);
  }
}
 
Hello defragster,
obviously you are mostly about the uniform cycles. Without the delayMicroseconds call, I'll get a 53% duty cycle and 375ns cycle time, iwth the difference between high and low cycle being 22 ns or about 2 clock cycles. The rise/fall times are 8ns vs. 7.5 ns, so it does not look like an 'analog' reason for the difference. A look into the datasheets may explain the reason. But besides that, I do not care about the duty cycle. The aspect I was looking for was a) the elapsed time between an external event and the ISR to be notified, and b) the elapsed in after which the main program is notified or some other event can be triggered. The fast loop was just a test in between, whether interrupts were detected bot not flagged. During a ISR, the main program loop would halted, and that would cause a longer negative or positive pulse.. And BTW, I have temporarily switched off the SysTick timer, which is the only interrupt source present for this test. But for the other, the hint to digitalFastWrite() was very helpful.
Best Regards
 
Maybe putting the qBlink() on the LED_BUILTIN (or another pin) in the ISR rather than overloading pin 10 - back in your first example.

T3.1 Speed is catching you somewhere. The transition High>Low happens really fast - more so with digitalFastWrite. The qBlink() toggle will cut the change freq in half and allow settling before the next change. You have a good way of seeing the freq and duty cycle so that alone should show if you are missing interrupts, or missing the indications you are seeing them.

I've not done a lot with interrupts here but PJRC warning that it gets complex fast may be hiding something.
 
Output switching speed

Hello defragster,
the missing intterupt problem is solved. It was just a wrong setting of the pulse generator, which I used to trigger the interrupt. The level was too low, and it was lat at night,so I did not see it at once. But looking for the reason strarted this good discussion over switching timing. This morning, I started to compare the data sheet with the setting in the pins_teensy.c file, and found, that pinMode() sets a slow slew rate. Once I changed it, the slew rate improved. I have attached the two figures for your loop (cycle 375ns) and my fastest loop (cycle 52ns). With fast slew rate, the issue of the output not touching the low level disappeared too. The pictures show a slew rate of about 2 ns. The probe has a tip capacitance of 14 pf, and the measuring set-up has a rise time of 2 ns, so the device my be faster. If you wonder what the change is:

Change line 791 in pins_teensy.c from:
*config = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
to
*config = PORT_PCR_DSE | PORT_PCR_MUX(1);
The SRE bit must be 0 for fast slew rate, but the code sets it to 1 for slow slew rate.
Regards
 

Attachments

  • Cycle 52ns.jpg
    Cycle 52ns.jpg
    111.7 KB · Views: 235
  • Cycle 375ns.jpg
    Cycle 375ns.jpg
    111 KB · Views: 274
Hmmm... maybe the website needs some updates about how to get fast signals. The pages were written back in the AVR-only days, when you just couldn't make a pin toggle faster than 8 MHz.
 
T3.1 fast slew rate: SRE=0 changes 7 ns risetime to sub-ns

Until reading this, I didn't realize the pins were configured to be slow. I went into C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3 and changed two files, core_pins.h and pins_teensy.c as a rude hack to add:
Code:
void pinModeFast(uint8_t pin, uint8_t mode); // for high drive strength (fast slew rate)

void pinModeFast(uint8_t pin, uint8_t mode)
{
	volatile uint32_t *config;

	if (pin >= CORE_NUM_DIGITAL) return;
	config = portConfigRegister(pin);

	if (mode == OUTPUT) {
#ifdef KINETISK
		*portModeRegister(pin) = 1;
#else
		*portModeRegister(pin) |= digitalPinToBitMask(pin); // TODO: atomic
#endif
		*config = PORT_PCR_DSE | PORT_PCR_MUX(1); // SRE bit = 0 for fast slew rate
	} else {
#ifdef KINETISK
		*portModeRegister(pin) = 0;
#else
		*portModeRegister(pin) &= ~digitalPinToBitMask(pin);
#endif
		if (mode == INPUT) {
			*config = PORT_PCR_MUX(1);
		} else {
			*config = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; // pullup
		}
	}
}

and tried this code:
Code:
const int LED1 = 0;  // output pin

void setup() {
  pinModeFast(LED1, OUTPUT); // high slew rate setting
}

void loop() {
  digitalWriteFast(LED1, HIGH); 
  delayMicroseconds(10); 
  digitalWriteFast(LED1, LOW); 
  delayMicroseconds(10); 
}
Previously, when using the normal pinMode() call I got 7 ns risetime. With the high slew rate enabled I get sub-nanosecond risetimes, maybe around 0.85 ns but my 500 MHz scope/probe is not accurate at those levels. At any rate, it is quite a difference.

"Before" (default slew rate)2015-06-08_08.49_Scope.jpg "After" (fast slew rate)2015-06-09%u00252B09.07.46.jpg
short ground wire probe2015-06-09%u00252B09.10.11.jpg
 
Last edited by a moderator:
Hi JBeale,
thanks for the update. I did not expect any further comments on that any more. I had assume that the transition times were faster, but with my scope & probe that fastest I could get was 2ns, which is the limit of that combination. The data sheet tells 6 ns, regardless of the mode set. So the data sheet may be wrong in that perspective. What I wanted to find out was a set-up, where an input signal would cause an output signal after a programmable delay. So ISR response times USING the teensuino environment is an issue. The best I could get at the moment is a delay range from 2µs to about 90 sec with about 20 ns resolution and 50 ns jitter, using the PIT units. At the moment. I try to figure out whether the PDB or Flex-Timer could be used instead. It looks like the PDB can be triggered itself externally, but I did not find a physical output signal, amd even if the signal exists in the die, it might not be routed to a pin of the CPU package, and that might not be routed to a pin of teensy.
I have coworkers which would always go to bare bone assembly code for these kind of tasks. But I want to set up a little framework which is well maintained, uses a high level language and can be used with little training. And for that, teensy 3.1 and Teensuino + Arduino seems to be perfect. Who ever want to go bare bone, can still do that.
The only issue if have with teensy 3.1 is the flimsy USB connector. I fear that it will not survive in the lab for long. For integration into another board, which is anyhow required for an analog circuit, simple soldering pads would be better. Then I could add a type B connector.
 
Until reading this, I didn't realize the pins were configured to be slow. I went into C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3 and changed two files, core_pins.h and pins_teensy.c as a rude hack to add:
....<snip>...
*config = PORT_PCR_DSE | PORT_PCR_MUX(1); // SRE bit = 0 for fast slew rate

Nice...
This little hack was the difference between an LCD running with the Teensy 3.2 @ 48Mhz and 120Mhz.
I couldn't get pinModeFast() to work, so I copied the line above into core_pins.h above and commented out the original.
Works like a champ.
 
Assembly language isn't the solution.

Relying on an interrupt latency of less than about 5 microseconds is imprudent in most any processor.
The ARM NVIC can be used to let interrupt handler A interrupt (preempt) handler B. But resorting to that means that there is some handler x that is badly written. Or the functional allocation to software should have been to hardware, due to speeds.
 
NOTE: my original code block for pinModeFast is missing a close bracket } at the end, somehow missed it in a cut-and-paste, sorry about that!

<edit>: added close bracket }
 
Last edited by a moderator:
Status
Not open for further replies.
Back
Top