diff --git a/boards/arm/nrf54l15pdk_nrf54l15/nrf54l15pdk_nrf54l15_cpuapp.dts b/boards/arm/nrf54l15pdk_nrf54l15/nrf54l15pdk_nrf54l15_cpuapp.dts index e6ef815e783..f64952ba886 100644 --- a/boards/arm/nrf54l15pdk_nrf54l15/nrf54l15pdk_nrf54l15_cpuapp.dts +++ b/boards/arm/nrf54l15pdk_nrf54l15/nrf54l15pdk_nrf54l15_cpuapp.dts @@ -201,3 +201,7 @@ t-exit-dpd = <35000>; }; }; + +&adc { + status = "okay"; +}; diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c index 6d1973ca0aa..e5e658c893a 100644 --- a/drivers/adc/adc_nrfx_saadc.c +++ b/drivers/adc/adc_nrfx_saadc.c @@ -6,7 +6,7 @@ #define ADC_CONTEXT_USES_KERNEL_TIMER #include "adc_context.h" -#include +#include #include #define LOG_LEVEL CONFIG_ADC_LOG_LEVEL @@ -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) && @@ -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; @@ -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; } @@ -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; } @@ -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); } } @@ -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); } @@ -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 }; /* diff --git a/samples/drivers/adc/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay b/samples/drivers/adc/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000..f185624f9b9 --- /dev/null +++ b/samples/drivers/adc/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay @@ -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 = ; + zephyr,input-positive = ; /* P1.11 */ + zephyr,resolution = <10>; + }; + + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* 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 = ; + zephyr,input-positive = ; /* P1.13 */ + zephyr,input-negative = ; /* P1.14 */ + zephyr,resolution = <12>; + }; +}; diff --git a/tests/drivers/adc/adc_api/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay b/tests/drivers/adc/adc_api/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000..e10ddc54328 --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay @@ -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 = ; + zephyr,input-positive = ; + zephyr,resolution = <10>; + }; + + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; + zephyr,resolution = <10>; + }; +};