Issue description
I am using SAADC in single-ended mode with 3 channels (0/1/2). Measurements are performed by enabling SAADC, running offset calibration, configuring the result buffer for 3 samples, starting, sampling once, waiting for END, then stopping.
Everything works as expected until I set channel limits on channel 0 using nrf_saadc_channel_limits_set(). If I set the low limit higher than approximately -100 (e.g., -10), the samples returned in the result buffer for all three channels become “meaningless” (very small numbers). If I remove the limits call, or set low limit to something like -200, the readings are correct again.
This behavior occurs only when applying limits to channel 0. Applying “similar” limits to channels 1 or 2 does not break readings.
Reproduction steps
Code below reproduces the issue:
typedef enum
{
CURRENT_SAADC_CH = 0,
TEMPERATURE_SAADC_CH,
VBATT_SAADC_CH,
SAADC_CHANNELS_ENABLED, // element count
} td_saadc_channels_t;
static nrf_saadc_value_t _sample[SAADC_CHANNELS_ENABLED];
static bool module_init = false;
void td_saadc_init(void)
{
nrf_saadc_resolution_set(NRF_SAADC_RESOLUTION_12BIT);
nrf_saadc_oversample_set(NRF_SAADC_OVERSAMPLE_DISABLED);
nrf_saadc_int_disable(NRF_SAADC_INT_ALL);
nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
// current
{
nrf_saadc_channel_config_t ch0_cfg = {
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
.gain = NRF_SAADC_GAIN1_6,
.reference = NRF_SAADC_REFERENCE_INTERNAL,
.acq_time = NRF_SAADC_ACQTIME_5US,
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
.burst = NRF_SAADC_BURST_DISABLED,
.pin_p = SAADC_INPUT_CURR,
.pin_n = NRF_SAADC_INPUT_DISABLED};
nrf_saadc_channel_init(CURRENT_SAADC_CH, &ch0_cfg);
}
// temperature
{
nrf_saadc_channel_config_t ch1_cfg = {
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
.gain = NRF_SAADC_GAIN1_3,
.reference = NRF_SAADC_REFERENCE_INTERNAL,
.acq_time = NRF_SAADC_ACQTIME_10US,
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
.burst = NRF_SAADC_BURST_DISABLED,
.pin_p = SAADC_INPUT_TEMP,
.pin_n = NRF_SAADC_INPUT_DISABLED};
nrf_saadc_channel_init(TEMPERATURE_SAADC_CH, &ch1_cfg);
}
// VBatt input ~7.2V through 100k/100k+470k divider, expected voltage ~7.2/5.3 = 1.36V
{
nrf_saadc_channel_config_t ch2_cfg = {
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
.gain = NRF_SAADC_GAIN1_3,
.reference = NRF_SAADC_REFERENCE_INTERNAL,
.acq_time = NRF_SAADC_ACQTIME_10US,
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
.burst = NRF_SAADC_BURST_DISABLED,
.pin_p = SAADC_INPUT_VBATT,
.pin_n = NRF_SAADC_INPUT_DISABLED};
nrf_saadc_channel_init(VBATT_SAADC_CH, &ch2_cfg);
}
module_init = true;
}
uint32_t td_saadc_single_measurement(void)
{
if (!module_init)
{
return NRF_ERROR_INVALID_STATE;
}
nrf_saadc_enable();
nrf_saadc_channel_limits_set(CURRENT_SAADC_CH, -10, 1365); // this is the line that cause the issue
nrf_saadc_event_clear(NRF_SAADC_EVENT_CALIBRATEDONE);
nrf_saadc_task_trigger(NRF_SAADC_TASK_CALIBRATEOFFSET);
while (!nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE))
;
nrf_saadc_event_clear(NRF_SAADC_EVENT_CALIBRATEDONE);
nrf_saadc_buffer_init(_sample, SAADC_CHANNELS_ENABLED);
nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);
nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
while (!nrf_saadc_event_check(NRF_SAADC_EVENT_STARTED))
;
nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
while (!nrf_saadc_event_check(NRF_SAADC_EVENT_END))
;
nrf_saadc_event_clear(NRF_SAADC_EVENT_END);
NRF_LOG_INFO("SAADC done event, samples: current %d, temperature %d, vbatt %d",
_sample[0],
_sample[1],
_sample[2]);
NRF_LOG_FLUSH();
nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
while (!nrf_saadc_event_check(NRF_SAADC_EVENT_STOPPED))
;
nrf_saadc_disable();
NRF_LOG_FLUSH();
return NRF_SUCCESS;
}
Observed log output (bad case)
When low limit is around -10 (or generally > ~-100):
<info> app: SAADC done event, samples: current 11, temperature 38, vbatt 57
Observed log output (good case)
When the low limit call is commented out, or low limit is <= ~-200:
<info> app: SAADC done event, samples: current 39, temperature 1814, vbatt 342
Key condition
- Only setting low limit on channel 0 causes the issue.
- Setting limits on other channels does not reproduce the problem (with the same measurement flow).
- High limit value doesn't influence the result
Development Setup
- Bare Metal
- NRF version: nRF5_SDK_17.1.0_ddde560
- SoftDevice: s140
- Bug reproduced on nrf52840dk 1.0.0 and 2.0.1