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

NRF52840 SAADC configuration with easyDMA interrupt after x samplings

hello Nordic

i am using nrf 52840 dev kit, sdk 16.0

i want to make a sampling of 2 channels at 32MHz rate. the data shell be passed to RAM via easyDMA, after every 4 samplings i whish to raise an interrupt so i can average the readings from each channel. 

(as i understand if i do oversampling and burst then it reads one channel several time and averaging and then do the same for the other channel, this solution is not good for me because i need to compare the reading of the 2 channels with each other so the data needs to be collected as simultaneously as possible) 

what the optimal way to achieve the above ?

also, if i use app_timer as the RTC timer for the saadc, does it involves the CPU ?, can it use ppi? (the name "app" makes me suspicious :) ) and is it a good solution for 32MHz sampling rate or is it to fit ?

best regards

Ziv

Parents
  • Do you mean kHz or MHz?
    I ask because the SAADC has a max sample rate of 200ksps.

  • hi haakonsh 

    so i found out that instead of using the "nrf_drv_timer_us_to_ticks()" , i can just put the value 500 assuming the default prescalar is 16MHz. pleas correct me if i am wrong .

    how ever, i want to check 2 channels every 31.25 microsec (which is 32KHz) with the saadc and i see that the 

    nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, TICS_FOR_32KHZ, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); //false);

    is defined for one channel, so how can i set that both channels will be sampled one after the other each timer event ? 

    also, in the saadc event handler where is the attention to which sample is read and where is the value saved ?

    void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
    {
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {...

    p.s. i am referring to the sdk example 

    last question for now is that in the future a ble will be implemented to the system, it will not work at the same time with the saadc but is there some timers issue i need to consider when adding softdevice ?

    best regards

    Ziv

Reply
  • hi haakonsh 

    so i found out that instead of using the "nrf_drv_timer_us_to_ticks()" , i can just put the value 500 assuming the default prescalar is 16MHz. pleas correct me if i am wrong .

    how ever, i want to check 2 channels every 31.25 microsec (which is 32KHz) with the saadc and i see that the 

    nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, TICS_FOR_32KHZ, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); //false);

    is defined for one channel, so how can i set that both channels will be sampled one after the other each timer event ? 

    also, in the saadc event handler where is the attention to which sample is read and where is the value saved ?

    void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
    {
        if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
        {...

    p.s. i am referring to the sdk example 

    last question for now is that in the future a ble will be implemented to the system, it will not work at the same time with the saadc but is there some timers issue i need to consider when adding softdevice ?

    best regards

    Ziv

Children
  • ziv123 said:
    so i found out that instead of using the "nrf_drv_timer_us_to_ticks()" , i can just put the value 500 assuming the default prescalar is 16MHz. pleas correct me if i am wrong .

     Yeah that's right. 

    ziv123 said:
    is defined for one channel, so how can i set that both channels will be sampled one after the other each timer event ?

     All enabled SAADC channels will be sampled once, in succession, starting with the lowest channel number, for each triggered SAMPLE task. 

    ziv123 said:
    also, in the saadc event handler where is the attention to which sample is read and where is the value saved ?

     It's stored in the buffer who's address was last used. See the SAADC example for use. 

    The SoftDevice Requires TIMER0, and also has execution priority. If you can use much larger buffers than 4 samples then you will be fine with BLE. When the RADIO peripheral is running the SAADC accuracy is a tiny bit lower. 

     

    if i use app_timer as the RTC timer for the saadc, does it involves the CPU ?, can it use ppi? (the name "app" makes me suspicious :) ) and is it a good solution for 32MHz sampling rate or is it to fit ?

    The app_timer does use an RTC peripheral, but it can not be used with PPI. For that you need to use the RTC driver.

  • All enabled SAADC channels will be sampled once, in succession, starting with the lowest channel number, for each triggered SAMPLE task.

    so i get one EVENT DONE for both channels ?

    how can i know to take the value sampled for each channel ? will it be  p_event->data.done.p_buffer[0] for channel 0 and p_event->data.done.p_buffer[1] for channel 1 ?

    or there will be an EVENT DONE for each channel ?

    in the saadc example only one channel is configured so i am not sure about the write way to configure and sample values of 2 channels, some guidance will be blessed

    If you can use much larger buffers than 4 samples then you will be fine with BLE.

    not sure what you meant by that, i want to sample the 2 channels at 32KHz rate for some time simultaneously i create my own oversampling i want to average the added values of each 4 samples (of both channels) i average the 4 readings of channel 0 and then averaging the 4 samples of channel 1 (so i actually gat 8KHz of my own oversampled (i can't use the built in oversampling mechanism cause it oversample channel 0 and then oversample channel 1 and so i get some time shift of sampled values)

    the BLE will be operational only after some defined time (several minutes) of sampling at the above rate is done , hope my goals are clearer now 

    i will try to explain my goal better, i want sampling rate of 32khz, of 2 channels but i want cpu intervention

    (i guess it starts at : "void saadc_callback(nrf_drv_saadc_evt_t const * p_event)"  ) only after 4 sampling event took place, so i am also looking for a way to get some interrupt from eDMA or something after 4x2 transffers of values where made to ram (also need to know where they are) , then cpu start to work to average the 4 saved sampled values of each channel, and then save the 2 average values (for ch0 and ch1).  

    hope my procedure is clearer now maybe it will be easier to guide me as to how to achieve it

    hope to read from you soon 

    best regards

    Ziv

  • ziv123 said:

    how can i know to take the value sampled for each channel ? will it be  p_event->data.done.p_buffer[0] for channel 0 and p_event->data.done.p_buffer[1] for channel 1 ?

    or there will be an EVENT DONE for each channel ?

     You will get an NRFX_SAADC_EVT_DONE when the buffer is filled. One buffer will contain samples from each enabled channel, see EasyDMA Fig 5 and 6 for examples of the buffer's memory map. 

     

    ziv123 said:

    not sure what you meant by that, i want to sample the 2 channels at 32KHz rate for some time simultaneously i create my own oversampling i want to average the added values of each 4 samples (of both channels) i average the 4 readings of channel 0 and then averaging the 4 samples of channel 1 (so i actually gat 8KHz of my own oversampled (i can't use the built in oversampling mechanism cause it oversample channel 0 and then oversample channel 1 and so i get some time shift of sampled values)

    the BLE will be operational only after some defined time (several minutes) of sampling at the above rate is done , hope my goals are clearer now 

    i will try to explain my goal better, i want sampling rate of 32khz, of 2 channels but i want cpu intervention

    (i guess it starts at : "void saadc_callback(nrf_drv_saadc_evt_t const * p_event)"  ) only after 4 sampling event took place, so i am also looking for a way to get some interrupt from eDMA or something after 4x2 transffers of values where made to ram (also need to know where they are) , then cpu start to work to average the 4 saved sampled values of each channel, and then save the 2 average values (for ch0 and ch1).  

    hope my procedure is clearer now maybe it will be easier to guide me as to how to achieve it

     Why can't you fill a large buffer and then run averaging? Do you have a latency-requirement? 

  • Why can't you fill a large buffer and then run averaging? Do you have a latency-requirement? 

    if you refer to the buffer filled by the eDMA in RAM then .. every second i have 32000 values of 4 bytes (2 values of 16 bits) if i run the sense sequence for 2 minutes i need about 16Mbyte of RAM, as fat as i know there is 256kb ram and 1Mb flash so my plane is to reduce it by averaging 4 reading first and then after some processing transfer it to an sRAM via QSPI (me next stage)

    You will get an NRFX_SAADC_EVT_DONE when the buffer is filled. One buffer will contain samples from each enabled channel, see EasyDMA Fig 5 and 6 for examples of the buffer's memory map. 

    i looked for the mentioned RESULT.MAXCNT in the saadc example and i could not find it to understand where it is defined or what value it holds, same for the RESULT.PTR that's about the data sheet ,

    but still, just to make sure i understand... if the following is my saadc_init function:

    #define SAMPLES_IN_BUFFER 4
    
    static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
    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;
    
    
    void saadc_init(void)
    {
        ret_code_t err_code;
        nrf_drv_saadc_config_t saadc_config;
        nrf_saadc_channel_config_t channel_config0;
    
        //Configure SAADC
        saadc_config.resolution = NRF_SAADC_RESOLUTION_14BIT;       //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_DISABLED;    //DIABLED - //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 = NRFX_SAADC_CONFIG_LP_MODE;
    	
        //Configure SAADC channel
        channel_config0.reference = NRF_SAADC_REFERENCE_INTERNAL;      //Set internal reference of fixed 0.6 volts
        channel_config0.gain = NRF_SAADC_GAIN1_6;                      //Set input gain to 1/6. The maximum SAADC input voltage is then 0.6V/(1/6)=3.6V. The single ended input range is then 0V-3.6V
        channel_config0.acq_time = NRF_SAADC_ACQTIME_10US;             //Set acquisition time. Set low acquisition time to enable maximum sampling frequency of 200kHz. Set high acquisition time to allow maximum source resistance up to 800 kohm, see the SAADC electrical specification in the PS. 
        channel_config0.mode = NRF_SAADC_MODE_SINGLE_ENDED;            //Set SAADC as single ended. This means it will only have the positive pin as input, and the negative pin is shorted to ground (0V) internally.
        channel_config0.pin_n = NRF_SAADC_INPUT_DISABLED;              //Since the SAADC is single ended, the negative pin is disabled. The negative pin is shorted to ground internally.
        channel_config0.resistor_p = NRF_SAADC_RESISTOR_DISABLED;      //Disable pullup resistor on the input pin
        channel_config0.resistor_n = NRF_SAADC_RESISTOR_DISABLED;        
        channel_config0.burst = NRF_SAADC_BURST_ENABLED;
        //nrf_saadc_channel_config_t channel_config_0 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);    
    
        err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
        APP_ERROR_CHECK(err_code);
    
        channel_config0.pin_p = NRF_SAADC_INPUT_AIN0; // TBD WRITE SOME NAME 
        err_code = nrf_drv_saadc_channel_init(0, &channel_config0);
        APP_ERROR_CHECK(err_code);
    
        channel_config0.pin_p = NRF_SAADC_INPUT_AIN1;
        err_code = nrf_drv_saadc_channel_init(1, &channel_config0);
        APP_ERROR_CHECK(err_code);
    
        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);
    
    }

    so if i put the "SAMPLES_IN_BUFFER = 4 " this means i will get the "NRFX_SAADC_EVT_DONE" only after 4 sampling of the 2 channels ? or maybe i need to define one buffer with 8 bytes in it instead of 2 buffers of 4 ? pretty sure the issue is not clear for me as you can see by my questions i don't really get how this works yet, sorry for that .. hope you help me with that

    also, what does the following function do in the nrf event handler when receiving an "NRFX_SAADC_EVT_DONE"  event

    nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);

    best regards

    Ziv

  • What I'm trying to say is that if you process your samples every 50ms you will have more than enough time to service the BLE stack at the same time, instead of every 125µs (4 samples @ 32kHz).

Related