Unexpected Spikes in SAADC Output When Using Noise Shaping on nRF54L15

Hi Nordic team,

I’m working on the nRF54L15 DK with the SAADC peripheral and have encountered an issue when enabling noise shaping.

Here’s the setup:

  • Platform: nRF54L15 DK
  • SAADC resolution: 12-bit
  • Input range: Single-ended input, 0~VDD (3.3V)
  • Gain setting: 1/4
  • Sampling rate: 10k samples/sec
  • Buffer size: 1 sample (streamed continuously using TIMER + GPPI)
  • Mode: Advanced mode with noise shaping enabled (NRF_SAADC->NOISESHAPE = 1)

After converting the signed 12-bit samples to unsigned using 2’s complement mapping, I expected a clean sine wave ranging from 0 to 4095.

However, the resulting waveform contains occasional sharp spikes, which seem to appear periodically or randomly and do not correspond to the analog input signal.

Here is an example of the output waveform plotted in Excel:

To eliminate data conversion issues, I also tried printing the raw signed values directly, and the spikes were already present before conversion.

Here’s what I’ve verified so far:

  • Without noise shaping, the waveform is clean and matches the input.
  • With noise shaping, the overall shape is correct, but spikes appear.
  • Spikes occur even when the input is a low-noise sine wave (from a function generator).

I’m wondering:

  1. Is this expected behavior when using noise shaping on the nRF54L15 SAADC?
  2. Are there any known limitations or special considerations when using noise shaping with TIMER + PPI triggering?
  3. Is there a recommended method to reduce or filter these spikes in software or hardware?

The code i use is attached

Any insights or suggestions would be greatly appreciated.
Thanks in advance!

static void saadc_event_handler(nrfx_saadc_evt_t const * p_event)
{
    nrfx_err_t err;
    switch (p_event->type)
    {
        case NRFX_SAADC_EVT_READY:
        
            nrfx_timer_enable(&timer_instance);
            break;                        
            
        case NRFX_SAADC_EVT_BUF_REQ:
        
            err = nrfx_saadc_buffer_set(saadc_sample_buffer[(saadc_current_buffer++)%2], SAADC_BUFFER_SIZE);
            //err = nrfx_saadc_buffer_set(saadc_sample_buffer[((saadc_current_buffer == 0 )? saadc_current_buffer++ : 0)], SAADC_BUFFER_SIZE);
            if (err != NRFX_SUCCESS) {
                LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
                return;
            }
            break;

        case NRFX_SAADC_EVT_DONE:

            int64_t average = 1;
            int16_t max = INT16_MIN;
            int16_t min = INT16_MAX;
            int16_t current_value;
            //LOG_INF("SAADC buffer at 0x%x filled with %d samples", (uint32_t)p_event->data.done.p_buffer, p_event->data.done.size);
            

            //printk("ADC Samples:\n");
            for (int i = 0; i < p_event->data.done.size; i++) {
                current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                uint16_t unsigned_val = (current_value < 0)?(uint16_t)(current_value + 4096):(uint16_t)(current_value);
                average += current_value;
                if (current_value > max) max = current_value;
                if (current_value < min) min = current_value;
                printk("%d\n", unsigned_val);
            }
            average /= p_event->data.done.size;
            //printk("\nAVG=%d, MIN=%d, MAX=%d\n", (int16_t)average, min, max);

            break;

        default:
            LOG_INF("Unhandled SAADC evt %d", p_event->type);
            break;
    }
}

