I am having two issues with the following code:
-
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).
-
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:
- boot and start sampling the ADC running DSP
- after 7 seconds of inactivity (no button interrupt) go to sleep
- 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();
}
}