ADC on multiple pins with PPI - nRF52832

I was trying to build off of the example of Lesson 6, exercise 3 from the Intermediate course on DevAcademy. 

https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-6-analog-to-digital-converter-adc/

In this example, the AN0 is sampled the amount of times set by SAADC_BUFFER_SIZE (8000).

In my application, I am trying to sample on more than 1 pin.  Is it possible to do scan multiple pins with the PPI?

Ideally, I would like to monitor, AN2 and AN3.  In some circumstances, I want to monitor AN2, AN3, AN5, and AN6. 

I tried to modify the example as such but it reports 0bad0004

  /* STEP 4.6 - Declare the struct to hold the configuration for the SAADC channel used to sample the battery voltage */
    nrfx_saadc_channel_t channel_2 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN2, 0);
    nrfx_saadc_channel_t channel_3 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 0);

    /* STEP 4.7 - Change gain config in default config and apply channel configuration */
    channel_2.channel_config.gain = NRF_SAADC_GAIN1_6;
    err = nrfx_saadc_channels_config(&channel_2, 0);
    if(err != NRFX_SUCCESS)
    {
        printk("nrfx_saadc_channels_config error\r\n");
        // LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
        return;
    }

    channel_3.channel_config.gain = NRF_SAADC_GAIN1_6;
    err = nrfx_saadc_channels_config(&channel_3, 1);
    if(err != NRFX_SUCCESS)
    {
        printk("nrfx_saadc_channels_config error\r\n");
        // LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
        return;
    }

    /* STEP 4.8 - Configure channel 0 in advanced mode with event handler (non-blocking mode) */
    nrfx_saadc_adv_config_t saadc_adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
    err = nrfx_saadc_advanced_mode_set(BIT(2)|BIT(3),
                                        NRF_SAADC_RESOLUTION_12BIT,
                                        &saadc_adv_config,
                                        saadc_event_handler);
    if(err != NRFX_SUCCESS)
    {
        printk("nrfx_saadc_advanced_mode_set error %08x\r\n", err);
        // LOG_ERR("nrfx_saadc_advanced_mode_set error: %08x", err);
        return;
    }

