Skip to content

Commit a26f2bd

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 7f14815 commit a26f2bd

File tree

1 file changed

+158
-32
lines changed

1 file changed

+158
-32
lines changed

drivers/adc/adc_nrfx_saadc.c

Lines changed: 158 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
#define ADC_CONTEXT_USES_KERNEL_TIMER
87
#include "adc_context.h"
98
#include <nrfx_saadc.h>
109
#include <zephyr/dt-bindings/adc/nrf-saadc-v3.h>
@@ -104,15 +103,25 @@ struct driver_data {
104103
uint8_t active_channel_cnt;
105104
void *mem_reg;
106105
void *user_buffer;
106+
struct k_timer timer;
107+
bool internal_timer_enabled;
107108
};
108109

110+
static void adc_context_on_timer_expired(struct k_timer *timer_id);
111+
109112
static struct driver_data m_data = {
110-
ADC_CONTEXT_INIT_TIMER(m_data, ctx),
111113
ADC_CONTEXT_INIT_LOCK(m_data, ctx),
112114
ADC_CONTEXT_INIT_SYNC(m_data, ctx),
115+
.timer = Z_TIMER_INITIALIZER(m_data.timer, \
116+
adc_context_on_timer_expired, \
117+
NULL),
113118
.mem_reg = DMM_DEV_TO_REG(DT_NODELABEL(adc)),
119+
.internal_timer_enabled = false,
114120
};
115121

122+
/* Maximum value of the internal timer interval in microseconds. */
123+
#define ADC_INTERNAL_TIMER_INTERVAL_MAX_US 128U
124+
116125
/* Forward declaration */
117126
static void event_handler(const nrfx_saadc_evt_t *event);
118127

@@ -385,22 +394,57 @@ static void adc_context_start_sampling(struct adc_context *ctx)
385394

