This shouldn't be an issue with the current code, but may become a time bomb if you ever decide to reduce the delayTimer to less than 1000 as this might cause a DIV/0 exeption.
I'm currently running this code as a test with 0.6.4
//SYSTEM_MODE(MANUAL);
SYSTEM_THREAD(ENABLED);
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
enum SENSOR_STATUS
{ SENSOR_FAIL = 2
, SENSOR_SUCCESS = 3
};
const uint32_t msMeasuring = 3000;
const uint32_t msRefresh = 0; // as rapid as possible 10000;
const int pinData = D3;
const int pinPower = C0;
const int pinPWM = D0; // for isolated test bridge pinPWM to pinData
volatile uint32_t rpmCount;
uint32_t rpm;
uint32_t timerStart;
uint32_t timerStop;
SENSOR_STATUS status;
void getRpm();
void interrupt();
void setup() {
Serial.begin();
pinMode(pinData , INPUT);
pinMode(pinPower, OUTPUT);
pinMode(pinPWM , OUTPUT); // for isolated test
}
void loop() {
static uint32_t msLastRefresh = 0;
static uint32_t pwmFrequency = 0;
if (millis() - msLastRefresh >= msRefresh) {
getRpm();
Serial.printlnf("RPM: %4lu, PWM: %4lu, millis: %10lu --------|---------|---", rpm, pwmFrequency, millis());
Serial.flush();
pwmFrequency = random(10, 5000);
analogWrite(pinPWM, 1, pwmFrequency); // set the test frequency for next turn
msLastRefresh = millis();
}
}
void getRpm() {
pinResetFast(pinPower);
delay(50);
rpmCount = 0;
timerStart = millis();
attachInterrupt(pinData, interrupt, RISING);
while(millis() - timerStart < msMeasuring) Particle.process(); // in MANUAL mode no need
timerStop = millis();
noInterrupts();
detachInterrupt(pinData);
interrupts();
rpm = (1000 * rpmCount) / (timerStop - timerStart);
status = (0 <= rpm && rpm < 5000) ? SENSOR_SUCCESS : SENSOR_FAIL;
pinSetFast(pinPower);
}
void interrupt() {
rpmCount++;
}
@ScruffR, yes, I agree that is a time bomb – not only for the reason you pointed out, but also because it limits my measurement intervals to whole seconds. Thanks for pointing that out.
@peekay123, the code in post #20 made it to location 8 and then crashed. Which I think means it crashed when interrupts() was called, re-enabling interrupts. Is this the right way to read this? Is there any other possible answer?
I can now see the same issue (but only at high trigger frequencies 30000+) and am now trying this
attachInterrupt(pinData, interrupt, RISING);
while(millis() - timerStart < msMeasuring) Particle.process(); // in MANUAL mode no need
timerStop = millis();
//noInterrupts();
ATOMIC_BLOCK() {
detachInterrupt(pinData);
}
//interrupts();
let’s see if that helps
Update:
That didn’t help.
Now testing 0.7.0
Update:
0.7.0 is no better in this regard.
How about not detaching the interrupt?
You depower the sensor, so no interrupts should happen anyway, right?
With the interrupts() in place like in your code the same happens to me.
With ATOMIC_BLOCK() the freeze happens in there.
Without noInterrupts()/interrupts() or ATOMIC_BLOCK() it happens with detachInterrupt().
This is definetly a bug that needs reporting, but for the time being you may have to find a workaround, right?
Would it be possible to add a divide-by-10 counter in between your sensor and the Electron, or is your hardware set in stone? One of those would get you back to about the same rate going into your device as you had before when things were working ok.
@Ric, I don’t think that 5kHz signals should be too excessive for a 120MHz controller to handle interrupts with (not even 50kHz for very short ISRs).
While this might be a viable workaround, I’d still think it’s a bug that needs to be addressed.
I originally tested all this on a atmega328p, so I made the assumption the more powerful processor in the Electron would be able to handle this pulse rate.
Also, I’m not sure why the device would run for hours at that rate, and then crash randomly, unless it is a software limitation.
I agree. I was thinking of it more as a workaround, if the fix takes time in coming (or even figuring out what the fix needs to be). Btw, do you ever sleep?
The workaround has been working for 15 hours or so, both in my normal application and in the smaller application above. I would say this is a good workaround for now, and I am looking forward to seeing this get fixed for real.
One question I had after thinking about this some more is what would a maximum pulse rate be for the Electron? 10kHz? 100kHz?
Yes, I've realized this for some time... this function originally was used to measure pulses per second from a variable reluctance sensor pointed at a ring gear on an engine. The ring gear had 60 teeth, so RPS == actual engine RPM. I should change the name now that we are doing other things with this function...
As my test with the PWM shows 65kHz is no problem by itself.
The theoretic maximum speed with attachInterrupt() having an interrupt latency of 5~8µS would be in the reagon of just above 100kHz, but with the new 0.8.0 feature of low level interrupts the latency could hopefully be reduced to under 2µs which would allow for 500kHz interrupts, but only hypothetically, since with anything above 100kHz your application code will spend more time being interrupted than actually running
Additionally higher priority interrupts will start interfering considerably with the timing of these high frequency low priority interrupts.