Interrupts: #, priority, architecture

I use Photon and Particle Build, the web based IDE.

  1. How many digital pins can I attach to an interrupt? Each having its own interrupt attachment. Is it really FIVE? (I have 6 sensors.) Another forum post about Core says it’s only five.

  2. Documentation says that once you are in an ISR, no other interrupts will occur. Sounds good. How does the “system” (the combination of CPU and firmware we know and love as the “Photon”) KNOW when I exit my ISR and it can resume allowing interrupts?

Does the hardware keep track of when an interrupt occurs and “watches” for the return from the ISR to resume allowing interrupts?

Must the ISR code explicitly issue some directive like “resume_allowing_interrupts_after_I_return”?

  1. Documentation says there is a priority to the interrupts. To me, that means if a low level interrupt (one might say a “less important or less critical” interrupt) ISR is running, then a higher level interrupt (like DMA according to the documentation) can interrupt the lower level one. But that seems to conflict with point 2 above: Once IN an ISR, no other interrupts can occur.

Where is reality?

  1. On some computers you have a priority of interrupts. But at each level of interrupt, you can have multiple things causing an interrupt. (Sorry, this is not explaining very well.) Does Photon have anything like this?

  2. Can someone point me to deeper documentation on this whole subject? I understand that the folks at Particle are trying to provide a base set of functionality which works great for most users. (And it looks like they are succeeding.) But I see lots of posts talking about, for example, low level programming well under any base set of functionality. How does one learn about this deeper functionality?

The docs state here

"All pins" include RX/TX (on the Core also)
And A pins are still fully capable digital pins.

Where does it say this, since it's not true (for all interrupts). The interrupt handling (including priorities and interrupting low prio ISRs in case of higher prio interrupts) is done by the µC according to its NVIC table (see datasheet p21f and ARM Cortex M3 specs)

2 Likes

Thanks for the fast reply.

What I am seeing in your note:

a) In Photon any pin with exception of D0 and A5 can have interrupt.

b) That statement does not address the question of how many of those pins can simultaneously have interrupts attached. (Is it more than five?)

c) Pins, for example, D4 and A1 cannot BOTH have interrupts attached simultaneously.

You said, “Where does it say this…?”

From documentation:
If your sketch uses multiple ISRs, only one can run at a time, other interrupts will be executed after the current one finishes in an order that depends on the priority they have.

So I’d say that sentence is slightly ambiguous. Say I have five digital input pins all with an interrupt attached. An interrupt happens on a “lower level priority pin”, so it’s ISR is called. While that ISR is running, a different and higher level interrupt happens. That WILL interrupt the lower level ISR.

WARNING: I clicked on the github link (Datasheet p21f) you provided and my Safari browser crashed.

Answering b & c:
Yes, it does indirectly
If you look at the complete quote, you could list the pins which can be set up for interrupts “simultaneously” like this
D1,D2,D3,D4,D5,D6,D7,A2,A7[,RX,TX] or substitute D1…D4 with one of their EXTI partners in above list.

About the docs:
When this talks about “your sketch uses multiple ISRs” than this doesn’t include system interrupts, which still can interrupt other interrupts (if needed), but your statement was “no other interrupts will occur”.

As for the tail-chaining of interrupts of “same” (“major”)priority (sorted by “minor” priority) (as all your sketch interrupts share one “major” priority level) you’ll find some info in the ARM Cortex link above.

This also is a nice (“surface”) read about interrupts on STM32 µC

I’ve been following this discussion, and had some questions. What does it mean in practical terms that all user interrupts have the same major priority, but different minor ones? If a higher priority interrupt occurs while the processor is in the ISR of a lower priority interrupt, is that ISR suspended until the higher priority one is dealt with, or is priority only a concern if multiple interrupts arrive at the same time? What determines the minor priorities; the only API exposed by Particle is attachInterrupt(), so does the order you attach them determine their (minor) priorities?

In the link above you'll find some rough info about the topic.

In short: Higher priority interrupts will preempt (=interrupt, get in between) lower priority ones, while same priority ones will be tail-chained according to their sub-priority set in the NVIC table.
The way how lower priority interrupts are dealt with (late arrival) can be controlled too (drop or cache), but I've no clue how this is set on the Particles.

AFAIK, the sub-priority of the GPIO interrupts goes in line with the bit number of the pin on its port (e.g. PB0..PB15 - see pinmap).
The lower the number, the higher the sub-priority level.

