Circular_Buffer

Hey Tim, you and Mike been busy... I'm working on something you will like though. I've got TeensyLC running F&F with SPI_MST with 16 bit transfers!
Theres alot of work to go through the code but it might take me a couple days to do the adaptation :)

Yea, you read that right! SPI_MST RUNNING ON TEENSY LC!!!!!!

Congrats Mike, welcome to the club :)

Busy indeed - doing what … not sure. Been lurking …

Nice you brought the T_LC onboard for MST! Will be interesting to see it's speed (SPI and CPU) and RAM left for worthy tasks.

In what I did a couple CB's were handy - and the MST was a great gift for the MPU use to spew tons of data keeping the core process running.

Both those things could add good utility to Teensy.
 
Guess I will have to solder up the LC and get ready for some more testing with the LC and T3.5.
 
Finally got around to implementing your circular buffer library. At first it seemed way above my knowledge, but once I did a few examples, implementing it into my existing code was so easy. Just want to say thanks for all the hard work you put into these libraries.
Now its time to figure out the teensyquitto program to send files that are being stored on the teensy 3.6 sd-card via wifi.
 
awesome! :)

Circular_Buffer has also been incorporated into SPI_MST as well as IFCT development, so it is finding it's way around for usages not just for users but for libraries as well

Enjoy
 
… At first it seemed way above my knowledge, but once I did a few examples, implementing it into my existing code was so easy ...

Any chance you might offer an example with your usage with some comments that might make clearer how simple and effective it is to use?

I was there as it was created - but it expanded beyond use at hand and what I saw how to use. When I tried one simple thing it worked well. It seems like it is more complicated to use than it is - just a new style of interface not knowing what is under the covers.

To keep most recent four values average:
Code:
Circular_Buffer<uint32_t, 4> cctASec;
volatile uint32_t cctAvg = F_CPU;

void rtc_seconds_isr(void) {
  cctASec.push_back( cctCntDif );
  cctAvg = cctASec.mean();
}
 
Hi,

For my needs I am filling an array with a frequency value based on a gear and optical sensor. What is interesting with my system is the collection rate varies because I am measuring the power output from cyclists, so depending on their cadence or power, the frequency varies. The other issue I had before the circular buffer is that if they were moving slow or had to stop for a period of time, the buffer would fill and I would miss values. So the circular buffer came in handy. Here is a snippet of how I use the circular buffer. As you can see, I am just putting the value at the back of the array, then when an event triggers, I get the size of the array (which is how many samples I saw in that period), then do some statistical stuff, transfer the circular buffer into another array, flush the array.........and this repeats over and over. Not sure if this helps anyone, nor if I am doing it correctly, but it seems to be working great.

Code:
Circular_Buffer<uint16_t, 2048> hz;
uint16_t hzmin;
uint16_t  hzmax;
uint16_t hzvariance;
uint16_t  hzdeviation;
uint16_t hzmean;
uint16_t  hzaverage;

void loop(void)
{

  if (FreqMeasure.available()) {
    HZ = FreqMeasure.countToFrequency(FreqMeasure.read());
    if (HZ > MINHZ && HZ < MAXHZ) {
      hz.push_back(HZ);
    }
  }

  // turn interrupts off quickly while we take local copies of the shared variables
  if (bUpdateFlagsShared)
  {
    noInterrupts();
    bUpdateFlags = bUpdateFlagsShared;
    if (bUpdateFlags & DEGREE_FLAG) //fill array at each tooth
    {
      CadenceTime = IT;
      DegreeTime = DT;
      HzSamples = hz.size();
      hzmin = hz.min();
      hzmax = hz.max();
      hzvariance = hz.variance();
      hzdeviation = hz.deviation();
      hzmean = hz.mean();
      hz.pop_front(HzArray, HzSamples);
      hz.flush();//was getting repeat hz if I do not flush queue
      msg.DTSum += DegreeTime;
      msg.HzSamplesSum += HzSamples;
    }
    bUpdateFlagsShared = 0;
    interrupts();
  }

}
 
Nice, you found the added stats - and used more than the .mean() that I did!

Ick to turning off interrupts.

@tonton81 - can you show a way to use a CA with two CB elements that would allow the _isr code to write to CA[0] or CA[1] based on a single volatile global?

It was the CA extension of the CB's that really got me with how to index to them.

When this code starts it stops interrupts - then when there is data is gathers stats and does a .flush() leaving the CB empty to start fresh. So having two CB's in a CA could do the same without stopping the _isr() or anything else with noInterrupts();

It seems with : volatile bool bUpdateFlagsShared;

if could start like this without turning off interrupts:
Code:
volatile bool bUpdateFlagsShared;
volatile uint32_t IndexCA;

 if (bUpdateFlagsShared & DEGREE_FLAG) IndexCA ^= 1; // force the _isr code to buffer into the OTHER CA
{
   uint32_t tmp_IndexCA = IndexCA ^ 1;

   // Add CA version of the Stats msg code from post #107
   // use CA[ tmp_IndexCA ] to access the just filled and updated like the CB code above

   bUpdateFlagsShared = 0; // Maybe this should go first in this in this {} - but it seems to be in the wrong place in post #107 as the _isr may have set it when not seen in that post.
}
 
