Commit 13658a66 authored by Bjarki Arge Andreasen's avatar Bjarki Arge Andreasen Committed by Alberto Escolar
Browse files

drivers: adc: adc_nrfx_saadc: Patch neg single ended readings



The NRF SAADC produces negative values in single ended mode if the
positive input is below 0V (ground). This behavior does not match
the ADC device driver API, which states that in single ended mode,
the readings must be positive [0 .. 2^resolution - 1].

This commit extends the adc_nrfx_saadc device driver to track which
channels in a sequence are configured to be single ended, to then
corrects negative readings for these channels to 0.

This patch only works if the ADC resolution is lower than the sample
bit size. This is the case for 8, 10, 12 and 14 bit resolutions for
the nRF 52, 53 and 91 series which store readings in a int16_t.

The nRF 54H and 54L series store 8-bit resolution readings in a 8-bit
sample size. A check has been added to start_read() to prevent single
ended mode readings if the resolution is 8-bit for these platforms.

Signed-off-by: default avatarBjarki Arge Andreasen <bjarki.andreasen@nordicsemi.no>
parent 7f420049
Loading
Loading
Loading
Loading
+57 −2
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ struct driver_data {
	struct adc_context ctx;

	uint8_t positive_inputs[SAADC_CH_NUM];
	uint8_t single_ended_channels;

#if defined(ADC_BUFFER_IN_RAM)
	void *samples_buffer;
@@ -262,8 +263,16 @@ static int adc_nrfx_channel_setup(const struct device *dev,
		return -EINVAL;
	}

	config.mode = (channel_cfg->differential ?
		NRF_SAADC_MODE_DIFFERENTIAL : NRF_SAADC_MODE_SINGLE_ENDED);
	/* Store channel mode to allow correcting negative readings in single-ended mode
	 * after ADC sequence ends.
	 */
	if (channel_cfg->differential) {
		config.mode = NRF_SAADC_MODE_DIFFERENTIAL;
		m_data.single_ended_channels &= ~BIT(channel_cfg->channel_id);
	} else {
		config.mode = NRF_SAADC_MODE_SINGLE_ENDED;
		m_data.single_ended_channels |= BIT(channel_cfg->channel_id);
	}

	/* Keep the channel disabled in hardware (set positive input to
	 * NRF_SAADC_INPUT_DISABLED) until it is selected to be included
@@ -432,11 +441,37 @@ static int check_buffer_size(const struct adc_sequence *sequence,
	return 0;
}

static bool has_single_ended(const struct adc_sequence *sequence)
{
	return sequence->channels & m_data.single_ended_channels;
}

static void correct_single_ended(const struct adc_sequence *sequence)
{
	uint16_t channel_bit = BIT(0);
	uint8_t selected_channels = sequence->channels;
	uint8_t single_ended_channels = m_data.single_ended_channels;
	int16_t *sample = nrf_saadc_buffer_pointer_get(NRF_SAADC);

	while (channel_bit <= single_ended_channels) {
		if (channel_bit & selected_channels) {
			if ((channel_bit & single_ended_channels) && (*sample < 0)) {
				*sample = 0;
			}

			sample++;
		}

		channel_bit <<= 1;
	}
}

static int start_read(const struct device *dev,
		      const struct adc_sequence *sequence)
{
	int error;
	uint32_t selected_channels = sequence->channels;
	uint8_t resolution = sequence->resolution;
	uint8_t active_channels;
	uint8_t channel_id;

@@ -465,6 +500,22 @@ static int start_read(const struct device *dev,
					    channel_id);
				return -EINVAL;
			}
			/* Signal an error if the channel is configured as
			 * single ended with a resolution which is identical
			 * to the sample bit size. The SAADC's "single ended"
			 * mode is really differential mode with the
			 * negative input tied to ground. We can therefore
			 * observe negative values if the positive input falls
			 * below ground. If the sample bitsize is larger than
			 * the resolution, we can detect negative values and
			 * correct them to 0 after the sequencen has ended.
			 */
			if ((m_data.single_ended_channels & BIT(channel_id)) &&
			    (NRF_SAADC_8BIT_SAMPLE_WIDTH == 8 && resolution == 8)) {
				LOG_ERR("Channel %u invalid single ended resolution",
					channel_id);
				return -EINVAL;
			}
			/* When oversampling is used, the burst mode needs to
			 * be activated. Unfortunately, this mode cannot be
			 * activated permanently in the channel setup, because
@@ -567,6 +618,10 @@ static void saadc_irq_handler(const struct device *dev)
		nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP);
		nrf_saadc_disable(NRF_SAADC);

		if (has_single_ended(&m_data.ctx.sequence)) {
			correct_single_ended(&m_data.ctx.sequence);
		}

#if defined(ADC_BUFFER_IN_RAM)
		memcpy(m_data.user_buffer, m_data.samples_buffer,
			samples_to_bytes(&m_data.ctx.sequence, m_data.active_channels));