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

Glitches in adc sampling

I am sampling 3 sensors using the ADC peripheral, clocking it once every 15us to sample the 3 sensors (3us acquisition time + 2us processing = 5us per channel).

I use the easy DMA in double buffer to sample 402 samples and interrupt me so i can process them and prepare the DMA next buffer (interrupt every 402*15/3=2100us).

In the interrupt context, I copy the buffer into a continuous big buffer and to excel and this is what i see:

when i stop the adc sampling and start it again (vertical spike above), i sometimes have a glitch that looks as if one sample is missed and i thus get channel 2 instead of 1 as shown in the above image. The vertical spike seen in the image is just a marker i add to the debug software buffer to mark where i re-enabled adc sampling.

The image above contains ~6 concatenated buffers (each buffer holds 402 samples = 134 samples from each of the 3).

Following is the code i use:

// this is double buffer hooked to the adc pointer. it is used to sample 3 channels 
static nrf_saadc_value_t adc_3_channels_double_buffer[2][402];
static uint32_t adc_3_channels_double_buffer_index = 0;

//this is a debug buffer holding 50 adc buffers 
static nrf_saadc_value_t adc_3_channels_debug_buffer[402*50]; 
static int adc_3_channels_debug_buffer_index = 0;

// tis function is called to start adc sampling 
void start_adc_sampling(void)
{
  // NRF_TIMER1 is a timer used to clock adc sampling of the 3 sensors we have - clock every 15us 
  NRF_TIMER1->MODE = (TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos); 
  NRF_TIMER1->PRESCALER = 4; // 1mhz --> 1us 
  NRF_TIMER1->CC[0] = 15; 
  NRF_TIMER1->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
  
  // ppi #6: connect adc timer to adc sampling 
  NRF_PPI->CH[5].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
  NRF_PPI->CH[5].TEP = (uint32_t) &NRF_SAADC->TASKS_SAMPLE;
  
  // ppi #9: connect adc buffer end to buffer start to get continuous double buffer sampling with no sampl
  NRF_PPI->CH[8].EEP = (uint32_t) &NRF_SAADC->EVENTS_END;
  NRF_PPI->CH[8].TEP = (uint32_t) &NRF_SAADC->TASKS_START;
  
  // Enable all ppi channels 
  NRF_PPI->CHENSET = (PPI_CHENSET_CH5_Enabled << PPI_CHENSET_CH5_Pos) | 
                     (PPI_CHENSET_CH8_Enabled << PPI_CHENSET_CH8_Pos);

  // general adc config (nrf_drv_saadc_init)
  NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos);
  NRF_SAADC->RESOLUTION = NRF_SAADC_RESOLUTION_12BIT;
  NRF_SAADC->OVERSAMPLE = NRF_SAADC_OVERSAMPLE_DISABLED;
  NRF_SAADC->INTENCLR = NRF_SAADC_INT_ALL;
  NRF_SAADC->EVENTS_END = 0x0UL;
  volatile uint32_t dummy = NRF_SAADC->EVENTS_END;
  NRF_SAADC->EVENTS_STARTED = 0x0UL;
  dummy = NRF_SAADC->EVENTS_STARTED;
  (void)dummy;
  NVIC_SetPriority(SAADC_IRQn, SAADC_CONFIG_IRQ_PRIORITY);
  NVIC_ClearPendingIRQ(SAADC_IRQn);
  NVIC_EnableIRQ(SAADC_IRQn);
  NRF_SAADC->INTENSET = NRF_SAADC_INT_END;
  
  // configure the adc to sample the 3 sensors 
  for ( int i=0; i<3; i++ )
  {
    NRF_SAADC->CH[i].PSELN = NRF_SAADC_INPUT_DISABLED;
    NRF_SAADC->CH[i].PSELP = NRF_SAADC_INPUT_DISABLED;
    NRF_SAADC->CH[i].CONFIG =
            ((NRF_SAADC_RESISTOR_DISABLED   << SAADC_CH_CONFIG_RESP_Pos)   & SAADC_CH_CONFIG_RESP_Msk)
            | ((NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESN_Pos)   & SAADC_CH_CONFIG_RESN_Msk)
            | ((NRF_SAADC_GAIN1_4           << SAADC_CH_CONFIG_GAIN_Pos)   & SAADC_CH_CONFIG_GAIN_Msk)
            | ((NRF_SAADC_REFERENCE_VDD4    << SAADC_CH_CONFIG_REFSEL_Pos) & SAADC_CH_CONFIG_REFSEL_Msk)
            | ((NRF_SAADC_ACQTIME_3US       << SAADC_CH_CONFIG_TACQ_Pos)   & SAADC_CH_CONFIG_TACQ_Msk)
            | ((NRF_SAADC_MODE_SINGLE_ENDED << SAADC_CH_CONFIG_MODE_Pos)   & SAADC_CH_CONFIG_MODE_Msk)
            | ((NRF_SAADC_BURST_DISABLED    << SAADC_CH_CONFIG_BURST_Pos)  & SAADC_CH_CONFIG_BURST_Msk);
  }
  NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_AIN1;
  NRF_SAADC->CH[1].PSELP = NRF_SAADC_INPUT_AIN2;
  NRF_SAADC->CH[2].PSELP = NRF_SAADC_INPUT_AIN3;
  
  // assigne the 1st adc buffer without starting it yet (nrf_drv_saadc_buffer_convert)
  NRF_SAADC->RESULT.PTR = (uint32_t)adc_3_channels_double_buffer[0];
  NRF_SAADC->RESULT.MAXCNT = 402;
  NRF_SAADC->EVENTS_STARTED = 0x0UL;
  dummy = NRF_SAADC->EVENTS_STARTED;
  
  // trigger samplig 
  uint8_t val;
  sd_nvic_critical_region_enter(&val); 
  NRF_TIMER1->TASKS_START = 1; 
  NRF_SAADC->TASKS_START = 1;
  sd_nvic_critical_region_exit(val);

  // configure the 2nd adc buffer once the 1st is started 
  while (NRF_SAADC->EVENTS_STARTED==0);
  NRF_SAADC->EVENTS_STARTED = 0x0UL;
  dummy = NRF_SAADC->EVENTS_STARTED;
  NRF_SAADC->RESULT.PTR = (uint32_t)adc_3_channels_double_buffer[1];
  adc_3_channels_double_buffer_index = 0;
}

