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

How to get NRF_DRV_SAADC_EVT _CALIBRATEDONE.

Related to the following Q & A
https://devzone.nordicsemi.com/f/nordic-q-a/70355/saadc_task_calibrateoffset

In the process of checking the operation, we created the following two types of code (A) and (B).

Inquiry items

  • Q1 Regarding (B) below, I would like to know if the correction method is correct. If it is wrong, I would like to know the correct correction method.
  • Q2 I would like to know how to obtain NRF_DRV_SAADC_EVT_CALIBRATEDONE for (B) below.

Explanation of (A)

To check the operation of nrf_drv_saadc_calibrate_offset() on nRF52810
https://github.com/NordicPlayground/nRF52-ADC-examples saadc_low_power
Ported to nRF5_SDK_15.3.0_59ac345\examples\peripheral\saadc. Reference: Code(A)
1.65V is applied to AIN0.

NRF_DRV_SAADC_EVT_CALIBRATEDONE occurs, but the AD value cannot be obtained from the second time onward.
The return value of nrf_drv_saadc_calibrate_offset () does not become NRF_SUCCESS. Reference: Code(A)log

We speculate that the following errata are related.
[237] SAADC: TASKS_CALIBRATEOFFSET shall only be used before TASKS_START or after EVENTS_END

Explanation of (B)

Corrected (A) with reference to the errata. Reference: Code(B)
The AD value can now be obtained repeatedly.

However, NRF_DRV_SAADC_EVT_CALIBRATEDONE cannot be obtained. Reference: Code(B)log


Code(A)

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define SAMPLES_IN_BUFFER 5
volatile uint8_t state = 1;

static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
static nrf_saadc_value_t     m_buffer_pool[2][SAMPLES_IN_BUFFER];
static nrf_ppi_channel_t     m_ppi_channel;
static uint32_t              m_adc_evt_counter;
static bool                    m_saadc_calibrate = false;      


void timer_handler(nrf_timer_event_t event_type, void * p_context)
{

}


void saadc_sampling_event_init(void)
{
    ret_code_t err_code;

    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);
    APP_ERROR_CHECK(err_code);
}


void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);

    APP_ERROR_CHECK(err_code);
}
#define SAADC_CALIBRATION_INTERVAL 5              //Determines how often the SAADC should be calibrated relative to NRF_DRV_SAADC_EVT_DONE event. E.g. value 5 will make the SAADC calibrate every fifth time the NRF_DRV_SAADC_EVT_DONE is received.
#define SAADC_SAMPLES_IN_BUFFER 1                 //Number of SAADC samples in RAM before returning a SAADC event. For low power SAADC set this constant to 1. Otherwise the EasyDMA will be enabled for an extended time which consumes high current.


void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;

        if((m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)               //Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
        {
            m_saadc_calibrate = true;                                           // Set flag to trigger calibration in main context when SAADC is stopped
        }

        int i;
        NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);

        for (i = 0; i < SAMPLES_IN_BUFFER; i++)
        {
            NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
        }

        if(m_saadc_calibrate == false)
        {
            err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);             //Set buffer so the SAADC can write to it again. 
            APP_ERROR_CHECK(err_code);
        }

        m_adc_evt_counter++;
    }
    else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
    {
        NRF_LOG_INFO("CALIBRATE DONE");
    }
}


#define SAADC_CHANNEL_AN0 (0)

void saadc_init(void)
{
    ret_code_t err_code;
    nrf_drv_saadc_config_t saadc_config;
    nrf_saadc_channel_config_t channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);

    //Configure SAADC
    saadc_config.low_power_mode = true;                       //Enable low power mode.
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;     //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=4096 (when input voltage is 3.6V for channel gain setting of 1/6).
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;  //Set oversample to 4x. This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times.
    saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;   //Set SAADC interrupt to low priority.
	
    channel_config.reference = NRF_SAADC_REFERENCE_VDD4;      // VDD/4
    channel_config.acq_time = NRF_SAADC_ACQTIME_40US ;
    channel_config.gain = NRF_SAADC_GAIN1_4 ;

    err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(SAADC_CHANNEL_AN0, &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);

}


/**
 * @brief Function for main application entry.
 */
int main(void)
{
    uint32_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();

    ret_code_t ret_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(ret_code);

    saadc_init();
    saadc_sampling_event_init();
    saadc_sampling_event_enable();
    NRF_LOG_INFO("SAADC HAL simple example started.");

    while (1)
    {
        if(m_saadc_calibrate == true)
        {
            nrf_drv_saadc_abort();                                  // Abort all ongoing conversions. Calibration cannot be run if SAADC is busy
            NRF_LOG_INFO("SAADC calibration starting...");    //Print on UART

            while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
            m_saadc_calibrate = false;

        }
        nrf_pwr_mgmt_run();
        NRF_LOG_FLUSH();
    }
}


/** @} */


Code(A) Log

<info> app: SAADC HAL simple example started.
<info> app: ADC event number: 0
<info> app: 2023
<info> app: 2027
<info> app: 2028
<info> app: 2032
<info> app: 2043
<info> app: SAADC calibration starting...
<info> app: CALIBRATE DONE
<info> app: ADC event number: 1
<info> app: 24576
<info> app: 8192
<info> app: 885
<info> app: 0
<info> app: 861
<info> app: ADC event number: 2
<info> app: 24576
<info> app: 8192
<info> app: 885
<info> app: 0
<info> app: 861
<info> app: ADC event number: 3
<info> app: 24576
<info> app: 8192
<info> app: 885
<info> app: 0
<info> app: 861
<info> app: ADC event number: 4
<info> app: 24576
<info> app: 8192
<info> app: 885
<info> app: 0
<info> app: 861
<info> app: ADC event number: 5
<info> app: 24576
<info> app: 8192
<info> app: 885
<info> app: 0
<info> app: 861
<info> app: SAADC calibration starting...
<info> app: CALIBRATE DONE

