This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

SAADC oversampling + burst mode on three channel

Hi,

    I have two questions as below. Thank you.

1. Oversampling + burst mode only can use on one channel, can't support two or more channel. Is it right?

2. If Q1's answer is yes. How can I average 30 numbers of ADC value on three channel? (for loop 30 times for nrf_drv_saadc_sample() -> is it right?)

for(int i=0;i<30;i++)
{
    err_code = nrf_drv_saadc_sample();
    APP_ERROR_CHECK(err_code); 
    nrf_delay_ms(10);
    v1 = v1 + read buffer[0];
    v2 = v2 + read_buffer[1];
}

v1 = v1/30;
v2 = v2/30;

Parents
  • Hi, 

    Thanks for the queries.

    1.  Oversampling + burst mode only can use on one channel, can't support two or more channel. Is it right?

    It is actually not right.

    It can support two or more channel if Burst is enable for those channel. Scan mode can be combined with burst if BURST = 1 for all channel. This is because if you do not use BURST, it will sample on every channel and later will perform the averaging. In Scan + Overlapping (without burst) , you will only get the average of all the channels. On the the other hand, BURST makes sure to finish sampling on single channel before moving to the next one. 

    I would like to recommend you to enable BURST for the two channels, and set oversampling to the appropriate value and to trigger sample once. This will make perform (number of) measurements before averaging them and placing them in the buffer. Furthermore, I would like to ask how often you intend to perform the sampling, and if it needs to fulfill some real-time constraint or high accuracy interval, I would suggest you not to trigger the samplings using CPU (like the way you call the nrf_drv_saadc_sample() ), instead you can make use of the PP peripheral to connect an EVENT to the TASKS_SAMPLE task. In this way that sampling is triggered whenever the event occurs, without requiring the CPU intervention. Since the CPU may be occupied or blocked by a higher priority interrupt or similar. 

    There are some explanation from previous devzone thread which you can look at as reference :  scan oversample - Nordic Q&A - Nordic DevZone - Nordic DevZone (nordicsemi.com)

    You also can look at this project contains code examples that shows how to use the nrfx SAADC driver with API v2 : https://github.com/NordicPlayground/nRF52-ADC-examples/tree/master/nrfx_saadc_simple_low_power_app_timer_multichannel_oversample 

    You can send your code after making these changes. 

    Hope it helps. Feel free to ask further queries.

    Best Regards,

    Kazi Afroza Sultana

  • Hi Kazi,

       Thank you for your support.

    I want to use 200ms to get ADC value from three channel and I want to get 30 times ADC value from every channel when 200ms timer trigger.

    I change my code for three channel and every channel enable burst mode + oversample is 8x. Below is my setting source code.

    static void adc_configure(void)
    {
        ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_event_handler);
        APP_ERROR_CHECK(err_code);
    
        nrf_saadc_channel_config_t config_current_sense =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
        err_code = nrf_drv_saadc_channel_init(0, &config_current_sense);
        APP_ERROR_CHECK(err_code);
        
        nrf_saadc_channel_config_t config_adc_temp =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
        err_code = nrf_drv_saadc_channel_init(1, &config_adc_temp);
        APP_ERROR_CHECK(err_code);
        
        nrf_saadc_channel_config_t config_em_temp =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);
        err_code = nrf_drv_saadc_channel_init(2, &config_em_temp);
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[0], 3);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[1], 3);
        APP_ERROR_CHECK(err_code);
    }
    
    #define NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PIN_P) \
        {                                                  \
            .resistor_p = NRF_SAADC_RESISTOR_DISABLED,     \
            .resistor_n = NRF_SAADC_RESISTOR_DISABLED,     \
            .gain       = NRF_SAADC_GAIN1_6,               \
            .reference  = NRF_SAADC_REFERENCE_INTERNAL,    \
            .acq_time   = NRF_SAADC_ACQTIME_10US,          \
            .mode       = NRF_SAADC_MODE_SINGLE_ENDED,     \
            .burst      = NRF_SAADC_BURST_ENABLED,         \
            .pin_p      = (nrf_saadc_input_t)(PIN_P),      \
            .pin_n      = NRF_SAADC_INPUT_DISABLED         \
        }
        
    // <o> SAADC_CONFIG_OVERSAMPLE  - Sample period
     
    // <0=> Disabled 
    // <1=> 2x 
    // <2=> 4x 
    // <3=> 8x 
    // <4=> 16x 
    // <5=> 32x 
    // <6=> 64x 
    // <7=> 128x 
    // <8=> 256x 
    
    #ifndef SAADC_CONFIG_OVERSAMPLE
    #define SAADC_CONFIG_OVERSAMPLE 3
    #endif
    

    Also, I create a 200ms timer to call nrf_drv_saadc_sample(), like as below.

    static void data_process_handler(void * p_context)
    {
        uint32_t err_code;
       
        //average_adc = true;
        nrf_drv_saadc_sample();
        
        //printf("%.4f-%.4f-%.4f\r\n",real_v_current_sense,real_v_adc_temp,real_v_em_temp);
        printf("%d-%d-%d\r\n",current_sense_vol,adc_temp_vol,em_temp_vol);
    }

    below is my ADC callback function and calibration function in main()

    void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event)
    {
        uint32_t err_code;
      
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
            nrf_saadc_value_t adc_result,adc_temp_result,em_temp_result;
            uint32_t          err_code;
            
            if((m_adc_evt_counter % 50000) == 0)               //Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
            {
                m_saadc_calibrate = true;                                           // Set flag to trigger calibration in main context when SAADC is stopped
            }
            
            if(m_saadc_calibrate == false)
            {
                err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 3);             //Set buffer so the SAADC can write to it again. 
                APP_ERROR_CHECK(err_code);
            }
            
            adc_result = p_event->data.done.p_buffer[0];
            adc_temp_result = p_event->data.done.p_buffer[1];
            em_temp_result = p_event->data.done.p_buffer[2];
            
            //printf("Hello : %d-%d-%d\r\n",adc_result,adc_temp_result,em_temp_result);
            
            current_sense_vol = adc_result;
            adc_temp_vol = adc_temp_result;
            em_temp_vol = em_temp_result;
            
            m_adc_evt_counter++;
        }
        else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
        {
            nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
            while(!nrf_saadc_event_check(NRF_SAADC_EVENT_STOPPED));
            nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
           
            //Set buffer so the SAADC can write to it again.
            err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[0], 3);
            APP_ERROR_CHECK(err_code);
            //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration.
            err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[1], 3);
            APP_ERROR_CHECK(err_code);
        }
    }
    
    int main(void)
    {
    
        for (;;)
        {
            if (m_saadc_calibrate == true) 
            {
                nrf_drv_saadc_abort();
                while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
                m_saadc_calibrate = false;
            }
        }
    }

    I will print my three channel ADC value every 200ms. 

    printf("%d-%d-%d\r\n",current_sense_vol,adc_temp_vol,em_temp_vol);

    This works is very good and correct.

    But I have a issue to need your support.

    As described above, My channel 0 is AIN0 / channel 1 is AIN2 / channel 2 is AIN3

    I expected that the arrangement of the data I printed should be 0->1->2

    But It is 1->2->0. Why?? Maybe I can use it, but i am confused

    Thank you.

    John.

        

