From 256a8bb7a5e84c41fef79872a3b2026388f42183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Mon, 16 Jun 2025 07:53:43 +0200 Subject: [PATCH 1/5] [nrf fromtree] drivers: adc_nrfx_saadc: Remove dead and misleading code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a long time (since version 3.3.0) nrfx contained an incorrectly defined symbol NRF_SAADC_8BIT_SAMPLE_WIDTH that was set to 8 for nRF54L and nRF54H Series SoCs, which was probably only true for very early engineering revisions of those. Based on this, the adc_nrfx_saadc driver was incorrectly writing consecutive 8-bit samples in supplied buffers, cutting off the highest 8 bits of the results. And for sequences with multiple channels, it was even causing that the results written as 16-bit words by hardware were partially overwritten in next iteration. In nrfx 3.12.0 (see commit f46798fa55881e6e24bb121d867c7753d4c6b775) this was finally corrected - the symbol is now deprecated and it is always set to 16. This commit is a follow-up to the above and removes parts of adc_nrfx_saadc that now became dead code to prevent further confusion regarding 8-bit sampling. Signed-off-by: Andrzej Głąbek (cherry picked from commit 616ec7522ccee368b1f6c5c1d27ad54a999279fe) --- drivers/adc/adc_nrfx_saadc.c | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c index 986cca49af8..54719a1136d 100644 --- a/drivers/adc/adc_nrfx_saadc.c +++ b/drivers/adc/adc_nrfx_saadc.c @@ -141,12 +141,8 @@ static struct driver_data m_data = { }; /* 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) +static uint32_t samples_to_bytes(uint16_t number_of_samples) { - if (NRF_SAADC_8BIT_SAMPLE_WIDTH == 8 && sequence->resolution == 8) { - return number_of_samples; - } - return number_of_samples * 2; } @@ -410,11 +406,11 @@ static void adc_context_update_buffer_pointer(struct adc_context *ctx, if (!repeat) { #if defined(ADC_BUFFER_IN_RAM) m_data.user_buffer = (uint8_t *)m_data.user_buffer + - samples_to_bytes(&ctx->sequence, nrfy_saadc_amount_get(NRF_SAADC)); + samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); #else 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)); + samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); nrfy_saadc_buffer_pointer_set(NRF_SAADC, buffer); #endif } @@ -501,7 +497,7 @@ static int check_buffer_size(const struct adc_sequence *sequence, { size_t needed_buffer_size; - needed_buffer_size = samples_to_bytes(sequence, active_channels); + needed_buffer_size = samples_to_bytes(active_channels); if (sequence->options) { needed_buffer_size *= (1 + sequence->options->extra_samplings); @@ -546,7 +542,6 @@ static int start_read(const struct device *dev, { int error; uint32_t selected_channels = sequence->channels; - uint8_t resolution = sequence->resolution; uint8_t active_channels; uint8_t channel_id; nrf_saadc_burst_t burst; @@ -576,22 +571,6 @@ 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 @@ -713,7 +692,7 @@ static void saadc_irq_handler(const struct device *dev) #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)); + samples_to_bytes(m_data.active_channels)); #endif adc_context_on_sampling_done(&m_data.ctx, dev); From 6728180a2f7c3e349d25910b46bcb67034b0aa31 Mon Sep 17 00:00:00 2001 From: Jakub Zymelka Date: Mon, 16 Jun 2025 15:03:37 +0200 Subject: [PATCH 2/5] [nrf fromtree] modules: hal_nordic: nrfx: Update NRFX API version to 3.12 Update NRFX_CONFIG_API_VER_MINOR to 12, to be compatible with latest SAADC driver changes. Signed-off-by: Jakub Zymelka (cherry picked from commit be8c61ab809faa793609cabe2201edd38c2c77a4) --- modules/hal_nordic/nrfx/nrfx_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hal_nordic/nrfx/nrfx_config.h b/modules/hal_nordic/nrfx/nrfx_config.h index 4a2d5af18b6..fac2341c1c5 100644 --- a/modules/hal_nordic/nrfx/nrfx_config.h +++ b/modules/hal_nordic/nrfx/nrfx_config.h @@ -9,7 +9,7 @@ /* Define nrfx API version used in Zephyr. */ #define NRFX_CONFIG_API_VER_MAJOR 3 -#define NRFX_CONFIG_API_VER_MINOR 8 +#define NRFX_CONFIG_API_VER_MINOR 12 #define NRFX_CONFIG_API_VER_MICRO 0 /* Macros used in zephyr-specific config files. */ From 358884b165f765c1a8c4c86ff98d969f81b6c555 Mon Sep 17 00:00:00 2001 From: Jakub Zymelka Date: Tue, 27 May 2025 11:57:13 +0200 Subject: [PATCH 3/5] [nrf fromtree] drivers: adc: rework Nordic SAADC driver SHIM Nordic modification for the ADC driver controlling the SAADC peripheral. Replaced HAL based implementation in favor of nrfx driver. As a next step, it is planned to implement a feature that will allow the peripheral SAADC timer to be used for sampling, and for this it is necessary to use the nrfx driver in this SHIM. This will allow more accurate and faster sampling than the kernel mechanism currently provides. Signed-off-by: Jakub Zymelka (cherry picked from commit f3d250742377ac24af0bc4bf78dd61afaefc2479) --- drivers/adc/Kconfig.nrfx | 1 + drivers/adc/adc_nrfx_saadc.c | 518 +++++++++++++++-------------------- 2 files changed, 222 insertions(+), 297 deletions(-) diff --git a/drivers/adc/Kconfig.nrfx b/drivers/adc/Kconfig.nrfx index d411d5c99e1..0a602f5b97b 100644 --- a/drivers/adc/Kconfig.nrfx +++ b/drivers/adc/Kconfig.nrfx @@ -27,5 +27,6 @@ config ADC_NRFX_SAADC default y depends on DT_HAS_NORDIC_NRF_SAADC_ENABLED select ADC_CONFIGURABLE_INPUTS + select NRFX_SAADC help Enable support for nrfx SAADC driver. diff --git a/drivers/adc/adc_nrfx_saadc.c b/drivers/adc/adc_nrfx_saadc.c index 54719a1136d..f9f136f2509 100644 --- a/drivers/adc/adc_nrfx_saadc.c +++ b/drivers/adc/adc_nrfx_saadc.c @@ -6,18 +6,15 @@ #define ADC_CONTEXT_USES_KERNEL_TIMER #include "adc_context.h" -#include +#include #include #include #include #include -#include -#include - -#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL #include #include -LOG_MODULE_REGISTER(adc_nrfx_saadc); + +LOG_MODULE_REGISTER(adc_nrfx_saadc, CONFIG_ADC_LOG_LEVEL); #define DT_DRV_COMPAT nordic_nrf_saadc @@ -103,16 +100,9 @@ BUILD_ASSERT((NRF_SAADC_AIN0 == NRF_SAADC_INPUT_AIN0) && #endif #if defined(CONFIG_NRF_PLATFORM_HALTIUM) - +#include /* Haltium devices always use bounce buffers in RAM */ - -#define SAADC_MEMORY_SECTION \ - COND_CODE_1(DT_NODE_HAS_PROP(DT_NODELABEL(adc), memory_regions), \ - (__attribute__((__section__(LINKER_DT_NODE_REGION_NAME( \ - DT_PHANDLE(DT_NODELABEL(adc), memory_regions)))))), \ - ()) - -static uint16_t adc_samples_buffer[SAADC_CH_NUM] SAADC_MEMORY_SECTION; +static uint16_t adc_samples_buffer[SAADC_CH_NUM] DMM_MEMORY_SECTION(DT_NODELABEL(adc)); #define ADC_BUFFER_IN_RAM @@ -120,14 +110,13 @@ static uint16_t adc_samples_buffer[SAADC_CH_NUM] SAADC_MEMORY_SECTION; struct driver_data { struct adc_context ctx; - - uint8_t positive_inputs[SAADC_CH_NUM]; uint8_t single_ended_channels; + nrf_saadc_value_t *buffer; /* Pointer to the buffer with converted samples. */ + uint8_t active_channel_cnt; #if defined(ADC_BUFFER_IN_RAM) void *samples_buffer; void *user_buffer; - uint8_t active_channels; #endif }; @@ -140,41 +129,37 @@ static struct driver_data m_data = { #endif }; -/* Helper function to convert number of samples to the byte representation. */ -static uint32_t samples_to_bytes(uint16_t number_of_samples) -{ - return number_of_samples * 2; -} +/* Forward declaration */ +static void event_handler(const nrfx_saadc_evt_t *event); /* 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) +static int acq_time_set(nrf_saadc_channel_config_t *ch_cfg, uint16_t acquisition_time) { - 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; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_3US; break; case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 5): - *p_tacq_val = NRF_SAADC_ACQTIME_5US; + ch_cfg->acq_time = 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; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_10US; break; case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 15): - *p_tacq_val = NRF_SAADC_ACQTIME_15US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_15US; break; case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20): - *p_tacq_val = NRF_SAADC_ACQTIME_20US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_20US; break; case ADC_ACQ_TIME_MAX: case ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40): - *p_tacq_val = NRF_SAADC_ACQTIME_40US; + ch_cfg->acq_time = NRF_SAADC_ACQTIME_40US; break; default: - result = -EINVAL; + LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; } #else #define MINIMUM_ACQ_TIME_IN_NS 125 @@ -191,130 +176,138 @@ static int adc_convert_acq_time(uint16_t acquisition_time, nrf_saadc_acqtime_t * 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; + LOG_ERR("Selected ADC acquisition time is not valid"); + return -EINVAL; } else { - *p_tacq_val = tacq; + ch_cfg->acq_time = tacq; } #endif - return result; + LOG_DBG("ADC acquisition_time: %d", acquisition_time); + + return 0; } -static int saadc_pm_hook(const struct device *dev, enum pm_device_action action) +static int input_assign(nrf_saadc_input_t *pin_p, + nrf_saadc_input_t *pin_n, + const struct adc_channel_cfg *channel_cfg) { - ARG_UNUSED(dev); - - switch (action) { - case PM_DEVICE_ACTION_SUSPEND: - nrf_saadc_disable(NRF_SAADC); - return 0; +#if (NRF_SAADC_HAS_AIN_AS_PIN) + if (channel_cfg->input_positive > ARRAY_SIZE(saadc_psels) || + channel_cfg->input_positive < NRF_SAADC_AIN0) { + LOG_ERR("Invalid analog positive input number: %d", channel_cfg->input_positive); + return -EINVAL; + } - case PM_DEVICE_ACTION_RESUME: - nrf_saadc_enable(NRF_SAADC); - return 0; + *pin_p = saadc_psels[channel_cfg->input_positive]; - default: - break; + if (channel_cfg->differential) { + if (channel_cfg->input_negative > ARRAY_SIZE(saadc_psels) || + channel_cfg->input_negative < NRF_SAADC_AIN0 || + (IS_ENABLED(CONFIG_NRF_PLATFORM_HALTIUM) && + (channel_cfg->input_positive > NRF_SAADC_AIN7) != + (channel_cfg->input_negative > NRF_SAADC_AIN7))) { + LOG_ERR("Invalid analog negative input number: %d", + channel_cfg->input_negative); + return -EINVAL; + } + *pin_n = saadc_psels[channel_cfg->input_negative]; + } else { + *pin_n = NRF_SAADC_INPUT_DISABLED; } +#else + *pin_p = channel_cfg->input_positive; + *pin_n = channel_cfg->differential ? channel_cfg->input_negative + : NRF_SAADC_INPUT_DISABLED; +#endif + LOG_DBG("ADC positive input: %d", *pin_p); + LOG_DBG("ADC negative input: %d", *pin_n); - return -ENOTSUP; + return 0; } -/* 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) +static int gain_set(nrf_saadc_channel_config_t *ch_cfg, enum adc_gain gain) { - nrf_saadc_channel_config_t config = { -#if NRF_SAADC_HAS_CH_CONFIG_RES - .resistor_p = NRF_SAADC_RESISTOR_DISABLED, - .resistor_n = NRF_SAADC_RESISTOR_DISABLED, -#endif -#if NRF_SAADC_HAS_CH_BURST - .burst = NRF_SAADC_BURST_DISABLED, -#endif - }; - uint8_t channel_id = channel_cfg->channel_id; - uint32_t input_negative = channel_cfg->input_negative; - - if (channel_id >= SAADC_CH_NUM) { - return -EINVAL; - } - #if NRF_SAADC_HAS_CH_GAIN - switch (channel_cfg->gain) { + switch (gain) { #if defined(SAADC_CH_CONFIG_GAIN_Gain1_6) case ADC_GAIN_1_6: - config.gain = NRF_SAADC_GAIN1_6; + ch_cfg->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; + ch_cfg->gain = NRF_SAADC_GAIN1_5; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain1_4) || defined(SAADC_CH_CONFIG_GAIN_Gain2_8) case ADC_GAIN_1_4: - config.gain = NRF_SAADC_GAIN1_4; + ch_cfg->gain = NRF_SAADC_GAIN1_4; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain2_7) case ADC_GAIN_2_7: - config.gain = NRF_SAADC_GAIN2_7; + ch_cfg->gain = NRF_SAADC_GAIN2_7; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain1_3) || defined(SAADC_CH_CONFIG_GAIN_Gain2_6) case ADC_GAIN_1_3: - config.gain = NRF_SAADC_GAIN1_3; + ch_cfg->gain = NRF_SAADC_GAIN1_3; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain2_5) case ADC_GAIN_2_5: - config.gain = NRF_SAADC_GAIN2_5; + ch_cfg->gain = NRF_SAADC_GAIN2_5; break; #endif #if defined(SAADC_CH_CONFIG_GAIN_Gain1_2) || defined(SAADC_CH_CONFIG_GAIN_Gain2_4) case ADC_GAIN_1_2: - config.gain = NRF_SAADC_GAIN1_2; + ch_cfg->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; + ch_cfg->gain = NRF_SAADC_GAIN2_3; break; #endif case ADC_GAIN_1: - config.gain = NRF_SAADC_GAIN1; + ch_cfg->gain = NRF_SAADC_GAIN1; break; case ADC_GAIN_2: - config.gain = NRF_SAADC_GAIN2; + ch_cfg->gain = NRF_SAADC_GAIN2; break; #if defined(SAADC_CH_CONFIG_GAIN_Gain4) case ADC_GAIN_4: - config.gain = NRF_SAADC_GAIN4; + ch_cfg->gain = NRF_SAADC_GAIN4; break; #endif default: #else - if (channel_cfg->gain != ADC_GAIN_1) { + if (ch_cfg->gain != ADC_GAIN_1) { #endif /* defined(NRF_SAADC_HAS_CH_GAIN) */ LOG_ERR("Selected ADC gain is not valid"); return -EINVAL; } - switch (channel_cfg->reference) { + return 0; +} + +static int reference_set(nrf_saadc_channel_config_t *ch_cfg, enum adc_reference reference) +{ + switch (reference) { #if defined(SAADC_CH_CONFIG_REFSEL_Internal) case ADC_REF_INTERNAL: - config.reference = NRF_SAADC_REFERENCE_INTERNAL; + ch_cfg->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; + ch_cfg->reference = NRF_SAADC_REFERENCE_VDD4; break; #endif #if defined(SAADC_CH_CONFIG_REFSEL_External) case ADC_REF_EXTERNAL0: - config.reference = NRF_SAADC_REFERENCE_EXTERNAL; + ch_cfg->reference = NRF_SAADC_REFERENCE_EXTERNAL; break; #endif default: @@ -322,133 +315,130 @@ static int adc_nrfx_channel_setup(const struct device *dev, return -EINVAL; } - int ret = adc_convert_acq_time(channel_cfg->acquisition_time, &config.acq_time); + return 0; +} - if (ret) { - LOG_ERR("Selected ADC acquisition time is not valid"); +/* 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) +{ + int err; + nrf_saadc_channel_config_t *ch_cfg; + nrfx_saadc_channel_t cfg = { + .channel_config = { +#if NRF_SAADC_HAS_CH_CONFIG_RES + .resistor_p = NRF_SAADC_RESISTOR_DISABLED, + .resistor_n = NRF_SAADC_RESISTOR_DISABLED, +#endif +#if NRF_SAADC_HAS_CH_BURST + .burst = NRF_SAADC_BURST_DISABLED, +#endif + }, + .channel_index = channel_cfg->channel_id, + }; + + if (channel_cfg->channel_id >= SAADC_CH_NUM) { + LOG_ERR("Invalid channel ID: %d", channel_cfg->channel_id); return -EINVAL; } + ch_cfg = &cfg.channel_config; + + err = input_assign(&cfg.pin_p, &cfg.pin_n, channel_cfg); + if (err != 0) { + return err; + } + + err = gain_set(ch_cfg, channel_cfg->gain); + if (err != 0) { + return err; + } + + err = reference_set(ch_cfg, channel_cfg->reference); + if (err != 0) { + return err; + } + + err = acq_time_set(ch_cfg, channel_cfg->acquisition_time); + if (err != 0) { + return err; + } + /* 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; + ch_cfg->mode = NRF_SAADC_MODE_DIFFERENTIAL; m_data.single_ended_channels &= ~BIT(channel_cfg->channel_id); } else { - config.mode = NRF_SAADC_MODE_SINGLE_ENDED; + ch_cfg->mode = NRF_SAADC_MODE_SINGLE_ENDED; m_data.single_ended_channels |= BIT(channel_cfg->channel_id); } -#if (NRF_SAADC_HAS_AIN_AS_PIN) - if ((channel_cfg->input_positive >= ARRAY_SIZE(saadc_psels)) || - (channel_cfg->input_positive < NRF_SAADC_AIN0)) { - return -EINVAL; - } - - if (config.mode == NRF_SAADC_MODE_DIFFERENTIAL) { -#if defined(CONFIG_NRF_PLATFORM_HALTIUM) - if ((input_negative > NRF_SAADC_AIN7) != - (channel_cfg->input_positive > NRF_SAADC_AIN7)) { -#else - if (input_negative > NRF_SAADC_AIN7 || - input_negative < NRF_SAADC_AIN0) { -#endif - return -EINVAL; - } + nrfx_err_t ret = nrfx_saadc_channel_config(&cfg); - input_negative = saadc_psels[input_negative]; - } else { - input_negative = NRF_SAADC_INPUT_DISABLED; + if (ret != NRFX_SUCCESS) { + LOG_ERR("Cannot configure channel %d: %d", channel_cfg->channel_id, ret); + return -EINVAL; } -#endif - /* 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] = channel_cfg->input_positive; - - nrf_saadc_channel_init(NRF_SAADC, channel_id, &config); - /* Keep the channel disabled in hardware (set positive input to - * NRF_SAADC_INPUT_DISABLED) until it is selected to be included - * in a sampling sequence. - */ - nrf_saadc_channel_input_set(NRF_SAADC, - channel_id, - NRF_SAADC_INPUT_DISABLED, - input_negative); return 0; } static void adc_context_start_sampling(struct adc_context *ctx) { -#if defined(CONFIG_PM_DEVICE_RUNTIME) - pm_device_runtime_get(DEVICE_DT_INST_GET(0)); -#else - nrf_saadc_enable(NRF_SAADC); -#endif - if (ctx->sequence.calibrate) { - nrf_saadc_task_trigger(NRF_SAADC, - NRF_SAADC_TASK_CALIBRATEOFFSET); + nrfx_saadc_offset_calibrate(event_handler); } else { - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START); - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE); + nrfx_err_t ret = nrfx_saadc_mode_trigger(); + + if (ret != NRFX_SUCCESS) { + LOG_ERR("Cannot start sampling: 0x%08x", ret); + adc_context_complete(&m_data.ctx, -EIO); + } } } -static void adc_context_update_buffer_pointer(struct adc_context *ctx, - bool repeat) +static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat) { - ARG_UNUSED(ctx); - if (!repeat) { #if defined(ADC_BUFFER_IN_RAM) - m_data.user_buffer = (uint8_t *)m_data.user_buffer + - samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); + m_data.user_buffer = (uint16_t *)m_data.user_buffer + m_data.active_channel_cnt; #else - nrf_saadc_value_t *buffer = - (uint8_t *)nrf_saadc_buffer_pointer_get(NRF_SAADC) + - samples_to_bytes(nrfy_saadc_amount_get(NRF_SAADC)); - nrfy_saadc_buffer_pointer_set(NRF_SAADC, buffer); + nrf_saadc_value_t *buffer = (uint16_t *)m_data.buffer + m_data.active_channel_cnt; + + nrfx_saadc_buffer_set(buffer, m_data.active_channel_cnt); #endif } } -static int set_resolution(const struct adc_sequence *sequence) +static int get_resolution(const struct adc_sequence *sequence, nrf_saadc_resolution_t *resolution) { - nrf_saadc_resolution_t nrf_resolution; - switch (sequence->resolution) { - case 8: - nrf_resolution = NRF_SAADC_RESOLUTION_8BIT; + case 8: + *resolution = NRF_SAADC_RESOLUTION_8BIT; break; case 10: - nrf_resolution = NRF_SAADC_RESOLUTION_10BIT; + *resolution = NRF_SAADC_RESOLUTION_10BIT; break; case 12: - nrf_resolution = NRF_SAADC_RESOLUTION_12BIT; + *resolution = NRF_SAADC_RESOLUTION_12BIT; break; case 14: - nrf_resolution = NRF_SAADC_RESOLUTION_14BIT; + *resolution = NRF_SAADC_RESOLUTION_14BIT; break; default: - LOG_ERR("ADC resolution value %d is not valid", - sequence->resolution); + LOG_ERR("ADC resolution value %d is not valid", sequence->resolution); return -EINVAL; } - nrf_saadc_resolution_set(NRF_SAADC, nrf_resolution); return 0; } -static int set_oversampling(const struct adc_sequence *sequence, - uint8_t active_channels) +static int get_oversampling(const struct adc_sequence *sequence, uint8_t active_channel_cnt, + nrf_saadc_oversample_t *oversampling) { - nrf_saadc_oversample_t nrf_oversampling; - - if ((active_channels > 1) && (sequence->oversampling > 0)) { + if ((active_channel_cnt > 1) && (sequence->oversampling > 0)) { LOG_ERR( "Oversampling is supported for single channel only"); return -EINVAL; @@ -456,48 +446,45 @@ static int set_oversampling(const struct adc_sequence *sequence, switch (sequence->oversampling) { case 0: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_DISABLED; + *oversampling = NRF_SAADC_OVERSAMPLE_DISABLED; break; case 1: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_2X; + *oversampling = NRF_SAADC_OVERSAMPLE_2X; break; case 2: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_4X; + *oversampling = NRF_SAADC_OVERSAMPLE_4X; break; case 3: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_8X; + *oversampling = NRF_SAADC_OVERSAMPLE_8X; break; case 4: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_16X; + *oversampling = NRF_SAADC_OVERSAMPLE_16X; break; case 5: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_32X; + *oversampling = NRF_SAADC_OVERSAMPLE_32X; break; case 6: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_64X; + *oversampling = NRF_SAADC_OVERSAMPLE_64X; break; case 7: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_128X; + *oversampling = NRF_SAADC_OVERSAMPLE_128X; break; case 8: - nrf_oversampling = NRF_SAADC_OVERSAMPLE_256X; + *oversampling = NRF_SAADC_OVERSAMPLE_256X; break; default: - LOG_ERR("Oversampling value %d is not valid", - sequence->oversampling); + LOG_ERR("Oversampling value %d is not valid", sequence->oversampling); return -EINVAL; } - nrf_saadc_oversample_set(NRF_SAADC, nrf_oversampling); return 0; } -static int check_buffer_size(const struct adc_sequence *sequence, - uint8_t active_channels) +static int check_buffer_size(const struct adc_sequence *sequence, uint8_t active_channel_cnt) { size_t needed_buffer_size; - needed_buffer_size = samples_to_bytes(active_channels); + needed_buffer_size = NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt); if (sequence->options) { needed_buffer_size *= (1 + sequence->options->extra_samplings); @@ -505,7 +492,7 @@ static int check_buffer_size(const struct adc_sequence *sequence, if (sequence->buffer_size < needed_buffer_size) { LOG_ERR("Provided buffer is too small (%u/%u)", - sequence->buffer_size, needed_buffer_size); + sequence->buffer_size, needed_buffer_size); return -ENOMEM; } @@ -522,7 +509,7 @@ 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); + int16_t *sample = (int16_t *)m_data.buffer; while (channel_bit <= single_ended_channels) { if (channel_bit & selected_channels) { @@ -540,11 +527,13 @@ static void correct_single_ended(const struct adc_sequence *sequence) static int start_read(const struct device *dev, const struct adc_sequence *sequence) { + nrfx_err_t nrfx_err; int error; uint32_t selected_channels = sequence->channels; - uint8_t active_channels; - uint8_t channel_id; - nrf_saadc_burst_t burst; + nrf_saadc_resolution_t resolution; + nrf_saadc_oversample_t oversampling; + uint8_t active_channel_cnt = 0U; + uint8_t channel_id = 0U; /* Signal an error if channel selection is invalid (no channels or * a non-existing one is selected). @@ -555,88 +544,55 @@ static int start_read(const struct device *dev, return -EINVAL; } - active_channels = 0U; - - /* Enable only the channels selected for the pointed sequence. - * Disable all the rest. - */ - channel_id = 0U; do { if (selected_channels & BIT(channel_id)) { - /* Signal an error if a selected channel has not been - * configured yet. - */ - if (m_data.positive_inputs[channel_id] == 0U) { - LOG_ERR("Channel %u not configured", - channel_id); + /* Signal an error if a selected channel has not been configured yet. */ + if (0 == (nrfx_saadc_channels_configured_get() & BIT(channel_id))) { + LOG_ERR("Channel %u not configured", 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 - * then the multiple channel sampling fails (the END - * event is not generated) after switching to a single - * channel sampling and back. Thus, when oversampling - * is not used (hence, the multiple channel sampling is - * possible), the burst mode have to be deactivated. - */ - burst = (sequence->oversampling != 0U ? - NRF_SAADC_BURST_ENABLED : NRF_SAADC_BURST_DISABLED); -#if NRF_SAADC_HAS_CH_BURST - nrf_saadc_channel_burst_set(NRF_SAADC, channel_id, burst); -#else - nrf_saadc_burst_set(NRF_SAADC, burst); -#endif - nrf_saadc_channel_pos_input_set( - NRF_SAADC, - channel_id, -#if NRF_SAADC_HAS_AIN_AS_PIN - saadc_psels[m_data.positive_inputs[channel_id]] -#else - m_data.positive_inputs[channel_id] -#endif - ); - ++active_channels; - } else { - burst = NRF_SAADC_BURST_DISABLED; -#if NRF_SAADC_HAS_CH_BURST - nrf_saadc_channel_burst_set(NRF_SAADC, channel_id, burst); -#else - nrf_saadc_burst_set(NRF_SAADC, burst); -#endif - nrf_saadc_channel_pos_input_set( - NRF_SAADC, - channel_id, - NRF_SAADC_INPUT_DISABLED); + ++active_channel_cnt; } } while (++channel_id < SAADC_CH_NUM); - error = set_resolution(sequence); + if (active_channel_cnt == 0) { + LOG_ERR("No channel configured"); + return -EINVAL; + } + + error = get_resolution(sequence, &resolution); if (error) { return error; } - error = set_oversampling(sequence, active_channels); + error = get_oversampling(sequence, active_channel_cnt, &oversampling); if (error) { return error; } - error = check_buffer_size(sequence, active_channels); + nrfx_err = nrfx_saadc_simple_mode_set(selected_channels, + resolution, + oversampling, + event_handler); + if (nrfx_err != NRFX_SUCCESS) { + return -EINVAL; + } + + error = check_buffer_size(sequence, active_channel_cnt); if (error) { return error; } + m_data.active_channel_cnt = active_channel_cnt; #if defined(ADC_BUFFER_IN_RAM) m_data.user_buffer = sequence->buffer; - m_data.active_channels = active_channels; - nrf_saadc_buffer_init(NRF_SAADC, - (nrf_saadc_value_t *)m_data.samples_buffer, - active_channels); + nrfx_saadc_buffer_set(m_data.samples_buffer, active_channel_cnt); #else - nrf_saadc_buffer_init(NRF_SAADC, - (nrf_saadc_value_t *)sequence->buffer, - active_channels); + /* Buffer is filled in chunks, each chunk composed of number of samples equal to number + * of active channels. Buffer pointer is advanced and reloaded after each chunk. + */ + nrfx_saadc_buffer_set(sequence->buffer, active_channel_cnt); #endif adc_context_start_read(&m_data.ctx, sequence); @@ -673,18 +629,12 @@ static int adc_nrfx_read_async(const struct device *dev, } #endif /* CONFIG_ADC_ASYNC */ -static void saadc_irq_handler(const struct device *dev) +static void event_handler(const nrfx_saadc_evt_t *event) { - if (nrf_saadc_event_check(NRF_SAADC, NRF_SAADC_EVENT_END)) { - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_END); + nrfx_err_t err; - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP); - -#if defined(CONFIG_PM_DEVICE_RUNTIME) - pm_device_runtime_put(DEVICE_DT_INST_GET(0)); -#else - nrf_saadc_disable(NRF_SAADC); -#endif + if (event->type == NRFX_SAADC_EVT_DONE) { + m_data.buffer = event->data.done.p_buffer; if (has_single_ended(&m_data.ctx.sequence)) { correct_single_ended(&m_data.ctx.sequence); @@ -692,39 +642,35 @@ static void saadc_irq_handler(const struct device *dev) #if defined(ADC_BUFFER_IN_RAM) memcpy(m_data.user_buffer, m_data.samples_buffer, - samples_to_bytes(m_data.active_channels)); + NRFX_SAADC_SAMPLES_TO_BYTES(m_data.active_channel_cnt)); #endif - adc_context_on_sampling_done(&m_data.ctx, dev); - } else if (nrf_saadc_event_check(NRF_SAADC, - NRF_SAADC_EVENT_CALIBRATEDONE)) { - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_CALIBRATEDONE); - - /* - * The workaround for Nordic nRF52832 anomalies 86 and - * 178 is an explicit STOP after CALIBRATEOFFSET - * before issuing START. - */ - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_STOP); - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_START); - nrf_saadc_task_trigger(NRF_SAADC, NRF_SAADC_TASK_SAMPLE); + adc_context_on_sampling_done(&m_data.ctx, DEVICE_DT_INST_GET(0)); + } else if (event->type == NRFX_SAADC_EVT_CALIBRATEDONE) { + err = nrfx_saadc_mode_trigger(); + if (err != NRFX_SUCCESS) { + LOG_ERR("Cannot start sampling: 0x%08x", err); + adc_context_complete(&m_data.ctx, -EIO); + } } } static int init_saadc(const struct device *dev) { - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_END); - nrf_saadc_event_clear(NRF_SAADC, NRF_SAADC_EVENT_CALIBRATEDONE); - nrf_saadc_int_enable(NRF_SAADC, - NRF_SAADC_INT_END | NRF_SAADC_INT_CALIBRATEDONE); - NRFX_IRQ_ENABLE(DT_INST_IRQN(0)); + nrfx_err_t err; + + /* The priority value passed here is ignored (see nrfx_glue.h). */ + err = nrfx_saadc_init(0); + if (err != NRFX_SUCCESS) { + LOG_ERR("Failed to initialize device: %s", dev->name); + return -EIO; + } - IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), - saadc_irq_handler, DEVICE_DT_INST_GET(0), 0); + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), nrfx_isr, nrfx_saadc_irq_handler, 0); adc_context_unlock_unconditionally(&m_data.ctx); - return pm_device_driver_init(dev, saadc_pm_hook); + return 0; } static DEVICE_API(adc, adc_nrfx_driver_api) = { @@ -763,29 +709,7 @@ static DEVICE_API(adc, adc_nrfx_driver_api) = { "1v8 inputs cannot be mixed with 3v3 inputs"); /* Validate configuration of all channels. */ -#define VALIDATE_CHANNELS_CONFIG(inst) DT_FOREACH_CHILD(DT_DRV_INST(inst), VALIDATE_CHANNEL_CONFIG) +DT_FOREACH_CHILD(DT_DRV_INST(0), VALIDATE_CHANNEL_CONFIG) -/* - * There is only one instance on supported SoCs, so inst is guaranteed - * to be 0 if any instance is okay. (We use adc_0 above, so the driver - * is relying on the numeric instance value in a way that happens to - * be safe.) - * - * Just in case that assumption becomes invalid in the future, we use - * a BUILD_ASSERT(). - */ -#define SAADC_INIT(inst) \ - BUILD_ASSERT((inst) == 0, \ - "multiple instances not supported"); \ - VALIDATE_CHANNELS_CONFIG(inst) \ - PM_DEVICE_DT_INST_DEFINE(0, saadc_pm_hook, 1); \ - DEVICE_DT_INST_DEFINE(0, \ - init_saadc, \ - PM_DEVICE_DT_INST_GET(0), \ - NULL, \ - NULL, \ - POST_KERNEL, \ - CONFIG_ADC_INIT_PRIORITY, \ - &adc_nrfx_driver_api); - -DT_INST_FOREACH_STATUS_OKAY(SAADC_INIT) +DEVICE_DT_INST_DEFINE(0, init_saadc, NULL, NULL, NULL, POST_KERNEL, + CONFIG_ADC_INIT_PRIORITY, &adc_nrfx_driver_api); From 2e76866d6825801fa73056db6b6f46060525e879 Mon Sep 17 00:00:00 2001 From: Jakub Zymelka Date: Wed, 28 May 2025 13:38:02 +0200 Subject: [PATCH 4/5] [nrf fromtree] samples: drivers: adc: Add variant for 8-bit resolution on nRF54x Add sample variant to run code in 8-bit resolution on nRF54x series. Signed-off-by: Jakub Zymelka (cherry picked from commit 7c14d7e6c672f20239cb3fc575874506cb18d33a) --- samples/drivers/adc/adc_sequence/sample.yaml | 31 +++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/samples/drivers/adc/adc_sequence/sample.yaml b/samples/drivers/adc/adc_sequence/sample.yaml index 9a824045d13..09edd4cc03c 100644 --- a/samples/drivers/adc/adc_sequence/sample.yaml +++ b/samples/drivers/adc/adc_sequence/sample.yaml @@ -1,10 +1,19 @@ sample: name: ADC driver sequence sample +common: + tags: + - adc + depends_on: adc + harness: console + timeout: 10 + harness_config: + type: multi_line + regex: + - "ADC sequence reading \\[\\d+\\]:" + - "- .+, channel \\d+, \\d+ sequence samples:" + - "- - \\d+ (= \\d+mV)|(\\(value in mV not available\\))" tests: sample.drivers.adc.adc_sequence: - tags: - - adc - depends_on: adc platform_allow: - cy8cproto_063_ble - cy8cproto_062_4343w @@ -18,11 +27,11 @@ tests: - stm32f3_disco integration_platforms: - nrf52840dk/nrf52840 - harness: console - timeout: 10 - harness_config: - type: multi_line - regex: - - "ADC sequence reading \\[\\d+\\]:" - - "- .+, channel \\d+, \\d+ sequence samples:" - - "- - \\d+ (= \\d+mV)|(\\(value in mV not available\\))" + sample.drivers.adc.adc_sequence.8bit: + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + extra_configs: + - CONFIG_SEQUENCE_RESOLUTION=8 From addc269b55bfe4957815a4e22f18516ec92f4655 Mon Sep 17 00:00:00 2001 From: Nikodem Kastelik Date: Mon, 23 Jun 2025 12:23:12 +0200 Subject: [PATCH 5/5] [nrf fromlist] manifest: update hal_nordic to have SAADC samples aligned to 3.12 API NRFX_SAADC_SAMPLE_GET() macro takes two arguments now instead of three. Upstream PR #: 92020 Signed-off-by: Nikodem Kastelik --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 76ca9284ec8..9fdf19767b5 100644 --- a/west.yml +++ b/west.yml @@ -200,7 +200,7 @@ manifest: groups: - hal - name: hal_nordic - revision: 71308dc6d8c021887ce5d98a36cafe9517375a91 + revision: pull/304/head path: modules/hal/nordic groups: - hal