Ric,
I was the one that introduced the idea of multiple interrupts at the same priority. I guess you could call them minor priorities.

I read over the notes others have provided and conclude a couple things:

  1. When you attach an interrupt to a pin, there IS a priority. One pin change ISR can be interrupted by another, slightly higher pin change interrupt on a different pin.

  2. This processor and the overall Photon environment is a classic case, as you alluded to, of a very complicated thingy existing but only a subset of the features are “exposed”. The ORDER of doing the attachment does not matter. I am perfectly fine with that so long as I can do my job.

  3. If you look at the deep documentation on the CPU, that’s where all this talk of sub-priority levels comes from. I don’t think it matters a hill of beans to the basic programming model which Particle is exposing.

ScruffR’s documentation links are outstanding. (As long as it doesn’t blow up your browser!)

Meanwhile I am left with six sensors but only five pins which can have interrupts attached. (Six Characters in Search of a Play.)

Hmm, @jim_hahn I’m not sure where you got the max. 5 pins from.
As shown above on the Photon you actually can have at least nine and possibly eleven, or have you got all the other pins occupied for other purposes?

@ScruffR and @jim_hahn I looked in the firmware to see if I could find out anything about user generated interrupts, and priority. It appears, from my (admittedly limited) understanding of the interrupts_hal.c file, that all the user interrupts have the same priority and sub-priority. The below method is called from attachInterrupts()

void HAL_Interrupts_Attach(uint16_t pin, HAL_InterruptHandler handler, void* data, InterruptMode mode, void* reserved)
    ...
    ...
    NVIC_InitStructure.NVIC_IRQChannel = GPIO_IRQn[GPIO_PinSource];
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

I don’t have much experience reading low level code like this, so I could be wrong in my interpretation.

@Ric, I think you are right there.
It fits my notion about preemt priority, but the thing with one sub-priority is unexpected and gives rise to the question if and how this impacts tail-chaining.
Will simultaneous interrupts be treated as one event, failing to call dedicated ISRs?
If not, will the order be non-deterministic?

Not that the probability is that high, but going with Murphy’s law, one should be prepared :wink:

Given my own experiments about interrupt latency (for another thread) of about 1800ns from trigger to serve, an application with lots of high frequency interrupts might need to know.

We are doing some HW experiments on latency now. If it’s 1800ns, I am screwed. The docs say its 12 instructions to get into the ISR, at least at the HW architecture level. Can the RTOS firmware be doing that much to slow things down?

(We ARE talking Photon here, right? Not PDP-8!)

Stay tuned.

The only reason for this loooooooooong latency I can think of, might be rooted in the indirection introduced by the Particle Hardware Abstraction Layer.
I’ve already aired a plea for assistance at Particle, but since we are talking holidays now, that might take a bit :wink:

/** stm32_it.c **/
// exemplary for other EXTIx
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) != RESET)
	{
		/* Clear the EXTI line pending bit */
		EXTI_ClearITPendingBit(EXTI_Line0);

		if(NULL != HAL_EXTI_Handler)
		{
			HAL_EXTI_Handler(0);
		}
	}
}

/** interrupts_hal.c **/
void HAL_EXTI_Handler(uint8_t EXTI_Line)
{
    HAL_Interrupts_Trigger(EXTI_Line, NULL);
}

void HAL_Interrupts_Trigger(uint16_t EXTI_Line, void* reserved)
{
  //fetch the user function pointer from the array
  void* data = exti_channels[EXTI_Line].data;
  HAL_InterruptHandler userISR_Handle = exti_channels[EXTI_Line].fn;

  if (userISR_Handle)
  {
    userISR_Handle(data);
  }
}

I guess you can get the actual hardware speed if you circumvent the HAL and replace their void EXTIx_IRQHandler(void) with your own.

@ScruffR, Your original idea about the port number seems to be true, regardless of the fact that the priorities and sub priorities are set to the same values in the HAL. I tested simultaneously triggering interrupts on various pins, and the ISR connected to the pin with the lower port number (regardless of whether the port was A, B, or C) always ran first. I never saw any non-deterministic result. This is the code I used to test,

volatile int x = 4;

void setup() {
    pinMode(D0, OUTPUT); // attached equal length wires from D0 to the two interrupt attached pins
    digitalWrite(D0, LOW);
    pinMode(A0, INPUT);
    pinMode(D3, INPUT);
    attachInterrupt(D3, addOne, RISING);
    attachInterrupt(A0, multiplyByTwo, RISING);
    Serial.begin(9600);
    delay(5000);
}