// adc interrupt - called once every 402 samples to process the sampled buffer and prepare the nexe buffer 
void SAADC_IRQHandler(void)
{
  // only event end interruopt is enabled anyway  
 if ( nrf_saadc_event_check(NRF_SAADC_EVENT_END) )
 {
   // clear event
    NRF_SAADC->EVENTS_END = 0x0UL;
    volatile uint32_t dummy = NRF_SAADC->EVENTS_END;
   
   // get filled buffer and swap logically 
   nrf_saadc_value_t *src = adc_3_channels_double_buffer[adc_3_channels_double_buffer_index];
   adc_3_channels_double_buffer_index = 1 - adc_3_channels_double_buffer_index;
   
    if ( !flag_stop_sampling )
    {
      // ensure start before init 
      while (NRF_SAADC->EVENTS_STARTED==0);
      NRF_SAADC->EVENTS_STARTED = 0x0UL;
      dummy = NRF_SAADC->EVENTS_STARTED;
      (void)dummy;
      
      // init next buffer (current buffer being sampled is the other buffer so we have time to do this) 
      NRF_SAADC->RESULT.PTR = (uint32_t)src;
      NRF_SAADC->RESULT.MAXCNT = 402;
    }

    // copy the buffer into a big continuous buffer with room for 50 buffers 
    for ( int i=0; i<402; i+=3, src += 3 )
    {
      adc_3_channels_debug_buffer[adc_3_channels_debug_buffer_index++] = src[0];
      adc_3_channels_debug_buffer[adc_3_channels_debug_buffer_index++] = src[1];
      adc_3_channels_debug_buffer[adc_3_channels_debug_buffer_index++] = src[2];
    }
    
    // if someone sets this flag - i want to stop the adc sampling 
    if ( flag_stop_sampling )
    {
      // disconenct ppi
      NRF_PPI->CHENCLR = ((PPI_CHENCLR_CH5_Clear << PPI_CHENCLR_CH5_Pos) | 
                          (PPI_CHENCLR_CH8_Clear << PPI_CHENCLR_CH8_Pos));
      
      // adc timer shutdown (nrf_drv_timer_uninit) 
      NRF_TIMER1->SHORTS = 0;
      NRF_TIMER1->INTENCLR = 0xFFFFFFFF;
      NRF_TIMER1->TASKS_SHUTDOWN = 1;
      
#if 0 // problem ! - this block must be commented out in order to avoid channels switching in between trains   

      // adc shutdown (nrf_drv_saadc_uninit)
      NRF_SAADC->INTENCLR = NRF_SAADC_INT_ALL; 
      NVIC_DisableIRQ(SAADC_IRQn);
      NRF_SAADC->TASKS_STOP = 0x1UL;
      uint32_t timeout = 10000;
      while (NRF_SAADC->EVENTS_STOPPED==0 && timeout > 0)
      {
        --timeout;
      }
      ASSERT(timeout > 0);
      NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos);
      NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_DISABLED;
      NRF_SAADC->CH[1].PSELP = NRF_SAADC_INPUT_DISABLED;
      NRF_SAADC->CH[2].PSELP = NRF_SAADC_INPUT_DISABLED;

#endif
    }
  }
  else
  {
    ASSERT(0);
  }
}

