Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update adc driver to support nRF54L15 #1494

Merged
merged 4 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,7 @@
t-exit-dpd = <35000>;
};
};

&adc {
status = "okay";
};
189 changes: 153 additions & 36 deletions drivers/adc/adc_nrfx_saadc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#define ADC_CONTEXT_USES_KERNEL_TIMER
#include "adc_context.h"
#include <hal/nrf_saadc.h>
#include <haly/nrfy_saadc.h>
#include <zephyr/dt-bindings/adc/nrf-adc.h>

#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL
Expand All @@ -16,6 +16,20 @@ LOG_MODULE_REGISTER(adc_nrfx_saadc);

#define DT_DRV_COMPAT nordic_nrf_saadc

#if (NRF_SAADC_HAS_AIN_AS_PIN)

static const uint8_t saadc_psels[NRF_SAADC_AIN7 + 1] = {
[NRF_SAADC_AIN0] = NRF_PIN_PORT_TO_PIN_NUMBER(4U, 1),
[NRF_SAADC_AIN1] = NRF_PIN_PORT_TO_PIN_NUMBER(5U, 1),
[NRF_SAADC_AIN2] = NRF_PIN_PORT_TO_PIN_NUMBER(6U, 1),
[NRF_SAADC_AIN3] = NRF_PIN_PORT_TO_PIN_NUMBER(7U, 1),
[NRF_SAADC_AIN4] = NRF_PIN_PORT_TO_PIN_NUMBER(11U, 1),
[NRF_SAADC_AIN5] = NRF_PIN_PORT_TO_PIN_NUMBER(12U, 1),
[NRF_SAADC_AIN6] = NRF_PIN_PORT_TO_PIN_NUMBER(13U, 1),
[NRF_SAADC_AIN7] = NRF_PIN_PORT_TO_PIN_NUMBER(14U, 1),
};

#else
BUILD_ASSERT((NRF_SAADC_AIN0 == NRF_SAADC_INPUT_AIN0) &&
(NRF_SAADC_AIN1 == NRF_SAADC_INPUT_AIN1) &&
(NRF_SAADC_AIN2 == NRF_SAADC_INPUT_AIN2) &&
Expand All @@ -28,8 +42,12 @@ BUILD_ASSERT((NRF_SAADC_AIN0 == NRF_SAADC_INPUT_AIN0) &&
#if defined(SAADC_CH_PSELP_PSELP_VDDHDIV5)
(NRF_SAADC_VDDHDIV5 == NRF_SAADC_INPUT_VDDHDIV5) &&
#endif
(NRF_SAADC_VDD == NRF_SAADC_INPUT_VDD),
#if defined(SAADC_CH_PSELP_PSELP_VDD)
(NRF_SAADC_VDD == NRF_SAADC_INPUT_VDD) &&
#endif
1,
"Definitions from nrf-adc.h do not match those from nrf_saadc.h");
#endif

struct driver_data {
struct adc_context ctx;
Expand All @@ -43,85 +61,158 @@ static struct driver_data m_data = {
ADC_CONTEXT_INIT_SYNC(m_data, ctx),
};

/* Helper function to convert number of samples to the byte representation. */
static uint32_t samples_to_bytes(const struct adc_sequence *sequence, uint16_t number_of_samples)
{
if (NRF_SAADC_8BIT_SAMPLE_WIDTH == 8 && sequence->resolution == 8) {
return number_of_samples;
}

return number_of_samples * 2;
}

/* Helper function to convert acquisition time to register TACQ value. */
static int adc_convert_acq_time(uint16_t acquisition_time, nrf_saadc_acqtime_t *p_tacq_val)
{
int result = 0;

#if NRF_SAADC_HAS_ACQTIME_ENUM
switch (acquisition_time) {
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 3):
*p_tacq_val = NRF_SAADC_ACQTIME_3US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 5):
*p_tacq_val = NRF_SAADC_ACQTIME_5US;
break;
case ADC_ACQ_TIME_DEFAULT:
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10):
*p_tacq_val = NRF_SAADC_ACQTIME_10US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 15):
*p_tacq_val = NRF_SAADC_ACQTIME_15US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20):
*p_tacq_val = NRF_SAADC_ACQTIME_20US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40):
*p_tacq_val = NRF_SAADC_ACQTIME_40US;
break;
default:
result = -EINVAL;
}
#else
#define MINIMUM_ACQ_TIME_IN_NS 125
#define DEFAULT_ACQ_TIME_IN_NS 10000

