T4 - ISR Handling Confusions!

AshPowers

Well-known member
I'm having a bit of difficulty wrapping my head around what happens when ISRs are used when there is another process which consumes more time than the rate at which ISR triggers are known to be occurring...

I am using an ILI9341 2.8" display and have famebuffering enabled. When measuring the time it takes to perform the tft.updateScreen() I find that this process requires ~24ms to complete.
I have two ISRs setup to monitor incoming pulses for engine RPM and vehicle speed.
Engine RPM pulses will vary from 42Hz at idle up to 370Hz at 7400RPM. 24ms to 2.7ms, respectively.
Vehicle speed pulses will vary from 0Hz up to 280Hz by 200MPH.. 0ms to 5ms, respectively.

Within these two ISR code sections (for engine RPM and vehicle speed), I have reduced both of them down to a bare minimum of 3 lines of code to capture the duration of time between the current pulse and the previous pulse.

This elapsed time between pulses are used in another section of code for displaying this information.. after performing the necessary math to convert the time metric into the respective units of RPM and MPH.

This arrangement works flawlessly and the reported vehicle speed and engine RPM are accurately shown at each refresh of the ILI9341 display over the full range of engine RPM as well as speeds upwards of 150MPH.
.
.
.

Considering 5000RPM at 90MPH... The RPM ISR is being triggered every 4ms and the speed ISR is being triggered every 7.9ms.. but the tft.updateScreen() process takes 6x longer than the RPM ISR trigger interval and ~3x longer than the vehicle speed ISR trigger interval.

Am I to understand that in this 5000RPM/90MPH condition when the tft.updateScreen() process is called, the micro is interrupting said process ~6 times for the RPM ISR AND ~3x for the vehicle speed ISR?
 
Last edited:
As a follow up to this question.. I have another section of code where I am calculating the torque and horsepower produced by the engine based on the change in vehicle speed over the change in time..

The speed ISR is producing a single public variable containing the time difference between the current pulse and the previous pulse...

Code:
void ISRSpeed() {
TimeNowSPD = micros();
SPDPulse1 = TimeNowSPD - SPDPulse2;
SPDPulse2 = TimeNowSPD;
}

This works perfectly to generate the current speed of the vehicle. But the speed value is calculated from the time interval into a MPH within the loop that ends with the tft.updateScreen(); Within the loop there is also the maths that determine acceleration a = dV/dt.. So, if the speed ISR kicks off ~3 times during the updateScreen, on the next iteration of the main loop, the value of dT cannot be used to represent the difference in time between time now and the last time that the speed was calculated in the previous iteration of the loop... While you can still accurately calculate speed as dT from the "time interval between the last two pulses" provided by the ISR, that time interval does NOT represent the duration between the last calculation of speed and the time of the speed calculation "now".....

Modifying code and testing... will report back soon...
 
@KurtE can correct details if needed ... but ...

If the tft.updateScreen() is the DMA driven code - AFAIK that 'generally' runs parallel to the processor loop() and interrupt code.

If DMA - it does, AFAIK, trigger interrupts at times for data feeding itself as a screen image can not be fed in a single block - but the transfers between those are under control of the 'independent' DMA engine, except where they may have bus contention for data access.

Given the above might be true - data updates and changes will be calculated and updated while the prior tft.updateScreen() completes it 24ms process. The display will always be somewhat behind, or miss updates to some degree due to the time it takes.

If the 'update region' for MPH and RPM and Acceleration is a limited rectangle excluding more static data - it seems the driver allows for that and updating a smaller portion of the screen would complete faster.
 
Actually: tft.updateScreen does not update the screen using DMA, think of it like a call to writeRect with a bitmap the size of the screen. So it is going to stay in this code however long it takes to write out the whole screen.

however if you use: tft.updateScreenAsync() - This is the DMA version, which stays in this call only as long as it takes to start the update process. It needs to output the preamble stuff, then sets up the DMA to output all of the pixel data and then returns... There are other calls you can do to find out if it completed or not. And or wait for it to complete.

There is also a version of updateScreenAsync(true) which will start it up and keep it running, but that gives you other complications, of partial updates showing up or flashing if you do things like: clearScreen() and draw...
 
@KurtE - thanks for the details/attention ... neglected to note is wasn't ___Async() in use.

So in that case the Isr()'s will get called - data recorded/overwritten - but not attended to in loop() until the synchronous updateScreen() completes.
 
Am I to understand that in this 5000RPM/90MPH condition when the tft.updateScreen() process is called, the micro is interrupting said process ~6 times for the RPM ISR AND ~3x for the vehicle speed ISR?

Just to add to the answers from KurtE and defragster, you can't necessarily update your TFT display as frequently as you get new data interrupts.

Take a look at library FreqMeasureMulti. This library does something similar to what you are doing in your ISR, but instead of using micros() and computing the delta time yourself, it uses the input capture capability in the hardware timers to do that for you. Each time an interrupt occurs, the signal period (delta time) is computed and written to a FIFO. That way, if your loop() can't keep up with the data interrupt rate, it can read/process as many readings are in the FIFO on each execution of loop.

If you use FreqMeasureMulti(), you could change your loop() so it does something like this:

- every 100 ms
- read however many period measurements are in the FIFO
- compute the average
- use that value when updating the display

This is just one possibility, but it's the kind of thing you can do when you have a variable input data rate.
 
Agree, using FreqMeasureMulti is by far the best way, as it uses timer hardware captures a timestamp at the moment the pulse happens, regardless of interrupt latency.

But if you do end up with a situation where some interrupt is highly sensitive to response latency, and you can't use something like FreqMeasureMulti to make it not so sensitive, you can try to use interrupt priority levels.

Every interrupt can have a priority between 0 to 255, where lower numbers mean higher priority. But the hardware implements nesting only 16 levels, so 0 to 15 are all the same (highest possible priority), 16 to 31 are identical, etc. By default, most interrupts are configured to 128. Some interrupt like audio DSP are at a lower priority, and others like the timer which keeps Arduino timing functions like millis() are higher priority.
 
Back
Top