In the code i have '#if 0' in line 131 commenting out the bit of code that causes the glitch problem.

When i remove it i see no glitches.

In the blocked code shuts down the ADc in order to save power.

It blocked code is art of code within the ADC interrupt that is trigered when someone set 'flag_stop_sampling' to 1 in order to stop ADC sampling.

My point is - if i disable the ADC peripheral (as i do) in between samples and later on wake it up again and re configure the entire logic - how come i see glitches of channels in my buffer.

My target was to make my software implementation to do the sampling regardless of softdevice and other tasks interrupts.

Parents
  • Hello,

    I have spoken with some of out ADC engineers. They say that this is most likely related to a bug.

     

    Can you please check Jørgen's answer marked in green on this page:

    https://devzone.nordicsemi.com/f/nordic-q-a/20291/offset-in-saadc-samples-with-easy-dma-and-ble

     

    Best regards,

    Edvin

     

  • I still haven't solved the excessive power consumption issue.

    In order to isolate the problem i have this code here that i use to test:

    void a2d_test(void)
    {
      // timer that clocks the adc once every 18us 
      NRF_TIMER1->MODE = (TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos); 
      NRF_TIMER1->PRESCALER = 4; // 1mhz --> 1us 
      NRF_TIMER1->CC[0] = 18; 
      NRF_TIMER1->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
    
      // connect the timer to adc sampling 
      NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
      NRF_PPI->CH[0].TEP = (uint32_t) &NRF_SAADC->TASKS_SAMPLE;
    
      // enable ppi 
      NRF_PPI->CHENSET = PPI_CHENSET_CH0_Enabled << PPI_CHENSET_CH0_Pos;
    
      // configure the adc to sample 3 channels 111 samples per channel (total 333 samples)
      NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos);
      NRF_SAADC->RESOLUTION = NRF_SAADC_RESOLUTION_12BIT;
      NRF_SAADC->OVERSAMPLE = NRF_SAADC_OVERSAMPLE_DISABLED;
      NRF_SAADC_EVENTS_CLEAR(EVENTS_END)
      NRF_SAADC_EVENTS_CLEAR(EVENTS_STARTED)
      for ( int i=0; i<3; i++ )
      {
        NRF_SAADC->CH[i].PSELN = NRF_SAADC_INPUT_DISABLED;
        NRF_SAADC->CH[i].CONFIG =
                ((NRF_SAADC_RESISTOR_DISABLED   << SAADC_CH_CONFIG_RESP_Pos)   & SAADC_CH_CONFIG_RESP_Msk)
                | ((NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESN_Pos)   & SAADC_CH_CONFIG_RESN_Msk)
                | ((NRF_SAADC_GAIN1_4           << SAADC_CH_CONFIG_GAIN_Pos)   & SAADC_CH_CONFIG_GAIN_Msk)
                | ((NRF_SAADC_REFERENCE_VDD4    << SAADC_CH_CONFIG_REFSEL_Pos) & SAADC_CH_CONFIG_REFSEL_Msk)
                | ((NRF_SAADC_ACQTIME_3US       << SAADC_CH_CONFIG_TACQ_Pos)   & SAADC_CH_CONFIG_TACQ_Msk)
                | ((NRF_SAADC_MODE_SINGLE_ENDED << SAADC_CH_CONFIG_MODE_Pos)   & SAADC_CH_CONFIG_MODE_Msk)
                | ((NRF_SAADC_BURST_DISABLED    << SAADC_CH_CONFIG_BURST_Pos)  & SAADC_CH_CONFIG_BURST_Msk);
      }
      NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_AIN1;
      NRF_SAADC->CH[1].PSELP = NRF_SAADC_INPUT_AIN2;
      NRF_SAADC->CH[2].PSELP = NRF_SAADC_INPUT_AIN3;
      NRF_SAADC->RESULT.PTR = (uint32_t)train_adc_buffer[0];
      NRF_SAADC->RESULT.MAXCNT = 333;
      NRF_SAADC_EVENTS_CLEAR(EVENTS_STARTED)
      
      // trigger the timer that clocks the adc 
      NRF_TIMER1->TASKS_START = 1; // 
    
      // repeat 10 times 
      for ( int i=0; i<10; i++ )
      {
        // trigger the adc 
        NRF_SAADC->TASKS_START = 1;
        
        // wait until adc ends its sampling (not stopped)  
        NRF_SAADC_EVENTS_CLEAR(EVENTS_END)
        while (NRF_SAADC->EVENTS_END==0);
    
        // set its pointer to the desired location 
        NRF_SAADC->RESULT.PTR = (uint32_t)train_adc_buffer[0];
      }
    
      // stop the ADC 
      // problem - any delay i add here or any interruption between EVENTS_END and STOP casues permaenent 1.2ma consumtion nrf_delay_us(3);
      NRF_SAADC_EVENTS_CLEAR(EVENTS_STOPPED)
      NRF_SAADC->TASKS_STOP = 1;
      while (NRF_SAADC->EVENTS_STOPPED==0);
      
      // disable the adc 
      NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos);
      NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_DISABLED;
      NRF_SAADC->CH[1].PSELP = NRF_SAADC_INPUT_DISABLED;
      NRF_SAADC->CH[2].PSELP = NRF_SAADC_INPUT_DISABLED;
    
      // disaple the ppi   
      NRF_PPI->CHENCLR = (PPI_CHENCLR_CH0_Clear << PPI_CHENCLR_CH0_Pos); 
    
      // disable the timer   
      NRF_TIMER1->TASKS_STOP = 1;
      NRF_TIMER1->SHORTS = 0;
      NRF_TIMER1->TASKS_SHUTDOWN = 1;
    }
    

    Please look at the comment where i say "problem" right when stopping the ADC.

    Whenever i have a delay between EVENTS_END and  NRF_SAADC->TASKS_STOP=1; i get excessive permanent power consumption of ~1.2mA.

    From this point on there is no way to reduce the power unless i RESET the chip.

    If i use PPI to STOP the ADC, the problem is gone since EVENTS_END and TASK_STOP occur at the same time. But in my system i cannot do this.

    Do you now about this issue and why it occurs?

