czwartek, 1 października 2020

C - Bufor cyklicznego

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:

  1. typedef struct TimeAndDate_s {
  2.     uint8_t Year;
  3.     uint8_t Month;
  4.     uint8_t DOM;
  5.     uint8_t Hour;
  6.     uint8_t Min;
  7.     uint8_t Sec;
  8. }TimeAndDate_t;
  9.  
  10. typedef struct dataToBuffer_s {
  11.     uint32_t Card1;
  12.     uint32_t Card2;
  13.     TimeAndDate_t TimeDateVal;
  14.     uint8_t U;
  15.     uint8_t U1;
  16.     uint8_t AdditionalData[8];
  17. }dataToBuffer_t;

  1. typedef struct {
  2.     dataToBuffer_t * const buffer;
  3.     uint16_t head;
  4.     uint16_t tail;
  5.     const uint16_t maxlen;
  6.     bool isFull;
  7. } circularBuffer_t;

Inicjalizację danych wykonuję jako zmienne globalne:

  1. dataToBuffer_t registerArray[300];
  2.  
  3. circ_bbuf_t circBuff =
  4. {
  5.     .buffer = &registerArray[0],
  6.         .head = 0,
  7.         .tail = 0,
  8.         .maxlen = 300,
  9.     .isFull = false
  10. };

Reset danych:

  1. void circularBuffer_Reset(circ_bbuf_t *c)
  2. {
  3.     c->head = 0;
  4.     c->tail = 0;
  5.     c->isFull = false;
  6. }

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:

  1. bool circularBuffer_GetFullFlag(circ_bbuf_t c)
  2. {
  3.     return c.isFull;
  4. }
  5.  
  6. bool circularBuffer_IsEmpty(circ_bbuf_t c)
  7. {
  8.     return (!c.isFull && (c.head == c.tail));
  9. }
  10.  

Sprawdzenie ile danych zostało umieszczonych w buforze:

  1. uint16_t circularBuffer_NumberOfElementsInBuffer(circ_bbuf_t c)
  2. {
  3.     uint16_t numberOfElements = c.maxlen;
  4.  
  5.     if(!c.isFull)
  6.     {
  7.         if(c.head >= c.tail)
  8.         {
  9.             numberOfElements = (c.head - c.tail);
  10.         }
  11.         else
  12.         {
  13.             numberOfElements = (c.maxlen + c.head - c.tail);
  14.         }
  15.     }
  16.  
  17.     return numberOfElements;
  18. }

Pobranie wielkości bufora:

  1. bool circularBuffer_GetCapacity(circ_bbuf_t c)
  2. {
  3.     return c.maxlen;
  4. }

Funkcja dodająca wartość do bufora:

  1. int8_t circularBuffer_PushData(circ_bbuf_t *c, dataToBuffer_t *structDataToPut)
  2. {
  3.     int next;
  4.  
  5.     next = c->head + 1;
  6.  
  7.     if (next >= c->maxlen) { next = 0; }
  8.  
  9.     if (next == c->tail) {
  10.         c->isFull = true;
  11.         return -1;
  12.     }
  13.  
  14.     //Wykorzystanie funkcji memcpy i skopiowanie danych

  15.     memcpy(&(c->buffer[c->head]), structDataToPut, sizeof(dataToBuffer_t));

  16.     /* alternatywnie można je poprostu przepisać
  17.     c->buffer[c->head].Card1 = structDataToPut->Card1;
  18. c->buffer[c->head].Card1 = structDataToPut->Card2;
  19.     c->buffer[c->head].TimeDateVal.Year = structDataToPut->TimeDateVal.Year;
  20.     c->buffer[c->head].TimeDateVal.Month= structDataToPut->TimeDateVal.Month;
  21.     c->buffer[c->head].TimeDateVal.DOM = structDataToPut->TimeDateVal.DOM;
  22.     c->buffer[c->head].TimeDateVal.Hour = structDataToPut->TimeDateVal.Hour;
  23.     c->buffer[c->head].TimeDateVal.Min = structDataToPut->TimeDateVal.Min;
  24.     c->buffer[c->head].TimeDateVal.Sec = structDataToPut->TimeDateVal.Sec;
  25.     c->buffer[c->head].U = structDataToPut->U;
  26.     */
  27.  
  28.     c->head = next;
  29.     return 0;   /* SUCCESS */
  30. }

Funkcja zabierająca wartość z bufora:

  1. int8_t circularBuffer_PopData(circ_bbuf_t *c, dataToBuffer_t *structDataToGet)
  2. {
  3.     int next;
  4.  
  5.     if (c->head == c->tail) { return -1; }
  6.  
  7.     next = c->tail + 1;
  8.  
  9.     if(next >= c->maxlen) { next = 0; }
  10.  
  11.     if(c->isFull == true) { c->isFull = false; }
  12.  
  13.     memcpy(structDataToGet, &(c->buffer[c->tail]), sizeof(dataToBuffer_t));
  14.  
  15.     c->tail = next;
  16.  
  17.     return 0;  /* SUCCESS */
  18. }