PPI ADC + Timer usage

I reference the sample at https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-6-analog-to-digital-converter-adc/topic/exercise-3-interfacing-with-adc-using-nrfx-drivers-and-timer-ppi/

My question is:
1. How to manually trigger timer to start SAADC sampling?

When I call method "nrfx_timer_enable(&timer_instance);", the PPI starts just once, another call of this function doesn't work, event if I called method "nrfx_timer_reconfigure(&timer_instance, &timer_config);"
2. How to use mutiple ADC channels with PPI?

/* STEP 4.1 - Define the buffer size for the SAADC */
#define SAADC_BUFFER_SIZE 65

/* STEP 4.2 - Declare the buffers for the SAADC */
static int16_t saadc_sample_buffer[2][SAADC_BUFFER_SIZE*2];
static nrfx_saadc_channel_t m_multiple_channels[] =

{
    NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN1, 0),

    NRFX_SAADC_DEFAULT_CHANNEL_SE(NRF_SAADC_INPUT_AIN6, 3)
};

m_multiple_channels[0].channel_config.gain = NRF_SAADC_GAIN1_6;
  m_multiple_channels[1].channel_config.gain = NRF_SAADC_GAIN1_5;
  err = nrfx_saadc_channels_config(&m_multiple_channels, 2);
  if (err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_saadc_channels_config error: %08x", err);
    return;
  }

  /* STEP 4.8 - Configure channel 0 in advanced mode with event handler
   * (non-blocking mode) */
  uint32_t mask = nrfx_saadc_channels_configured_get();
  nrfx_saadc_adv_config_t saadc_adv_config = NRFX_SAADC_DEFAULT_ADV_CONFIG;
  err = nrfx_saadc_advanced_mode_set(mask, NRF_SAADC_RESOLUTION_14BIT,
                                     &saadc_adv_config, saadc_event_handler);
  if (err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_saadc_advanced_mode_set error: %08x", err);
    return;
  }

  /* STEP 4.9 - Configure two buffers to make use of double-buffering feature of
   * SAADC */
  err = nrfx_saadc_buffer_set(saadc_sample_buffer[0], SAADC_BUFFER_SIZE * 2);
  if (err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
    return;
  }
  err = nrfx_saadc_buffer_set(saadc_sample_buffer[1], SAADC_BUFFER_SIZE * 2);
  if (err != NRFX_SUCCESS) {
    LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
    return;
  }

void saadc_event_handler(nrfx_saadc_evt_t const *p_event) {
  nrfx_err_t err;
  switch (p_event->type) {
    case NRFX_SAADC_EVT_READY:

      /* STEP 5.1 - Buffer is ready, timer (and sampling) can be started. */
      // nrfx_timer_enable(&timer_instance);
      break;

    case NRFX_SAADC_EVT_BUF_REQ:

      /* STEP 5.2 - Set up the next available buffer. Alternate between buffer 0
       * and 1 */
      err = nrfx_saadc_buffer_set(
          saadc_sample_buffer[(saadc_current_buffer++) % 2], SAADC_BUFFER_SIZE);
      // err = nrfx_saadc_buffer_set(saadc_sample_buffer[((saadc_current_buffer
      // == 0 )? saadc_current_buffer++ : 0)], SAADC_BUFFER_SIZE);
      if (err != NRFX_SUCCESS) {
        LOG_ERR("nrfx_saadc_buffer_set error: %08x", err);
        return;
      }
      break;

    case NRFX_SAADC_EVT_DONE:

      /* STEP 5.3 - Buffer has been filled. Do something with the data and
       * proceed */
      int64_t average = 0;
      int16_t max = INT16_MIN;
      int16_t min = INT16_MAX;
      int16_t current_value;
      LOG_INF("size: %d", p_event->data.done.size);
      for (int i = 0; i < p_event->data.done.size; i++) {
        current_value = ((int16_t *)(p_event->data.done.p_buffer))[i];
        LOG_INF("value1: %d, value2: %d", current_value, ((int16_t *)(p_event->data.done.p_buffer))[i++]);
        average += current_value;
        if (current_value > max) {
          max = current_value;
        }
        if (current_value < min) {
          min = current_value;
        }
      }
      average = average / p_event->data.done.size;
      LOG_INF("SAADC buffer at 0x%x filled with %d samples",
              (uint32_t)p_event->data.done.p_buffer, p_event->data.done.size);
      LOG_INF("AVG=%d, MIN=%d, MAX=%d", (int16_t)average, min, max);
      break;
    default:
      LOG_INF("Unhandled SAADC evt %d", p_event->type);
      break;
  }
}

Parents
  • Hello,

    Were you able to see the output as per exercise? And now you want to make some modifications?

    Do you mean you want to trigger the timer once, or twice?

    What do you mean by "manually" trigger timer? Do you intent NOT using PPI?

    The exercise you have referred to uses the SAADC driver to measure voltage and uses a hardware TIMER to trigger sampling through DPPI/PPI, without any CPU involvement. Do you still want to use the PPI or Not?

    At the top, you can see the sampling interval is set as 50us. You can change that.

    In the configure_timer function, timer channel0 is used for extended mode. You can use other channels like channel0 is used.

  • Sorry, I didn't describe my requirements clealy.
    My requirement is:

    |----------------------------cycle1-------------------------------------||----------------------------cycle2-------------------------------------|
    |3ms delay|AIN1 sample 65 times|AIN2 sample 65times| |3ms delay|AIN1 sample 65 times|AIN2 sample 65times| 

    In each cycle:
    I want to use PPI to do ADC sampling, so I need to trigger sample manuall.
    I checked the specification, This is a module named "EGU", I'm not sure if it can be used to trigger ?

  • Hi hugh512,

    First, I want to make sure we are on the same page that the DevAcademy exercise you linked described the steps to create a TIMER, SAADC, and PPI setup that runs automatically indefinitely, with only a manual trigger setup once at the beginning.

    Now regarding your requirement, I see here that you want to use two analog inputs.

    Is it possible to instead scan both these inputs in 65 cycles, each cycle sample AIN1 once and then AIN2 once?
    The reason is because if you enabled more than one SAADC channel, the device enters Scan Mode, and each sample task will sequentially sample all enabled channels.

    If that is not possible, and you strictly need to sample AIN1 alone 65 times, before sampling AIN2 alone 65 other times, then you will have to reconfigure the entire setup manually.

    To reconfigure it, you don't need to use the EGU. It's best to just do it in the SAADC event handler. The reconfiguration should be reasonably short enough to do there.

    Does this answer your questions?

Reply
  • Hi hugh512,

    First, I want to make sure we are on the same page that the DevAcademy exercise you linked described the steps to create a TIMER, SAADC, and PPI setup that runs automatically indefinitely, with only a manual trigger setup once at the beginning.

    Now regarding your requirement, I see here that you want to use two analog inputs.

    Is it possible to instead scan both these inputs in 65 cycles, each cycle sample AIN1 once and then AIN2 once?
    The reason is because if you enabled more than one SAADC channel, the device enters Scan Mode, and each sample task will sequentially sample all enabled channels.

    If that is not possible, and you strictly need to sample AIN1 alone 65 times, before sampling AIN2 alone 65 other times, then you will have to reconfigure the entire setup manually.

    To reconfigure it, you don't need to use the EGU. It's best to just do it in the SAADC event handler. The reconfiguration should be reasonably short enough to do there.

    Does this answer your questions?

Children
No Data
Related