Any advice?

  • Hi jablackann,

    One thing I have spotted so far is that you should probably put it like this instead: 

    nrfx_saadc_channel_t channel_2 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN2, 0);
    nrfx_saadc_channel_t channel_3 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 1);

    Each channel shoud have a unique index number.

    Just letting you know we have started working on this. I will get back to you when I have had a closer look.

  • I took inspiration from this related post.

     Sampling ADC with PPI and Timer on nrf connect sdk 

    In my application, I wanted to monitor AN2, AN3, AN5, and AN6.

    Above is the modifications that I made.   I have placed my code on here as well. At the bottom of the application is the previous way the device was calculating using a method of just capturing one reading at a time for each of the analog pins the circuit was using.

    #include "a_to_d.h"
    #include "main.h"
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/adc.h>
    #include <zephyr/logging/log.h>
    
    #define ADC_NODE		DT_NODELABEL(adc)
    static const struct device *adc_dev = DEVICE_DT_GET(ADC_NODE);
    
    #define ADC_RESOLUTION 	12
    #define ADC_CHANNEL_0 	0
    #define ADC_CHANNEL_1	1
    #define ADC_CHANNEL_2 	2
    #define ADC_CHANNEL_3	3
    #define ADC_CHANNEL_5	5
    #define ADC_CHANNEL_6	6
    #define ADC_PORT_AN0	SAADC_CH_PSELN_PSELN_AnalogInput0	// AIN0
    #define ADC_PORT_AN1	SAADC_CH_PSELN_PSELN_AnalogInput1	// AIN1
    #define ADC_PORT_AN2	SAADC_CH_PSELN_PSELN_AnalogInput2	// AIN2
    #define ADC_PORT_AN3	SAADC_CH_PSELN_PSELN_AnalogInput3	// AIN3
    #define ADC_PORT_AN5	SAADC_CH_PSELN_PSELN_AnalogInput5	// AIN5
    #define ADC_PORT_AN6	SAADC_CH_PSELN_PSELN_AnalogInput6	// AIN6
    #define ADC_REFERENCE	ADC_REF_INTERNAL					// 0.6V
    #define ADC_GAIN		ADC_GAIN_1_5						// ADC_REFERENCE * 5
    
    struct adc_channel_cfg channel_0_cfg =
    {
    	.gain = ADC_GAIN,
    	.reference = ADC_REFERENCE,
    	.acquisition_time = ADC_ACQ_TIME_DEFAULT,
    	.channel_id = ADC_CHANNEL_0,
    	#ifdef CONFIG_ADC_NRFX_SAADC
    	.input_positive = ADC_PORT_AN0
    	#endif
    };
    
    struct adc_channel_cfg channel_1_cfg =
    {
    	.gain = ADC_GAIN,
    	.reference = ADC_REFERENCE,
    	.acquisition_time = ADC_ACQ_TIME_DEFAULT,
    	.channel_id = ADC_CHANNEL_1,
    	#ifdef CONFIG_ADC_NRFX_SAADC
    	.input_positive = ADC_PORT_AN1
    	#endif
    };
    
    struct adc_channel_cfg channel_2_cfg =
    {
    	.gain = ADC_GAIN,
    	.reference = ADC_REFERENCE,
    	.acquisition_time = ADC_ACQ_TIME_DEFAULT,
    	.channel_id = ADC_CHANNEL_2,
    	#ifdef CONFIG_ADC_NRFX_SAADC
    	.input_positive = ADC_PORT_AN2
    	#endif
    };
    
    struct adc_channel_cfg channel_3_cfg =
    {
    	.gain = ADC_GAIN,
    	.reference = ADC_REFERENCE,
    	.acquisition_time = ADC_ACQ_TIME_DEFAULT,
    	.channel_id = ADC_CHANNEL_3,
    	#ifdef CONFIG_ADC_NRFX_SAADC
    	.input_positive = ADC_PORT_AN3
    	#endif
    };
    
    struct adc_channel_cfg channel_5_cfg =
    {
    	.gain = ADC_GAIN,
    	.reference = ADC_REFERENCE,
    	.acquisition_time = ADC_ACQ_TIME_DEFAULT,
    	.channel_id = ADC_CHANNEL_5,
    	#ifdef CONFIG_ADC_NRFX_SAADC
    	.input_positive = ADC_PORT_AN5
    	#endif
    };
    
    struct adc_channel_cfg channel_6_cfg =
    {
    	.gain = ADC_GAIN,
    	.reference = ADC_REFERENCE,
    	.acquisition_time = ADC_ACQ_TIME_DEFAULT,
    	.channel_id = ADC_CHANNEL_6,
    	#ifdef CONFIG_ADC_NRFX_SAADC
    	.input_positive = ADC_PORT_AN6
    	#endif
    };
    
    #include <nrfx_saadc.h>
    #include <nrfx_timer.h>
    #include <helpers/nrfx_gppi.h>
    #if defined(DPPI_PRESENT)
    #include <nrfx_dppi.h>
    #else
    #include <nrfx_ppi.h>
    #endif
    
    
    /* STEP 3.1 - Define the SAADC sample interval in microseconds */
    #define SAADC_SAMPLE_INTERVAL_US 50
    
    
    /* STEP 4.1 - Define the buffer size for the SAADC */
    #define SAADC_BUFFER_SIZE (8000)
    
    
    /* STEP 3.2 - Declaring an instance of nrfx_timer for TIMER2. */
    const nrfx_timer_t timer_instance = NRFX_TIMER_INSTANCE(2);
    
    /* STEP 4.2 - Declare the buffers for the SAADC */
    static int16_t saadc_sample_buffer[2][SAADC_BUFFER_SIZE];
    
    /* STEP 4.3 - Declare variable used to keep track of which buffer was last assigned to the SAADC driver */
    static uint32_t saadc_current_buffer = 0; 
    
    static void configure_timer(void)
    {
        nrfx_err_t err;
    
        /* STEP 3.3 - Declaring timer config and intialize nrfx_timer instance. */
        nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(1000000);
        err = nrfx_timer_init(&timer_instance, &timer_config, NULL); 
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_timer_init error %08x\r\n", err);
            // LOG_ERR("nrfx_timer_init error: %08x", err);
            return;
        }           
    
        /* STEP 3.4 - Set compare channel 0 to generate event every SAADC_SAMPLE_INTERVAL_US. */
        uint32_t timer_ticks = nrfx_timer_us_to_ticks(&timer_instance, SAADC_SAMPLE_INTERVAL_US);
        nrfx_timer_extended_compare(&timer_instance, NRF_TIMER_CC_CHANNEL0, timer_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    
    }
    
    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:
            
               /* STEP 5.1 - Buffer is ready, timer (and sampling) can be started. */
                nrfx_timer_enable(&timer_instance);
    
                break;                        
                
            case NRFX_SAADC_EVT_BUF_REQ:
            
                /* STEP 5.2 - Set up the next available buffer. Alternate between buffer 0 and 1 */
                err = nrfx_saadc_buffer_set(saadc_sample_buffer[(saadc_current_buffer++)%2], SAADC_BUFFER_SIZE);
                if(err != NRFX_SUCCESS)
                {
                    printk("nrf_saadc_buffer_set error %08x\r\n", err);
                    // LOG_ERR("nrf_saadc_buffer_set error: %08x", err);
                    return;
                }
                break;
    
            case NRFX_SAADC_EVT_DONE:
    
                /* STEP 5.3 - Buffer has been filled. Do something with the data and proceed */
                int64_t average_AN2 = 0;
                int16_t max_AN2 = INT16_MIN;
                int16_t min_AN2 = INT16_MAX; 
                int16_t current_value;
    
                int64_t average_AN3 = 0;
                int16_t max_AN3 = INT16_MIN;
                int16_t min_AN3 = INT16_MAX; 
    
                int64_t average_AN5 = 0;
                int16_t max_AN5 = INT16_MIN;
                int16_t min_AN5 = INT16_MAX; 
    
                int64_t average_AN6 = 0;
                int16_t max_AN6 = INT16_MIN;
                int16_t min_AN6 = INT16_MAX; 
                
                for(int i = 0; i < p_event->data.done.size;i++)
                {
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN2 += current_value;
                    if(current_value > max_AN2)
                    {
                        max_AN2 = current_value;
                    }
                    if(current_value < min_AN2)
                    {
                        min_AN2 = current_value;
                    }
    
                    i++;
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN3 += current_value;
                    if(current_value > max_AN3)
                    {
                        max_AN3 = current_value;
                    }
                    if(current_value < min_AN3)
                    {
                        min_AN3 = current_value;
                    }
    
                    i++;
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN5 += current_value;
                    if(current_value > max_AN5)
                    {
                        max_AN5 = current_value;
                    }
                    if(current_value < min_AN5)
                    {
                        min_AN5 = current_value;
                    }
    
                    i++;
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN6 += current_value;
                    if(current_value > max_AN6)
                    {
                        max_AN6 = current_value;
                    }
                    if(current_value < min_AN6)
                    {
                        min_AN6 = current_value;
                    }
                }
                average_AN2 = average_AN2 /(p_event->data.done.size /4);
    
                average_AN3 = average_AN3 /(p_event->data.done.size / 4);
    
                average_AN5 = average_AN5 /(p_event->data.done.size / 4);
    
                average_AN6 = average_AN6 /(p_event->data.done.size / 4);
            
                // LOG_INF("SAADC buffer at 0x%x filled with %d samples", (uint32_t)p_event->data.done.p_buffer, p_event->data.done.size);
                // LOG_INF("AVG=%d, MIN=%d, MAX=%d", (int16_t)average, min, max);
                //printk("SAADC buffer at 0x%x filled with %d samples\r\n", (uint32_t)p_event->data.done.p_buffer, p_event->data.done.size);
                printk("AVG_AN2=%d, MIN_AN2=%d, MAX_AN2=%d\t", (int16_t)average_AN2, min_AN2, max_AN2);
    
                printk("AVG_AN3=%d, MIN_AN3=%d, MAX_AN3=%d\t", (int16_t)average_AN3, min_AN3, max_AN3);
    
                printk("AVG_AN5=%d, MIN_AN5=%d, MAX_AN5=%d\t", (int16_t)average_AN5, min_AN5, max_AN5);
    
                printk("AVG_AN6=%d, MIN_AN6=%d, MAX_AN6=%d\r\n", (int16_t)average_AN6, min_AN6, max_AN6);
    
                break;
    
            default:
                // LOG_INF("Unhandled SAADC evt %d", p_event->type);
                printk("Unhandled SAADC evt %d", p_event->type);
                break;
        }
    }
    
    static nrfx_saadc_channel_t m_multiple_channels[] =
    {
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN2, 0),
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 1),
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN5, 2),
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN6, 3)
    };
    
    static void configure_saadc(void)
    {
        nrfx_err_t err;
    
        /* STEP 4.4 - Connect ADC interrupt to nrfx interrupt handler */
        IRQ_CONNECT(DT_IRQN(DT_NODELABEL(adc)),
                    DT_IRQ(DT_NODELABEL(adc), priority),
                    nrfx_isr, nrfx_saadc_irq_handler, 0);
        
        /* STEP 4.5 - Initialize the nrfx_SAADC driver */
        err = nrfx_saadc_init(DT_IRQ(DT_NODELABEL(adc), priority));
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_init error\r\n");
            // LOG_ERR("nrfx_saadc_init error: %08x", err);
            return;
        }
    
        /* STEP 4.6 - Declare the struct to hold the configuration for the SAADC channel used to sample the battery voltage */
        // nrfx_saadc_channel_t channel_2 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN2, 0);
        // nrfx_saadc_channel_t channel_3 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 0);
    
    #define NRFX_SAADC_CHANNEL_COUNT 4
    
        /* STEP 4.7 - Change gain config in default config and apply channel configuration */
        /* for my application the max that can be applied to an input is 3.3V*/
        m_multiple_channels[0].channel_config.gain = NRF_SAADC_GAIN1_5;
        m_multiple_channels[1].channel_config.gain = NRF_SAADC_GAIN1_5;
        m_multiple_channels[2].channel_config.gain = NRF_SAADC_GAIN1_5;
        m_multiple_channels[3].channel_config.gain = NRF_SAADC_GAIN1_5;
     
        nrfx_saadc_channels_config(m_multiple_channels,NRFX_SAADC_CHANNEL_COUNT);
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_channels_config error\r\n");
            // LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
            return; 
        }
    
        uint32_t channels_mask = nrfx_saadc_channels_configured_get();
    
        /* STEP 4.8 - Configure channel 0 in advanced mode with event handler (non-blocking mode) */
        nrfx_saadc_adv_config_t saadc_adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
        err = nrfx_saadc_advanced_mode_set(channels_mask, 
                                            NRF_SAADC_RESOLUTION_12BIT,
                                            &saadc_adv_config, 
                                            saadc_event_handler);
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_advanced_mode_set error %08x\r\n", err);
            // LOG_ERR("nrfx_saadc_advanced_mode_set error: %08x", err);
            return;
        }
                                                
        /* STEP 4.9 - Configure two buffers to make use of double-buffering feature of SAADC */
        err  = nrfx_saadc_buffer_set(saadc_sample_buffer[0], SAADC_BUFFER_SIZE);
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_buffer_set error %08x\r\n", err);
            // 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)
        {
            printk("nrfx_saadc_buffer_set error %08x\r\n", err);
            // LOG_ERR("nrfx_saadc_buffer_set error %08x", err);
            return;
        }
    
        /* STEP 4.10 - Trigger the SAADC. This will not start sampling, but will prepare buffer for sampling triggered through PPI */
        err = nrfx_saadc_mode_trigger();
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_mode_trigger error %08x\r\n", err);
            // LOG_ERR("nrfx_saadc_mode_trigger error: %08x", err);
            return; 
        }
    
    }
    
    static void configure_ppi(void)
    {
        nrfx_err_t err;
        /* STEP 6.1 - Declare variables used to hold the (D)PPI channel number */
        uint8_t m_saadc_sample_ppi_channel;
        uint8_t m_saadc_start_ppi_channel;
    
        /* STEP 6.2 - Trigger task sample from timer */
        err = nrfx_gppi_channel_alloc(&m_saadc_sample_ppi_channel);
        if (err != NRFX_SUCCESS) 
        {
            printk("nrfx_gppi_channel_alloc error %08x\r\n", err);
            // LOG_ERR("nrfx_gppi_channel_alloc error: %08x", err);
            return;
        }
    
        err = nrfx_gppi_channel_alloc(&m_saadc_start_ppi_channel);
        if (err != NRFX_SUCCESS) 
        {
            printk("nrfx_gppi_channel_alloc error %08x\r\n\r\n", err);
            // LOG_ERR("nrfx_gppi_channel_alloc error: %08x", err);
            return;
        }
    
    
        /* STEP 6.3 - Trigger task sample from timer */
        nrfx_gppi_channel_endpoints_setup(m_saadc_sample_ppi_channel,
                        nrfx_timer_compare_event_address_get(&timer_instance,
                                            NRF_TIMER_CC_CHANNEL0),
                        nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_SAMPLE));
    
        /* STEP 6.4 - Trigger task start from end event */
        nrfx_gppi_channel_endpoints_setup(m_saadc_start_ppi_channel,
    				  nrf_saadc_event_address_get(NRF_SAADC, NRF_SAADC_EVENT_END),
    				  nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_START));
    
        /* STEP 6.5 - Enable both (D)PPI channels */ 
        nrfx_gppi_channels_enable(BIT(m_saadc_sample_ppi_channel));
        nrfx_gppi_channels_enable(BIT(m_saadc_start_ppi_channel));
    }
    
    int16_t sample_buffer[4];
    struct adc_sequence sequence = 
    {
    	.channels 	= BIT(ADC_CHANNEL_2) | BIT(ADC_CHANNEL_3) | BIT(ADC_CHANNEL_5) | BIT (ADC_CHANNEL_6),
    	.buffer 	= sample_buffer,
    	/* buffer size in bytes, not number of samples*/
    	.buffer_size = sizeof(sample_buffer),
    	.resolution = ADC_RESOLUTION
    };
    
    struct adc_sequence sequence_adc_ch3 = 
    {
    	.channels 	= BIT(ADC_CHANNEL_3),
    	.buffer 	= sample_buffer,
    	/* buffer size in bytes, not number of samples*/
    	.buffer_size = sizeof(sample_buffer),
    	.resolution = ADC_RESOLUTION
    };
    
    bool read_analog_inputs = false;
    
    int16_t control_input_an2_value = 0;
    int16_t feedback_input_an3_value = 0;
    int16_t speed_pot_an5_value = 0;
    int16_t hyst_pot_an6_value = 0;
    
    void init_A_to_D_w_Timer(void)
    {
        configure_timer();
        configure_saadc();  
        configure_ppi();
    }
    
    #define USE_ZEPHYR_ADC (0)
    #if USE_ZEPHYR_ADC
    int init_A_to_D(void)
    {
    	int err = 0;
    
    	if (!device_is_ready(adc_dev)) 
        {
    		printk("adc_dev not ready\n");
    		return 0;
    	}
    
    	// err = adc_channel_setup(adc_dev, &channel_0_cfg);
    	// if(err != 0)
    	// {
    	// 	printk("ADC adc_channel_setup failed with error %d\n", err);
    	// 	return;
    	// }
    
    	//err = adc_channel_setup(adc_dev, &channel_3_cfg);
    
        err = adc_channel_setup(adc_dev, &channel_2_cfg);
    	if(err != 0)
    	{
    		printk("ADC adc_channel_setup 2 failed with error %d\n", err);
    		return err;
    	}
    
        err = adc_channel_setup(adc_dev, &channel_3_cfg);
    	if(err != 0)
    	{
    		printk("ADC adc_channel_setup 3 failed with error %d\n", err);
    		return err;
    	}
    
        err = adc_channel_setup(adc_dev, &channel_5_cfg);
    	if(err != 0)
    	{
    		printk("ADC adc_channel_setup 5 failed with error %d\n", err);
    		return err;
    	}
        err = adc_channel_setup(adc_dev, &channel_6_cfg);
    	if(err != 0)
    	{
    		printk("ADC adc_channel_setup 6 failed with error %d\n", err);
    		return err;
    	}
    
        return err;
    }
    
    
    int read_A_to_D_ch3 (void)
    {
        if (!device_is_ready(adc_dev)) 
        {
            printk("adc_dev not ready\n");
            return -1;
        }
    
        int err = adc_read(adc_dev, &sequence_adc_ch3);
        if(err != 0)
        {
            printk("ADC reading failed with error %d\n", err);
            return err;
        }
    
        int32_t mv_value = sample_buffer[0];
        printk(" ADC value ch 3 %d \n", mv_value);
        // mv_value = sample_buffer[1];
        // printk(" ADC value ch 1 %d mV\n", mv_value);
        int32_t adc_vref = adc_ref_internal(adc_dev);
        adc_raw_to_millivolts(adc_vref, ADC_GAIN, ADC_RESOLUTION, &mv_value);
        printk(" ADC voltage ch3 %d mV\n", mv_value);
    
        k_sleep(DELAY_TIME_10_MS);
    
        return err;
    }
    
    int read_A_to_D_channels(bool print_value)
    {
        if (!device_is_ready(adc_dev)) 
        {
            printk("adc_dev not ready\n");
            return -1;
        }
    
        int err = adc_read(adc_dev, &sequence);
        if(err != 0)
        {
            printk("ADC reading failed with error %d\n", err);
            return err;
        }
    
        int32_t mv_value_control_input_an2 = sample_buffer[0];
        if(mv_value_control_input_an2 < 0)
        {
            mv_value_control_input_an2 = 0; 
        }
        printk(" ADC value ch 2 %d  ", mv_value_control_input_an2);
    
        int32_t mv_value_feedback_input_an3 = sample_buffer[1];
        if(mv_value_feedback_input_an3 < 0)
        {
            mv_value_feedback_input_an3 = 0;
        }
        printk(" ADC value ch 3 %d   ", mv_value_feedback_input_an3);
    
        int32_t mv_value_speed_pot_an5 = sample_buffer[2];
        if(mv_value_speed_pot_an5 < 0)
        {
            mv_value_speed_pot_an5 = 0;
        }
        printk(" ADC value ch 5 %d  ", mv_value_speed_pot_an5);
    
        int32_t mv_value_hyst_pot_an6 = sample_buffer[3];
        if(mv_value_hyst_pot_an6 < 0)
        {
            mv_value_hyst_pot_an6 = 0; 
        }
        printk(" ADC value ch 6 %d \n", mv_value_hyst_pot_an6);
        // mv_value = sample_buffer[1];
        // printk(" ADC value ch 1 %d mV\n", mv_value);
        // int32_t adc_vref = adc_ref_internal(adc_dev);
        // adc_raw_to_millivolts(adc_vref, ADC_GAIN, ADC_RESOLUTION, &mv_value_control_input_an2);
        // printk(" ADC voltage ch2 %d mV\n", mv_value_control_input_an2);
    
        // adc_raw_to_millivolts(adc_vref, ADC_GAIN, ADC_RESOLUTION, &mv_value_feedback_input_an3);
        // printk(" ADC voltage ch3 %d mV\n", mv_value_feedback_input_an3);
    
        // adc_raw_to_millivolts(adc_vref, ADC_GAIN, ADC_RESOLUTION, &mv_value_speed_pot_an5);
        // printk(" ADC voltage ch5 %d mV\n", mv_value_speed_pot_an5);
    
        // adc_raw_to_millivolts(adc_vref, ADC_GAIN, ADC_RESOLUTION, &mv_value_hyst_pot_an6);
        // printk(" ADC voltage ch6 %d mV\n", mv_value_hyst_pot_an6);
    
        k_sleep(DELAY_TIME_10_MS);
    
        return err;
    }
    
    int read_A_to_D_ch6 (void)
    {
       
        if (!device_is_ready(adc_dev)) 
        {
            printk("adc_dev not ready\n");
            return -1;
        }
    
        int err = adc_read(adc_dev, &sequence);
        if(err != 0)
        {
            printk("ADC reading failed with error %d\n", err);
            return err;
        }
    
        int32_t mv_value = sample_buffer[0];
        printk(" ADC value ch 6 %d \n", mv_value);
        // mv_value = sample_buffer[1];
        // printk(" ADC value ch 1 %d mV\n", mv_value);
        int32_t adc_vref = adc_ref_internal(adc_dev);
        adc_raw_to_millivolts(adc_vref, ADC_GAIN, ADC_RESOLUTION, &mv_value);
        printk(" ADC voltage ch6 %d mV\n", mv_value);
    
        k_sleep(DELAY_TIME_10_MS);
    
        return err;
    }
    #endif
    
    void set_read_analog_inputs (void)
    {
        read_analog_inputs = true;
    }
    
    bool get_read_analog_inputs (void)
    {
        return read_analog_inputs;
    }
    
    void clear_read_analog_inputs (void)
    {
        read_analog_inputs = false;
    }
    
    void set_control_input_an2_value(int16_t temp)
    {
        control_input_an2_value = temp;
    }
    
    int16_t get_control_input_an2_value(void)
    {
        return control_input_an2_value;
    }
    
    void set_feedback_input_an3_value(int16_t temp)
    {
        feedback_input_an3_value = temp;
    }
    
    int16_t get_feedback_input_an3_value(void)
    {
        return feedback_input_an3_value;
    }
    
    void set_speed_pot_an5_value(int16_t temp)
    {
        speed_pot_an5_value = temp;
    }
    
    int16_t get_speed_pot_an5_value(void)
    {
        return speed_pot_an5_value;
    }
    
    void set_hyst_pot_an6_value(int16_t temp)
    {
        hyst_pot_an6_value = temp;
    }
    
    int16_t get_hyst_pot_an6_value(void)
    {
        return hyst_pot_an6_value;
    }
    a_to_d.h

    Screenshot of the output via terminal.

    static nrfx_saadc_channel_t m_multiple_channels[] =
    {
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN2, 0),
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 1),
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN5, 2),
        NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN6, 3)
    };
    static void configure_saadc(void)
    {
        nrfx_err_t err;

        /* STEP 4.4 - Connect ADC interrupt to nrfx interrupt handler */
        IRQ_CONNECT(DT_IRQN(DT_NODELABEL(adc)),
                    DT_IRQ(DT_NODELABEL(adc), priority),
                    nrfx_isr, nrfx_saadc_irq_handler, 0);
       
        /* STEP 4.5 - Initialize the nrfx_SAADC driver */
        err = nrfx_saadc_init(DT_IRQ(DT_NODELABEL(adc), priority));
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_init error\r\n");
            // LOG_ERR("nrfx_saadc_init error: %08x", err);
            return;
        }

        /* STEP 4.6 - Declare the struct to hold the configuration for the SAADC channel used to sample the battery voltage */
        // nrfx_saadc_channel_t channel_2 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN2, 0);
        // nrfx_saadc_channel_t channel_3 = NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN3, 0);

    #define NRFX_SAADC_CHANNEL_COUNT 4

        /* STEP 4.7 - Change gain config in default config and apply channel configuration */
        /* for my application the max that can be applied to an input is 3.3V*/
        m_multiple_channels[0].channel_config.gain = NRF_SAADC_GAIN1_5;
        m_multiple_channels[1].channel_config.gain = NRF_SAADC_GAIN1_5;
        m_multiple_channels[2].channel_config.gain = NRF_SAADC_GAIN1_5;
        m_multiple_channels[3].channel_config.gain = NRF_SAADC_GAIN1_5;
     
        nrfx_saadc_channels_config(m_multiple_channels,NRFX_SAADC_CHANNEL_COUNT);
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_channels_config error\r\n");
            // LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
            return;
        }

        uint32_t channels_mask = nrfx_saadc_channels_configured_get();

        /* STEP 4.8 - Configure channel 0 in advanced mode with event handler (non-blocking mode) */
        nrfx_saadc_adv_config_t saadc_adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
        err = nrfx_saadc_advanced_mode_set(channels_mask,
                                            NRF_SAADC_RESOLUTION_12BIT,
                                            &saadc_adv_config,
                                            saadc_event_handler);
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_advanced_mode_set error %08x\r\n", err);
            // LOG_ERR("nrfx_saadc_advanced_mode_set error: %08x", err);
            return;
        }
                                               
        /* STEP 4.9 - Configure two buffers to make use of double-buffering feature of SAADC */
        err  = nrfx_saadc_buffer_set(saadc_sample_buffer[0], SAADC_BUFFER_SIZE);
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_buffer_set error %08x\r\n", err);
            // 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)
        {
            printk("nrfx_saadc_buffer_set error %08x\r\n", err);
            // LOG_ERR("nrfx_saadc_buffer_set error %08x", err);
            return;
        }

        /* STEP 4.10 - Trigger the SAADC. This will not start sampling, but will prepare buffer for sampling triggered through PPI */
        err = nrfx_saadc_mode_trigger();
        if(err != NRFX_SUCCESS)
        {
            printk("nrfx_saadc_mode_trigger error %08x\r\n", err);
            // LOG_ERR("nrfx_saadc_mode_trigger error: %08x", err);
            return;
        }

    }
    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:
           
               /* STEP 5.1 - Buffer is ready, timer (and sampling) can be started. */
                nrfx_timer_enable(&timer_instance);

                break;                        
               
            case NRFX_SAADC_EVT_BUF_REQ:
           
                /* STEP 5.2 - Set up the next available buffer. Alternate between buffer 0 and 1 */
                err = nrfx_saadc_buffer_set(saadc_sample_buffer[(saadc_current_buffer++)%2], SAADC_BUFFER_SIZE);
                if(err != NRFX_SUCCESS)
                {
                    printk("nrf_saadc_buffer_set error %08x\r\n", err);
                    // LOG_ERR("nrf_saadc_buffer_set error: %08x", err);
                    return;
                }
                break;

            case NRFX_SAADC_EVT_DONE:

                /* STEP 5.3 - Buffer has been filled. Do something with the data and proceed */
                int64_t average_AN2 = 0;
                int16_t max_AN2 = INT16_MIN;
                int16_t min_AN2 = INT16_MAX;
                int16_t current_value;

                int64_t average_AN3 = 0;
                int16_t max_AN3 = INT16_MIN;
                int16_t min_AN3 = INT16_MAX;

                int64_t average_AN5 = 0;
                int16_t max_AN5 = INT16_MIN;
                int16_t min_AN5 = INT16_MAX;

                int64_t average_AN6 = 0;
                int16_t max_AN6 = INT16_MIN;
                int16_t min_AN6 = INT16_MAX;
               
                for(int i = 0; i < p_event->data.done.size;i++)
                {
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN2 += current_value;
                    if(current_value > max_AN2)
                    {
                        max_AN2 = current_value;
                    }
                    if(current_value < min_AN2)
                    {
                        min_AN2 = current_value;
                    }

                    i++;
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN3 += current_value;
                    if(current_value > max_AN3)
                    {
                        max_AN3 = current_value;
                    }
                    if(current_value < min_AN3)
                    {
                        min_AN3 = current_value;
                    }

                    i++;
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN5 += current_value;
                    if(current_value > max_AN5)
                    {
                        max_AN5 = current_value;
                    }
                    if(current_value < min_AN5)
                    {
                        min_AN5 = current_value;
                    }

                    i++;
                    current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
                    average_AN6 += current_value;
                    if(current_value > max_AN6)
                    {
                        max_AN6 = current_value;
                    }
                    if(current_value < min_AN6)
                    {
                        min_AN6 = current_value;
                    }
                }
                average_AN2 = average_AN2 /(p_event->data.done.size /4);

                average_AN3 = average_AN3 /(p_event->data.done.size / 4);

                average_AN5 = average_AN5 /(p_event->data.done.size / 4);

                average_AN6 = average_AN6 /(p_event->data.done.size / 4);
           
                // LOG_INF("SAADC buffer at 0x%x filled with %d samples", (uint32_t)p_event->data.done.p_buffer, p_event->data.done.size);
                // LOG_INF("AVG=%d, MIN=%d, MAX=%d", (int16_t)average, min, max);
                //printk("SAADC buffer at 0x%x filled with %d samples\r\n", (uint32_t)p_event->data.done.p_buffer, p_event->data.done.size);
                printk("AVG_AN2=%d, MIN_AN2=%d, MAX_AN2=%d\t", (int16_t)average_AN2, min_AN2, max_AN2);

                printk("AVG_AN3=%d, MIN_AN3=%d, MAX_AN3=%d\t", (int16_t)average_AN3, min_AN3, max_AN3);

                printk("AVG_AN5=%d, MIN_AN5=%d, MAX_AN5=%d\t", (int16_t)average_AN5, min_AN5, max_AN5);

                printk("AVG_AN6=%d, MIN_AN6=%d, MAX_AN6=%d\r\n", (int16_t)average_AN6, min_AN6, max_AN6);

                break;

            default:
                // LOG_INF("Unhandled SAADC evt %d", p_event->type);
                printk("Unhandled SAADC evt %d", p_event->type);
                break;
        }
    }
  • Looks like it is working now. Thank you for posting the code!

  • By the way, it looks like you are using a 50μs sample interval. If T_ACQ is low enough it should be fine but one cannot trigger sampling more often than N_channels*(T_ACQ+T_CONV). It looks like 10μs is default for ADC_ACQ_TIME_DEFAULT, so it should be just enough with 50μs for 4 channels. Thought it might be good to be aware of this.
    https://github.com/nrfconnect/sdk-zephyr/blob/25f14b4f8ecba7423f39ead3c117c0d86a096c5b/drivers/adc/adc_nrfx_saadc.c#L130-L133

  • I did find that if I used a value of

    #define SAADC_SAMPLE_INTERVAL_US 50

    and I tried to breakpoint and step through the code (using Ozone) that the debugger/IDE would seem to get stuck and not be able to step through the code. 

    Using a value of

    #define SAADC_SAMPLE_INTERVAL_US 500

    seemed to work much better for being able to step through the code while debugging. 

    This will definitely change how often the device processes the A to D pins but I willing to set it slower while I do my development.

    Any insight into why this would make debugging problematic?

Related