This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

configuring multiple saadc channels

Hi,

I want to configure 2 channels of saadc in differential mode as follows:

void saadc_init(void) //ok
{
    ret_code_t err_code;

	nrf_saadc_channel_config_t channel_config1 =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN4,NRF_SAADC_INPUT_AIN6);
	nrf_saadc_channel_config_t channel_config2 =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN5,NRF_SAADC_INPUT_AIN7);

    err_code = nrf_drv_saadc_init(NULL, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &channel_config1);
    APP_ERROR_CHECK(err_code);
		err_code = nrf_drv_saadc_channel_init(1, &channel_config2);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
    APP_ERROR_CHECK(err_code);
		
		nrf_saadc_continuous_mode_enable(800);
	

}

But when I received data, it seems that only the channel that its channel index is zero is enabled. may you help me to find the issue and configure this 2 channel in scan mode? i am working based on the saadc example in sk17. 

Parents
  • Hi,

    It looks like you are using the internal timer to enable continuous sample mode, by calling the nrf_saadc_continuous_mode_enable() function. As written in the SAADC peripheral documentation:

    "The SAMPLERATE timer mode cannot be combined with SCAN mode, and only one channel can be enabled in this mode."

    You need to use an external TIMER/RTC to trigger the sampling when using SCAN mode.

    Best regards,
    Jørgen

  • There is a SAADC example in the SDK that shows how to use a TIMER to sample the SAADC at a regular interval. This can easily be extended with multiple channels.

    If you want low-power SAADC, there are some examples available in this GitHub repository.

    If you want to use the legacy SAADC driver API with low power and multiple channels, see the example in this post.

  • Thanks it helped. I have another question. Can we configure multiple differential channels with a common ANI? for example like :

    nrf_saadc_channel_config_t channel_config1 =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN4,NRF_SAADC_INPUT_AIN0);
    nrf_saadc_channel_config_t channel_config2 =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN5,NRF_SAADC_INPUT_AIN0);
    nrf_saadc_channel_config_t channel_config3 =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN6,NRF_SAADC_INPUT_AIN0);
    nrf_saadc_channel_config_t channel_config4 =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN7,NRF_SAADC_INPUT_AIN0);

  • Also I have another Problem. I want to measure the input noise of my analog system, so I shorted the input signals. I saw that the second channel of ADC data is some how incorrect and quantized. the data from ch1 and 2 are like below: 

    the code that I am using for this is:

    /**
     * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    #include "sdk_common.h"
    #include "nrf.h"
    #include "nrf_esb.h"
    #include "nrf_error.h"
    #include "nrf_esb_error_codes.h"
    #include "nrf_delay.h"
    #include "nrf_gpio.h"
    #include "nrf_clock.h"
    #include "boards.h"
    #include "nrf_delay.h"
    #include "app_util.h"
    
    /** user's code begins 1. 
    */
    #include <stdio.h>
    #include "nrf_drv_saadc.h"
    #include "nrf_drv_ppi.h"
    #include "nrf_drv_timer.h"
    #include "app_error.h"
    #include "app_util_platform.h"
    #include "nrf_pwr_mgmt.h"
    /** user's code ends 1. 
    */
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    /** user's code begins 2. 
    */
    
    #define SAMPLES_IN_BUFFER 20
    volatile uint8_t state = 1;
    
    static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
    static nrf_saadc_value_t     m_buffer_pool[2][SAMPLES_IN_BUFFER];
    static nrf_ppi_channel_t     m_ppi_channel;
    static uint32_t              m_adc_evt_counter;
    
    static nrf_esb_payload_t        tx_payload = NRF_ESB_CREATE_PAYLOAD(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
    
    
    static nrf_esb_payload_t        rx_payload;
    uint8_t flag=0;
    
    static uint32_t              Sending_packet_passed;
    static uint32_t              Sending_packet_failed;
    
    bool buf_flag=false;
    
    void timer_handler(nrf_timer_event_t event_type, void * p_context)
    {
    
    }
    
    void saadc_sampling_event_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
        err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
        APP_ERROR_CHECK(err_code);
    
        /* setup m_timer for compare event every 50us */
        uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 50);
        nrf_drv_timer_extended_compare(&m_timer,
                                       NRF_TIMER_CC_CHANNEL0,
                                       ticks,
                                       NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
                                       false);
        nrf_drv_timer_enable(&m_timer);
    
        uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
                                                                                    NRF_TIMER_CC_CHANNEL0);
        uint32_t saadc_sample_task_addr   = nrf_drv_saadc_sample_task_get();
    
        /* setup ppi channel so that timer compare event is triggering sample task in SAADC */
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                              timer_compare_event_addr,
                                              saadc_sample_task_addr);
        APP_ERROR_CHECK(err_code);
    }
    
    
    void saadc_sampling_event_enable(void)
    {
        ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
    
        APP_ERROR_CHECK(err_code);
    }
    
    
    void saadc_callback(nrf_drv_saadc_evt_t const * p_event) //ok
    {
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
            ret_code_t err_code;
    				if (!buf_flag)
    					err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
    				else
    					err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
            APP_ERROR_CHECK(err_code);
    
            int i;
    				int k=1;
    				for (i = 0; i < SAMPLES_IN_BUFFER; i+=2)
    			  {
    					tx_payload.data[k] =   (uint8_t)(p_event->data.done.p_buffer[i]);
    					tx_payload.data[k+1] = (uint8_t)(p_event->data.done.p_buffer[i] >> 8) | (uint8_t)(p_event->data.done.p_buffer[i+1] << 4);
    					tx_payload.data[k+2] = (uint8_t)(p_event->data.done.p_buffer[i+1] >> 4);
    					k+=3;
    			  }
    				tx_payload.data[0]=m_adc_evt_counter++;
    				tx_payload.data[31]=tx_payload.data[0];
    				if (nrf_esb_write_payload(&tx_payload) ==NRF_SUCCESS)
    				{
    					Sending_packet_passed++;
    				}
    				else
    				{
    					Sending_packet_failed++;
    				}
    				buf_flag = !buf_flag;
        }
    }
    
    
    void saadc_init(void) //ok
    {
        ret_code_t err_code;
    
    	nrf_saadc_channel_config_t channel_config1 =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN4,NRF_SAADC_INPUT_AIN6);
    	nrf_saadc_channel_config_t channel_config2 =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_DIFFERENTIAL(NRF_SAADC_INPUT_AIN5,NRF_SAADC_INPUT_AIN7);
    
        err_code = nrf_drv_saadc_init(NULL, saadc_callback);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_channel_init(0, &channel_config1);
        APP_ERROR_CHECK(err_code);
    		err_code = nrf_drv_saadc_channel_init(1, &channel_config2);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
        APP_ERROR_CHECK(err_code);
    		
    //		nrf_saadc_continuous_mode_enable(800);
    
    }
    
    /** user's code ends 2. 
    */
    
    
    void nrf_esb_event_handler(nrf_esb_evt_t const * p_event)
    {
        switch (p_event->evt_id)
        {
            case NRF_ESB_EVENT_TX_SUCCESS:
                NRF_LOG_DEBUG("TX SUCCESS EVENT");
                break;
            case NRF_ESB_EVENT_TX_FAILED:
                NRF_LOG_DEBUG("TX FAILED EVENT");
                (void) nrf_esb_flush_tx();
                (void) nrf_esb_start_tx();
                break;
            case NRF_ESB_EVENT_RX_RECEIVED:
                NRF_LOG_DEBUG("RX RECEIVED EVENT");
                while (nrf_esb_read_rx_payload(&rx_payload) == NRF_SUCCESS)
                {
                    if (rx_payload.length > 0)
                    {
                        NRF_LOG_DEBUG("RX RECEIVED PAYLOAD");
                    }
                }
                break;
        }
    }
    
    
    void clocks_start( void )
    {
        NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
        NRF_CLOCK->TASKS_HFCLKSTART = 1;
    
        while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
    }
    
    
    void gpio_init( void )
    {
        nrf_gpio_range_cfg_output(8, 15);
        bsp_board_init(BSP_INIT_LEDS);
    }
    
    
    uint32_t esb_init( void )
    {
        uint32_t err_code;
        uint8_t base_addr_0[4] = {0xE7, 0xE7, 0xE7, 0xE7};
        uint8_t base_addr_1[4] = {0xC2, 0xC2, 0xC2, 0xC2};
        uint8_t addr_prefix[8] = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8 };
    
        nrf_esb_config_t nrf_esb_config         = NRF_ESB_DEFAULT_CONFIG;
        nrf_esb_config.protocol                 = NRF_ESB_PROTOCOL_ESB_DPL;
        nrf_esb_config.retransmit_delay         = 600;
        nrf_esb_config.bitrate                  = NRF_ESB_BITRATE_2MBPS_BLE;
        nrf_esb_config.event_handler            = nrf_esb_event_handler;
        nrf_esb_config.mode                     = NRF_ESB_MODE_PTX;
        nrf_esb_config.selective_auto_ack       = false;
    
        err_code = nrf_esb_init(&nrf_esb_config);
    
        VERIFY_SUCCESS(err_code);
    
        err_code = nrf_esb_set_base_address_0(base_addr_0);
        VERIFY_SUCCESS(err_code);
    
        err_code = nrf_esb_set_base_address_1(base_addr_1);
        VERIFY_SUCCESS(err_code);
    
        err_code = nrf_esb_set_prefixes(addr_prefix, NRF_ESB_PIPE_COUNT);
        VERIFY_SUCCESS(err_code);
    
        return err_code;
    }
    
    
    int main(void)
    {
    	
        ret_code_t err_code;
        gpio_init();
      	nrf_gpio_pin_clear(10);
    
        err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    	
      	nrf_gpio_cfg_output(27);
    		nrf_gpio_pin_clear(27);
    	
    	
    		clocks_start();
    	
        err_code = esb_init();
        APP_ERROR_CHECK(err_code);
    		
    		/** user's code begins 3. 
    		*/
    		ret_code_t ret_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(ret_code);
    
        saadc_init();
        saadc_sampling_event_init();
        saadc_sampling_event_enable();
    		
    		/** usr's code ends 3. 
    		*/ 
        while (true)
        { 
    				nrf_pwr_mgmt_run();
        }
    }
    

  • Masoumeh said:
    Can we configure multiple differential channels with a common ANI?

    Since the SAADC input muxes needs to be reconfigured between each channel anyway, I do not see any technical reasons that this should not work.

    Masoumeh said:
    I want to measure the input noise of my analog system, so I shorted the input signals. I saw that the second channel of ADC data is some how incorrect and quantized.

    Are the samples somehow processed before plotted in the graphs? What does the raw SAADC samples look like?

    Have you tested with a slower sample rate, to make sure that the previous sampling is completed on both channels before starting the next? By default, the acquisition time per channel should be 10us, so unless you have not modified the drivers, you should have plenty of time for 2 channels in 50 us.

