реклама на сайте
подробности

 
 
> Проблема с Ring Buffer.
Jenya7
сообщение Aug 31 2016, 08:46
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Я сделал такую реализацию.
CODE
typedef enum CycBuffState {cbsOk = 0, cbsOverwrite = 1, cbsBuffFull = 2, cbsBuffEmpty = 3, cbsError = 4} enCycBuffState;

typedef struct
{
uint32_t MaxBufSize,
HeadIdx,
TailIdx;
enCycBuffState State;
uint8_t *Buffer;
} stRingBuffer;

void RingBuf_Init (stRingBuffer *ring_buf, uint8_t *buf, uint32_t max_size)
{
ring_buf->Buffer = buf;
ring_buf->MaxBufSize = max_size;
ring_buf->HeadIdx = ring_buf->TailIdx = 0;
ring_buf->State = cbsBuffEmpty;
}

enCycBuffState RingBuf_PushByte(stRingBuffer *ring_buf, uint8_t val, uint8_t allow_overwrite)
{
if ((ring_buf->HeadIdx+1) == ring_buf->TailIdx)
{
if (!allow_overwrite)
return cbsBuffFull;
}

ring_buf->Buffer[ring_buf->HeadIdx] = val;
ring_buf->HeadIdx++;

if (ring_buf->HeadIdx >= ring_buf->MaxBufSize)
ring_buf->HeadIdx = 0;

return cbsOk;
}

enCycBuffState RingBuf_PushBytes(stRingBuffer *ring_buf, uint8_t len, uint8_t *buf, uint8_t allow_overwrite)
{
uint32_t free_space;

if (ring_buf->HeadIdx == ring_buf->TailIdx)
{
free_space = ring_buf->MaxBufSize;
}
else
{
if (ring_buf->HeadIdx >= ring_buf->TailIdx)
free_space = ring_buf->HeadIdx - ring_buf->TailIdx;
else
free_space = (ring_buf->MaxBufSize - ring_buf->TailIdx) + ring_buf->HeadIdx;

if (free_space < len)
return cbsBuffFull;
}
while (len)
{
RingBuf_PushByte(ring_buf, *buf, allow_overwrite);
buf++;
len--;
}

return cbsOk;
}

enCycBuffState RingBuf_PopByte(stRingBuffer *ring_buf, uint8_t *val)
{
if (ring_buf->HeadIdx == ring_buf->TailIdx)
return cbsError;

*val = ring_buf->Buffer[ring_buf->TailIdx];
ring_buf->Buffer[ring_buf->TailIdx] = 0;
ring_buf->TailIdx++;

if (ring_buf->TailIdx >= ring_buf->MaxBufSize)
ring_buf->TailIdx = 0;

return cbsOk;
}

enCycBuffState RingBuf_PopBytes(stRingBuffer *ring_buf, uint8_t len, uint8_t *val)
{
while (len)
{
RingBuf_PopByte(ring_buf, val);
val++;
len--;
}

return cbsOk;
}


И потом проверяю (в IAR, в симуляторе)
Код
#include <string.h>
#include "ringbuf.h"

stRingBuffer ring_buf;

uint8_t *temp_buff;
uint8_t *test_buff = "Hello World";
uint8_t *read_buff;

int main()
{
  RingBuf_Init (&ring_buf, temp_buff, 32);
  while (1)
  {
     RingBuf_PushBytes(&ring_buf, strlen(test_buff), test_buff, 0);
    
     RingBuf_PopBytes(&ring_buf, strlen(test_buff), read_buff);
  }
}