386395
static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat)
387396
{
388-
void *samples_buffer;
397+
if (!m_data.internal_timer_enabled) {
398+
void *samples_buffer;
399+
400+
if (!repeat) {
401+
m_data.user_buffer =
402+
(uint16_t *)m_data.user_buffer + m_data.active_channel_cnt;
403+
}
404+
405+
int error = dmm_buffer_in_prepare(
406+
m_data.mem_reg, m_data.user_buffer,
407+
NRFX_SAADC_SAMPLES_TO_BYTES(m_data.active_channel_cnt), &samples_buffer);
408+
if (error != 0) {
409+
LOG_ERR("DMM buffer allocation failed err=%d", error);
410+
adc_context_complete(ctx, -EIO);
411+
}
389412

390-
if (!repeat) {
391-
m_data.user_buffer = (uint16_t *)m_data.user_buffer + m_data.active_channel_cnt;
413+
nrfx_err_t nrfx_err =
414+
nrfx_saadc_buffer_set(samples_buffer, m_data.active_channel_cnt);
415+
if (nrfx_err != NRFX_SUCCESS) {
416+
LOG_ERR("Failed to set buffer: %08x", nrfx_err);
417+
adc_context_complete(ctx, -EIO);
418+
}
392419
}
420+
}
393421

394-
int error = dmm_buffer_in_prepare(
395-
m_data.mem_reg, m_data.user_buffer,
396-
NRFX_SAADC_SAMPLES_TO_BYTES(m_data.active_channel_cnt),
397-
&samples_buffer);
398-
if (error != 0) {
399-
LOG_ERR("DMM buffer allocation failed err=%d", error);
400-
adc_context_complete(ctx, -EIO);
422+
static inline void adc_context_enable_timer(struct adc_context *ctx)
423+
{
424+
if (!m_data.internal_timer_enabled) {
425+
k_timer_start(&m_data.timer, K_NO_WAIT, K_USEC(ctx->options.interval_us));
426+
} else {
427+
nrfx_err_t ret = nrfx_saadc_mode_trigger();
428+
429+
if (ret != NRFX_SUCCESS) {
430+
LOG_ERR("Cannot start sampling: %d", ret);
431+
adc_context_complete(&m_data.ctx, -EIO);
432+
}
401433
}
434+
}
435+
436+
static inline void adc_context_disable_timer(struct adc_context *ctx)
437+
{
438+
if (!m_data.internal_timer_enabled) {
439+
k_timer_stop(&m_data.timer);
440+
}
441+
}
442+
443+
static void adc_context_on_timer_expired(struct k_timer *timer_id)
444+
{
445+
ARG_UNUSED(timer_id);
402446

403-
nrfx_saadc_buffer_set(samples_buffer, m_data.active_channel_cnt);
447+
adc_context_request_next_sampling(&m_data.ctx);
404448
}
405449

406450
static int get_resolution(const struct adc_sequence *sequence, nrf_saadc_resolution_t *resolution)
@@ -490,31 +534,70 @@ static int check_buffer_size(const struct adc_sequence *sequence, uint8_t active
490534
return 0;
491535
}
492536

537+
static inline void single_ended_channel_cut_negative_sample(uint16_t channel_bit,
538+
uint8_t single_ended_channels,
539+
int16_t **sample)
540+
{
541+
if ((channel_bit & single_ended_channels) && (*sample < 0)) {
542+
**sample = 0;
543+
}
544+
545+
(*sample)++;
546+
}
547+
493548
static bool has_single_ended(const struct adc_sequence *sequence)
494549
{
495550
return sequence->channels & m_data.single_ended_channels;
496551
}
497552

498-
static void correct_single_ended(const struct adc_sequence *sequence, nrf_saadc_value_t *buffer)
553+
static void correct_single_ended(const struct adc_sequence *sequence,
554+
nrf_saadc_value_t *buffer,
555+
uint16_t buffer_size)
499556
{
500-
uint16_t channel_bit = BIT(0);
501557
uint8_t selected_channels = sequence->channels;
502558
uint8_t single_ended_channels = m_data.single_ended_channels;
503559
int16_t *sample = (int16_t *)buffer;
504560

505-
while (channel_bit <= single_ended_channels) {
506-
if (channel_bit & selected_channels) {
507-
if ((channel_bit & single_ended_channels) && (*sample < 0)) {
508-
*sample = 0;
561+
for (uint16_t channel_bit = BIT(0); channel_bit <= single_ended_channels;
562+
channel_bit <<= 1) {
563+
if (!(channel_bit & selected_channels)) {
564+
continue;
565+
}
566+
567+
if (m_data.internal_timer_enabled) {
568+
if (!(channel_bit & single_ended_channels)) {
569+
continue;
509570
}
510571

511-
sample++;
572+
for (int i = 0; i < buffer_size; i++) {
573+
if (sample[i] < 0) {
574+
sample[i] = 0;
575+
}
576+
}
577+
} else {
578+
single_ended_channel_cut_negative_sample(channel_bit,
579+
single_ended_channels,
580+
&sample);
512581
}
513-
514-
channel_bit <<= 1;
515582
}
516583
}
517584

585+
/* The internal timer runs at 16 MHz, so to convert the interval in microseconds
586+
* to the internal timer CC value, we can use the formula:
587+
* interval_cc = interval_us * 16 MHz
588+
* where 16 MHz is the frequency of the internal timer.
589+
*
590+
* The maximum value for interval_cc is 2047, which corresponds to
591+
* approximately 7816 Hz ~ 128us.
592+
* The minimum value for interval_cc is depends on the SoC.
593+
*/
594+
static inline uint16_t interval_to_cc(uint16_t interval_us)
595+
{
596+
NRFX_ASSERT((interval_us <= ADC_INTERNAL_TIMER_INTERVAL_MAX_US) && (interval_us > 0));
597+
598+
return (interval_us * 16) - 1;
599+
}
600+
518601
static int start_read(const struct device *dev,
519602
const struct adc_sequence *sequence)
520603
{
@@ -562,10 +645,33 @@ static int start_read(const struct device *dev,
562645
return error;
563646
}
564647

565-
nrfx_err = nrfx_saadc_simple_mode_set(selected_channels,
566-
resolution,
567-
oversampling,
568-
event_handler);
648+
if ((active_channel_cnt == 1) &&
649+
(sequence->options != NULL) &&
650+
(sequence->options->interval_us <= ADC_INTERNAL_TIMER_INTERVAL_MAX_US) &&
651+
(sequence->options->interval_us > 0)) {
652+
653+
nrfx_saadc_adv_config_t adv_config = {
654+
.oversampling = oversampling,
655+
.burst = NRF_SAADC_BURST_DISABLED,
656+
.internal_timer_cc = interval_to_cc(sequence->options->interval_us),
657+
.start_on_end = true,
658+
};
659+
660+
m_data.internal_timer_enabled = true;
661+
662+
nrfx_err = nrfx_saadc_advanced_mode_set(selected_channels,
663+
resolution,
664+
&adv_config,
665+
event_handler);
666+
} else {
667+
m_data.internal_timer_enabled = false;
668+
669+
nrfx_err = nrfx_saadc_simple_mode_set(selected_channels,
670+
resolution,
671+
oversampling,
672+
event_handler);
673+
}
674+
569675
if (nrfx_err != NRFX_SUCCESS) {
570676
return -EINVAL;
571677
}
@@ -578,9 +684,11 @@ static int start_read(const struct device *dev,
578684
m_data.active_channel_cnt = active_channel_cnt;
579685
m_data.user_buffer = sequence->buffer;
580686

581-
error = dmm_buffer_in_prepare(m_data.mem_reg,
582-
m_data.user_buffer,
583-
NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt),
687+
error = dmm_buffer_in_prepare(m_data.mem_reg, m_data.user_buffer,
688+
(m_data.internal_timer_enabled
689+
? (NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt) *
690+
(1 + sequence->options->extra_samplings))
691+
: NRFX_SAADC_SAMPLES_TO_BYTES(active_channel_cnt)),
584692
&samples_buffer);
585693
if (error != 0) {
586694
LOG_ERR("DMM buffer allocation failed err=%d", error);
@@ -590,7 +698,15 @@ static int start_read(const struct device *dev,
590698
/* Buffer is filled in chunks, each chunk composed of number of samples equal to number
591699
* of active channels. Buffer pointer is advanced and reloaded after each chunk.
592700
*/
593-
nrfx_saadc_buffer_set(samples_buffer, active_channel_cnt);
701+
nrfx_err = nrfx_saadc_buffer_set(
702+
samples_buffer,
703+
(m_data.internal_timer_enabled
704+
? (active_channel_cnt * (1 + sequence->options->extra_samplings))
705+
: active_channel_cnt));
706+
if (nrfx_err != NRFX_SUCCESS) {
707+
LOG_ERR("Failed to set buffer: %08x", nrfx_err);
708+
return -EINVAL;
709+
}
594710

595711
adc_context_start_read(&m_data.ctx, sequence);
596712

@@ -633,11 +749,17 @@ static void event_handler(const nrfx_saadc_evt_t *event)
633749
if (event->type == NRFX_SAADC_EVT_DONE) {
634750
dmm_buffer_in_release(
635751
m_data.mem_reg, m_data.user_buffer,
636-
NRFX_SAADC_SAMPLES_TO_BYTES(m_data.active_channel_cnt),
752+
(m_data.internal_timer_enabled
753+
? (NRFX_SAADC_SAMPLES_TO_BYTES(
754+
m_data.active_channel_cnt) *
755+
(1 + m_data.ctx.sequence.options->extra_samplings))
756+
: NRFX_SAADC_SAMPLES_TO_BYTES(
757+
m_data.active_channel_cnt)),
637758
event->data.done.p_buffer);
638759

639760
if (has_single_ended(&m_data.ctx.sequence)) {
640-
correct_single_ended(&m_data.ctx.sequence, m_data.user_buffer);
761+
correct_single_ended(&m_data.ctx.sequence, m_data.user_buffer,
762+
event->data.done.size);
641763
}
642764
adc_context_on_sampling_done(&m_data.ctx, DEVICE_DT_INST_GET(0));
643765
} else if (event->type == NRFX_SAADC_EVT_CALIBRATEDONE) {
@@ -646,13 +768,17 @@ static void event_handler(const nrfx_saadc_evt_t *event)
646768
LOG_ERR("Cannot start sampling: 0x%08x", err);
647769
adc_context_complete(&m_data.ctx, -EIO);
648770
}
771+
} else if (event->type == NRFX_SAADC_EVT_FINISHED) {
772+
adc_context_complete(&m_data.ctx, 0);
649773
}
650774
}
651775

652776
static int init_saadc(const struct device *dev)
653777
{
654778
nrfx_err_t err;
655779

780+
// k_timer_init(&m_data.timer, adc_context_on_timer_expired, NULL);
781+
656782
/* The priority value passed here is ignored (see nrfx_glue.h). */
657783
err = nrfx_saadc_init(0);
658784
if (err != NRFX_SUCCESS) {

0 commit comments

Comments
 (0)