You got me curious, so here is what I understood, and tested.

Code:
  Circular_Buffer<Circular_Buffer<uint16_t, 2048>, 1, 2> hz;

  hz.size();

  hz.front()[0].push_back(0x06);
  hz.front()[0].push_back(0x07);
  hz.front()[0].push_back(0x08);

  hz.front()[1].push_back(0x02);
  hz.front()[1].push_back(0x03);
  hz.front()[1].push_back(0x04);
  hz.front()[1].push_back(0x05);

  Serial.println("\tIndex [0]");
  Serial.print("Size: "); Serial.println(hz.front()[0].size());
  Serial.println(hz.front()[0].read());
  Serial.println(hz.front()[0].read());
  Serial.println(hz.front()[0].read());

  Serial.println("\tIndex [1]");
  Serial.print("Size: "); Serial.println(hz.front()[1].size());
  Serial.println(hz.front()[1].read());
  Serial.println(hz.front()[1].read());
  Serial.println(hz.front()[1].read());
  Serial.println(hz.front()[1].read());
  while (1);

Output:
Code:
	Index [0]
Size: 3
6
7
8
	Index [1]
Size: 4
2
3
4
5

This created a CBA array of CB objects and accesses them by index value, not sure if thats what you wanted, but it's impressive it works :p

Tony
 
Awesome - I expected it to work but you never covered that in what I saw [and I never had to bother so didn't test the syntax] - apparently since you didn't expect it to work :)

Tony - does my post #108 idea look to be _isr safe? Either the _isr won't fire and it will work - or the _isr will fire safely during or after this test using the volatile flag and update the correct CBA location? :: if (bUpdateFlagsShared & DEGREE_FLAG) IndexCA ^= 1;

Except looking to cut and paste and update the code I see other vars [ IT & DT ] that the ISR may update on the fly that won't be SAFE unless they are also made two deep using the same IndexCA?

And your example doesn't show these lines in CBA form?
hzdeviation = hz.deviation();
hzmean = hz.mean();
hz.pop_front(HzArray, HzSamples);
hz.flush();//was getting repeat hz if I do not flush queue

This is why I didn't trust using CBA's as I didn't understand the absolute indexing method I expected to work :)
 
well the front() is a pointer to the index containing the object, im pretty sure if 2 isr's are writing to the same index might not be safe, unless one was writing to back and another reading from front of the CB object, you'd need to test and find out :p CBA isn't actually doing any push/pops, but only pointing to the memory space belonging to the CB

To me you can treat it as single CB objects, SPI_MST has consumer and producer as example, this *shouldn't* be any different, front() is just a pointer of the index to the object, like accessing the CB directly
for post #110, those will work as long as you do it this way:

Code:
hz.front()[0].deviation();
hz.front()[0].mean();
hz.front()[0].pop_front(HzArray, HzSamples);
hz.front()[0].flush();

where [0] is the index of the CB object.
 
As far as interrupt safety the _isr will only write to the one indexed to by IndexCA. Whenever it interrupts it will use the current volatile value. I was hoping that test in the loop() code would safely change it with any _isr either using the OLD or NEW value and adding one to the OLD just before it is cleared out - or update to NEW index and the ISR would do its work on the NEW while the OLD was being cleared out - with no need to disable interrupts.

Okay that is what I was writing with front(). - but didn't know what front(). meant … Does this look right?

Given the above snippet - similar adding of the "[ IndexCA ]" would be needed as well as the updated CB >> CBA and perhaps IT and DT updates as well as the volatile declarations.
Code:
Circular_Buffer<Circular_Buffer<uint16_t, 2048>, 1, 2> hz;
volatile bool bUpdateFlagsShared =0;
volatile uint32_t IndexCA =0;
uint32_t IT[2];
uint32_t DT[2];

void loop(void)
{
  // …

 if (bUpdateFlagsShared & DEGREE_FLAG) IndexCA ^= 1; // force the _isr code to buffer into the other NEW CBA
{
   bUpdateFlagsShared = 0; // I'm not sure how the _isr updates this? It may need to be [2] two deep too ???
   uint32_t tIC = IndexCA ^ 1;  // Temp Index to CA - the old filled one

[B]      CadenceTime = IT[ tIC ];
      DegreeTime = DT[ tIC ];
[/B]
      HzSamples = hz.front()[ tIC ].size();
      hzmin = hz.front()[ tIC ].min();
      hzmax = hz.front()[ tIC ].max();
      hzvariance = hz.front()[ tIC ].variance();
      hzdeviation = hz.front()[ tIC ].deviation();
      hzmean = hz.front()[ tIC ].mean();
      hz.front()[ tIC ].pop_front(HzArray, HzSamples);
      hz.front()[ tIC ].flush();
      msg.DTSum += DegreeTime;
      msg.HzSamplesSum += HzSamples;

}
 
yes that looks correct, tIC will be the index for front(), everything after "hz.front()[ tIC ]" is considered a single CB access ".mean();" :)