Reply
  • I still haven't solved the excessive power consumption issue.

    In order to isolate the problem i have this code here that i use to test:

    void a2d_test(void)
    {
      // timer that clocks the adc once every 18us 
      NRF_TIMER1->MODE = (TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos); 
      NRF_TIMER1->PRESCALER = 4; // 1mhz --> 1us 
      NRF_TIMER1->CC[0] = 18; 
      NRF_TIMER1->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
    
      // connect the timer to adc sampling 
      NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
      NRF_PPI->CH[0].TEP = (uint32_t) &NRF_SAADC->TASKS_SAMPLE;
    
      // enable ppi 
      NRF_PPI->CHENSET = PPI_CHENSET_CH0_Enabled << PPI_CHENSET_CH0_Pos;
    
      // configure the adc to sample 3 channels 111 samples per channel (total 333 samples)
      NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos);
      NRF_SAADC->RESOLUTION = NRF_SAADC_RESOLUTION_12BIT;
      NRF_SAADC->OVERSAMPLE = NRF_SAADC_OVERSAMPLE_DISABLED;
      NRF_SAADC_EVENTS_CLEAR(EVENTS_END)
      NRF_SAADC_EVENTS_CLEAR(EVENTS_STARTED)
      for ( int i=0; i<3; i++ )
      {
        NRF_SAADC->CH[i].PSELN = NRF_SAADC_INPUT_DISABLED;
        NRF_SAADC->CH[i].CONFIG =
                ((NRF_SAADC_RESISTOR_DISABLED   << SAADC_CH_CONFIG_RESP_Pos)   & SAADC_CH_CONFIG_RESP_Msk)
                | ((NRF_SAADC_RESISTOR_DISABLED << SAADC_CH_CONFIG_RESN_Pos)   & SAADC_CH_CONFIG_RESN_Msk)
                | ((NRF_SAADC_GAIN1_4           << SAADC_CH_CONFIG_GAIN_Pos)   & SAADC_CH_CONFIG_GAIN_Msk)
                | ((NRF_SAADC_REFERENCE_VDD4    << SAADC_CH_CONFIG_REFSEL_Pos) & SAADC_CH_CONFIG_REFSEL_Msk)
                | ((NRF_SAADC_ACQTIME_3US       << SAADC_CH_CONFIG_TACQ_Pos)   & SAADC_CH_CONFIG_TACQ_Msk)
                | ((NRF_SAADC_MODE_SINGLE_ENDED << SAADC_CH_CONFIG_MODE_Pos)   & SAADC_CH_CONFIG_MODE_Msk)
                | ((NRF_SAADC_BURST_DISABLED    << SAADC_CH_CONFIG_BURST_Pos)  & SAADC_CH_CONFIG_BURST_Msk);
      }
      NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_AIN1;
      NRF_SAADC->CH[1].PSELP = NRF_SAADC_INPUT_AIN2;
      NRF_SAADC->CH[2].PSELP = NRF_SAADC_INPUT_AIN3;
      NRF_SAADC->RESULT.PTR = (uint32_t)train_adc_buffer[0];
      NRF_SAADC->RESULT.MAXCNT = 333;
      NRF_SAADC_EVENTS_CLEAR(EVENTS_STARTED)
      
      // trigger the timer that clocks the adc 
      NRF_TIMER1->TASKS_START = 1; // 
    
      // repeat 10 times 
      for ( int i=0; i<10; i++ )
      {
        // trigger the adc 
        NRF_SAADC->TASKS_START = 1;
        
        // wait until adc ends its sampling (not stopped)  
        NRF_SAADC_EVENTS_CLEAR(EVENTS_END)
        while (NRF_SAADC->EVENTS_END==0);
    
        // set its pointer to the desired location 
        NRF_SAADC->RESULT.PTR = (uint32_t)train_adc_buffer[0];
      }
    
      // stop the ADC 
      // problem - any delay i add here or any interruption between EVENTS_END and STOP casues permaenent 1.2ma consumtion nrf_delay_us(3);
      NRF_SAADC_EVENTS_CLEAR(EVENTS_STOPPED)
      NRF_SAADC->TASKS_STOP = 1;
      while (NRF_SAADC->EVENTS_STOPPED==0);
      
      // disable the adc 
      NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos);
      NRF_SAADC->CH[0].PSELP = NRF_SAADC_INPUT_DISABLED;
      NRF_SAADC->CH[1].PSELP = NRF_SAADC_INPUT_DISABLED;
      NRF_SAADC->CH[2].PSELP = NRF_SAADC_INPUT_DISABLED;
    
      // disaple the ppi   
      NRF_PPI->CHENCLR = (PPI_CHENCLR_CH0_Clear << PPI_CHENCLR_CH0_Pos); 
    
      // disable the timer   
      NRF_TIMER1->TASKS_STOP = 1;
      NRF_TIMER1->SHORTS = 0;
      NRF_TIMER1->TASKS_SHUTDOWN = 1;
    }
    

    Please look at the comment where i say "problem" right when stopping the ADC.

    Whenever i have a delay between EVENTS_END and  NRF_SAADC->TASKS_STOP=1; i get excessive permanent power consumption of ~1.2mA.

    From this point on there is no way to reduce the power unless i RESET the chip.

    If i use PPI to STOP the ADC, the problem is gone since EVENTS_END and TASK_STOP occur at the same time. But in my system i cannot do this.

    Do you now about this issue and why it occurs?

