Buffer and Average
General
For Embedded system projects, one of the most used data structure is the ring Buffer (also called circular buffer). This Buffer is fixed in size and operate in a contiguous or circular manner. Adding (putting) or fetching (getting) data from the Buffer doesn’t reorder the data, it only manipulates the pointers and pointees of the “head” and “tail” of the data in the ring buffer — in fact it operates as a queue. When adding data to the ring buffer, the data is written to where the head pointer is pointing and the head pointer is incremented by one. When fetching data, the data is read from where the tail pointer is pointing and the tail pointer is advanced. If a pointer reaches the end of the buffer, the pointers simply wraps around to the beginning.
There are three different types of buffers available:
- RingBuffer
- LastInBuffer
- LastInDmaBuffer
Buffer implementations are thread save by using CriticalSection. For using a Buffer you need one object of CriticalSection in your project, this will be automaticly used by all buffer objects. In the following examples we use the STM32F4 HAL implementation.
Furthermore, in many applications it is necessary to determine the average value of the content of the buffer. The average of the data inside a buffer can easily be determined by Average.
RingBuffer Example
A RingBuffer synchronously handles the incoming and outgoing data. The incoming data can only be written to the RingBuffer, if the Buffer has a free spot in its queue. Freeing up a spot is done by reading the FiFo buffer with a call to get().
Create a RingBuffer object by first creating a data array of the desired size. Then create the RingBuffer object by passing the pointer to the data array and the size of the data array to the constructor.
#include "Core/Buffer/ringbuffer.h" #include "HardwareAbstraction/Stm32F4/stm32f4criticalsection.h" Stm32F4CritialSection critialSection; const size_t bufferArraySize = 8; uint8_t bufferArray[bufferArraySize] = { 0 }; semf::RingBuffer buffer(bufferArray, bufferArraySize);
Writing and reading data can be accomplished by the methods put() and get(). Every call to get() pops an element from the Buffer and makes the position in the buffer available for a future put().
// ... buffer.put(0x01); buffer.put(0x02); buffer.put(0x03); buffer.put(0x04); buffer.put(0x05); uint8_t temp = buffer.get(); // temp is 0x01
In the following picture you can see the RingBuffer object containing 4 valid elements (5 written elements minus 1 read element):
Be careful: If the Buffer is full of valid elements, the new data you put() will be ignored. Checking if the Buffer is full can be done by a call to full(). Similarly if the buffer is empty, a call to get() will return 0. Checking if the buffer is empty can be done by a call to empty().
Average
The Average class builds an average out of all valid elements (the ones that have not been read yet). The read position will not be changed.
#include "Components/Processing/average.h" // avg is (2+3+4+5) / 4 = 3 uint8_t avg = semf::Average::value(buffer);
LastInBuffer Example
An LastInBuffer is a Buffer for handling asynchronous data. By for example cyclicly sampling an AnalogIn you can asynchronously get the latest measurements available when you need it in the application. By putting new data, the oldest data will be overwritten. You have access to the latest number of values according to the size of the data array.
Create an LastInBuffer object by first creating a data array of the desired size. Then create the LastInBuffer object by passing the pointer to and the size of the data array to the constructor.
#include "Core/Buffer/lastinbuffer.h" #include "HardwareAbstraction/Stm32F4/stm32f4criticalsection.h" Stm32F4CritialSection critialSection; const size_t bufferArraySize = 8; uint8_t bufferArray[bufferArraySize] = { 0 }; semf::LastInBuffer buffer(bufferArray, bufferArraySize);
Writing is accomplished by calling put(). In order to get the newest entry, you can use a combination of at() and pos().
for (uint8_t i = 1; i <= 10; i++) { buffer.put(i); } uint8_t temp1 = buffer.at(0); // temp is 0x0A uint8_t temp2 = buffer.at(2); // temp is 0x08
In the following picture you can see our LastInBuffer:
Average
The Average class builds an average out of the whole data array.
#include "Components/Processing/average.h" // avg is (9+10+3+4+5+6+7+8) / 8 = 6 uint8_t avg = semf::Average::value(buffer);
LastInDmaBuffer Example
A LastInDmaBuffer is a Buffer for handling asynchron data from multiple inputs and outputs in one data array. By using an AnalogInDma with multiple inputs sampling cyclic you can asynchronously get the latest measurements, when you need it in the application. Create a LastInDmaBuffer object by first creating a data array of the desired size. Then create the LastInDmaBuffer object by passing the pointer to and the size of the data array to the constructor.
#include "Core/Buffer/lastindmabuffer.h" #include "HardwareAbstraction/Stm32F4/stm32f4criticalsection.h" Stm32F4CritialSection critialSection; const size_t bufferArraySize = 9; uint8_t bufferArray[bufferArraySize] = { 0 }; semf::LastInDmaBuffer bufferSens1(bufferArray, bufferArraySize); semf::LastInDmaBuffer bufferSens2(bufferArray, bufferArraySize); semf::LastInDmaBuffer bufferSens3(bufferArray, bufferArraySize); // Setting for three LastInDmaBuffer in one data array bufferSens1.setStride(3); bufferSens2.setStride(3); bufferSens3.setStride(3); // Setting bufferSens1 first element to array index 0, bufferSens1.setPos(0); // bufferSens2 first element to array index 1 and bufferSens2.setPos(1); // bufferSens3 first element to array index 2 bufferSens3.setPos(2);
Writing into the LastInDmaBuffer occurs as with usual Buffers.
For getting the newest entry, you can use a combination of at() and pos().
bufferSens1.put(0x01); // Data input 1 bufferSens1.put(0x02); bufferSens1.put(0x03); bufferSens2.put(0x11); // Data input 2 bufferSens2.put(0x12); bufferSens2.put(0x13); bufferSens3.put(0x21); // Data input 3 bufferSens3.put(0x22); bufferSens3.put(0x23); // Read data uint8_t tempSens1 = bufferSens1.at(0); // temp is 0x03 uint8_t tempSens2 = bufferSens2.at(1); // temp is 0x12
In the following picture you can see how the LastInDmaBuffer handles the incoming and outgoing data:
Average
The Average class determines the average value from the part of the array associated with the LastInDmaBuffer.
#include "Components/Processing/average.h" // avg is (0x21+0x22) / 2 = 33 uint8_t avg = semf::Average::value(bufferSens3);