You can even think of "hz.front()[ tIC ]" as a "this" pointer, like you're actually calling this->mean();
 
Wow, you guys are taking this and running with it. Gives me ideas also. I know I only posted a snippet, but how my system works is I have a 36 or 72 tooth gear that an optical sensor reads, this is what I call DT (degree time), I also have an index sensor to reset when the cyclists are at the top of the pedal stroke, this is what I call IT or index time. This is actually what would be used to determine the cyclists cadence. The only issue I had when I first ran my system with the circular array was if I didn't flush the queue, I would get repeat values. I thought pop.front removed all the values, but for some reason I would get a repeat of Hz values.
Thanks again for all the help.
 
the flush only clears the CB, CBA wipes out the array index every push pop, for CBA you’re in charge of the entire array, for CB, repeat values i’d chech the ISR for queuing duplicates :)

also for CB you can use pop, read, pop_front, and pop_back as well
 
Tony - if moved to a CBA does that mean this post @112 won't clear the data? :: hz.front()[ tIC ].flush();

Maybe this was meant to do that? :: hz.front()[ tIC ].pop_front(HzArray, HzSamples);

I think the CB has real promise - I was just hoping to further my understanding by osmosis/example - rather than reading.
 
Good. Maybe I'll make an example with _isr and similar code.

When you called them Circ ARRAY - I expected a way to [index] them - not seeing that I decided to save some of my sanity :)
 
can only index first/last(fifo/lifo) of a CBA, and point to front() or back() when accessing the single array index, however for what we just did, its considered an array of circular buffers, which is pretty nice considering it was never tested before heh
 
I really want to try out some of these examples with my code, but just got to Australia for work and away from my device for 2 weeks. When I get back I will try it.
My only question is how to I fill the two different CB that are currently being filled using the freqmeasure libarary.
Code:
  if (FreqMeasure.available()) {//Jitter also seen using SRM torque box, so I accept
    HZ = FreqMeasure.countToFrequency(FreqMeasure.read());
    if (HZ > MINHZ && HZ < MAXHZ) {// HzSamplesShared < BUFFERSIZE &&
      hz.push_back(HZ);
    }
  }

Would I just do this hz.front()[index].push_back(HZ);, and then update the index value as soon as my sensor triggers the interrupt. It is all making sense now, but most of my learning is done by actually running examples and see what happens.
I am filling my array from the back, then I use the size to determine the size of my array and then use pop_front. But is that taking the data from the back or the front. I am still a bit confused on the pop.front, pop.back. What does hz.front indicate, fill the front CBA?
 
Tim was referring to indexing multiple circular buffers, don’t know if that is your intent or not but, if it is yes you need to use front()[index] where index is the circular buffer object your accessing. read/pop/pop_front dequeues an item from the front of the queue. the commands are the same and there for simplicity and help the users who are already familiar with circular buffer queue known methods. to read from the back of the queue you must use pop_back (last entry inserted).

front() is just a pointer to CBA’s array index, you see when i constructed 2048>1,2..., 1 is a power of 2 so its valid, and is considered a single array which is all we need. 2 depicts the array has 2 values, or in our case, it’s 2 circular buffer objects, containing 2048 entries max each. so every time you call front()[index], your pointing to the first(0) or second(1), depending on index value) object, for which your push/pops will be used upon. back() also exists, their main use was for the CBA system FIFO/LIFO ability, but using the trick we discovered recently, front() helped us uncover the new indexing feature already built in for an array of circular buffers. you most likely wont use back() or use more higher values than 1,2 for this type of setup though, their purpose is different than our intentions.

also if your sensor is changing values, changing the index mainly changes the circular buffer your writing to. if thats your intent then thats okay, if not, and your meant to only read/write from one queue, you should use CB instead of a CBA of CBs :)
 
what did you expect *cough*microsoft*cough*

maybe theyll integrate Bing into it one day...lol...

I searched for “ circular buffer teensy” and its the only one that showed up :)

i added arduino keyword and separated circular and buffer

now im in top 5 of github search for “circular buffer arduino”

also listed on page 6 of 50 of “circular buffer”
 
It was the "_" underscore in the name where I did the one word … I just thought it was funny there were 10 pages of ten plus one more with that name. Not sure how many might be forks/clones.

My second search I did tonton and it pointed to the CAN code and then I went to you and there it was.

Copied above posted code - and started a new sketch - wired pin 0 to 1 for Serial1 loop. Going to use the Rx _isr code I did on the other thread to trigger interrupts and track cycles counts or something and then swap the CBA used each second and see what falls out for a usable example.

I made vars to cover most of those in the posted example - except the msg.

What is HzArray param in this line to do?: hz.front()[ tIC ].pop_front(HzArray, HzSamples);
… okay I looked at the readme - the T * and assignment wasn't included in the snippet? :: T pop_front(T *buffer, uint16_t length)
 
thats doing a CBA of CBAs because that command is for CBA only :)
pop_front for CB has no length overload because it’s a single primitive
 
Last edited:
Back
Top