Close

Ping Pong Buffer

A project log for Arduino TIMECODE smpte LTC reader generator SHIELD

Using libltc to make a SMPTE Time Code Receiver/Generator for Arduino !

ben bilesben biles 09/26/2016 at 14:493 Comments

its looking like i need to try using a ping pong buffer in the code. This will allow me to read the samples continuously and dump blocks of samples into LibLTC as an array with the pointer.

I thought the circular buffer was the answer but you cannot really use a pointer to point to large block of samples this way. its hard to achieve any kind of timing with the circular buffer. Or at least I am not sure how to make the timing work !!

If I say load a ping pong buffer of say 1920 samples 1/25th of a second ( 1 video frame in time ) of audio at 48khz sample rate. then when its full send it to LibLTC for decoding. whilst its being copied next 1920 samples are coming into array B of the ping pong buffer..

Thats the idea but I'm new to the Ping Pong Buffer concept !!! does anyone here have any pingpong buffer example code that they think that would work on on arduino DUE??

Will post code if I get this working....

Discussions

K.C. Lee wrote 09/26/2016 at 16:24 point

You just need 2 set of buffers (current/next) in the PDC and keep track of which one using a variable.  When the transfer is complete, it'll raise an interrupt.  Inside the interrupt code, you update the new one and set a flag for your main processing code.

I have 2 flags that are updated in my IRQ code.

Audio_Data.Conv_HalfDone: This tell me which half of the buffer has data
Audio_Data.Conv_Done: Set by the interrupt and cleared by the processing code.  This tell the processing code that there is a new block of data.  

Inside my main event loop, I poll for the Audio_Data.Conv_Done flag to call the processing code.

while(1)
{
   __WFI( );    // sleep and wait for interrupt
  if(Audio_Data.Conv_Done)
  {
     Audio_Data.Conv_Done = 0;
     Audio_Processing();
     UpdateDisplay();
   }
// do other things
}

The DMA just keeps the buffer filled in the background.

Inside my processing code, it uses the flag to figure out which block of data to process.

if(Audio_Data.Conv_HalfDone)
  Start = Cur = &Audio_Data.AudioBuffer[0];
else
  Start = Cur = &Audio_Data.AudioBuffer[ADC_MAX_CH*ADC_BLOCK_SIZE];

Make sure that these flags are declared as volatile to let the compiler know that their values might be changed inside IRQ and main code, so that it doesn't try to do smart things.

  Are you sure? yes | no

ben biles wrote 09/26/2016 at 16:19 point

interesting.. so are you saying that I could read the SAM3X DMA transfer counters and use them as inturrupts for my ping pong buffer ? I'll try and read the SAM3 datasheet 26.4.3 Transfer Counters (for PDC) and see if I can understand it ! 

  Are you sure? yes | no

K.C. Lee wrote 09/26/2016 at 15:37 point

I am using a ping pong buffer in my Audio switch project, but it is 100% STM32F030 bare metal C code. 

https://github.com/FPGA-Computer/Automatic-Audio-Switch

There is a half transfer and a full transfer interrupt on the STM32F0 DMA controller that signals which half of the buffer is filled.  I basically set a couple of flags to my main code to process a block of data at a time.  It works beautifully.

Took a quick read on the SAM3 datasheet. 26.4.3   Transfer Counters (for PDC) 

>Each channel has two 16-bit counters, one for current transfer and the other one for next transfer. These counters define the size of data to be transferred by the channel. The current transfer counter is decremented first as the data addressed by current memory pointer starts to be transferred. When the current transfer counter reaches zero, the channel checks its next transfer counter. If the value of next counter is zero, the channel stops transferring data and sets the appropriate flag. But if the next counter value is greater then zero, the values of the next pointer/next counter are copied into the current pointer/current counter and the channel resumes the transfer whereas next pointer/next counter get zero/zero as values.

So looks like that is a ping pong buffer (current vs next) that you have to manage at the interrupt of each completion.  So you can set some flags there to tell your processing code which buffer to use.  (STM32F0 have the option to reuse the same values and run forever.)

Can't help you with Arduino code unfortunately.

  Are you sure? yes | no