Children
  • Did you try to change the TASKS_SAMPLE to TASKS_START (now at NRF_PPI->CH[0].TEP = (uint32_t)&NRF_SAADC->TASKS_START;

    Like I suggested in the previous reply?

     

    Best regards,

    Edvin

  • Nothing happens when i do that. No sampling at all.

    Nothing also happens when i do:

    NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
    NRF_PPI->CH[0].TEP = (uint32_t) &NRF_SAADC->TASKS_SAMPLE;
    NRF_PPI->FORK[0].TEP = (uint32_t) &NRF_SAADC->TASKS_START;

  • Hello,

    I am sorry that this is taking a while. Trust me, if I knew exactly what to do to fix this, I would have told you at once. I have also consulted with our SAADC guys, and they only point to Jørgens answer in the ticket that I linked to in an earlier post:

    1. Use PPI to trigger START task on an END event. This will avoid the delayed triggering og the START task due to a queued interrupt generated by the END event, but in the case of high sample frequency and long delays, it can cause your buffer to be overwritten before you are able to process the buffer. In case of using this solution, it is neccessary to use double buffering and large enough buffers to avoid data loss.

    I am sorry. I meant that you should connect EVENTS_END with TASKS_START. 

    Then you only need to start the SAADC by calling the first sample call.

     

    Is it possible to send the project, and I can look at it, or maybe get the SAADC experts to do it. Is it possible to reproduce on a regular DK, or do you have a custom board?

     

    Best regards,

    Edvin

     

     

  • Thanks for all the effort. I appreciate it. 

    I have already connected EVENTS_END to TASKS_START and this is the way i am working ever since. 

    The problem is that when i STOP the ADC i do it manually (not through PPI) and get excessive 1.2mA power consupmption. 

    In the last hour i may have a breakthrough by using PPI to connect EVENTS_END to TASKS_START as you suggested, but on the last SAADC buffer i sample, i overwrite this PPI channel such that EVENTS_END will do TASKS_STOP instead.

    This approach seems to work.

    I will try to use it in my real code (now i am using jut a side SAADC test) and see if it works.

    Can you consult with the SAADC expert to see if my solution makes sense?

    I will send you the project later on (how d oi do that?).

    This is what i do at init with PPI:

    // connect the timer to adc sampling
    NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
    NRF_PPI->CH[0].TEP = (uint32_t) &NRF_SAADC->TASKS_SAMPLE;

    // connect end to start
    NRF_PPI->CH[1].EEP = (uint32_t) &NRF_SAADC->EVENTS_END;
    NRF_PPI->CH[1].TEP = (uint32_t) &NRF_SAADC->TASKS_START;

    And this is what i do at run time after the last buffer EVENTS_STARTED is set:

    NRF_PPI->CHENCLR = PPI_CHENCLR_CH1_Clear << PPI_CHENCLR_CH1_Pos;
    NRF_PPI->CH[1].EEP = (uint32_t) &NRF_SAADC->EVENTS_END;
    NRF_PPI->CH[1].TEP = (uint32_t) &NRF_SAADC->TASKS_STOP;
    NRF_PPI->CHENSET = PPI_CHENSET_CH1_Enabled << PPI_CHENSET_CH1_Pos;
    while ( NRF_SAADC->EVENTS_STOPPED==0 );

    NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos);

  • Hello,

    That seems like a reasonable solution. I believe that the problem when doing SAADC sampling fast is that the events are showing up in the wrong order, and the difficult part is to stop the sampling at the correct time, so that you don't do an extra sample, causing a shift in the buffer.

    Imagine that you are sampling channels: 1,2,3,1,2,3,1,2,3, ...You want to stop after channel 3, but a delayed stop may cause it to stop on channel 1, that is one more than you intended.

    Another approach you can test is to disable the CHEN, which I see that you do:

    NRF_PPI->CHENCLR = PPI_CHENCLR_CH1_Clear << PPI_CHENCLR_CH1_Pos; which should stop the "ADC train".

    I will talk to someone in the ADC group. Please keep me updated if you find anything new.

     

    Best regards,

    Edvin

Related