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

nRF5 with input-multiplexer on ADC pin, optimization required

Hey, I am trying to sample from 10 analog sensors and stream the results via bluetooth. Since the nRF5 is on a board that only lets me access 5 Pins, I am using a 16:1 multiplexer connected to a single ADC pin and 4 digital-out pins to control the multiplexer (channel selection).

I changed the Blinky Example to set the digital-out pins and I added code from the ADC Example to read ADC values and send them via UART.

It works so far, but I am not sure if I am doing it correctly since there are so many different ways to make timing, adc,... work. Here is my code:

#include <stdbool.h>
#include <stdint.h>
#include "boards.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "nrf.h"
#include <stdio.h>
#include "nrf_drv_adc.h"
#include "nordic_common.h"
#include "nrf_log.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"

// GENERATED CODE (Digital-out values for MUX channel selection)
const uint8_t PINS[] = {5,4,3,13};
const uint8_t NUM_PINS = 4;
const uint8_t NUM_CHANNELS = 10;

const uint32_t SET_MAP[] = {
0x0,
0x20,
0x10,
0x30,
0x8,
0x28,
0x18,
0x38,
0x2000,
0x2020
};

const uint32_t CLR_MAP[] = {
0x2038,
0x2018,
0x2028,
0x2008,
0x2030,
0x2010,
0x2020,
0x2000,
0x38,
0x18
};
// END GENERATED CODE


// ADC Setup:
// Note: ADC_BUFFER_SIZE has to be equal to NUM_CHANNELS!
#define ADC_BUFFER_SIZE 10                                /**< Size of buffer for ADC samples.  */
static nrf_adc_value_t       adc_buffer[ADC_BUFFER_SIZE]; /**< ADC buffer. */
static nrf_drv_adc_channel_t m_channel_config = NRF_DRV_ADC_DEFAULT_CHANNEL(NRF_ADC_CONFIG_INPUT_2); /**< Channel instance. Default configuration used. */

static void adc_event_handler(nrf_drv_adc_evt_t const * p_event)
{
    if (p_event->type == NRF_DRV_ADC_EVT_DONE)
    {
        uint32_t i;
        for (i = 0; i < p_event->data.done.size; i++)
        {
			NRF_LOG_PRINTF("ADC: %d\r\n", p_event->data.done.p_buffer[i]);
        }
    }
}

static void adc_config(void)
{
    ret_code_t ret_code;
    nrf_drv_adc_config_t config = NRF_DRV_ADC_DEFAULT_CONFIG;

    ret_code = nrf_drv_adc_init(&config, adc_event_handler);
    APP_ERROR_CHECK(ret_code);

    nrf_drv_adc_channel_enable(&m_channel_config);
}

int main(void)
{
	// GPIO:
	// Configure pins as outputs.
	for (int i = 0; i < NUM_PINS; i++)
	{
		nrf_gpio_cfg_output(PINS[i]);
	}

	// ADC:
	LEDS_CONFIGURE(BSP_LED_0_MASK);
    LEDS_OFF(BSP_LED_0_MASK);

    adc_config();
    UNUSED_RETURN_VALUE(NRF_LOG_INIT());

    NRF_LOG_PRINTF("ADC example\r\n");
	
    while (true)
    {
		APP_ERROR_CHECK(nrf_drv_adc_buffer_convert(adc_buffer,ADC_BUFFER_SIZE));
				
        for (int i = 0; i < NUM_CHANNELS; i++)
		{
			// Set pins for mux channel selection:
			nrf_gpio_pins_clear(CLR_MAP[i]);
			nrf_gpio_pins_set(SET_MAP[i]);
			// sample from adc;
			nrf_drv_adc_sample();
			
            __SEV();
            __WFE();
            __WFE();

            nrf_delay_ms(10);
            LEDS_INVERT(BSP_LED_0_MASK);
		}
		nrf_delay_ms(1000);
    }
}

It doesnt have to be perfect, but I would like to keep power consumption low and I need stable sampling at around 100hz (10 channels every 10ms). Also I will need to replace the UART with a Bluetooth stream and I need to switch from a nRF51 DK to a nRF52832 (which is a bit different about ADC/SAADC but I dont think its a huge problem).

  1. Am I using the SEV(),WFE(),WFE() correctly?
  2. Do I need a delay between the sampling of the 10 channels (in the code currently 10ms)
  3. Will this approach still work when I add Bluetooth or are there going to be problems with Sleeping/Timing etc.?

Best regards Johannes

Parents
  • Hi,

    1. Yes, this is how the examples in the SDK enters sleep mode.
    2. A call to nrf_drv_adc_sample() will assert if the ADC is busy. You should rather check if the ADC is available using nrf_drv_adc_is_busy() before starting sampling.
    3. I would not recommend using busy-wait in the main loop for ADC sampling. You should setup a timer (use RTC/App timer for low power), and trigger the ADC sampling in the callback handler. As you mention, the ADC peripheral on the nRF52832 is different from nRF51, and it use a different driver (nrf_drv_saadc). You can find a couple of examples using the SAADC on our GitHub. It should not be a problem to get the sample rate you desire along BLE communication.

    Best regards,

    Jørgen

  • I am working on similar design using 16:1 multiplexer connected to single analog input, but I need to sample all 16 sensors every 4ms. I think this can be done using PPI, GPIOTE and DMA SAADC data to memory without CPU intervention. I wonder if you already have something similar implemented as it is fairly common case. Can you please point me in the right direction

    Best regards,

    Kerim

Reply Children
No Data
Related