No log is output after that.


Code(B)

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define SAMPLES_IN_BUFFER 5
volatile uint8_t state = 1;

static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
static nrf_saadc_value_t     m_buffer_pool[2][SAMPLES_IN_BUFFER];
static nrf_ppi_channel_t     m_ppi_channel;
static uint32_t              m_adc_evt_counter;
static bool                    m_saadc_calibrate = false;      


void timer_handler(nrf_timer_event_t event_type, void * p_context)
{

}


void saadc_sampling_event_init(void)
{
    ret_code_t err_code;

    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);
    APP_ERROR_CHECK(err_code);
}


void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);

    APP_ERROR_CHECK(err_code);
}
#define SAADC_CALIBRATION_INTERVAL 5              //Determines how often the SAADC should be calibrated relative to NRF_DRV_SAADC_EVT_DONE event. E.g. value 5 will make the SAADC calibrate every fifth time the NRF_DRV_SAADC_EVT_DONE is received.
#define SAADC_SAMPLES_IN_BUFFER 1                 //Number of SAADC samples in RAM before returning a SAADC event. For low power SAADC set this constant to 1. Otherwise the EasyDMA will be enabled for an extended time which consumes high current.


void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        ret_code_t err_code;

        if((m_adc_evt_counter % SAADC_CALIBRATION_INTERVAL) == 0)               //Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
        {
            m_saadc_calibrate = true;                                           // Set flag to trigger calibration in main context when SAADC is stopped
        }

        int i;
        NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);

        for (i = 0; i < SAMPLES_IN_BUFFER; i++)
        {
            NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
        }

        if(m_saadc_calibrate == false)
        {
            err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAADC_SAMPLES_IN_BUFFER);             //Set buffer so the SAADC can write to it again. 
            APP_ERROR_CHECK(err_code);
        }

        m_adc_evt_counter++;
    }
    else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
    {
        NRF_LOG_INFO("CALIBRATE DONE");
    }
}


#define SAADC_CHANNEL_AN0 (0)

void saadc_init(void)
{
    ret_code_t err_code;
    nrf_drv_saadc_config_t saadc_config;
    nrf_saadc_channel_config_t channel_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);

    //Configure SAADC
    saadc_config.low_power_mode = true;                       //Enable low power mode.
    saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;     //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=4096 (when input voltage is 3.6V for channel gain setting of 1/6).
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;  //Set oversample to 4x. This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times.
    saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;   //Set SAADC interrupt to low priority.
	
    channel_config.reference = NRF_SAADC_REFERENCE_VDD4;      // VDD/4
    channel_config.acq_time = NRF_SAADC_ACQTIME_40US ;
    channel_config.gain = NRF_SAADC_GAIN1_4 ;

    err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(SAADC_CHANNEL_AN0, &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);

}


/**
 * @brief Function for main application entry.
 */
int main(void)
{
    uint32_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();

    ret_code_t ret_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(ret_code);

    saadc_init();
    saadc_sampling_event_init();
    saadc_sampling_event_enable();
    NRF_LOG_INFO("SAADC HAL simple example started.");

    while (1)
    {
        if(m_saadc_calibrate == true)
        {
            NRF_LOG_INFO("SAADC calibration starting...");    //Print on UART

            nrf_drv_saadc_abort();
            while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task

            nrf_drv_saadc_uninit(); //ADD(B)
            saadc_init();           //ADD(B)
            m_saadc_calibrate = false;

        }
        nrf_pwr_mgmt_run();
        NRF_LOG_FLUSH();
    }
}


/** @} */


Code(B)Log

<info> app: SAADC HAL simple example started.
<info> app: ADC event number: 0
<info> app: 2613
<info> app: 1487
<info> app: 2651
<info> app: 1551
<info> app: 2655
<info> app: SAADC calibration starting...
<info> app: ADC event number: 1
<info> app: 1228
<info> app: 2369
<info> app: 1316
<info> app: 2166
<info> app: 1473
<info> app: ADC event number: 2
<info> app: 2102
<info> app: 1668
<info> app: 1969
<info> app: 1865
<info> app: 2012
<info> app: ADC event number: 3
<info> app: 1947
<info> app: 2369
<info> app: 1316
<info> app: 2166
<info> app: 1473
<info> app: ADC event number: 4
<info> app: 2031
<info> app: 1668
<info> app: 1969
<info> app: 1865
<info> app: 2012
<info> app: ADC event number: 5
<info> app: 1934
<info> app: 2369
<info> app: 1316
<info> app: 2166
<info> app: 1473
<info> app: SAADC calibration starting...
<info> app: ADC event number: 6
<info> app: 2022
<info> app: 1905
<info> app: 2008
<info> app: 1917
<info> app: 2004
<info> app: ADC event number: 7
<info> app: 2022
<info> app: 1986
<info> app: 2007
<info> app: 1963
<info> app: 2056
<info> app: ADC event number: 8
<info> app: 1953
<info> app: 1905
<info> app: 2008
<info> app: 1917
<info> app: 2004
<info> app: ADC event number: 9
<info> app: 2025
<info> app: 1986
<info> app: 2007
<info> app: 1963
<info> app: 2056
<info> app: ADC event number: 10
<info> app: 1987
<info> app: 1905
<info> app: 2008
<info> app: 1917
<info> app: 2004
<info> app: SAADC calibration starting...

After that, log output continuously.
NRF_DRV_SAADC_EVT_CALIBRATEDONE cannot be obtained.

Related