nrf_saadc_acqtime_t tacq = 0;
uint16_t acq_time =
(acquisition_time == ADC_ACQ_TIME_DEFAULT
? DEFAULT_ACQ_TIME_IN_NS
: (ADC_ACQ_TIME_VALUE(acquisition_time) *
(ADC_ACQ_TIME_UNIT(acquisition_time) == ADC_ACQ_TIME_MICROSECONDS
? 1000
: 1)));

tacq = (nrf_saadc_acqtime_t)(acq_time / MINIMUM_ACQ_TIME_IN_NS) - 1;
if ((tacq > NRF_SAADC_ACQTIME_MAX) || (acq_time < MINIMUM_ACQ_TIME_IN_NS)) {
result = -EINVAL;
} else {
*p_tacq_val = tacq;
}
#endif

return result;
}

/* Implementation of the ADC driver API function: adc_channel_setup. */
static int adc_nrfx_channel_setup(const struct device *dev,
const struct adc_channel_cfg *channel_cfg)
{
nrf_saadc_channel_config_t config = {
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
.burst = NRF_SAADC_BURST_DISABLED,
#if NRF_SAADC_HAS_CH_CONFIG_RES
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
#endif
.burst = NRF_SAADC_BURST_DISABLED,
};
uint8_t channel_id = channel_cfg->channel_id;
uint32_t input_negative = channel_cfg->input_negative;

if (channel_id >= SAADC_CH_NUM) {
return -EINVAL;
}

switch (channel_cfg->gain) {
#if defined(SAADC_CH_CONFIG_GAIN_Gain1_6)
case ADC_GAIN_1_6:
config.gain = NRF_SAADC_GAIN1_6;
break;
#endif
#if defined(SAADC_CH_CONFIG_GAIN_Gain1_5)
case ADC_GAIN_1_5:
config.gain = NRF_SAADC_GAIN1_5;
break;
#endif
#if defined(SAADC_CH_CONFIG_GAIN_Gain1_4)
case ADC_GAIN_1_4:
config.gain = NRF_SAADC_GAIN1_4;
break;
#endif
#if defined(SAADC_CH_CONFIG_GAIN_Gain1_3)
case ADC_GAIN_1_3:
config.gain = NRF_SAADC_GAIN1_3;
break;
#endif
#if defined(SAADC_CH_CONFIG_GAIN_Gain1_2)
case ADC_GAIN_1_2:
config.gain = NRF_SAADC_GAIN1_2;
break;
#endif
#if defined(SAADC_CH_CONFIG_GAIN_Gain2_3)
case ADC_GAIN_2_3:
config.gain = NRF_SAADC_GAIN2_3;
break;
#endif
case ADC_GAIN_1:
config.gain = NRF_SAADC_GAIN1;
break;
case ADC_GAIN_2:
config.gain = NRF_SAADC_GAIN2;
break;
#if defined(SAADC_CH_CONFIG_GAIN_Gain4)
case ADC_GAIN_4:
config.gain = NRF_SAADC_GAIN4;
break;
#endif
default:
LOG_ERR("Selected ADC gain is not valid");
return -EINVAL;
}

switch (channel_cfg->reference) {
#if defined(SAADC_CH_CONFIG_REFSEL_Internal)
case ADC_REF_INTERNAL:
config.reference = NRF_SAADC_REFERENCE_INTERNAL;
break;
#endif
#if defined(SAADC_CH_CONFIG_REFSEL_VDD1_4)
case ADC_REF_VDD_1_4:
config.reference = NRF_SAADC_REFERENCE_VDD4;
break;
#endif
#if defined(SAADC_CH_CONFIG_REFSEL_External)
case ADC_REF_EXTERNAL0:
config.reference = NRF_SAADC_REFERENCE_EXTERNAL;
break;
#endif
default:
LOG_ERR("Selected ADC reference is not valid");
return -EINVAL;
}

switch (channel_cfg->acquisition_time) {
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 3):
config.acq_time = NRF_SAADC_ACQTIME_3US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 5):
config.acq_time = NRF_SAADC_ACQTIME_5US;
break;
case ADC_ACQ_TIME_DEFAULT:
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10):
config.acq_time = NRF_SAADC_ACQTIME_10US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 15):
config.acq_time = NRF_SAADC_ACQTIME_15US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20):
config.acq_time = NRF_SAADC_ACQTIME_20US;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40):
config.acq_time = NRF_SAADC_ACQTIME_40US;
break;
default:
int ret = adc_convert_acq_time(channel_cfg->acquisition_time, &config.acq_time);

