Running SAADC in continuous mode without timer/ppi trigger

Hi All,

I have problem running nRF5340 in continuous mode without any external trigger (timer/ppi). I would like to setup SAADC so that last conversion triggered new set of conversions.

I'm using nrfx_saadc_advanced_mode_set function with internal_timer_cc set to 0 and start_on_end to true. Then I setup two buffers (primary and secondary) and then call nrfx_saadc_mode_trigger.

With that configuration I get irq handler called with NRF_SAADC_EVENT_STARTED, but not get NRF_SAADC_EVENT_END which I believe should restart conversion with provided configuration.

Can you point me what I'm doing wrong?

Parents
  • Hi,

    It would help to determine exactly what the problem if you could post the exact code you used for configuring the SAADC, but it sounds like you are missing a trigger of the sampling.

    The advanced mode is mainly intended for use with PPI and external triggering of the sampling. Setting internal_timer_cc to 0 will disable continuous mode (the internal sampling timer in the SAADC peripheral).In the interrupt handler, the SAMPLE task is only triggered on STARTED event if continuous mode is enabled (in advanced mode). 

    Setting start_on_end will only cause the driver to trigger the START task in the interrupt handler when the END event is received, if this is not set, you need to do this in the application through PPI (this is done to mitigate this issue). 

    The SAMPLE task is triggered in the driver in simple mode, right away if no event handler is provided, or in the interrupt handler when the STARTED event is received. The SAMPLE task is also triggered in nrfx_saadc_mode_trigger() in advanced mode if no event handler is provided.

    Do you require to use the advanced mode? Sounds like the simple mode may suit your requirements better.

    Best regards,
    Jørgen

  • You still need to trigger the SAMPLE task externally when internal_timer_cc is set to 0, even it you call nrfx_saadc_mode_trigger(). This is from the SAADC driver API documentation:

    "When performing conversions in the non-blocking manner and nrfx_saadc_adv_config_t::internal_timer_cc is set to 0, sampling needs to be done by triggering NRF_SAADC_TASK_SAMPLE externally (for example by using the TIMER and/or the PPI/DPPI)."

    If you do not want to use PPI, you can trigger it in your application similar to how the driver does it for the blocking mode:

    nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);

  • Hi,

    I have created sample code below with two channels setup. It works when ADC_BUFFER_SIZE is set to 2. This defines buffer size for samples. I get NRFX_SAADC_EVT_READY in first callback and then alternately NRFX_SAADC_EVT_DONE and NRFX_SAADC_EVT_BUF_REQ.

    However, when i changed ADC_BUFFER_SIZE to 4 (to fit 2 samples for each channel) I only get NRFX_SAADC_EVT_READY and don't have any samples. I expected to get NRFX_SAADC_EVT_DONE twice and then one NRFX_SAADC_EVT_BUF_REQ. Can you tell me why buffer size can't be bigger than number of channels?

    #include <kernel.h>
    #include <nrfx_saadc.h>
    
    #define ADC_BUFFER_SIZE 2
    static nrf_saadc_value_t adcBuffer[2][ADC_BUFFER_SIZE];
    
    uint8_t currentBuffer = 0;
    
    static void adcCallback( nrfx_saadc_evt_t const * p_event )
    {
        nrfx_saadc_evt_type_t eventType = p_event->type;
    
        switch( eventType )
        {
            case NRFX_SAADC_EVT_DONE:
            {
                nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);
            }
            break;
    
            case NRFX_SAADC_EVT_LIMIT:
            {
            }
            break;
    
            case NRFX_SAADC_EVT_CALIBRATEDONE:
            {
            }
            break;
    
            case NRFX_SAADC_EVT_BUF_REQ:
            {
                nrfx_saadc_buffer_set( adcBuffer[currentBuffer], ADC_BUFFER_SIZE );
                ++currentBuffer;
                currentBuffer = currentBuffer % 2;
            }
            break;
    
            case NRFX_SAADC_EVT_READY:
            {
                nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);
            }
            break;
    
            case NRFX_SAADC_EVT_FINISHED:
            {
            }
            break;
        }
    }
    
    /*!
     * @brief Application entry point.
     */
    int main( int argc, char** argv )
    {
        printk("ADC example on %s\n", CONFIG_BOARD);
    
        nrfx_saadc_channel_t channelConfig = {
            .channel_config =
                {
                    .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
                    .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
                    .gain       = NRF_SAADC_GAIN1_4,
                    .reference  = SAADC_CH_CONFIG_REFSEL_VDD1_4,
                    .acq_time   = SAADC_CH_CONFIG_TACQ_40us,
                    .mode       = NRF_SAADC_MODE_SINGLE_ENDED,
                    .burst      = NRF_SAADC_BURST_DISABLED,
                },
                .pin_p          = NRF_SAADC_INPUT_AIN0,
                .pin_n          = NRF_SAADC_INPUT_DISABLED,
                .channel_index  = 0
        };
    
        nrfx_saadc_adv_config_t adcAdvancedConfig = NRFX_SAADC_DEFAULT_ADV_CONFIG;
        adcAdvancedConfig.start_on_end = true;
    
        nrfx_err_t nrfxError = nrfx_saadc_init( 0 );
    
        IRQ_DIRECT_CONNECT( SAADC_IRQn, 0, nrfx_saadc_irq_handler, IRQ_ZERO_LATENCY );
    
        // First channel.
        nrfxError = nrfx_saadc_channel_config( &channelConfig );
    
        // Second channel.
        channelConfig.pin_p = NRF_SAADC_INPUT_AIN1;
        channelConfig.channel_index = 1;
    
        nrfxError = nrfx_saadc_channel_config( &channelConfig );
        nrfxError = nrfx_saadc_advanced_mode_set( 0x03, NRF_SAADC_RESOLUTION_12BIT, &adcAdvancedConfig, adcCallback );
    
        // Set buffers.
        nrfxError = nrfx_saadc_buffer_set( adcBuffer[0], ADC_BUFFER_SIZE );
        nrfxError = nrfx_saadc_buffer_set( adcBuffer[1], ADC_BUFFER_SIZE );
    
        // Trigger conversion.
        nrfxError = nrfx_saadc_mode_trigger();
    
        while( 1 )
        {
            printk( "ADC1: p_buffer = %d\n", adcBuffer[currentBuffer][0] );
            printk( "ADC2: p_buffer = %d\n", adcBuffer[currentBuffer][1] );
            k_msleep( 1000 );
        };
    
        return 0;
    }