void loop() {
   digitalWrite(D0, HIGH);
   delay(10);
   digitalWrite(D0, LOW);
   Serial.print(x);
   delay(100);
   x = 4;
}

void addOne() {
    x = x + 1;
}

void multiplyByTwo() {
    x = x * 2;
} 

Could this be based on the order that the processor reads the port pins?

1 Like

I did get this notion originally out an STM32 programming manual and got the impression that this is “hardwired” due to the order the port pins are fed to the EXTI “muxes” and the EXTI lines are fed into the NVIC logic.
But since I can’t find my original source and with reading up on the NVIC capabilities to dynamically assign priorities I started to doubt my memory :stuck_out_tongue:

I said “stay tuned” for the results of the timing experiments to determine interrupt latency.

With wifi ON:
Most of the time the latency is about 230 clock ticks. That translates to 1.9 microseconds and there’s a lot of variability in that number. The experiment was run a dozen times. Once we saw a latency as long as 33 microseconds. Ouch.

With wifi OFF:
Now we get no excursions to 33 us. The range is from 218 to 236 ticks, which translates to 1.82 us to 1.97 us. That’s an uncertainty band of 18 ticks, or 150us.

That probably kills attaching our sensor pins to Photon pins and using interrupts. I don’t see how we run our application turning wifi on and off while things are running. (Reconnecting can take 5+seconds.) We want better than half microsecond accuracy.

Our ISR is incredibly short and simple. It’s 3 instructions long and the ‘return’ is one of them. (Grab tick count. Store it. Return.) Wish we could run at the top of the interrupt priority list.

Keep in mind that interrupts are queued by priority. There’s nothing to stop you setting up your own interrupt handler, using the NVIC directly and using a higher priority.

1 Like

I was not aware that “normal users” were able to do this! I am glad to hear you say it is possible. We had thought that doing something like this was a major violation of the HAL abstraction.

Is there any documentation on how to do this? Or are there any examples?

Since I have you on the phone and you are a knowledgeable person, let me ask a question to which I have not been able to get an answer. In Photon, what is the maximum number of digital pins that can have an interrupt attached at once? Some people say 4 or 5. I need six.

OK, I don't take it personal, but @jim_hahn why do you not believe what I've said several times about the max. number of attachable interrupts at once?

In the above post I also had a link that shows how to do the interrupt setup manually.

Mat might prove me wrong on the one or the other pin (especially about RX/TX), but no way it's only five.
I even wouldn't be surprised if you could even attach interrupts to the pins sharing one EXTI line (for sure you won't be able to have contradicting trigger-modes or seperate ISRs).

Yes, it does violate the HAL attraction, but keep in mind this abstraction is mainly for system firmware so we can be sure it is portable. For application code,you are free to break portability and use STM32 code directly if you need it.

For examples, see the HAL code that is referenced earlier in this thread. The CMSIS library is how the NVIC interrupts are programmed.

Regarding the maximum number of pin-driven interrupts, ScruffR’s advice still stands - there’s definitely more than 5 interrupt gpio sources - internally there are 16 channels. Not all of these are exposed to external pins.


/*
 * GPIO_PinSource0: A7 (WKP), P1S0, P1S2, B2, B4
 * GPIO_PinSource1: D5, RGBR, P1S1, P1S5, B3, B5
 * GPIO_PinSource2: A2, RGBG, C0, PWR_UC
 * GPIO_PinSource3: D4, A1, RGBB
 * GPIO_PinSource4: D3, A6 (DAC/DAC1), P1S3, RESET_UC
 * GPIO_PinSource5: D2, A0, A3 (DAC2)
 * GPIO_PinSource6: D1, A4, B1
 * GPIO_PinSource7: D0, A5, SETUP_BUTTON
 * GPIO_PinSource8: B0, C5, PM_SCL_UC
 * GPIO_PinSource9: TX, C4, PM_SDA_UC
 * GPIO_PinSource10: RX, C3, TXD_UC
 * GPIO_PinSource11: C2, RXD_UC
 * GPIO_PinSource12: C1, RI_UC
 * GPIO_PinSource13: D7, P1S4, CTS_UC, LOW_BAT_UC
 * GPIO_PinSource14: D6, RTS_UC
 * GPIO_PinSource15: D5, LVLOE_UC
*/

This table includes all pins arranged by source for the Photon, P1, and Electron, so not all of them make sense for the Photon.

I count 13 separate pin sources (including RX/TX) on the Photon.

4 Likes