Reply
  • Hi Kazi,

       Thank you for your support.

    I want to use 200ms to get ADC value from three channel and I want to get 30 times ADC value from every channel when 200ms timer trigger.

    I change my code for three channel and every channel enable burst mode + oversample is 8x. Below is my setting source code.

    static void adc_configure(void)
    {
        ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_event_handler);
        APP_ERROR_CHECK(err_code);
    
        nrf_saadc_channel_config_t config_current_sense =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
        err_code = nrf_drv_saadc_channel_init(0, &config_current_sense);
        APP_ERROR_CHECK(err_code);
        
        nrf_saadc_channel_config_t config_adc_temp =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
        err_code = nrf_drv_saadc_channel_init(1, &config_adc_temp);
        APP_ERROR_CHECK(err_code);
        
        nrf_saadc_channel_config_t config_em_temp =
            NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);
        err_code = nrf_drv_saadc_channel_init(2, &config_em_temp);
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[0], 3);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[1], 3);
        APP_ERROR_CHECK(err_code);
    }
    
    #define NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PIN_P) \
        {                                                  \
            .resistor_p = NRF_SAADC_RESISTOR_DISABLED,     \
            .resistor_n = NRF_SAADC_RESISTOR_DISABLED,     \
            .gain       = NRF_SAADC_GAIN1_6,               \
            .reference  = NRF_SAADC_REFERENCE_INTERNAL,    \
            .acq_time   = NRF_SAADC_ACQTIME_10US,          \
            .mode       = NRF_SAADC_MODE_SINGLE_ENDED,     \
            .burst      = NRF_SAADC_BURST_ENABLED,         \
            .pin_p      = (nrf_saadc_input_t)(PIN_P),      \
            .pin_n      = NRF_SAADC_INPUT_DISABLED         \
        }
        
    // <o> SAADC_CONFIG_OVERSAMPLE  - Sample period
     
    // <0=> Disabled 
    // <1=> 2x 
    // <2=> 4x 
    // <3=> 8x 
    // <4=> 16x 
    // <5=> 32x 
    // <6=> 64x 
    // <7=> 128x 
    // <8=> 256x 
    
    #ifndef SAADC_CONFIG_OVERSAMPLE
    #define SAADC_CONFIG_OVERSAMPLE 3
    #endif
    

    Also, I create a 200ms timer to call nrf_drv_saadc_sample(), like as below.

    static void data_process_handler(void * p_context)
    {
        uint32_t err_code;
       
        //average_adc = true;
        nrf_drv_saadc_sample();
        
        //printf("%.4f-%.4f-%.4f\r\n",real_v_current_sense,real_v_adc_temp,real_v_em_temp);
        printf("%d-%d-%d\r\n",current_sense_vol,adc_temp_vol,em_temp_vol);
    }

    below is my ADC callback function and calibration function in main()

    void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event)
    {
        uint32_t err_code;
      
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {
            nrf_saadc_value_t adc_result,adc_temp_result,em_temp_result;
            uint32_t          err_code;
            
            if((m_adc_evt_counter % 50000) == 0)               //Evaluate if offset calibration should be performed. Configure the SAADC_CALIBRATION_INTERVAL constant to change the calibration frequency
            {
                m_saadc_calibrate = true;                                           // Set flag to trigger calibration in main context when SAADC is stopped
            }
            
            if(m_saadc_calibrate == false)
            {
                err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 3);             //Set buffer so the SAADC can write to it again. 
                APP_ERROR_CHECK(err_code);
            }
            
            adc_result = p_event->data.done.p_buffer[0];
            adc_temp_result = p_event->data.done.p_buffer[1];
            em_temp_result = p_event->data.done.p_buffer[2];
            
            //printf("Hello : %d-%d-%d\r\n",adc_result,adc_temp_result,em_temp_result);
            
            current_sense_vol = adc_result;
            adc_temp_vol = adc_temp_result;
            em_temp_vol = em_temp_result;
            
            m_adc_evt_counter++;
        }
        else if (p_event->type == NRF_DRV_SAADC_EVT_CALIBRATEDONE)
        {
            nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
            while(!nrf_saadc_event_check(NRF_SAADC_EVENT_STOPPED));
            nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);
           
            //Set buffer so the SAADC can write to it again.
            err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[0], 3);
            APP_ERROR_CHECK(err_code);
            //Need to setup both buffers, as they were both removed with the call to nrf_drv_saadc_abort before calibration.
            err_code = nrf_drv_saadc_buffer_convert(adc_buf_adc[1], 3);
            APP_ERROR_CHECK(err_code);
        }
    }
    
    int main(void)
    {
    
        for (;;)
        {
            if (m_saadc_calibrate == true) 
            {
                nrf_drv_saadc_abort();
                while(nrf_drv_saadc_calibrate_offset() != NRF_SUCCESS); //Trigger calibration task
                m_saadc_calibrate = false;
            }
        }
    }

    I will print my three channel ADC value every 200ms. 

    printf("%d-%d-%d\r\n",current_sense_vol,adc_temp_vol,em_temp_vol);

    This works is very good and correct.

    But I have a issue to need your support.

    As described above, My channel 0 is AIN0 / channel 1 is AIN2 / channel 2 is AIN3

    I expected that the arrangement of the data I printed should be 0->1->2

    But It is 1->2->0. Why?? Maybe I can use it, but i am confused

    Thank you.

    John.

        

Children
No Data
Related