Hello,
I am sampling two analog signals at different sampling rate with burst and oversample enabled.
- nRF52832 (Rigado BMD-350)
- SDK 15.2
- IDE - Segger Embedded Studio 4.12
- SAADC Config
- resolution 12bit
- oversample 4x
- interrupt priority = APP_IRQ_PRIORITY_LOW
- low power mode = true
- Channel 0 - Burst enable, sample period = 250ms
- Channel 1 - Burst enable, sample period = 3 seconds
According to this post, https://devzone.nordicsemi.com/f/nordic-q-a/15882/sampling-two-analog-signals-using-saadc-at-two-different-rate#post-id-91845, for sampling two analog signals at different sampling rate which can be achieved by initialize and un-nitialize the channel.
Therefore I initialize and un-nitialize the channel 1 at the SAADC callback function. The first sampling result of channel 1 is OK, get the correct value. However when trigger the second sample, after initialize channel 1 and set the buffer with nrfx_saadc_buffer_convert again, the SAADC stop response, I cannot get any callback.
Below is my log and code, could anyone help me to solve this? Any idea why this is happening?
Here is the Log:
app: ch1 uninit, ch0 cnt: 0, raw: 117 app: ch1 uninit, ch0 cnt: 1, raw: 120 app: ch1 uninit, ch0 cnt: 2, raw: 122 app: ch1 uninit, ch0 cnt: 3, raw: 130 app: ch1 uninit, ch0 cnt: 4, raw: 130 app: ch1 uninit, ch0 cnt: 5, raw: 126 app: ch1 uninit, ch0 cnt: 6, raw: 127 app: ch1 uninit, ch0 cnt: 7, raw: 125 app: ch1 uninit, ch0 cnt: 8, raw: 125 app: ch1 uninit, ch0 cnt: 9, raw: 124 app: ch1 uninit, ch0 cnt: 10, raw: 133 app: ch1 init, ch0 cnt: 11, raw: 128 <--- channel 1 init first time app: ch1 set uninit, ch0 cnt: 12, raw: 122 <--- channel 1 unit app: ch1 result, ch1 cnt: 0, raw:3529, value: 4.1365571 <--- channel 1 get correct result app: ch1 uninit, ch0 cnt: 13, raw: 189 app: ch1 uninit, ch0 cnt: 14, raw: 117 app: ch1 uninit, ch0 cnt: 15, raw: 112 app: ch1 uninit, ch0 cnt: 16, raw: 113 app: ch1 uninit, ch0 cnt: 17, raw: 111 app: ch1 uninit, ch0 cnt: 18, raw: 112 app: ch1 uninit, ch0 cnt: 19, raw: 110 app: ch1 uninit, ch0 cnt: 20, raw: 109 app: ch1 uninit, ch0 cnt: 21, raw: 112 app: ch1 uninit, ch0 cnt: 22, raw: 112 app: ch1 init, ch0 cnt: 23, raw: 109 <--- channel 1 init second time
<--- SAADC Stop response, no more callback
Here is my code:
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
//#include "nrf_drv_saadc.h"
#include "nrfx_saadc.h"
//#include "boards.h"
#include "app_timer.h"
#include "app_error.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_drv_power.h"
#include "nrf_drv_clock.h"
//#include "nrf_drv_rtc.h"
#include "nrfx_rtc.h"
#include "nrf_delay.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#define RTC_FREQUENCY 32
#define RTC_CC_VALUE 8 //Determines the RTC interrupt frequency and thereby the SAADC sampling frequency
// ADC chnnel1 timer for sampling
APP_TIMER_DEF(appTimerChannel1);
typedef enum _CHANNEL_STATES
{
channelUninitialize,
channelInitialize,
channelResult,
} CHANNEL_STATES;
static const nrfx_rtc_t rtc = NRFX_RTC_INSTANCE(2); /**< Declaring an instance of nrf_drv_rtc for RTC2. */
static nrf_saadc_channel_config_t channel_config1 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
static nrf_saadc_value_t m_buffer_pool[2][2];
static uint32_t countCh0;
static uint32_t countCh1;
static CHANNEL_STATES stateCh1;
static char logChar[64];
static void lfclk_config(void)
{
ret_code_t err_code = nrf_drv_clock_init(); //Initialize the clock source specified in the nrf_drv_config.h file, i.e. the CLOCK_CONFIG_LF_SRC constant
APP_ERROR_CHECK(err_code);
nrf_drv_clock_lfclk_request(NULL);
}
static void rtc_handler(nrfx_rtc_int_type_t int_type)
{
uint32_t err_code;
if (int_type == NRFX_RTC_INT_COMPARE0)
{
//if(!m_saadc_initialized)
//{
// saadc_init(); //Initialize the SAADC. In the case when SAADC_SAMPLES_IN_BUFFER > 1 then we only need to initialize the SAADC when the the buffer is empty.
//}
//m_saadc_initialized = true; //Set SAADC as initialized
nrfx_saadc_sample(); //Trigger the SAADC SAMPLE task
err_code = nrfx_rtc_cc_set(&rtc, 0, RTC_CC_VALUE, true); //Set RTC compare value. This needs to be done every time as the nrf_drv_rtc clears the compare register on every compare match
APP_ERROR_CHECK(err_code);
nrfx_rtc_counter_clear(&rtc); //Clear the RTC counter to start count from zero
}
}
static void rtc_config(void)
{
uint32_t err_code;
//Initialize RTC instance
nrfx_rtc_config_t rtc_config = NRFX_RTC_DEFAULT_CONFIG;
rtc_config.prescaler = RTC_FREQ_TO_PRESCALER(RTC_FREQUENCY);
err_code = nrfx_rtc_init(&rtc, &rtc_config, rtc_handler); //Initialize the RTC with callback function rtc_handler. The rtc_handler must be implemented in this applicaiton. Passing NULL here for RTC configuration means that configuration will be taken from the sdk_config.h file.
APP_ERROR_CHECK(err_code);
err_code = nrfx_rtc_cc_set(&rtc, 0, RTC_CC_VALUE, true); //Set RTC compare value to trigger interrupt. Configure the interrupt frequency by adjust RTC_CC_VALUE and RTC_FREQUENCY constant in top of main.c
APP_ERROR_CHECK(err_code);
//Power on RTC instance
nrfx_rtc_enable(&rtc); //Enable RTC
}
static void saadc_callback(nrfx_saadc_evt_t const * p_event)
{
//NRF_LOG_INFO("SAADC callback\r\n");
if (p_event->type == NRFX_SAADC_EVT_DONE) //Capture offset calibration complete event
{
ret_code_t err_code;
switch (stateCh1) {
case channelUninitialize:
{
err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 1); //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("ch1 uninit, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
countCh0++;
} break;
case channelInitialize:
{
// init channel 1
err_code = nrfx_saadc_channel_init(1, &channel_config1);
APP_ERROR_CHECK(err_code);
// set buffer to 2 for both channels 1&2
err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 2); //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("ch1 init, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
countCh0++;
stateCh1 = channelResult;
} break;
case channelResult:
{
err_code = nrfx_saadc_channel_uninit(1);
APP_ERROR_CHECK(err_code);
// set buffer to 1 for channels 0
err_code = nrfx_saadc_buffer_convert(p_event->data.done.p_buffer, 1); //Set buffer so the SAADC can write to it again. This is either "buffer 1" or "buffer 2"
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("ch1 set uninit, ch0 cnt: %i, raw: %i", countCh0, p_event->data.done.p_buffer[0]);
// for channel1 adc result
// gain: 1/4, external voltage divider: 1/2, internal ref. 0.6,
float ch1Value = (((float) p_event->data.done.p_buffer[1]) * 8.0f * 0.6f) / 4095.0f;
sprintf(logChar, " ch1 result, ch1 cnt: %d, raw:%i, value: %.7f", countCh1, p_event->data.done.p_buffer[1], ch1Value);
NRF_LOG_INFO("%s", logChar);
countCh0++;
countCh1++;
stateCh1 = channelUninitialize;
} break;
default:
stateCh1 = channelUninitialize;
break;
}
}
}
static void saadc_init(void)
{
ret_code_t err_code;
//Configure SAADC
nrfx_saadc_config_t saadc_config;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=2048 (when input voltage is 3.6V for channel gain setting of 1/6).
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; //Set oversample to 4x. This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times.
saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW; //Set SAADC interrupt to low priority.
saadc_config.low_power_mode = true;
//Initialize SAADC
err_code = nrfx_saadc_init(&saadc_config, saadc_callback); //Initialize the SAADC with configuration and callback function. The application must then implement the saadc_callback function, which will be called when SAADC interrupt is triggered
APP_ERROR_CHECK(err_code);
//Configure SAADC channel 0
nrf_saadc_channel_config_t channel_config0 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
channel_config0.gain = NRF_SAADC_GAIN1;
channel_config0.reference = NRF_SAADC_REFERENCE_VDD4;
channel_config0.acq_time = NRF_SAADC_ACQTIME_10US;
channel_config0.burst = NRF_SAADC_BURST_ENABLED;
err_code = nrfx_saadc_channel_init(0, &channel_config0);
APP_ERROR_CHECK(err_code);
//nrf_saadc_channel_config_t channel_config1 = NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
channel_config1.gain = NRF_SAADC_GAIN1_4;
channel_config1.reference = NRF_SAADC_REFERENCE_INTERNAL; // internal ref.: 0.6V
channel_config1.acq_time = NRF_SAADC_ACQTIME_15US;
channel_config1.burst = NRF_SAADC_BURST_ENABLED;
//err_code = nrfx_saadc_channel_init(1, &channel_config1);
//APP_ERROR_CHECK(err_code);
stateCh1 = channelUninitialize;
err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0],1); //Set SAADC buffer 1. The SAADC will start to write to this buffer
APP_ERROR_CHECK(err_code);
//err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1],1); //Set SAADC buffer 2. The SAADC will write to this buffer when buffer 1 is full. This will give the applicaiton time to process data in buffer 1.
//APP_ERROR_CHECK(err_code);
}
static void appTimerCallbackChannel1(void * p_context)
{
// do nothing
UNUSED_PARAMETER(p_context);
if (stateCh1 == channelUninitialize)
{
stateCh1 = channelInitialize;
}
return;
}
static void app_timer_config(void)
{
ret_code_t err_code;
// init application timer
err_code = app_timer_init();
APP_ERROR_CHECK(err_code);
// Create timers for adc channel1 sampling
err_code = app_timer_create(&appTimerChannel1, APP_TIMER_MODE_REPEATED, appTimerCallbackChannel1);
APP_ERROR_CHECK(err_code);
// channel1 sample every 3 second
err_code = app_timer_start(appTimerChannel1, APP_TIMER_TICKS(3000), NULL);
APP_ERROR_CHECK(err_code);
return;
}
/**
* @brief Function for main application entry.
*/
int main(void)
{
ret_code_t err_code;
// log init
err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
//initializing power management.
err_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(err_code);
//Enabling the DCDC converter for lower current consumption
NRF_POWER->DCDCEN = 1;
NRF_LOG_INFO("Test \r\n");
//Configure low frequency 32kHz clock
lfclk_config();
// init saadc
countCh0 = 0;
countCh1 = 0;
saadc_init();
// init app timer
app_timer_config();
//Configure RTC. The RTC will generate periodic interrupts. Requires 32kHz clock to operate.
rtc_config();
while(1)
{
if (NRF_LOG_PROCESS() == false)
{
nrf_pwr_mgmt_run();
}
}
}