Reply
  • Masoumeh said:
    Can we configure multiple differential channels with a common ANI?

    Since the SAADC input muxes needs to be reconfigured between each channel anyway, I do not see any technical reasons that this should not work.

    Masoumeh said:
    I want to measure the input noise of my analog system, so I shorted the input signals. I saw that the second channel of ADC data is some how incorrect and quantized.

    Are the samples somehow processed before plotted in the graphs? What does the raw SAADC samples look like?

    Have you tested with a slower sample rate, to make sure that the previous sampling is completed on both channels before starting the next? By default, the acquisition time per channel should be 10us, so unless you have not modified the drivers, you should have plenty of time for 2 channels in 50 us.

Children
  • Yes, I convert them to voltage but the program is ok, I tested it. I reduce the SR but the problem is still there. Also my acquisition time is 10us. the interesting thing is that, when I imply an sin input to the second channel, its function is ok and I will have the correct answer. 

    Another problem I have is that I have some kind of noise on my signal which includes some harmonic in 2k,4k,6k,..... I think it comes from the power switching between LDO and DCDC. Do you know how often do MCU switch between them? or what is the frequency of this task? 

  • The switching between LDO and DCDC is handled automatically by the chip depending on the load: "Automatic switching between LDO and DC/DC regulator based on load to maximize efficiency"

    The DCDC switching itself happens at 8MHz, as mentioned in this post. It is possible that it could be harmonics to this, but I would need to verify that with our developers. Have you verified that the harmonics disappear if you do not enable the DCDC regulator?

Related