Hi everyone,
I'm trying to use nRF52810 with SoftDevice S112 to sample five ADC channels.
I'm finding this very weird issue, where first channel is showing correct value (i.e. 4050), but then next channels should
be around 0-100 range, however they are slowly decreasing from previous channel value.
It's almost as if previously sampled value is affecting the next channel.
Also, if I start decreasing voltage on first channel, others seem to decrease as well...
<info> app: ADC 4055 1303 688 494 389 <info> app: ADC 4054 1281 656 443 318 <info> app: ADC 4054 1296 673 463 346 <info> app: ADC 4056 1298 672 470 360 <info> app: ADC 4058 1293 659 445 312 <info> app: ADC 4057 1299 686 484 374 <info> app: ADC 4053 1279 654 433 306 <info> app: ADC 4058 1304 686 485 388 <info> app: ADC 4057 1271 648 425 284 <info> app: ADC 4052 1303 691 495 397 <info> app: ADC 4055 1275 644 428 292 <info> app: ADC 4057 1299 683 482 376 <info> app: ADC 4055 1289 663 460 340 <info> app: ADC 4058 1294 663 450 318 <info> app: ADC 4056 1301 683 484 383 <info> app: ADC 4056 1284 651 432 297 <info> app: ADC 4053 1299 689 492 396 <info> app: ADC 4059 1279 643 430 289 <info> app: ADC 4058 1302 688 491 400 <info> app: ADC 4055 1286 653 441 315 <info> app: ADC 4057 1293 662 454 323
Now, if I move the voltage from channel 0 to channel 1, I will see that channel 0 is showing correct value but ch 2, ch 3 and ch 4 are affected.
I have tried changing sampling time from min to max, it does not change anything.
Also, tried over-sampling, using PPI, triggering manually by timer but the issue still persists.
I'm running out of ideas on what to try, and also I haven't used SAADC on nRF52810.
All channels have pull-down enabled. Also, when I connect load to every channel, it performs a little better but I'm still concerned that I might get issues (false positive/negative) when there is nothing connected to the ADC pin.
Here is the code I'm using:
#include <math.h> #include "common.h" #include "saadc.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" // Definitions #define ADC_PRINT 1 #define SAMPLES_IN_BUFFER 5 // Number of channels being sampled #define SAADC_SAMPLE_RATE 500 // SAADC sample rate in ms // #define APP_ADC_RMS_MAX_SAMPLES 100 // MAX number of samples for RMS calculation #define APP_ADC_RMS_MAX_SAMPLES 120 // MAX number of samples for RMS calculation #define SAADC_CH1 NRF_SAADC_INPUT_AIN5 #define SAADC_CH2 NRF_SAADC_INPUT_AIN7 #define SAADC_CH3 NRF_SAADC_INPUT_AIN0 #define SAADC_CH4 NRF_SAADC_INPUT_AIN1 #define SAADC_CH5 NRF_SAADC_INPUT_AIN2 #define RMS_WINDOW_ON_SINGLE_THRESH 512 // Check if we will overflow with the current config // FIXME: Doesn't work currently! #define RMS_MAX_SUM_VALUE (APP_ADC_RMS_MAX_SAMPLES * 4095*4095) #if (RMS_MAX_SUM_VALUE >= 0xFFFFFFFF) #error "RMS Window Size will overflow UINT32" #endif // Global variables static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(1); static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_IN_BUFFER]; static nrf_ppi_channel_t m_ppi_channel; static uint32_t m_adc_evt_counter; static uint32_t arru32_ADCsamples[SAMPLES_IN_BUFFER] = {0}; // ADC^2 SUM samples static uint32_t nADC_currentSample = 0; // ADC sample counter static uint32_t nRMS_Channel[SAMPLES_IN_BUFFER] = {0}; // RMS values for each channel static bool bCalcRMSPending = false; // RMS calc pending flag // Local function prototypes static void saadc_callback(nrf_drv_saadc_evt_t const * p_event); static void timer_handler(nrf_timer_event_t event_type, void * p_context); static bool rmsCalc(uint32_t nRMSWindowSUM, uint32_t *pResult); static void saadc_callback(nrf_drv_saadc_evt_t const * p_event) { // NRF_LOG_INFO("p_event->type ==%d", (uint8_t)p_event->type); if (p_event->type == NRF_DRV_SAADC_EVT_DONE) { ret_code_t err_code; uint8_t i =0; // If RMS calculation is pending/in-progress, discard any new samples if(bCalcRMSPending) { return; } err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, p_event->data.done.size); APP_ERROR_CHECK(err_code); #if (ADC_PRINT!=0) // NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter); #endif // Check if invalid state if(nADC_currentSample >= APP_ADC_RMS_MAX_SAMPLES) { NRF_LOG_ERROR("%s: nADC_currentSample >= APP_ADC_RMS_MAX_SAMPLES (%d >= %d)", __FUNCTION__, nADC_currentSample, APP_ADC_RMS_MAX_SAMPLES); return; } // Loop through samples for (i = 0; i < SAMPLES_IN_BUFFER; i++) { // SUM current ADC value arru32_ADCsamples[i] += (p_event->data.done.p_buffer[i]*p_event->data.done.p_buffer[i]); } #if (ADC_PRINT!=0) NRF_LOG_INFO("ADC %5d %5d %5d %5d %5d", p_event->data.done.p_buffer[0], p_event->data.done.p_buffer[1], p_event->data.done.p_buffer[2], p_event->data.done.p_buffer[3], p_event->data.done.p_buffer[4] ); #endif return; // Housekeeping before exiting nADC_currentSample++; // Check if we should calculate RMS if(nADC_currentSample >= APP_ADC_RMS_MAX_SAMPLES) { nADC_currentSample = 0; bCalcRMSPending = true; // Set flag to reject any new samples until RMS calc is complete NRF_LOG_INFO("bCalcRMSPending"); for(i=0; i< SAMPLES_IN_BUFFER; i++) { rmsCalc(arru32_ADCsamples[i], &nRMS_Channel[i]); machine_UpdateRMS(i, nRMS_Channel[i]); nRMS_Channel[i] = 0; arru32_ADCsamples[i] = 0; } bCalcRMSPending = false; } #if (ADC_PRINT!=0) NRF_LOG_INFO("\r\n"); #endif m_adc_evt_counter++; } } static void timer_handler(nrf_timer_event_t event_type, void * p_context) { if (event_type == NRF_TIMER_EVENT_COMPARE0) { // Trigger another sample ret_code_t err_code; err_code = nrfx_saadc_sample(); if(err_code !=NRF_SUCCESS) { NRF_LOG_ERROR("Error: 0x%x", err_code); } APP_ERROR_CHECK(err_code); // nrf_saadc_value_t adc[5] = {0}; // err_code = nrfx_saadc_sample_convert(0, &adc[0]); // APP_ERROR_CHECK(err_code); // while(nrfx_saadc_is_busy()); // err_code = nrfx_saadc_sample_convert(1, &adc[1]); // APP_ERROR_CHECK(err_code); // while(nrfx_saadc_is_busy()); // err_code = nrfx_saadc_sample_convert(2, &adc[2]); // APP_ERROR_CHECK(err_code); // while(nrfx_saadc_is_busy()); // err_code = nrfx_saadc_sample_convert(3, &adc[3]); // APP_ERROR_CHECK(err_code); // while(nrfx_saadc_is_busy()); // err_code = nrfx_saadc_sample_convert(4, &adc[4]); // APP_ERROR_CHECK(err_code); // while(nrfx_saadc_is_busy()); // NRF_LOG_INFO("ADC %5d %5d %5d %5d %5d", adc[0], adc[1], adc[2], adc[3], adc[4]); } } // Calculate rms based on nRMSWindowSUM and store value in pResult // Return true if RMS is over set RMS_WINDOW_ON_SINGLE_THRESH to indicate machine is on static bool rmsCalc(uint32_t nRMSWindowSUM, uint32_t *pResult) { if(pResult == NULL) { NRF_LOG_ERROR("Error: Passing NULL pointer to rmsCalc!!"); return false; } if(nRMSWindowSUM == 0) { *pResult = 0; return false; } return true; } void saadc_sampling_event_init(void) { ret_code_t err_code; #if 1 // err_code = nrfx_saadc_sample(); // if(err_code !=NRF_SUCCESS) // { // NRF_LOG_ERROR("Error: 0x%x", err_code); // } // APP_ERROR_CHECK(err_code); // err_code = nrf_drv_ppi_init(); // APP_ERROR_CHECK(err_code); nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32; err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler); APP_ERROR_CHECK(err_code); /* setup m_timer for compare event every 400ms */ uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, SAADC_SAMPLE_RATE); nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); nrf_drv_timer_enable(&m_timer); #else uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0); uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get(); /* setup ppi channel so that timer compare event is triggering sample task in SAADC */ err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel); APP_ERROR_CHECK(err_code); err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_sample_task_addr); APP_ERROR_CHECK(err_code); #endif } void saadc_sampling_event_enable(void) { ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel); APP_ERROR_CHECK(err_code); } void saadc_init(void) { ret_code_t err_code; // Setup ADC channels // -- CH1 nrf_saadc_channel_config_t channel_CH1_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH1); channel_CH1_config.resistor_p = NRF_SAADC_RESISTOR_PULLDOWN; channel_CH1_config.reference = NRF_SAADC_REFERENCE_VDD4; channel_CH1_config.gain = NRF_SAADC_GAIN1_4; channel_CH1_config.burst = NRF_SAADC_BURST_DISABLED; channel_CH1_config.acq_time = NRF_SAADC_ACQTIME_40US; // -- CH2 nrf_saadc_channel_config_t channel_CH2_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH2); channel_CH1_config.resistor_p = NRF_SAADC_RESISTOR_PULLDOWN; channel_CH1_config.reference = NRF_SAADC_REFERENCE_VDD4; channel_CH1_config.gain = NRF_SAADC_GAIN1_4; channel_CH1_config.burst = NRF_SAADC_BURST_DISABLED; channel_CH1_config.acq_time = NRF_SAADC_ACQTIME_40US; // -- CH3 nrf_saadc_channel_config_t channel_CH3_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH3); channel_CH1_config.resistor_p = NRF_SAADC_RESISTOR_PULLDOWN; channel_CH1_config.reference = NRF_SAADC_REFERENCE_VDD4; channel_CH1_config.gain = NRF_SAADC_GAIN1_4; channel_CH1_config.burst = NRF_SAADC_BURST_DISABLED; channel_CH1_config.acq_time = NRF_SAADC_ACQTIME_40US; // -- CH4 nrf_saadc_channel_config_t channel_CH4_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH4); channel_CH1_config.resistor_p = NRF_SAADC_RESISTOR_PULLDOWN; channel_CH1_config.reference = NRF_SAADC_REFERENCE_VDD4; channel_CH1_config.gain = NRF_SAADC_GAIN1_4; channel_CH1_config.burst = NRF_SAADC_BURST_DISABLED; channel_CH1_config.acq_time = NRF_SAADC_ACQTIME_40US; // -- CH5 nrf_saadc_channel_config_t channel_CH5_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(SAADC_CH5); channel_CH1_config.resistor_p = NRF_SAADC_RESISTOR_PULLDOWN; channel_CH1_config.reference = NRF_SAADC_REFERENCE_VDD4; channel_CH1_config.gain = NRF_SAADC_GAIN1_4; channel_CH1_config.burst = NRF_SAADC_BURST_DISABLED; channel_CH1_config.acq_time = NRF_SAADC_ACQTIME_40US; // SAADC init nrf_drv_saadc_config_t nrfx_saadc_config_t = NRFX_SAADC_DEFAULT_CONFIG; nrfx_saadc_config_t.resolution = NRF_SAADC_RESOLUTION_12BIT; nrfx_saadc_config_t.oversample = NRF_SAADC_OVERSAMPLE_DISABLED; nrfx_saadc_config_t.interrupt_priority = 6; nrfx_saadc_config_t.low_power_mode = false; err_code = nrf_drv_saadc_init(&nrfx_saadc_config_t, saadc_callback); APP_ERROR_CHECK(err_code); // Initialize ADC channels // -- CH1 err_code = nrf_drv_saadc_channel_init(0, &channel_CH1_config); APP_ERROR_CHECK(err_code); // -- CH2 err_code = nrf_drv_saadc_channel_init(1, &channel_CH2_config); APP_ERROR_CHECK(err_code); // -- CH3 err_code = nrf_drv_saadc_channel_init(2, &channel_CH3_config); APP_ERROR_CHECK(err_code); // -- CH4 err_code = nrf_drv_saadc_channel_init(3, &channel_CH4_config); APP_ERROR_CHECK(err_code); // -- CH5 err_code = nrf_drv_saadc_channel_init(4, &channel_CH5_config); APP_ERROR_CHECK(err_code); while(nrfx_saadc_is_busy()); nrfx_saadc_calibrate_offset(); APP_ERROR_CHECK(err_code); while(nrfx_saadc_is_busy()); err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); // err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER); // APP_ERROR_CHECK(err_code); }