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

Array list with ring buffer

Good afternoon,

I have a new small, but I think important, question. I did not find a concrete answer on this forum.

The question concerns the correct switching of buffers in the continuous reception mode with EasyDMA Array List mode.

For example.
I have data reception from the sensor via the I2C bus. The data comes with a frequency of 250 Hz. For processing it is convenient for me to use a frame of 25 samples.

For this I do the following:
1. I initialize the modules: I2C, PPI, GPIOTE, TIMER (Counter);
2. I configure the short-circuits and PPIs so that the readiness of the data starts the process of reading the sensor (I2C_WRITE -> I2C_READ -> I2C_STOP);
3. After each STOP event, the timer is incremented (PPI->Task = Timer Task Count);
4. When the value reaches 25, the interrupt of the timer starts (Timer Channel compare) , in which I switch to the next buffer.

So, now i do next:

void EventCounter_IRQHandler(void)
{
  BaseType_t xHigherPriorityTaskWoken;
 
  static TaskNotification_t NotifyEvent;
  static uint8_t used_buffer_num = 0;
  // Clear event
  EventCounter->EVENTS_COMPARE[0] = 0;
  volatile uint32_t dummy = EventCounter->EVENTS_COMPARE[0];
  (void)dummy;

  // Save fully buffer num
  NotifyEvent.flags.ArrayNum = used_buffer_num&(PGG_IN_FRAME_MAX_NUM-1);
  // Switch to next
  used_buffer_num++;
  used_buffer_num &= (PGG_IN_FRAME_MAX_NUM-1);
  // Set next buffer
  PPG_TWIM->RXD.PTR = (uint32_t)&PpgDataFrame[used_buffer_num];
  
  NotifyEvent.flags.fFrameReady = true;
  // Send Task Notification
  xTaskNotifyFromISR(hPpgControl, NotifyEvent.Value, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

But I think that this is not entirely correct. Because the interrupt can be delayed for some reason and I just do not have time to switch the buffer. In this case, the new count will be written to the next memory location. Which will lead to fatal consequences.


How to properly protect yourself in this case?

  • Hi,

    The only thing I can think about that could reduce the risk of overwriting the buffers is to allocate a larger buffer, for instance 30, to allow some more writes after the 25 transfer if the interrupt handling are delayed for some reason. You then need to read the TIMER counter in the interrupt to know how many transfers completed, and clear it when you set the new buffer.

    When you use PPI/shorts to trigger transfers, there is no other way to handle this. The CPU can be busy at any time, and there is no way for the TWI peripheral to know if you have handled the timer interrupt or not.

    Best regards,
    Jørgen

  • Hi Jørgen,

    I thought about the implementation of protective intervals. In fact, it has some difficulties.
    In a simple case, we just "lose" a few points in the protective interval.
    It will be more correct to implement an algorithm that will track the appearance of points in the guard interval and transfer them to the main buffer.

    I'm thinking about a way to implement this algorithm.

    I will ask some clarifying questions:
    Q1. Is it correct to change the pointer "on the fly"?
    Q2. If I need to disconnect the TWIM module (or another), should I disable the PPI channels (which connect to this module)?
    Q3. To reset the event flag, I use a view design (the explanation of why I should read this in one of the answers on this forum (About (void)dummy).This construction was found in the code of SDK).

    // Clear event
    EventCounter->EVENTS_COMPARE[0] = 0;
    volatile uint32_t dummy = EventCounter->EVENTS_COMPARE[0];
    (void)dummy;


    should this be used when writing a new pointer?

    Thanks,

    Max

  • Q1. The RXD/TXD register pointers of TWIM peripheral is double buffered. If a transfer is started, the new buffer will be used for the next transfer.

    Q2. If you stop the TWIM peripheral, no events will be generated. It should not cause any issues to leave the PPI channels enabled. 

    Q3. This should only be relevant when clearing events. You should not need to read back the register when writing buffer pointers.

  • It turns out that the current operation (reception / transmission) will occur using the previous pointer?
    That is, it is possible that the pointer was changed, but the record was made in the next memory cell?

  • The buffer used will depend on when the pointer is set and when when the START task is triggered. From the documentation:

    The .PTR and .MAXCNT registers are double-buffered. They can be updated and prepared for the next RX/TX transmission immediately after having received the RXSTARTED/TXSTARTED event.

Related