Interrupting a time-consuming library function to sample data without gaps?

Hi, I wasn't sure whether this should go in 'Project Feasibility' or 'Programming Questions', but it deals entirely with programming so I figured I would put it here.

I'm using the Arduino IDE with an Adafruit Circuit Playground Express microcontroller to build a proof-of-concept for an engineering class I'm in. To make a long story short, what I'm attempting to do is to sample accelerometer data from the microcontroller and use the 'arduinoFFT.h' library to analyze its frequency spectrum.

This has worked perfectly thus far, but what I would like to do is be able to 'reanalyze' the data every two seconds or so, while still using all the data from the past ten seconds. The problem is that the FFT library that I use takes about 240ms to complete a run, and I need to sample from the accelerometer every 20ms. So running the FFT analysis means losing out on 12 datapoints every 2 seconds, which will cause major artifacts in my spectrum.

I read online about timer interrupts and how they can be used to 'stop' what the microcontroller is doing to run a small script, but many sources tell me that I can't do things like read from an accelerometer within an interrupt routine.

Is there any alternative way to do this? What would be ideal is if it was possible for me to halt the FFT script when I need to sample data and then pick up from where I left off after the accelerometer data is written into an array. Does such a thing exist in Arduino programming?

Probably the cleanest way would be to modify/rewrite the library to do its work in small, quick to process chunks, each chunk being processed on a single pass through loop(). Depending on your ability, that might be a challenge.

The alternative, as you have mentioned, would be to use interrupts. Normally you want an interrupt service routine to be as short and as quick as possible since, while an interrupt handler is running, other interrupts are blocked. What you could do however, is re-enable interrupts from within the ISR. This makes the ISR sort of into a normal function call and allows other interrupts to happen as normal. The caveat is that you must ensure that your ISR can never be called again before the first call is finished, otherwise you end up in a recursive situation which could corrupt your data structures, or even crash the stack.

Before writing any interrupt code, make sure you read Nick Gammon's helpful tips on interrupts.

I've just searched for the CPX specs, and I see it uses the LIS3DH accelerometer. The ST datasheet says that this device includes a 32 level fifo buffer, so you should be able to use that to give you a 32x20ms = 640ms window for the FFT code to run. I suggest you find the full application notes for the device and learn how to use the fifo.

arduarn:
What you could do however, is re-enable interrupts from within the ISR. This makes the ISR sort of into a normal function call and allows other interrupts to happen as normal. The caveat is that you must ensure that your ISR can never be called again before the first call is finished

This sounds ideal to me. If I'm understanding this correctly, then my program should work since it takes far less than 20ms to sample acceleration data.

You wouldn't happen to know where I can find some example timer interrupt scripts for Circuit Playground Express microcontrollers? There's a wealth of examples for Arduino Uno but not very many for my specific board :frowning:

alexmonro:
I've just searched for the CPX specs, and I see it uses the LIS3DH accelerometer. The ST datasheet says that this device includes a 32 level fifo buffer, so you should be able to use that to give you a 32x20ms = 640ms window for the FFT code to run. I suggest you find the full application notes for the device and learn how to use the fifo.

This also sounds ideal. When you say to find the full application notes, are you talking about the CPX or the LIS3DH accelerometer?

if it was possible for me to halt the FFT script when I need to sample data and then pick up from where I left off after the accelerometer data is written into an array

That is pretty much the definition of of "Interrupts"...

I read online about timer interrupts and how they can be used to 'stop' what the microcontroller is doing to run a small script, but many sources tell me that I can't do things like read from an accelerometer within an interrupt routine.

The reason that you can't (shouldn't) read an accelerometer from an interrupt routine is that functions like "analogRead()" or I2C reads are pretty slow by the standards that are "desirable" for an ISR. There are a couple of ways around this:

  • You can decide that with a 20ms sample interval and 240ms compute-bound main task, that the 200us needed to read the sensor isn't so bad after all (assuming that it works.) This is (may be?) especially the case on an ARM-based board with multiple interrupt priorities. This is the easiest solution. While general opinion is against it, it IS a valid microcontroller strategy to do a lot of processing in interrupts while some "background task" happens in the foreground...
  • The SAM chips have fancy peripherals. You can probably set up the peripherals to take samples and store them in memory with little to no CPU intervention at all (DMA, Event System, pin-scan.) Even on a lowly AVR, it would be possible to use the timer interrupt to start an ADC or I2C operation, and then use separate ADC or I2C interrupts to actually read the data. This is probably the most "correct" solution, but it involves stepping considerably beyond the normal complexity of Arduino operation.
  • You could speed up and/or break apart the FFT library. For example, I see that it uses "double" for all the variables - does it need to?
  • You could run an RTOS. Essentially that would run timer interrupts and implement (3) "invisibly" - you could set your measurement task at high priority to run every 20ms, and your FFT task to run at low priority. You might have to throw away most of the Arduino libraries, though.

As it stands, I think I'm going to go with the interrupt-based sampling since I'm confident that there won't be any problems with my accelerometers that will make it take longer than 20ms. This is also the only solution I'm confident that I can actually implement in a couple of days.

The only problem is that I don't actually know where to find instructions on how to set up timer interrupts on a Circuit Playground Express. The vast majority of the documentation on how to program timer interrupts is written for Arduino microcontrollers with ATMEGA chips.

Sorry, I didn't even notice your mentioning of the somewhat unusual hardware in your first post.

Try searching for "ATSAMD21 timer library" and see if any of the existing libraries do what you want. If not, then you may need to start reading the data sheet for your MCU.

arduarn:
Sorry, I didn't even notice your mentioning of the somewhat unusual hardware in your first post.

Try searching for "ATSAMD21 timer library" and see if any of the existing libraries do what you want. If not, then you may need to start reading the data sheet for your MCU.

Will this work? It's for the Arduino Zero, which uses a very very similar microprocessor. Can I try running this on my microcontroller without fearing damage?

PossiblyJimmy97:
Will this work? It's for the Arduino Zero, which uses a very very similar microprocessor. Can I try running this on my microcontroller without fearing damage?

I should think the risk of damage is minimal. If you can get it to compile then give it a go. Where is your sense of adventure?