SAADC PPI trigger channels separately

Hello, I'm working on an application with precise timing requirements. Basically I need to generate a short pulse and sample two channels during the pulse. In addition, I would like to use a third channel for a slower/low-priority battery monitor task.

I already have implemented the GPIO control for the pulse via Timers and PPI, and I was planning to use it as well to trigger ADC acquisitions during the pulse. However, I have two doubts:

  • Since the pulses happen with high frequency, I would like the ADC to store samples in a buffer autonomously. While the ADC supports buffering, it's unclear to me if I can still trigger manually each acquisition (via PPI) when using this feature (nrf_drv_saadc_buffer_convert or similar), or if the ADC just samples on its own time. For me, it's critical that the trigger is controlled for each sample by the PPI. To clarify with an example, I would like to setup a buffer (eg: 10 samples), then trigger via PPI 10 single acquisitions and only after the 10th get the callback/read the ADC buffered data. Is this possible?
  • Secondly, I would like to add a third channel that I can trigger occasionally for battery monitoring, independent of the time-sensitive application.

If not, what approach do you suggest?

Thank you!

  • Hi Andrea

    For me, it's critical that the trigger is controlled for each sample by the PPI. To clarify with an example, I would like to setup a buffer (eg: 10 samples), then trigger via PPI 10 single acquisitions and only after the 10th get the callback/read the ADC buffered data. Is this possible?

    Yes, this is possible, and as it happens there is a chapter in the DevAcademy that walks through this specifically. Please have a look at the relevant chapter here

    Essentially you can trigger the SAADC SAMPLE task from anywhere over PPI, and only get a callback from the ADC driver when the buffer is full. 

    Secondly, I would like to add a third channel that I can trigger occasionally for battery monitoring, independent of the time-sensitive application.

    There is no good way to implement different channels that you sample at different frequencies unfortunately. Essentially you have to choose between one of the following workarounds:

    a) Sample all the channels at the same, high frequency, and discard the samples you don't need for the slow channel. 

    b) Reconfigure the SAADC module every time you want to sample the slow channel, and add the slow channel to the list. This only works if you can accept a bit of downtime when the SAADC module is being reconfigured. 

    Best regards
    Torbjørn

  • Hi, thank you the resource you provided is very useful. the solution you linked uses double buffering which I do not really need.

    However, it was good to understand the problem. It looks like the buffers are "consumed" as if in a queue, correct?
    I imagined that the START event would prepare the ADC with the current buffer, without the need to setup a new one (since I am only using one).

  • Regarding the second point, ok I will try to think of something thanks for confirming

  • Hi Andrea

    Double buffering allows you to switch from one buffer to the next almost instantaneously, which is necessary if you want to sample the ADC very quickly without any gaps in the sampling. The SAADC module supports sampling up to 200kHz, which means you only have 5us between one sample and the next, which is not sufficient time to reconfigure the buffer if other interrupts occur in the system. 

    If you sample much slower, and if you can decide when the sampling should occur, then I agree the double buffering might not be required. Still, using double buffering shouldn't have any negative impacts on the application, except for slightly more complex buffer handling in the application. 

    Best regards
    Torbjørn

  • My use case is a bit different, I don't really need a very fast sampling rate but I need to sample in a very precise moment during a short GPIO pulse.
    I have a trigger that starts this task with N short GPIO pulses, and I want to acquire a few samples during the pulses. PPI is used to toggle GPIOs and to trigger the sampling in the correct moment.
    Afterwards, I have plenty of time to reconfigure the ADC if needed.

    However I really would like to understand well how this peripheral works, as I did not find very precise information for some edge cases.
    For example, while the double-buffered solution works, with a single buffer it does not, and I just have an idea why that might be the case.

    With the doubled buffered solution, I add the new buffer in the callback as shown in the example, and I can always trigger the ADC by 1) triggering TASK_START and then 2) triggering TASK_SAMPLE via PPI.

    However, if I only use one buffer TASK_START is not sufficient, and I need to call nrfx_saadc_mode_trigger() again before I can start sampling with PPI.

    I believe this is the case because in the first case the acquisition is never "finished" (intended as EVT_FINISHED is asserted, aka all buffers are filled), while in the second case since there is a single buffer EVT_DONE and EVT_FINISHED both trigger. And it seems that after a EVT_FINISH I cannot simply use a TASK_START to re-trigger the ADC (not even if I add a buffer first).

    Would be nice to have a confirmation that this is the case, as I found it quite confusing! Slight smile

Related