static void configure_saadc(void)
{
    nrfx_err_t err;

    IRQ_CONNECT(DT_IRQN(DT_NODELABEL(adc)),
                DT_IRQ(DT_NODELABEL(adc), priority),
                nrfx_isr, nrfx_saadc_irq_handler, 0);

    
    err = nrfx_saadc_init(DT_IRQ(DT_NODELABEL(adc), priority));
    if (err != NRFX_SUCCESS) {
        LOG_ERR("nrfx_saadc_init error: %08x", err);
        return;
    }

    NRF_SAADC->NOISESHAPE = 1;

    #if defined(CONFIG_SOC_NRF54L15)
        channel.channel_config.gain = NRF_SAADC_GAIN1_4;
    #else
        channel.channel_config.gain = NRF_SAADC_GAIN1_4;
    #endif

    err = nrfx_saadc_channels_config(&channel, 1);
    if (err != NRFX_SUCCESS) {
        LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
        return;
    }

    nrfx_saadc_adv_config_t saadc_adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
    err = nrfx_saadc_advanced_mode_set(BIT(0),
                                        NRF_SAADC_RESOLUTION_12BIT,
                                        &saadc_adv_config,
                                        saadc_event_handler);
    if (err != NRFX_SUCCESS) {
        LOG_ERR("nrfx_saadc_advanced_mode_set error: %08x", err);
        return;
    }
                                            
    err = nrfx_saadc_buffer_set(saadc_sample_buffer[0], SAADC_BUFFER_SIZE);
    if (err != NRFX_SUCCESS) {
        LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
        return;
    }
    err = nrfx_saadc_buffer_set(saadc_sample_buffer[1], SAADC_BUFFER_SIZE);
    if (err != NRFX_SUCCESS) {
        LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
        return;
    }

    err = nrfx_saadc_mode_trigger();
    if (err != NRFX_SUCCESS) {
        LOG_ERR("nrfx_saadc_mode_trigger error: %08x", err);
        return;
    }

}

Best regards,
Kyle Anderson

Parents Reply Children
  • Hi Hieu,

    Of course, I will attached my test application along with the usage.

    Environment: nRF Connect SDK 3.0.2

    Base Configuration:  prj.conf

    Extra Devicetree Overlay: boards/nrf54l15_nrf54l15_cpuapp.overlay

    UART baud rate:1000000

    Usage: the AIN4 port will be ADC input

    adc_test_ns.zip

    Thank you for your help.

    Best regards,

    Kyle Anderson

  • Hi Kyle,

    In setting up the measurement today, I was reminded of an issue: the nRF54L15 DK VDD is 1.8V by default.

    Have you changed this when you measured?

    The issue is that the maximum voltage the SAADC can measure is VDD, and the absolute maximum rating of the pin would be VDD + 0.3V.
    Therefore, we can expect voltage above 1.8V to be measured incorrectly.

    Here are the results of the few tests I run today.
    There was no strange spike like you had at first.
    There was also no weird graphs like the ones you last sent.
    There are only the clipped off peaks that is expected.

    I was a little careless with note taking, so I lost track of the 2nd and 3rd result and am not sure what exactly the input was. However, consider they have the same characteristics of interest as the other results, I hope that is not a big problem.

    For now, I would say, if you want to measure the signal correctly, reconfigure the nRF54L15 DK to 3.6V supply, which is the highest recommended operating voltage.
    Then you can reattempt with the 3.0Vpp, 1.5V offset signal.

    Please also make sure to enable oversampling and 12-bit or higher accuracy, as mentioned in the datasheet.

    As for the graphs you last sent, I initially thought it would be aliasing. That's why I asked for your input and sampling frequency.
    After I reproduce the setup today, I am now fairly certain that is the case.

    I actually was assigned to something non-DevZone from yesterday and will be away for a few weeks. Another engineer will continue to support you, so please feel free to reply.
    I will see if I can find time to reproduce my result some day, because they don't make total sense. 1.6Vpp shouldn't have been clipped off. I didn't notice that when I was just gathering data.

  • Hi Hieu,

    I just wanted to sincerely thank you for the time and effort you’ve dedicated to helping me investigate the issue with the nRF54L15 SAADC noise shaping.
    Although the problem hasn’t been fully resolved yet, your insights, testing suggestions, and guidance have been very valuable in moving the investigation forward.

    I understand that you will now be assigned to other tasks, and I truly appreciate your support up to this point. Your assistance has made a real difference in narrowing down potential causes, and I’ll continue to build on the work we’ve done together.

    Thanks again, and I wish you the best in your upcoming projects.

    Best regards,

    Kyle Anderson

  • Hi Kyle,

    Thank you for the wish.

    If you can consistently reproduce the issues after trying the recommendations from my last reply, please simply reply here with the details of the experiment. We have got your application, so you only need to include information about input signals and any changes you made. 

    I fully trust that the next support engineer will be able to help you.
    Then, best of luck to both of us.

    Best regards,

    Hieu

Related