This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Not sure on how to extend the saadc example to multiple analog input pins and cut out delay

Hi all,

I've gotten the saadc example working on my product, reading in values as I would expect. However, I have two problems. First, I want to be able to use multiple input pins after I first initialize the adc. Since the channel choice (`channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7)`) is at the very top of the init code, it seems like I'd have to run the init again with a different NRF_SAADC_INPUT chosen in order to use a different channel. Is this incorrect? If so, how do I simultaneously enable multiple channels.

Second, I've had to build a 5-second delay into my AdcRead function in order to give the ADC enough time to collect sample. If I shorten the delay or return a value immediately, I get an error saying that the ADC is busy. I've inserted my code below for clarity.

My init function looks like:

    ret_code_t err_code;
    nrf_saadc_channel_config_t channelConfig =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);

    err_code = nrf_drv_saadc_init(NULL, saadcCB);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &channelConfig);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(ADCBufferPool[0], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(ADCBufferPool[1], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

And my AdcRead function looks like:

    ret_code_t err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
    APP_ERROR_CHECK(err_code);

    // setup m_timer for compare event every 400ms
    uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, 400);
    
    nrf_drv_timer_extended_compare(&m_timer,
                                   NRF_TIMER_CC_CHANNEL0,
                                   ticks,
                                   NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                   false);
    nrf_drv_timer_enable(&m_timer);

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
                                                                                NRF_TIMER_CC_CHANNEL0);
    uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();

    // setup ppi channel so that timer compare event is triggering sample task in SAADC
    err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                          timer_compare_event_addr,
                                          saadc_sample_task_addr);

    // enable sampling
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);

    // Wait for sampling to happen
    BuDelayMs(5000);

    *val = AdcRetVal;
    if (err_code != NRF_SUCCESS)
    {
        return ADC_CHANNEL_ERROR;
    }
    return ADC_SUCCESS;

This is my callback event handler:

    uint32_t adcValAccum = 0;
    uint16_t i;
    ret_code_t err_code;
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);

        for (i = 0; i < SAMPLES_IN_BUFFER; i++)
        {
            adcValAccum += p_event->data.done.p_buffer[i];
        }

        AdcRetVal = adcValAccum / 5;
    }

Parents
  • Hi,

    Q1) As mentioned in the driver documentation, each channel can be configured independently and they are configured and enabled by the function nrf_drv_saadc_channel_init. That is all there is too it. This code for example, starts sampling on channel 0 and 1:

        ret_code_t err_code;
        nrf_saadc_channel_config_t channel_config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
    
        err_code = nrf_drv_saadc_init(NULL, saadc_callback);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_channel_init(0, &channel_config);
        APP_ERROR_CHECK(err_code);
    
    
        channel_config.pin_p = NRF_SAADC_INPUT_AIN1;
        err_code = nrf_drv_saadc_channel_init(1, &channel_config);
        APP_ERROR_CHECK(err_code);
    
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);

    Note that the parameter size of function nrf_drv_saadc_buffer_convert() must be a multiple of the number of enabled channels. Otherwise, undefined behavior may occur.

    Q2) Which function is returning the busy error? Have you looked up the function documentation? 

Reply
  • Hi,

    Q1) As mentioned in the driver documentation, each channel can be configured independently and they are configured and enabled by the function nrf_drv_saadc_channel_init. That is all there is too it. This code for example, starts sampling on channel 0 and 1:

        ret_code_t err_code;
        nrf_saadc_channel_config_t channel_config =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
    
        err_code = nrf_drv_saadc_init(NULL, saadc_callback);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_channel_init(0, &channel_config);
        APP_ERROR_CHECK(err_code);
    
    
        channel_config.pin_p = NRF_SAADC_INPUT_AIN1;
        err_code = nrf_drv_saadc_channel_init(1, &channel_config);
        APP_ERROR_CHECK(err_code);
    
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);

    Note that the parameter size of function nrf_drv_saadc_buffer_convert() must be a multiple of the number of enabled channels. Otherwise, undefined behavior may occur.

    Q2) Which function is returning the busy error? Have you looked up the function documentation? 

Children
  • Hi Martin. Thanks for the reply.

    Q1) Ah yes, I understand now. I wasn't quite understanding the documentation, I believe. So I just have to enable both channels, as you said above, and then the results buffer will contain my the results of the sampling of both channels according to the documentation in section 6.23.4 in the product spec?

    Q2) The error is returning from the nrf_drv_saadc_buffer_convert function call in my callback function. After further reviewing the documentation, it seems like the delay is necessary as otherwise I would be attempting to read the results immediately after enabling sampling. Looking at the source code tells me that the driver is still initializing and is unable to enable interrupts (which is the internal fn call that causes the error).

  • Q1) Yes, that is correct.

    Q2) Great! Let me know if you run into further troubles. 

Related