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

SAADC Double Buffer fails after re-initialisation

I am having two issues with the following code:

  1. When I run this sample code on my NRF52DK (there's external hardware connceted but that does not affect this operation) I want to have the device capturing SAADC data very quickly so I can do DSP functions, thus I double-buffer the data into memory. The problem lies in when I need to sleep the device. Before sleeping I uninitialized the SAADC so it doesn't wake my device, but after resume (where I initialize it again) the SAADC fails to double buffer correctly (it seems to go into a mode where it buffers only into the second buffer).

  2. I would like to ultimately use the softdevice and ultra low power, but I have to use WFI command to get this code to pause at the point indicated in the main loop, the device does not stop if I use __WFE __SEV __WFE and sontinues to run?

Ultimately I want the device to do the following:

  1. boot and start sampling the ADC running DSP
  2. after 7 seconds of inactivity (no button interrupt) go to sleep
  3. wake upon button press and resume for 7 seconds...

It responds to my interrupt on GPIO just fine, the timer works perfectly, the SAADC double-buffer works perfectly until the I call nrf_drv_saadc_uninit() and subsequently initialise it again when I wake it up. I have found no other way to get the double-buffering to stop and start. Using nrf_drv_saadc_abort() and nrf_drv_saadc_sample() doesn't work in this instance because it holds the system ON.

The way I discovered this is by using the two flags in the below code, if I break in the SAADC callback function (before WFI) I get the following buffer order:

transfer_flag_h1

transfer_flag_h2

transfer_flag_h1

transfer_flag_h2

transfer_flag_h1

transfer_flag_h2 ...

Which is expected behaviour, but after the WFI call and subsequent wake i get:

transfer_flag_h1

transfer_flag_h2

transfer_flag_h2

transfer_flag_h2 ...

#include "defines.h"
#include "nrf_drv_config.h"
#include "app_util_platform.h"
#include "main.h"
#include "nrf.h"
#include "nrf_gpio.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_timer.h"

#define PIN_LED1                17
#define PIN_LED2                18
#define PIN_BUTTON1             13
#define NB_SAMPLES_ALGO     2048
#define TIME_TO_SLEEP_MS    7000	

#define SYSTEM_INIT          	1
#define SYSTEM_SLEEP          	2
#define SYSTEM_WAKE          	3
#define SYSTEM_ANALYZE      	4

static nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
const nrf_drv_timer_t TIMER_SLEEP = NRF_DRV_TIMER_INSTANCE(1);
uint16_t samples_buffer[NB_SAMPLES_ALGO];     // Our acquisition buffer
volatile unsigned short system_state_machine; // Our system state machine
volatile bool saadc_transfer_flag_h1;
volatile bool saadc_transfer_flag_h2;
void wake_from_sleep();
uint32_t last_pointer;

static void saadc_callback(nrf_drv_saadc_evt_t const *p_event) {
  if (p_event->type == NRF_DRV_SAADC_EVT_DONE) {
    ret_code_t err_code;
    if (p_event->data.done.p_buffer == (nrf_saadc_value_t *)&samples_buffer[0])
      saadc_transfer_flag_h1 = true;
    else
      saadc_transfer_flag_h2 = true;
    err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, NB_SAMPLES_ALGO / 2);
    APP_ERROR_CHECK(err_code);
  }
}

void init_saadc(void) {
  ret_code_t err_code;
  nrf_saadc_channel_config_t channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PIN_MIC_IN);
  channel_config.acq_time = NRF_SAADC_ACQTIME_10US;              //100Khz sampling (2048 samples @ 48.8Hz)
  channel_config.reference = NRF_SAADC_REFERENCE_VDD4;          // full range ADC to supply voltage
  channel_config.gain = NRF_SAADC_GAIN1_4;                      // full range ADC to supply voltage
  err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback); // initialise SAADC
  NRF_SAADC->SAMPLERATE = 4176;                                 //(16MHz/[80] | enable timer mode [4096])
  APP_ERROR_CHECK(err_code);
  err_code = nrf_drv_saadc_channel_init(1, &channel_config); // initialise the SAADC Channel
  APP_ERROR_CHECK(err_code);
  err_code = nrf_drv_saadc_buffer_convert(&samples_buffer[0], (uint16_t)(NB_SAMPLES_ALGO / 2)); // we do this twice to create two buffers in different memory locations
  APP_ERROR_CHECK(err_code);
  err_code = nrf_drv_saadc_buffer_convert(&samples_buffer[NB_SAMPLES_ALGO / 2], (uint16_t)(NB_SAMPLES_ALGO / 2));
  APP_ERROR_CHECK(err_code);
  nrf_drv_saadc_sample();
}

