Skip to content

Commit 0736723

Browse files
committed
[nrf fromlist] drivers: adc: nrfx_saadc: Add support for SAADC internal sampling timer
The SAMPLERATE register can be used as a local timer instead of triggering individual SAMPLE tasks. When SAMPLERATE.MODE is set to Timers, it is sufficient to trigger SAMPLE task only once in order to start the SAADC and triggering the STOP task will stop sampling. The SAMPLERATE.CC field controls the sample rate. The SAMPLERATE timer should not be combined with SCAN mode and only one channel should be enabled when using the internal timer. Upstream PR #: 91368 Signed-off-by: Jakub Zymelka <[email protected]>
1 parent 6703d14 commit 0736723

File tree

3 files changed

+184
-20
lines changed

3 files changed

+184
-20
lines changed

drivers/adc/adc_context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ static inline void adc_context_request_next_sampling(struct adc_context *ctx)
115115
}
116116

117117
#ifdef ADC_CONTEXT_USES_KERNEL_TIMER
118+
#ifndef ADC_DRIVER_USES_INTERNAL_TIMER
118119
static inline void adc_context_enable_timer(struct adc_context *ctx)
119120
{
120121
k_timer_start(&ctx->timer, K_NO_WAIT, K_USEC(ctx->options.interval_us));
@@ -124,6 +125,7 @@ static inline void adc_context_disable_timer(struct adc_context *ctx)
124125
{
125126
k_timer_stop(&ctx->timer);
126127
}
128+
#endif /* ADC_DRIVER_USES_INTERNAL_TIMER */
127129

128130
static void adc_context_on_timer_expired(struct k_timer *timer_id)
129131
{

drivers/adc/adc_nrfx_saadc.c

Lines changed: 172 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66

77
#define ADC_CONTEXT_USES_KERNEL_TIMER
8+
#define ADC_DRIVER_USES_INTERNAL_TIMER
9+
810
#include "adc_context.h"
911
#include <nrfx_saadc.h>
1012
#include <zephyr/dt-bindings/adc/nrf-saadc-v3.h>
@@ -104,15 +106,26 @@ struct driver_data {
104106
uint8_t active_channel_cnt;
105107
void *mem_reg;
106108
void *user_buffer;
109+
#ifdef ADC_DRIVER_USES_INTERNAL_TIMER
110+
bool enable_internal_timer;
111+
#endif
112+
bool internal_timer_enabled;
107113
};
108114

109115
static struct driver_data m_data = {
110116
ADC_CONTEXT_INIT_TIMER(m_data, ctx),
111117
ADC_CONTEXT_INIT_LOCK(m_data, ctx),
112118
ADC_CONTEXT_INIT_SYNC(m_data, ctx),
113119
.mem_reg = DMM_DEV_TO_REG(DT_NODELABEL(adc)),
120+
#ifdef ADC_DRIVER_USES_INTERNAL_TIMER
121+
.enable_internal_timer = DT_INST_PROP(0, enable_internal_timer),
122+
#endif
123+
.internal_timer_enabled = false,
114124
};
115125

126+
/* Maximum value of the internal timer interval in microseconds. */
127+
#define ADC_INTERNAL_TIMER_INTERVAL_MAX_US 128U
128+
116129
/* Forward declaration */
117130
static void event_handler(const nrfx_saadc_evt_t *event);
118131

@@ -385,7 +398,7 @@ static void adc_context_start_sampling(struct adc_context *ctx)
385398

386399
static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat)
387400
{
388-
if (!repeat) {
401+
if (!repeat && !m_data.internal_timer_enabled) {
389402
void *samples_buffer;
390403

391404
m_data.user_buffer = (uint16_t *)m_data.user_buffer + m_data.active_channel_cnt;
@@ -399,11 +412,39 @@ static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repe
399412
adc_context_complete(ctx, -EIO);
400413
}
401414
#if !defined(CONFIG_HAS_NORDIC_DMM)
402-
nrfx_saadc_buffer_set(samples_buffer, m_data.active_channel_cnt);
415+
nrfx_err_t nrfx_err =
416+
nrfx_saadc_buffer_set(samples_buffer, m_data.active_channel_cnt);
417+
if (nrfx_err != NRFX_SUCCESS) {
418+
LOG_ERR("Failed to set buffer: %08x", nrfx_err);
419+
adc_context_complete(ctx, -EIO);
420+
}
403421
#endif
404422
}
405423
}
406424

425+
#ifdef ADC_DRIVER_USES_INTERNAL_TIMER
426+
static inline void adc_context_enable_timer(struct adc_context *ctx)
427+
{
428+
if (!m_data.internal_timer_enabled) {
429+
k_timer_start(&ctx->timer, K_NO_WAIT, K_USEC(ctx->options.interval_us));
430+
} else {
431+
nrfx_err_t ret = nrfx_saadc_mode_trigger();
432+
433+
if (ret != NRFX_SUCCESS) {
434+
LOG_ERR("Cannot start sampling: %d", ret);
435+
adc_context_complete(&m_data.ctx, -EIO);
436+
}
437+
}
438+
}
439+
440+
static inline void adc_context_disable_timer(struct adc_context *ctx)
441+
{
442+
if (!m_data.internal_timer_enabled) {
443+
k_timer_stop(&ctx->timer);
444+
}
445+
}
446+
#endif /* ADC_DRIVER_USES_INTERNAL_TIMER */
447+
407448
static int get_resolution(const struct adc_sequence *sequence, nrf_saadc_resolution_t *resolution)
408449
{
409450
switch (sequence->resolution) {
@@ -491,29 +532,96 @@ static int check_buffer_size(const struct adc_sequence *sequence, uint8_t active
491532
return 0;
492533
}
493534

535+
static inline void single_ended_channel_cut_negative_sample(uint16_t channel_bit,
536+
uint8_t single_ended_channels,
537+
int16_t **sample)
538+
{
539+
if ((channel_bit & single_ended_channels) && (*sample < 0)) {
540+
**sample = 0;
541+
}
542+
543+
(*sample)++;
544+
}
545+
494546
static bool has_single_ended(const struct adc_sequence *sequence)
495547
{
496548
return sequence->channels & m_data.single_ended_channels;
497549
}
498550

499-
static void correct_single_ended(const struct adc_sequence *sequence, nrf_saadc_value_t *buffer)
551+
static void correct_single_ended(const struct adc_sequence *sequence,
552+
nrf_saadc_value_t *buffer,
553+
uint16_t buffer_size)
500554
{
501-
uint16_t channel_bit = BIT(0);
502555
uint8_t selected_channels = sequence->channels;
503556
uint8_t single_ended_channels = m_data.single_ended_channels;
504557
int16_t *sample = (int16_t *)buffer;
505558

506-
while (channel_bit <= single_ended_channels) {
507-
if (channel_bit & selected_channels) {
508-
if ((channel_bit & single_ended_channels) && (*sample < 0)) {
509-
*sample = 0;
559+
for (uint16_t channel_bit = BIT(0); channel_bit <= single_ended_channels;
560+
channel_bit <<= 1) {
561+
if (!(channel_bit & selected_channels)) {
562+
continue;
563+
}
564+
565+
#ifdef ADC_DRIVER_USES_INTERNAL_TIMER
566+
if (m_data.internal_timer_enabled) {
567+
if (!(channel_bit & single_ended_channels)) {
568+
continue;
510569
}
511570

512-
sample++;
571+
for (int i = 0; i < buffer_size; i++) {
572+
if (sample[i] < 0) {
573+
sample[i] = 0;
574+
}
575+
}
576+
} else {
577+
single_ended_channel_cut_negative_sample(channel_bit,
578+
single_ended_channels,
579+
&sample);
513580
}
581+
#else
582+
single_ended_channel_cut_negative_sample(channel_bit,
583+
single_ended_channels,
584+
&sample);
585+
#endif
586+
}
587+
}
514588

515-
channel_bit <<= 1;
589+
/* The internal timer runs at 16 MHz, so to convert the interval in microseconds
590+
* to the internal timer CC value, we can use the formula:
591+
* interval_cc = interval_us * 16 MHz
592+
* where 16 MHz is the frequency of the internal timer.
593+
*
594+
* The maximum value for interval_cc is 2047, which corresponds to
595+
* approximately 7816 Hz ~ 128us.
596+
* The minimum value for interval_cc is depends on the SoC.
597+
*/
598+
static inline uint16_t interval_to_cc(uint32_t interval_us)
599+
{
600+
uint16_t interval_cc = interval_us * 16;
601+
602+
if (interval_cc > NRF_SAADC_SAMPLERATE_CC_MAX) {
603+
LOG_INF("Interval %u us exceeds maximum CC value, setting to %lu",
604+
interval_us, NRF_SAADC_SAMPLERATE_CC_MAX);
605+
return NRF_SAADC_SAMPLERATE_CC_MAX;
606+
} else if (interval_cc < NRF_SAADC_SAMPLERATE_CC_MIN) {
607+
LOG_INF("Interval %u us exceeds minimum CC value, setting to %lu",
608+
interval_us, NRF_SAADC_SAMPLERATE_CC_MIN);
609+
return NRF_SAADC_SAMPLERATE_CC_MIN;
516610
}
611+
612+
return interval_cc;
613+
}
614+
615+
static inline nrfx_err_t disable_internal_timer(uint32_t selected_channels,
616+
nrf_saadc_resolution_t resolution,
617+
nrf_saadc_oversample_t oversampling)
618+
{
619+
m_data.internal_timer_enabled = false;
620+
621+
return nrfx_saadc_simple_mode_set(selected_channels,
622+
resolution,
623+
oversampling,
624+
event_handler);
517625
}
518626

519627
static int start_read(const struct device *dev,
@@ -563,10 +671,36 @@ static int start_read(const struct device *dev,
563671
return error;
564672
}
565673

566-
nrfx_err = nrfx_saadc_simple_mode_set(selected_channels,
567-
resolution,
568-
oversampling,
569-
event_handler);
674+
#ifdef ADC_DRIVER_USES_INTERNAL_TIMER
675+
if (m_data.enable_internal_timer &&
676+
(active_channel_cnt == 1) &&
677+
(sequence->options->interval_us <= ADC_INTERNAL_TIMER_INTERVAL_MAX_US) &&
678+
(sequence->options->interval_us > 0)) {
679+
680+
nrfx_saadc_adv_config_t adv_config = {
681+
.oversampling = oversampling,
682+
.burst = NRF_SAADC_BURST_DISABLED,
683+
.internal_timer_cc = interval_to_cc(sequence->options->interval_us),
684+
.start_on_end = true,
685+
};
686+
687+
m_data.internal_timer_enabled = true;
688+
689+
nrfx_err = nrfx_saadc_advanced_mode_set(selected_channels,
690+
resolution,
691+
&adv_config,
692+
event_handler);
693+
} else {
694+
nrfx_err = disable_internal_timer(selected_channels,
695+
resolution,
696+
oversampling);
697+
}
698+
#else
699+
nrfx_err = disable_internal_timer(selected_channels,
700+
resolution,
701+
oversampling);
702+
#endif
703+
570704
if (nrfx_err != NRFX_SUCCESS) {
571705
return -EINVAL;
572706
}
@@ -579,9 +713,11 @@ static int start_read(const struct device *dev,
579713
m_data.active_channel_cnt = active_channel_cnt;
580714
m_data.user_buffer = sequence->buffer;
581715

582-
error = dmm_buffer_in_prepare(m_data.mem_reg,
583-
m_data.user_buffer,
584-
NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt),
716+
error = dmm_buffer_in_prepare(m_data.mem_reg, m_data.user_buffer,
717+
(m_data.internal_timer_enabled
718+
? (NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt) *
719+
(1 + sequence->options->extra_samplings))
720+
: NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt)),
585721
&samples_buffer);
586722
if (error != 0) {
587723
LOG_ERR("DMM buffer allocation failed err=%d", error);
@@ -591,7 +727,15 @@ static int start_read(const struct device *dev,
591727
/* Buffer is filled in chunks, each chunk composed of number of samples equal to number
592728
* of active channels. Buffer pointer is advanced and reloaded after each chunk.
593729
*/
594-
nrfx_saadc_buffer_set(samples_buffer, active_channel_cnt);
730+
nrfx_err = nrfx_saadc_buffer_set(
731+
samples_buffer,
732+
(m_data.internal_timer_enabled
733+
? (active_channel_cnt * (1 + sequence->options->extra_samplings))
734+
: active_channel_cnt));
735+
if (nrfx_err != NRFX_SUCCESS) {
736+
LOG_ERR("Failed to set buffer: %08x", nrfx_err);
737+
return -EINVAL;
738+
}
595739

596740
adc_context_start_read(&m_data.ctx, sequence);
597741

@@ -634,11 +778,17 @@ static void event_handler(const nrfx_saadc_evt_t *event)
634778
if (event->type == NRFX_SAADC_EVT_DONE) {
635779
dmm_buffer_in_release(
636780
m_data.mem_reg, m_data.user_buffer,
637-
NRFX_SAADC_SAMPLES_TO_BYTES(m_data.active_channel_cnt),
781+
(m_data.internal_timer_enabled
782+
? (NRFX_SAADC_SAMPLES_TO_BYTES(
783+
m_data.active_channel_cnt) *
784+
(1 + m_data.ctx.sequence.options->extra_samplings))
785+
: NRFX_SAADC_SAMPLES_TO_BYTES(
786+
m_data.active_channel_cnt)),
638787
event->data.done.p_buffer);
639788

640789
if (has_single_ended(&m_data.ctx.sequence)) {
641-
correct_single_ended(&m_data.ctx.sequence, m_data.user_buffer);
790+
correct_single_ended(&m_data.ctx.sequence, m_data.user_buffer,
791+
event->data.done.size);
642792
}
643793
adc_context_on_sampling_done(&m_data.ctx, DEVICE_DT_INST_GET(0));
644794
} else if (event->type == NRFX_SAADC_EVT_CALIBRATEDONE) {
@@ -647,6 +797,8 @@ static void event_handler(const nrfx_saadc_evt_t *event)
647797
LOG_ERR("Cannot start sampling: 0x%08x", err);
648798
adc_context_complete(&m_data.ctx, -EIO);
649799
}
800+
} else if (event->type == NRFX_SAADC_EVT_FINISHED) {
801+
adc_context_complete(&m_data.ctx, 0);
650802
}
651803
}
652804

dts/bindings/adc/nordic,nrf-saadc.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ properties:
1414
interrupts:
1515
required: true
1616

17+
enable-internal-timer:
18+
type: boolean
19+
description: |
20+
Enable the internal timer for the SAADC to improve interval accuracy.
21+
When enabled, the SAADC will utilize its built-in local timer for sampling.
22+
If disabled, the SAADC will rely on the kernel timer.
23+
WARNING: The internal timer is only applicable for sequences with a single channel.
24+
It can be used only for intervals between consecutive samplings ranging
25+
from 500 ns to 128 us.
26+
1727
"#io-channel-cells":
1828
const: 1
1929

0 commit comments

Comments
 (0)