if (ret) {
LOG_ERR("Selected ADC acquisition time is not valid");
return -EINVAL;
}
Expand All @@ -134,17 +225,37 @@ static int adc_nrfx_channel_setup(const struct device *dev,
* in a sampling sequence.
*/

nrf_saadc_channel_init(NRF_SAADC, channel_id, &config);
nrf_saadc_channel_input_set(NRF_SAADC,
channel_id,
NRF_SAADC_INPUT_DISABLED,
channel_cfg->input_negative);
#if (NRF_SAADC_HAS_AIN_AS_PIN)
if ((channel_cfg->input_positive > NRF_SAADC_AIN7) ||
(channel_cfg->input_positive < NRF_SAADC_AIN0)) {
return -EINVAL;
}

if (config.mode == NRF_SAADC_MODE_DIFFERENTIAL) {
if (input_negative > NRF_SAADC_AIN7 ||
input_negative < NRF_SAADC_AIN0) {
return -EINVAL;
}

input_negative = saadc_psels[input_negative];
} else {
input_negative = NRF_SAADC_INPUT_DISABLED;
}

/* Store the positive input selection in a dedicated array,
* to get it later when the channel is selected for a sampling
* and to mark the channel as configured (ready to be selected).
*/
m_data.positive_inputs[channel_id] = saadc_psels[channel_cfg->input_positive];
#else
m_data.positive_inputs[channel_id] = channel_cfg->input_positive;
#endif

nrf_saadc_channel_init(NRF_SAADC, channel_id, &config);
nrf_saadc_channel_input_set(NRF_SAADC,
channel_id,
NRF_SAADC_INPUT_DISABLED,
input_negative);

return 0;
}
Expand All @@ -168,10 +279,11 @@ static void adc_context_update_buffer_pointer(struct adc_context *ctx,
ARG_UNUSED(ctx);

if (!repeat) {
nrf_saadc_buffer_pointer_set(
NRF_SAADC,
(uint16_t *)nrf_saadc_buffer_pointer_get(NRF_SAADC) +
nrf_saadc_amount_get(NRF_SAADC));
nrf_saadc_value_t *buffer =
(uint8_t *)nrf_saadc_buffer_pointer_get(NRF_SAADC) +
samples_to_bytes(&ctx->sequence, nrfy_saadc_amount_get(NRF_SAADC));

nrfy_saadc_buffer_pointer_set(NRF_SAADC, buffer);
}
}

Expand Down Expand Up @@ -256,7 +368,8 @@ static int check_buffer_size(const struct adc_sequence *sequence,
{
size_t needed_buffer_size;

needed_buffer_size = active_channels * sizeof(uint16_t);
needed_buffer_size = samples_to_bytes(sequence, active_channels);

if (sequence->options) {
needed_buffer_size *= (1 + sequence->options->extra_samplings);
}
Expand Down Expand Up @@ -429,7 +542,11 @@ static const struct adc_driver_api adc_nrfx_driver_api = {
#ifdef CONFIG_ADC_ASYNC
.read_async = adc_nrfx_read_async,
#endif
#if defined(CONFIG_SOC_NRF54L15)
.ref_internal = 900,
#else
.ref_internal = 600,
#endif
};

/*
Expand Down
45 changes: 45 additions & 0 deletions samples/drivers/adc/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2024 Nordic Semiconductor ASA
*/

/ {
zephyr,user {
io-channels = <&adc 0>, <&adc 1>, <&adc 7>;
};
};

&adc {
#address-cells = <1>;
#size-cells = <0>;

channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_AIN4>; /* P1.11 */
zephyr,resolution = <10>;
};

channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_AIN2>; /* P1.06 */
zephyr,resolution = <12>;
zephyr,oversampling = <8>;
};

channel@7 {
reg = <7>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_AIN6>; /* P1.13 */
zephyr,input-negative = <NRF_SAADC_AIN7>; /* P1.14 */
zephyr,resolution = <12>;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2024 Nordic Semiconductor ASA
*/

/ {
zephyr,user {
io-channels = <&adc 0>, <&adc 2>;
};
};

&adc {
#address-cells = <1>;
#size-cells = <0>;

channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10)>;
zephyr,input-positive = <NRF_SAADC_AIN1>;
zephyr,resolution = <10>;
};

channel@2 {
reg = <2>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10)>;
zephyr,input-positive = <NRF_SAADC_AIN2>;
zephyr,resolution = <10>;
};
};
Loading