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.