Interrupts: #, priority, architecture

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

I don’t know if this is helpful or not, but please take note that hardware interrupts “pend”. I get the sense from this thread that there is a concern about missing one hardware interrupt due to another hardware interrupt occurring on a different pin at the same time. Each time the microcontroller tests interrupts (between machine language instructions), it will end up processing the highest priority interrupt that is pending. But lower priority interrupts continue to pend in hardware registers, and when interrupts return back down the tail chain to the level of a pending interrupt, its ISR will then execute. This means that if you are worried about missing a CHANGE interrupt because it will take too much time to get to it, you need not worry because the hardware detects the CHANGE and pends it in a register, If, on the other hand, you must pick up the precise microseconds time on the interrupts, then you are subject to the variabilities of the environment where multiple, higher priority interrupts can occur (often outside of your control).

2 Likes

BobG: I assume interrupts will never get lost. (!)

My concern was totally with getting the time, via the TICK count, of when my external event took place. We measured latency at 1.9us usually but hopped up to 33us undoubtedly because a higher priority interrupt took place.

I open coded a polling loop which we will run with interrupts off. It takes 143ns to go around this loop so our induced error is in that magnitude. Lots better than 1900ns. Our sensor application wants less than 500ns timing error.

We are afraid of changing the priority level of pin changes by modifying underlying RTOS maintained data structures. And there are not enough Input Capture registers to do the job; we need six.

1 Like

ScruffR
Didn’t take it personal! When my partner and I jumped into the swimming pool named Particle, we we hit with a ton of information. Some of it stuck even if it was wrong. Perhaps it was in a discussion of Core chip that I saw something that said FIVE pins at once. Then I saw an underlying table of interrupt information for the Photon which showed there were five EXTI levels. So I equated them and that helped set the bad idea in concrete.

Another example of this was my partner kept telling me not to use D2. He had early on read, in the Core documentation, that D2 was unavailable for something we needed. Turns out that in the Photon that wasn’t true. But my partner kept bringing it up until we figured out where the false impression came from.

So, sorry - yes, I should have listened to you.

2 Likes

@jim_hahn: 500 ns resolution is getting down into dedicated hardware range, albeit not quite there yet. But I would venture to say that you need a dedicated, fast microcontroller for sure. That means no RTOS, no higher priority interrupts from WiFi, USARTs, internal counter-timers used for micros/millis, etc. If you can figure this out on the Photon, the more power to you. Personally, I’d be inclined to use a dedicated fast microcontroller, such as on the Core or Photon, but dedicated to your code that responds to sensor interrupts and stores the timings and then communicate the results over to a Photon for further processing and Internet communication when there is time between sets of critical measurements (assuming that such times exist). Just a thought, anyway. P.S., I think that some for the Freescale microcontrollers that are meant for automotive ECU use might fit this bill.

1 Like