Но несмотря на то что я отслеживаю наличие свободного места.
Код
uint32_t free_space;
  
  if (ring_buf->HeadIdx == ring_buf->TailIdx)
  {
    free_space = ring_buf->MaxBufSize;
  }
  else
  {
    if (ring_buf->HeadIdx >= ring_buf->TailIdx)
       free_space = ring_buf->HeadIdx - ring_buf->TailIdx;
     else
       free_space = (ring_buf->MaxBufSize - ring_buf->TailIdx) + ring_buf->HeadIdx;
  
    if (free_space < len)
      return cbsBuffFull;

В отладчике я вижу перезапись буфера. Что я упустил?

Сообщение отредактировал Jenya7 - Aug 31 2016, 08:51
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
brag
сообщение Sep 7 2016, 08:48
Сообщение #2


Профессионал
*****

Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046



Хм. Зачем все так сложно мудрить, если есть уже давно простейшая рабочая реализация прописанная в букварях по программированию, остается ее только портировать на свой язык.
При чем тут еще от задачи зависит. Если это SPSC, тогда вообще все просто и без блокировок. Если MPSC/MPMC/SPMC - нужны уже блокировки.
Приведу простейший пример SPSC, написан был мной лет 8 назад(то есть просто реализован на ц++ алгоритм из букваря) и очень часто используется по сей день везде(драйверы, очередя сообщений, итд итп)
CODE

template<class T, int Size> class Fifo_spsc{
public:
static_assert((Size&(Size-1)) == 0, "Size must be power of 2");
Fifo_spsc(){
rx = 0;
wx = 0;
}

// Producer
bool push(const T &v){
uint32_t w = (wx+1)&(Size-1);
if(w==rx)return false; //ERR_FIFO_FULL;
data[wx] = v;
__DMB(); // just a memory barrier - ensure data has been completely stored
wx = w; // make item available for reading
return true;
}

// Consumer
bool pop(T *v){
uint32_t r = rx;
if(r==wx)return false; //ERR_FIFO_EMPTY;
*v = data[r];
__DMB(); // ensure data has been completely loaded
rx = (r+1)&(Size-1); // make item available for writing
return true;
}

// Consumer
void flush(){
__DMB();
rx = wx;
}

protected:
uint16_t rx, wx;
T data[Size];
};

Памяти занимает - собственно сами данные + 2 16-битных слова.
Хоть и c++, что позволяет использовать этот буфер для любых типов - как простого uint_8t, так и для сложных обьектов. Это базовая реализация для практически всех других более сложных очередей.
Один элемент всегда пустой - это делает код гораздо проще и быстрее, и как ни странно зачастую - меньше требовательным к памяти sm.gif

Также, если у нас строго один писатель и один читатель, то можно сделать и чтение не по одному элементу, а сразу массивом.
CODE

// Consumer, use with caution
const T* getConsecutiveData_ptr()const{
if(rx==wx)return 0; // empty
return &data[rx];
}

int getConsecutiveData_len()const{
if(wx>rx)return wx-rx;
else return Size-rx;
}

void popConsecutiveData(unsigned n){
__DMB();
rx = (rx+n)&(Size-1);
}

Важно понимать, что связь между producer и consumer должна быть только через функции этого буфера, без каких либо других переменных, вызовов функций итд в обход этого FIFO. Иначе однозначно рано или поздно схватите гонку.
То есть он подходит, например когда producer - это прерывание, а consumer - вечный цикл. Что для большинства задач достаточно.

Если у нас еvent-driven-движок (то есть без вечных циклов, все на событиях) - тогда нужна более сложная реализация. Но использовать нужно опять же только ее, без всяких сторонних связей.
Если интересно - расскажу и покажу как, я на этих циклических буферах(в том числе с обьектами не фиксированного размера) уже собаку сьел за много лет, и в своих программах использую только их - как самый простой, эффективный и безопасный интерфейс для связи между обьектами.

Но это все C++, зато позволяет эффективно и безопасно работать и писать очень простой код.
Например. Прерывание uart
Код
void U0RxCbk::readyRead(LPC_UART_TypeDef *uart){ // interrupt
    while(Uart::isCharAvailable(uart)){
        bool r = rxfifo.push(Uart::read_char(uart));
        if(!r){
            printf("U0RxCbk::readyRead(): rxfifo full!\n");
        }
    }
}

Приемник - просто функция, которая будет вызвана, когда на это появится время(в порядке приоритета и очереди).
Которая в свою очередь принимает текст NMEA и дальше парсит его.
Код
void Gps::start_receiver(){
    // Устанавливаем обработчик события
    rxfifo.set_onReadyRead([this](){ // собственно сам обработчик
        uint8_t ch;
        while(true){
            bool r = rxfifo.pop(&ch); // читаем из очереди
            parse(ch); // парсим
            if(!r)break; // если очередь стала пустой - выходим
        }
    }, GPS_TASK_PRIORITY); // приоритет обработчика
}


Это более старый вариант, привел, чтобы понять как оно работает, скажем так, внутри.
Сейчас я пользуюсь в основном таким:
Код
class GpsReceiver : public UmFifoSingleReceiver<uint8_t, GpsReceiver, GPS_TASK_PRIORITY>{
protected:
    void onItemReceived(uint8_t ch){
        parse(ch);
    }
};

То есть вся логика чтения из буфера реализована один раз отдельно и используется повторно сколько угодно раз.
Так же есть аналогичные реализации, когда приемник может обрабатывать не один элемент - а массив из нескольких(например отправка через DMA), физически - подряд лежащих в нашем ring-buffere.
Overhed-a нет, наоборот такая реализация на шаблонах работает быстрее, чем обычные сишные sm.gif Большинство работы делает компилятор, там даже вызова функции не будет - компилятор функцию parse встроит в недра самой очереди.
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Sep 11 2016, 17:58
Сообщение #3


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(brag @ Sep 7 2016, 13:48) *
Хм. Зачем все так сложно мудрить.....

лично для меня мудрить это
Код
template<class T, int Size> class Fifo_spsc

sm.gif
мне бы по простому. по сишному. sm.gif
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- Jenya7   Проблема с Ring Buffer.   Aug 31 2016, 08:46
- - smalcom   Много чего, там и переполнение и... проще показать...   Aug 31 2016, 09:17
|- - Jenya7   Цитата(smalcom @ Aug 31 2016, 15:17) Мног...   Aug 31 2016, 10:23
|- - smalcom   Цитата(Jenya7 @ Aug 31 2016, 13:23) Но вы...   Aug 31 2016, 14:14
|- - Jenya7   Цитата(smalcom @ Aug 31 2016, 20:14) заме...   Aug 31 2016, 14:28
- - XVR   Кодuint8_t *temp_buff; uint8_t *read_buff;Ну и куд...   Aug 31 2016, 12:41
|- - Jenya7   Цитата(XVR @ Aug 31 2016, 18:41) Кодuint8...   Aug 31 2016, 14:13
- - DASM   Чет мудреные какие-то реализации. Так не проще? CO...   Aug 31 2016, 14:53
|- - Jenya7   Цитата(DASM @ Aug 31 2016, 20:53) Чет муд...   Aug 31 2016, 15:05
|- - DASM   Цитата(Jenya7 @ Aug 31 2016, 18:05) Нет п...   Aug 31 2016, 15:09
- - smalcom   Кодbuf[(head + sz_contains++) % SZ_MAX...   Aug 31 2016, 18:03
|- - DASM   Цитата(smalcom @ Aug 31 2016, 21:03) Кодb...   Aug 31 2016, 18:12
- - smalcom   ЦитатаПосле 2^32 push? я так на волне паранойи и п...   Aug 31 2016, 19:24
- - smalcom   самое первое (а там и дальше так) Цитатаuint32_t w...   Sep 7 2016, 17:47
|- - brag   Цитата(smalcom @ Sep 7 2016, 20:47) это н...   Sep 7 2016, 17:51
- - smalcom   о, пардон. странно как-то - следующей строки с пр...   Sep 8 2016, 01:43
|- - brag   Цитата(smalcom @ Sep 8 2016, 04:43) о, па...   Sep 8 2016, 07:48
- - brag   Цитата(Jenya7 @ Sep 11 2016, 20:58) мне б...   Sep 11 2016, 22:06


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 17:59
Рейтинг@Mail.ru


Страница сгенерированна за 0.01394 секунд с 7
ELECTRONIX ©2004-2016