Report
2 months, 3 weeks ago.

As the I2C component is not ISR safe, what is best practise for precise sampling?

I've now understood that the I2C and SPI component are not interrupt safe (although they are thread safe)

https://docs.mbed.com/docs/mbed-os-handbook/en/latest/concepts/thread_safety/

This raises the question: how would I perform very precise (low jitter) sampling of an I2C sensor?

One option might be to create a minimalist driver using an API below mbed to grab the sample in real time, then dispatch this off to a thread context using mailbox. However I would prefer to use the mbed APIs "if" possible.

Another suggestion was to signal a waiting thread from a Ticker ISR, using either signal-wait or an event queue, and perform the read in the thread context. Furthermore the waiting thread should have higher priority than all the others. However this surely this will result in some sampling jitter as presumably mbed os won't pre-empt a lower priority thread immediately? ( I assume the scheduler at least allows the lower priority thread a full epoc if it is already in the running state)?

Does anybody have any suggestions ?

Comment on this question

2 Answers

2 months, 3 weeks ago.

Quote:

Another suggestion was to signal a waiting thread from a Ticker ISR, using either signal-wait or an event queue, and perform the read in the thread context. Furthermore the waiting thread should have higher priority than all the others. However this surely this will result in some sampling jitter as presumably mbed os won't pre-empt a lower priority thread immediately

This seems the most natural approach. The thread that dispatches the event queue could be real-time and would preempt any running thread on the return from interrupt context (see conversation here about global queues https://github.com/ARMmbed/mbed-os/pull/4406). Also, regarding the jitter, most sensors I've seen (SPI, I2C, etc.) tolerate jitter as they have to acquire the next sample and shift across / notify the host of data to collect assuming you're not just polling for data managing sample time from the main MCU in which case you might have jitter.

Thanks for the answer. The issue with jitter is one of signal integrity - for example, if I want to sample audio (using an external 16 or 24 bit ADC) and perform some signal processing, then it's critical that the sampling interval is as deterministic and jitter free as possible. Therefore, I need to *start* the conversion on precise intervals.

So to clarify with an example: let's say I want to compute the signal spectrum of some incoming audio. I choose to perform double buffering of the samples - populate front buffer with samples; perform FFT on the back buffer; then swap the buffers every time the buffer is full.

This is what I am proposing:

- I use a Ticker to simply signal a waiting thread A on precise intervals (it is the only interrupt I am currently using).

- Thread A has the highest priority and spends most of its time blocked in the WAITING state (waiting for a signal); once unblocked, it obtains the next sample via I2C and adds it into the front buffer;

- Thread B is lower priority and will perform a background FFT on the back buffer (utilising CPU cycles while thread A is blocked in the WAITING state).

- Conceivably, I might run other lower-priority threads that perform background tasks (save to SSD, Serial comms etc..)

I suppose the question comes down to this:

- Are you saying that when the ISR signals thread A, thread A will pre-empt all other threads *immediately*? - If thread B is in the RUNNING state, won't it be given it's full epoch? (and hence the jitter)

I had assumed the context switch would occur only once a full epoch has elapsed (unless a thread explicitly yields).

Many thanks.

posted by Nicholas Outram 02 Jul 2017

Quote:

Therefore, I need to *start* the conversion on precise intervals.

Most ADC/DAC devices have a sample period which can be set but assuming the one you have doesn't, there could be small jitter but even this approach should be quite accurate on timing. Here is a snippet of the behavior of the scheduler. http://www.keil.com/pack/doc/cmsis_dev/rtos2/html/theory_of_operation.html#Scheduler

Alternatively you could build a library that samples the ADC and override the lock and unlock functions and take ownership of synchronization or use the asynchronous API for I2C or SPI which aren't synchronized.

posted by Sam Grove 02 Jul 2017

Many thanks for taking the time to answer this - it really is appreciated. I'll certainly take a closer look at the docs on the scheduler.

Delegating hard real-time requirements to hardware is probably the way I am going to go in the near-short term (we also have an FPGA to mop-up such tasks and abstract such problems from the MCU) but it's good to know what can be achieved in software.

A nice exercise for my students would be to write an (initially) unprotected I2C driver and, as you say, take ownership of synchronization.

I wonder if there is a case for some additional components without thread locks, for example, I2C_UNSAFE and SPI_UNSAFE ?

posted by Nicholas Outram 03 Jul 2017
2 months, 3 weeks ago.

Correct me if I am wrong, but isn't it perfectly save to use those functions from an interrupt, as long as only that interrupt is using that specific peripheral?

So lets say, if you have on the (same) I2C peripheral two sensors, one you poll from your main function, the other one from an interrupt, then it is probably a matter of time until it goes wrong. However if you just have one sensor you poll from the interrupt, or two sensors you poll from equal priority interrupts, can it really go wrong?

Edit: In older question/answer I now see that apparently the lock from thread safety blocks usage in an interrupt. However this is only the case then for mbed OS 5. Mbed OS 2 (without RTOS) it should work fine in IRQ context.

Thanks for the answer. We've now jumped to mbed os 5 which is first alerted us to this issue. (The attraction of the schedular was too much :o)

posted by Nicholas Outram 02 Jul 2017

To post an answer, please log in.