Argon Interrupts Running Very Slowly

My problem basically comes down to simple instructions on the Argon taking much longer than is reasonable for a controller with a 64 Mhz crystal.

I’m using the particle argon to run two 360 degree feedback servos (parallax 360 if you’re curious ) The servo sends information about its current position using pulse width modulation, so I need to record the time of both the rising and falling actions. I have code that works for reading and controlling the angle of one servo, but I’ve been running into problems trying to read from both servos simultaneously. When i have two servos attached, one interrupt will get called during the other interrupt and by the time the second interrupt gets serviced, it wont give an accurate time.

This is the code that I’m using

#include <Arduin.h>
volatile unsigned long rise[] = {0,0} , fall[] = {0,0}, tHigh[] = {0,0};
bool newVal[] = {false,false};
int feed360 = D3;
int feed180 = D5;

float beg, fin;

void setup() {
  pinMode(feed360, INPUT);
  pinMode(feed180, INPUT)
  attachInterrupt(digitalPinToInterrupt(feed360), feedback360, CHANGE);
  attachInterrupt(digitalPinToInterrupt(feed180), feedback180, CHANGE);
  Serial.begin(9600);
}
void loop() {
}

void feedback360() {
//  beg = micros();
    if (digitalRead(feed360)) {
        rise[0] = micros();
        newVal[0] = true;
    }
    else {
        fall[0] = micros();  
        tHigh[0] = fall[0] - rise[0];
    }
//  fin = micros();
//  Serial.println(fin - beg);
}
void feedback180() {
    if (digitalRead(feed180)) {
        rise[1] = micros();
        newVal[1] = true;
    }
    else {
        fall[1] = micros();  
        tHigh[1] = fall[1] - rise[1];
    }
}

I checked the length of time that my interrupt was taking and found that the code took 28 microsecond to run when the line was high and 32 microseconds when it was low. This seemed really long. I couldn’t find a way to check the number of instruction for the argon, so I compiled the same code for an Arduino (which I assumed would be a close analog) and found that it was 154 instructions when the line was high and 185 instructions when the line was low. The Arduino has a 16Mhz crystal and, as expected, this code took 10 and 11 microseconds respectively to run on the Arduino. The Argon has 64 Mhz crystal so should go four times faster than the Arduino, and when I’ve tested longer functions the Argon has been faster than the Arduino. But for this set of instructions (and some other short instructions) the Argon has been running much slower than the same code on the Arduino.

Does anyone know why the Argon is running so slowly here or how I could make it run faster?

digitalRead() is very slow on Gen 3 devices. First try using pinReadFast() and see if that provides enough of a speed increase.

Thanks for the suggestion! pinReadFast() decreases it to 20 and 24 microseconds respectively. This definitely helps, but it only decreases my total error by about 30%.

If i can get the interrupt down to 10 microseconds then the error should be negligible.
Another less elegant solution I’ve thought about is a condition in the interrupt to throw out times that come in too soon after the previous interrupt. This decreases my overall error, but the minimum time between the rise and fall interrupts is 30 microseconds. So if the rise interrupt takes longer than 30 microseconds I’m no longer able to read angles close to zero.

this is the filter I’ve been considering, but I need to get the interrupt dow to about 15 microseconds to safely add this.

if (rise[1] - rise[0] > 30 || rise[1] - fall[0] > 30) {newVal = true;}

I was afraid of that. So the problem is that the nRF52 interrupt latency is really high, and quite variable. It’s probably not going to be possible to do it that way.

I’m pretty sure it’s possible to use the nRF52 timer peripheral to measure those pulses in hardware, which eliminates the interrupt latency problem. It’s even possible to do it from user firmware. Except it’s not at all documented, and even the nRF52 timer docs are super-confusing.

There’s some code in this library that shows how to access the nRF52 timer peripheral from user firmware, but I will warn you that it’s very confusing and hard to debug.

Interrupt latency may also be causing issues, but the times I’m measuring right now are from after the interrupt starts to before the interrupt ends. To get a picture of the time, I’ve also put the code from my interrupt in the main loop to measure time and gotten the same values that I’ve shared above, so my current measurements shouldn’t be picking up any of the latency from the interrupts.

I was just playing around with seeing what sort of latency micros() has. When I called micros() twice in a row with nothing between, I was getting values as high as 16 microseconds. With this level of inaccuracy, I won’t actually know if my interrupts have reached the time goals I’ve set. Is there a faster/more accurate way to get time on the argon? Perhaps something like timer1 on Arduinos?

In that case, you may have a higher priority interrupt that is interrupting your interrupt. If you’ve ever configured mesh networking on that device, that could be it. Doing a Mesh.off() and not using BLE at the same time may help. That’s just a guess; not positive that’s what’s happening.