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

nRF52832 SDK14 - SAADC calibration issues

I'm using the nRF52832 with softdevice s132 and SDK14 with ARM GCC

I'm trying to set up the SAADC peripheral to read battery voltage, and I can get everything working as expected except periodic calibration. At boot, I call my initialization function, and then the application starts a timer for some number of seconds or minutes. When that timer expires, before trying to start a reading, my code checks to see if I should do calibration first. Due to the way it's set up, it always calibrates on the first timer expiration after boot, and this is where it fails. Here's a simplified version of my logic, which I have confirmed reproduces the issue without any timers or anything. There's more context in the code snippet comments. What am I doing wrong?

I've also tried setting it up like one of the examples where I don't actually check to see if I should calibrate until I get a NRF_DRV_SAADC_EVT_DONE event in the SAADC event handler in case that made a difference for some reason, but it doesn't. I skip the calibration check, start a reading, get the DONE event, check if calibration is needed, and then try to abort, but the abort call still locks up.

ret_code_t err_code;

/*
 * Another configuration I've been testing uses AIN3 and 1/2 gain.
 */
nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);

channel_config.gain = NRF_SAADC_GAIN1_6;
channel_config.burst = NRF_SAADC_BURST_ENABLED;
channel_config.acq_time = NRF_SAADC_ACQTIME_40US;

/*
 * sdk_config.h settings
 *      #define SAADC_CONFIG_RESOLUTION 3   // 14-bit
 *      #define SAADC_CONFIG_OVERSAMPLE 3   // 8
 *      #define SAADC_CONFIG_LP_MODE 1      // low power enabled
 *      #define SAADC_CONFIG_IRQ_PRIORITY 7
 */
err_code = nrf_drv_saadc_init(NULL, battery_saadc_callback);
APP_ERROR_CHECK(err_code);

// Initialize SAADC channel 0 with the specified channel configuration
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code);

// SAMPLES_IN_BUFFER set to 1
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);

/* do some stuff to set up application logic */

/*
 * Based on examples and forum posts, I call this to try to abort any pending
 * actions before trying to start calibration. But this call always times out
 * at nrf_drv_saadc.c line 604:
 *  (ERR0204) /source/firmware/nrf5-sdk/components/drivers_nrf/saadc/nrf_drv_saadc.c:604
 */
nrf_drv_saadc_abort();

/*
 * Alternatively, I've tried uninitializing and then reinitializing the SAADC
 * and the channel without aborting. When reinitializing, I make sure _not_ to
 * set up the buffers since registration of the buffers seems to be related to
 * the problem. This actually does allow calibration to start and complete, but
 * the next time after that that I call nrf_drv_saadc_sample(), it always fails
 * with NRF_ERROR_INVALID_STATE.
 */
//nrf_drv_saadc_uninit();
//err_code = nrf_drv_saadc_init(NULL, battery_saadc_callback);
//APP_ERROR_CHECK(err_code);
//err_code = nrf_drv_saadc_channel_init(0, &channel_config);
//APP_ERROR_CHECK(err_code);


while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS);

/*
 * After this, the SAADC event handler expects a NRF_DRV_SAADC_EVT_CALIBRATEDONE.
 * When that is received, I call nrf_drv_saadc_buffer_convert() on my two buffers
 * and then call nrf_drv_saadc_sample(). As described above, if I replace the abort call
 * with the uninit/reinit code commented out above, it does actually get to this point
 * but nrf_drv_saadc_sample() always returns NRF_ERROR_INVALID_STATE.
 */

Edit: Here's the relevant code for the follow-up issue where calibration completes correctly but the subsequent sample doesn't generate any events. This is the CALIBRATEDONE part of my SAADC event handler and the corresponding function that gets scheduled:

static void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
      ret_code_t err_code;
      switch(p_event->type)
      {
            case NRF_DRV_SAADC_EVT_CALIBRATEDONE:
                  ret_code_t err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
                  APP_ERROR_CHECK(err_code);
                  // same for m_buffer_pool[1]

                  err_code = app_sched_event_put(NULL, 0, on_cal_complete_handler);
                  APP_ERROR_CHECK(err_code);

                  nrf_delay_us(500); //delay avoids the issue
                  break;
      }
}

static void on_cal_complete_handler(void * p_event_data, uint16_t event_size)
{
    ret_code_t err_code = nrf_drv_saadc_sample();
    APP_ERROR_CHECK(err_code);
}
Related