void sleep_timer_event_handler(nrf_timer_event_t event_type, void *p_context) {
  system_state_machine = SYSTEM_SLEEP; // change to sleep mode
  nrf_drv_timer_uninit(&TIMER_SLEEP);
  NVIC_ClearPendingIRQ(TIMER1_IRQn);
}

void init_timer() {
  ret_code_t err_code;
  uint32_t timer_ticks;
  err_code = nrf_drv_timer_init(&TIMER_SLEEP, NULL, sleep_timer_event_handler);
  APP_ERROR_CHECK(err_code);
  NRF_TIMER1->PRESCALER = 9;
  timer_ticks = nrf_drv_timer_ms_to_ticks(&TIMER_SLEEP, TIME_TO_SLEEP_MS);
  nrf_drv_timer_extended_compare(&TIMER_SLEEP, NRF_TIMER_CC_CHANNEL1, timer_ticks, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);
  nrf_drv_timer_enable(&TIMER_SLEEP);
}

void init_outputs(void) {
  nrf_gpio_cfg(PIN_LED1, GPIO_PIN_CNF_DIR_Output, GPIO_PIN_CNF_INPUT_Disconnect, GPIO_PIN_CNF_PULL_Pullup, GPIO_PIN_CNF_DRIVE_H0D1, GPIO_PIN_CNF_SENSE_Disabled);
  nrf_gpio_pin_set(PIN_LED1);
  nrf_gpio_cfg(PIN_LED2, GPIO_PIN_CNF_DIR_Output, GPIO_PIN_CNF_INPUT_Disconnect, GPIO_PIN_CNF_PULL_Pullup, GPIO_PIN_CNF_DRIVE_H0D1, GPIO_PIN_CNF_SENSE_Disabled);
  nrf_gpio_pin_set(PIN_LED2);
}

void init_gpio_interrupts() {
  NRF_GPIOTE->INTENCLR = GPIOTE_INTENSET_PORT_Msk;
  nrf_gpio_cfg_sense_input(PIN_BUTTON1, GPIO_PIN_CNF_PULL_Pullup, GPIO_PIN_CNF_SENSE_Low);
  NRF_GPIOTE->EVENTS_PORT = 0;
  NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
  NVIC_SetPriority(GPIOTE_IRQn, APP_IRQ_PRIORITY_LOW);
  NVIC_EnableIRQ(GPIOTE_IRQn);
}

void GPIOTE_IRQHandler(void) {
  NRF_GPIOTE->EVENTS_PORT = 0;
  nrf_gpio_pin_toggle(PIN_LED1);
  nrf_drv_timer_clear(&TIMER_SLEEP);
}

void prepare_for_sleep() {
  ret_code_t err_code;
  nrf_gpio_pin_clear(PIN_LED2);
  nrf_drv_timer_uninit(&TIMER_SLEEP);
  nrf_drv_saadc_abort();
  nrf_drv_saadc_uninit();
}

void wake_from_sleep() {
  nrf_gpio_pin_set(PIN_LED2);
  init_timer();
  init_saadc();
  system_state_machine = SYSTEM_ANALYZE; // ready to start our business again
}

void main(void) {
  init_outputs();         // Init GPIO output
  init_gpio_interrupts();
  init_timer();           // Init timeout timer
  init_saadc();
  system_state_machine = SYSTEM_INIT;
   while (1) {
    if(system_state_machine == SYSTEM_SLEEP) {
      prepare_for_sleep();
      __WFI();
      system_state_machine = SYSTEM_WAKE;
    }
    if(system_state_machine == SYSTEM_WAKE) wake_from_sleep();
  }
}
Related