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!

Parents
  • 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

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

Reply Children
  • 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

  • Hi Andrea

    I tested the saadc example in the SDK (examples/peripheral/saadc), and I have no problems running the example with only one buffer configured. This example is doing something quite similar, using a timer to trigger sampling over PPI. If I comment out the second call to nrf_drv_saadc_buffer_convert(..) in the saadc_init() function the example works just as well as when both buffers are used. 

    Possibly you are configuring the SAADC driver in a different way? 

    Best regards
    Torbjørn

  • Hi,

    If you update the buffer in the callback, doesn't the ADC continue sampling? In my setup I want it to stop once that buffer is full, and later retrieve the data and trigger manually.
    To reproduce my setup you would need to comment out the same function also in the saadc_callback.

    In your setup, are you getting the FINISHED event as well?

    Does it make sense?

  • Hi Andrea

    Yaxit said:
    If you update the buffer in the callback, doesn't the ADC continue sampling? In my setup I want it to stop once that buffer is full, and later retrieve the data and trigger manually.

    Updating the buffer will start the SAADC module, true, but it won't sample it. Sampling is handled through PPI as mentioned earlier. 

    If you want it should certainly be possible not to set the buffers automatically in the callback, but rather do that at a later point in time when you want to run a new sample sequence. 

    Yaxit said:
    In your setup, are you getting the FINISHED event as well?

    I am getting the END event, yes, if that is what you mean by finished?

    I enabled logging for the nrfx_saadc module, and I can't spot any important differences when changing from two buffers to one. 

    Best regards
    Torbjørn

Related