Reply
  • Hi,

    I have created sample code below with two channels setup. It works when ADC_BUFFER_SIZE is set to 2. This defines buffer size for samples. I get NRFX_SAADC_EVT_READY in first callback and then alternately NRFX_SAADC_EVT_DONE and NRFX_SAADC_EVT_BUF_REQ.

    However, when i changed ADC_BUFFER_SIZE to 4 (to fit 2 samples for each channel) I only get NRFX_SAADC_EVT_READY and don't have any samples. I expected to get NRFX_SAADC_EVT_DONE twice and then one NRFX_SAADC_EVT_BUF_REQ. Can you tell me why buffer size can't be bigger than number of channels?

    #include <kernel.h>
    #include <nrfx_saadc.h>
    
    #define ADC_BUFFER_SIZE 2
    static nrf_saadc_value_t adcBuffer[2][ADC_BUFFER_SIZE];
    
    uint8_t currentBuffer = 0;
    
    static void adcCallback( nrfx_saadc_evt_t const * p_event )
    {
        nrfx_saadc_evt_type_t eventType = p_event->type;
    
        switch( eventType )
        {
            case NRFX_SAADC_EVT_DONE:
            {
                nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);
            }
            break;
    
            case NRFX_SAADC_EVT_LIMIT:
            {
            }
            break;
    
            case NRFX_SAADC_EVT_CALIBRATEDONE:
            {
            }
            break;
    
            case NRFX_SAADC_EVT_BUF_REQ:
            {
                nrfx_saadc_buffer_set( adcBuffer[currentBuffer], ADC_BUFFER_SIZE );
                ++currentBuffer;
                currentBuffer = currentBuffer % 2;
            }
            break;
    
            case NRFX_SAADC_EVT_READY:
            {
                nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE);
            }
            break;
    
            case NRFX_SAADC_EVT_FINISHED:
            {
            }
            break;
        }
    }
    
    /*!
     * @brief Application entry point.
     */
    int main( int argc, char** argv )
    {
        printk("ADC example on %s\n", CONFIG_BOARD);
    
        nrfx_saadc_channel_t channelConfig = {
            .channel_config =
                {
                    .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
                    .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
                    .gain       = NRF_SAADC_GAIN1_4,
                    .reference  = SAADC_CH_CONFIG_REFSEL_VDD1_4,
                    .acq_time   = SAADC_CH_CONFIG_TACQ_40us,
                    .mode       = NRF_SAADC_MODE_SINGLE_ENDED,
                    .burst      = NRF_SAADC_BURST_DISABLED,
                },
                .pin_p          = NRF_SAADC_INPUT_AIN0,
                .pin_n          = NRF_SAADC_INPUT_DISABLED,
                .channel_index  = 0
        };
    
        nrfx_saadc_adv_config_t adcAdvancedConfig = NRFX_SAADC_DEFAULT_ADV_CONFIG;
        adcAdvancedConfig.start_on_end = true;
    
        nrfx_err_t nrfxError = nrfx_saadc_init( 0 );
    
        IRQ_DIRECT_CONNECT( SAADC_IRQn, 0, nrfx_saadc_irq_handler, IRQ_ZERO_LATENCY );
    
        // First channel.
        nrfxError = nrfx_saadc_channel_config( &channelConfig );
    
        // Second channel.
        channelConfig.pin_p = NRF_SAADC_INPUT_AIN1;
        channelConfig.channel_index = 1;
    
        nrfxError = nrfx_saadc_channel_config( &channelConfig );
        nrfxError = nrfx_saadc_advanced_mode_set( 0x03, NRF_SAADC_RESOLUTION_12BIT, &adcAdvancedConfig, adcCallback );
    
        // Set buffers.
        nrfxError = nrfx_saadc_buffer_set( adcBuffer[0], ADC_BUFFER_SIZE );
        nrfxError = nrfx_saadc_buffer_set( adcBuffer[1], ADC_BUFFER_SIZE );
    
        // Trigger conversion.
        nrfxError = nrfx_saadc_mode_trigger();
    
        while( 1 )
        {
            printk( "ADC1: p_buffer = %d\n", adcBuffer[currentBuffer][0] );
            printk( "ADC2: p_buffer = %d\n", adcBuffer[currentBuffer][1] );
            k_msleep( 1000 );
        };
    
        return 0;
    }

Children
No Data
Related