W tym poście chciałem opisać sposób wykonania bufora kołowego.
[Źródło: https://en.wikipedia.org/wiki/Circular_buffer]
Działanie:
Bufor działa na zasadzie utworzenia stałej tablicy danych pojedynczych bądź całych struktur. Następnie tworzone są odpowiednie wskaźniki informujące, który element tablicy jest aktualnie zapisywany oraz drugi wskaźnik z położeniem danych do odczytu. Po zapisaniu całego bufora licznik przechodzi na początek po czym następuje nadpisanie wcześniejszych danych.
Projekt:
Struktura przechowująca informacje o buforze:
- typedef struct TimeAndDate_s {
- uint8_t Year;
- uint8_t Month;
- uint8_t DOM;
- uint8_t Hour;
- uint8_t Min;
- uint8_t Sec;
- }TimeAndDate_t;
- typedef struct dataToBuffer_s {
- uint32_t Card1;
- uint32_t Card2;
- TimeAndDate_t TimeDateVal;
- uint8_t U;
- uint8_t U1;
- uint8_t AdditionalData[8];
- }dataToBuffer_t;
- typedef struct {
- dataToBuffer_t * const buffer;
- uint16_t head;
- uint16_t tail;
- const uint16_t maxlen;
- bool isFull;
- } circularBuffer_t;
Inicjalizację danych wykonuję jako zmienne globalne:
- dataToBuffer_t registerArray[300];
- circ_bbuf_t circBuff =
- {
- .buffer = ®isterArray[0],
- .head = 0,
- .tail = 0,
- .maxlen = 300,
- .isFull = false
- };
Reset danych:
- void circularBuffer_Reset(circ_bbuf_t *c)
- {
- c->head = 0;
- c->tail = 0;
- c->isFull = false;
- }
Tutaj nie resetuje samej tablicy. Ponieważ dane i tak będą odczytywane tylko z podanych lokalizacji.
Wykonanie sprawdzania czy bufor jest pełny, lub czy jest pusty:
- bool circularBuffer_GetFullFlag(circ_bbuf_t c)
- {
- return c.isFull;
- }
- bool circularBuffer_IsEmpty(circ_bbuf_t c)
- {
- return (!c.isFull && (c.head == c.tail));
- }
Sprawdzenie ile danych zostało umieszczonych w buforze:
- uint16_t circularBuffer_NumberOfElementsInBuffer(circ_bbuf_t c)
- {
- uint16_t numberOfElements = c.maxlen;
- if(!c.isFull)
- {
- if(c.head >= c.tail)
- {
- numberOfElements = (c.head - c.tail);
- }
- else
- {
- numberOfElements = (c.maxlen + c.head - c.tail);
- }
- }
- return numberOfElements;
- }
Pobranie wielkości bufora:
- bool circularBuffer_GetCapacity(circ_bbuf_t c)
- {
- return c.maxlen;
- }
Funkcja dodająca wartość do bufora:
- int8_t circularBuffer_PushData(circ_bbuf_t *c, dataToBuffer_t *structDataToPut)
- {
- int next;
- next = c->head + 1;
- if (next >= c->maxlen) { next = 0; }
- if (next == c->tail) {
- c->isFull = true;
- return -1;
- }
- //Wykorzystanie funkcji memcpy i skopiowanie danych
- memcpy(&(c->buffer[c->head]), structDataToPut, sizeof(dataToBuffer_t));
- /* alternatywnie można je poprostu przepisać
- c->buffer[c->head].Card1 = structDataToPut->Card1;
- c->buffer[c->head].Card1 = structDataToPut->Card2;
- c->buffer[c->head].TimeDateVal.Year = structDataToPut->TimeDateVal.Year;
- c->buffer[c->head].TimeDateVal.Month= structDataToPut->TimeDateVal.Month;
- c->buffer[c->head].TimeDateVal.DOM = structDataToPut->TimeDateVal.DOM;
- c->buffer[c->head].TimeDateVal.Hour = structDataToPut->TimeDateVal.Hour;
- c->buffer[c->head].TimeDateVal.Min = structDataToPut->TimeDateVal.Min;
- c->buffer[c->head].TimeDateVal.Sec = structDataToPut->TimeDateVal.Sec;
- c->buffer[c->head].U = structDataToPut->U;
- */
- c->head = next;
- return 0; /* SUCCESS */
- }
Funkcja zabierająca wartość z bufora:
- int8_t circularBuffer_PopData(circ_bbuf_t *c, dataToBuffer_t *structDataToGet)
- {
- int next;
- if (c->head == c->tail) { return -1; }
- next = c->tail + 1;
- if(next >= c->maxlen) { next = 0; }
- if(c->isFull == true) { c->isFull = false; }
- memcpy(structDataToGet, &(c->buffer[c->tail]), sizeof(dataToBuffer_t));
- c->tail = next;
- return 0; /* SUCCESS */
- }