drivers: adc: Update adc driver to support nRF54L15 device
Expands driver to cover nRF54L15 features like AIN as GPIO configuration, new reference voltage, different set of supported gain options. Signed-off-by: Karol Lasończyk <karol.lasonczyk@nordicsemi.no>
This commit is contained in:
parent
481bd6db8b
commit
3ca3131a7d
1 changed files with 153 additions and 36 deletions
|
@ -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
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue