Skip to Content

RingBuffer для embedded

Долго терпел но всё-таки порвало и решил набросать маленький template для кольцевого буфера чуток оптимизированный для экономии ОЗУ. Вот его сырая версия. Если будет настроение то доделаю.

Небольшое замечание по работе с ним, если в конструктор класса ring_buff переадать указатель на область памяти и число обектов которое хотим создать в ней, то конструктор создаст то кол-во обеектов которое может в этой области уместиться и не больше, при этом деструктор не будет освобождать эту область, и этот ритуал :) обязан выполнить программист во избежании утечки памяти.

А вот и долгожданный template в прикреплённом файле :)

ПредпросмотрВложениеРазмер
ring_buff.txt3.52 КБ

Комментарии

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

говнокод!

До осваивания C++ стоило бы научиться отделять мух от котлет (регистры AT91SAM7xxx от шаблона).

А собственно никакой реализации fifo и не увидел, вчитавшись. Доставляет также блокирующий Write. Из неблокирующего сделать блокирующий простым применением while() всегда можно и это стоит оставить для самого верхнего уровня.

Собственно это был комментарий к тому, что внизу страницы. Если говорить и том, что выше -- без 0.5 не разобраться.

Имею аналогичный код, тоже в виде "шаблона" (что разменивает больший объём программной памяти на более высокую скорость, если уартов больше одного, а иначе просто быстрей и компактней) на голом C -- весь код рассован по-макросам. Прошло много времени, много разных MCU и проектов. И пришла в голову очевидная мысль, что вообще реализацию fifo следует отделить от "драйвера UART-модуля". Ибо последние очень уж разные. Что для 8-битников врукопашную, то для больших MCU очень сильно не так. А при наличии ОС и вовсе fifo не особо нужно (ибо своё наличествует, в драйвере).

Считаю, в проекте без ОС нужны 3 вещи: "драйвер" непосредстенно к модулю UART микроконтроллера, побайтовое быстрое fifo, и прослойка между этим всем и программой. Последние 2 вещи аппаратно-независимые вообще. Прослойка для программной реализации CTS/RTS (если в контроллере нет) и т.п. вещей, а также для приведения интерфейса близкого к виндовому/линуховому.

PS: да, блокировка по IP тоже доставляет.

[ZX]

>> До осваивания C++ стоило

>> До осваивания C++ стоило бы научиться отделять мух от котлет (регистры AT91SAM7xxx от шаблона).

Это шаблон драйвера UART SAM9XE это раз по этому мухи и котлеты на своих местах,

 

>> А собственно никакой реализации fifo и не увидел

Ее там и нету, это моя ошибка, хотел дать ответ на

 >> C++ with templates иEmbedded?

>> Какое-то странное сочетание...

И код тупо демонстрирует что используются шаблоны и в эмбеддед.

>>И пришла в голову очевидная мысль, что вообще реализацию fifo следует отделить от "драйвера UART-модуля"

В моем модуле так и сделано только в качестве буфера выступает PDC вот реализация кто знаком с архитектурой AT91SAM9XE тот поймет, этот код можно прилепить к любой периферии проца которая имеет PDC. Что и сделано в моем коде:

 typedef THwTXPDC<PORT,TX_BUFF_SIZE> TxFIFO; //FIFO передачи
 typedef THwRXPDC<PORT,RX_BUFF_SIZE> RxFIFO; //FIFO приема

Если сильно интересно прилагаю шаблон класса реализующего обмен через PDC с периферией. В текущей версии реализация только для UART

#ifndef __HwPDC_H__
#define __HwPDC_H__

#include <cstdint>
#include <cstring>
#include "AT91SAM9XE512.h"

template <int ID,int TSIZE>
class THwTXPDC
{
  protected:
    static uint8_t* tx_data_ptr;
    static uint8_t  tx_buff[TSIZE] @ ".not_cached";
    /******************************************************************************
    * FUNCTION:     PDC_BASE
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS:      
    *
    ******************************************************************************/
    inline static volatile AT91S_PDC *PDC_BASE()
    {
      switch (ID)
      {
        case AT91C_ID_US0: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US1: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US2: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US3: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US4: return AT91C_BASE_PDC_US0;
        case AT91C_ID_SYS: return AT91C_BASE_PDC_DBGU;
      
		        //todo: дописать идентификаторы остальной периферии
	
        default:           return NULL;
      }
    };
  public:
    enum
    {
      SIZE  = TSIZE
    };
    /******************************************************************************
    * FUNCTION:     TXBUFE_Handler
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS: true - если надо отключить прерывания      
    *
    ******************************************************************************/
    inline static uint32_t TXBUFE_Handler(void)
    {
      uint32_t size;
      uint8_t *pdc_addr = reinterpret_cast<uint8_t*>(PDC_BASE()->PDC_TPR); //Warning[Pe1053]: conversion from integer to smaller pointer
      PDC_BASE()->PDC_TPR  = reinterpret_cast<uint32_t>(pdc_addr); 
      if (tx_data_ptr >= pdc_addr)
      {
        size = (tx_data_ptr - pdc_addr);
      }
      else
      {
        size = SIZE - (pdc_addr - tx_data_ptr);
      }
      if ( size )
      {
        if (( pdc_addr + size) <= &tx_buff[SIZE])
        {
          PDC_BASE()->PDC_TCR = size;
        }
        else
        {
          PDC_BASE()->PDC_TCR  = reinterpret_cast<uint32_t>(&tx_buff[SIZE]) -  reinterpret_cast<uint32_t>(pdc_addr);
          PDC_BASE()->PDC_TNPR = reinterpret_cast<uint32_t>(tx_buff);
          PDC_BASE()->PDC_TNCR = size - (reinterpret_cast<uint32_t>(&tx_buff[SIZE]) - reinterpret_cast<uint32_t>(pdc_addr));
        }
      }
      return size;
    };
    /******************************************************************************
    * FUNCTION:     Enable
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS:      
    *
    ******************************************************************************/    
    static void Init(void)
    {
      PDC_BASE()->PDC_PTCR = AT91C_PDC_TXTDIS;
      tx_data_ptr          = tx_buff;
      PDC_BASE()->PDC_TPR  = reinterpret_cast<uint32_t>(tx_buff);
      PDC_BASE()->PDC_TCR  = 0;
      PDC_BASE()->PDC_TNCR = 0;
      PDC_BASE()->PDC_PTCR = AT91C_PDC_TXTEN;
    }
    /******************************************************************************
    * FUNCTION:     Disable
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS:      
    *
    ******************************************************************************/
    static void DeInit(void)
    {
      while((PDC_BASE()->PDC_TCR !=0) || (PDC_BASE()->PDC_TNCR !=0));
      PDC_BASE()->PDC_PTCR = AT91C_PDC_TXTDIS;
    }
    /******************************************************************************
    * FUNCTION:     Free
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS:      
    *
    ******************************************************************************/
    static uint_fast16_t Free(void)
    {
      uint8_t* pdc_addr = reinterpret_cast<uint8_t*>(PDC_BASE()->PDC_TPR);
      if ( tx_data_ptr >= pdc_addr )
      {
        return (SIZE-1) - (tx_data_ptr - pdc_addr);
      }
      else
      {
        return (pdc_addr - tx_data_ptr)-1;
      }
    }
    /******************************************************************************
    * FUNCTION:     Write
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS:      
    *
    ******************************************************************************/
    static uint32_t Write(const uint8_t* data, uint_fast16_t size)
    {
      uint32_t ssize,txfree;
      uint8_t const *ptr = data;
      txfree = Free();
      ssize = (txfree >= size)?size:txfree;
      if((tx_data_ptr + ssize) < &tx_buff[SIZE])
      {
        memcpy(tx_data_ptr,ptr,ssize);
        tx_data_ptr += ssize;
      }
      else
      {
        uint32_t L_size = &tx_buff[SIZE] - tx_data_ptr;  
        memcpy(tx_data_ptr,ptr,L_size);
        uint32_t Rrsize = ssize - L_size;   
        memcpy(tx_buff,(ptr+L_size),Rrsize); 
        tx_data_ptr = tx_buff + Rrsize;
      }
      return ssize;
    };
};

template <int ID, int TSIZE> 
uint8_t* THwTXPDC<ID,TSIZE>::tx_data_ptr;

template <int ID, int TSIZE> 
uint8_t THwTXPDC<ID,TSIZE>::tx_buff[TSIZE];


template <int ID,int TSIZE>
class THwRXPDC
{
protected:
    static uint8_t* rx_data_ptr;
    static uint8_t  rx_buff[TSIZE] @ ".not_cached";
    /******************************************************************************
    * FUNCTION:     PDC_BASE
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS:      
    *
    ******************************************************************************/
    inline static volatile AT91S_PDC *PDC_BASE()
    {
      switch (ID)
      {
        case AT91C_ID_US0: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US1: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US2: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US3: return AT91C_BASE_PDC_US0;
        case AT91C_ID_US4: return AT91C_BASE_PDC_US0;
        case AT91C_ID_SYS: return AT91C_BASE_PDC_DBGU;
        default:           return NULL;
      }
    };
  public:
    enum
    {
      SIZE  = TSIZE
    };
    /******************************************************************************
    * FUNCTION:     PDC_BASE
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS:      
    *
    ******************************************************************************/
    static void Init(void)
    {
      PDC_BASE()->PDC_PTCR = AT91C_PDC_RXTDIS;
      rx_data_ptr = rx_buff;
      PDC_BASE()->PDC_RPR   = reinterpret_cast<uint32_t>(rx_buff);
      PDC_BASE()->PDC_RCR   = SIZE-1;
      PDC_BASE()->PDC_RNCR  = 0;
      PDC_BASE()->PDC_PTCR = AT91C_PDC_RXTEN;
    };
    /******************************************************************************
    * FUNCTION:     TXBUFE_Handler
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS: true - если надо отключить прерывания      
    *
    ******************************************************************************/
    inline static uint32_t RXBUFF_Handler(void)
    {
      uint32_t size;
      uint8_t *pdc_addr = reinterpret_cast<uint8_t*>(PDC_BASE()->PDC_RPR);
      if ( pdc_addr ==  &rx_buff[SIZE])
      {
        pdc_addr = rx_buff;
      };
      PDC_BASE()->PDC_RPR  = reinterpret_cast<uint32_t>(pdc_addr);    
      if ( rx_data_ptr > pdc_addr)
      {
        size = (rx_data_ptr - pdc_addr) - 1;
      }
      else
      {
        size = ((SIZE + rx_data_ptr) - pdc_addr) - 1;       
      }
      if( size )
      {
        if (( size + pdc_addr ) <= &rx_buff[SIZE] )
        {
          PDC_BASE()->PDC_RCR  = size;
        }
        else
        {
          PDC_BASE()->PDC_RCR  = &rx_buff[SIZE] - pdc_addr;
          PDC_BASE()->PDC_RNPR = reinterpret_cast<uint32_t>(rx_buff);
          PDC_BASE()->PDC_RNCR = size - (&rx_buff[SIZE] - pdc_addr);
        }
      }
      return size;
    }
    /******************************************************************************
    * FUNCTION:     TXBUFE_Handler
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS: true - если надо отключить прерывания      
    *
    ******************************************************************************/
    static uint_fast16_t Recived(void)
    {
      uint32_t size;
      uint8_t *pdc_addr = reinterpret_cast<uint8_t*>(PDC_BASE()->PDC_RPR);
      size = (pdc_addr >= rx_data_ptr)?pdc_addr - rx_data_ptr:(SIZE + (pdc_addr - rx_data_ptr));
      return size;
    }
    /******************************************************************************
    * FUNCTION:     TXBUFE_Handler
    *
    * DESCRIPTION:  
    * PARAMETERS:   
    * RETURNS: true - если надо отключить прерывания      
    *
    ******************************************************************************/
    static uint_fast16_t Read(void *data, uint_fast16_t buff_size)
    {
      uint32_t rsize = Recived();
      uint8_t *ptr = static_cast<uint8_t*>(data); 
      if( rsize > buff_size ) rsize = buff_size;    
      if (( rx_data_ptr + rsize) < &rx_buff[SIZE])
      {
        memcpy(ptr,rx_data_ptr,rsize);
        rx_data_ptr += rsize;
      }
      else
      {
        uint32_t Lrsize = &rx_buff[SIZE] - rx_data_ptr;
        memcpy(ptr,rx_data_ptr,Lrsize);
        uint32_t Rrsize = rsize - Lrsize;
        memcpy(ptr + Lrsize,rx_buff,Rrsize);
        rx_data_ptr = rx_buff + Rrsize;
      }
      return rsize;
    };
};

template <int ID, int TSIZE> 
uint8_t* THwRXPDC<ID,TSIZE>::rx_data_ptr;

template <int ID, int TSIZE> 
uint8_t THwRXPDC<ID,TSIZE>::rx_buff[TSIZE];

#endif



P.S. Еще раз повторю это не демонстрация кода или приемов программирования, это демонстрация того что C++ и шаблоны используются еще как.

Очегнь даже ничего сочитается

	Мой шаблон драйера уартов для SAM9XE могу сказать что никакго оверхеда нету а в некоторых 
случаях код еще быстрее.
/********************************************* COMMON UARTs *****************************************/
template <int PORT, int TX_BUFF_SIZE, int RX_BUFF_SIZE>
class THwUART
{
private:
  static uint32_t last_err;

  typedef THwTXPDC<PORT,TX_BUFF_SIZE> TxFIFO;
  typedef THwRXPDC<PORT,RX_BUFF_SIZE> RxFIFO;

  static inline volatile AT91S_USART  *UART_BASE()
  {
    switch ( PORT )
    {
      case AT91C_ID_US0: return AT91C_BASE_US0;
      case AT91C_ID_US1: return AT91C_BASE_US1;
      case AT91C_ID_US2: return AT91C_BASE_US2;
      case AT91C_ID_US3: return AT91C_BASE_US3;
      case AT91C_ID_US4: return AT91C_BASE_US4;
      default: return NULL;
    }
  }

  static void irq_handler()
  {
    uint32_t status = UART_BASE()->US_CSR; 
    if( status &  AT91C_US_TXBUFE)
    {
      if( TxFIFO::TXBUFE_Handler() == 0)
      {
        UART_BASE()->US_IDR = AT91C_US_TXBUFE;
      }
    }
    if( status &  AT91C_US_RXBUFF)
    {
      if ( RxFIFO::RXBUFF_Handler() == 0 )
      {
        UART_BASE()->US_IDR = AT91C_US_RXBUFF;
      }
    };
    if ( status & (AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE))
    {
      last_err = UART_BASE()->US_CSR & (AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE);
      UART_BASE()->US_CR = AT91C_US_RSTSTA;
    }
  };

public:
  enum 
  {
    ID = PORT,
    TXBUFF_SIZE = TX_BUFF_SIZE, 
    RXBUFF_SIZE = RX_BUFF_SIZE
  };
  typedef enum {ERR_OK,ERR_OPEN,ERR_MODE} err_t;

  static err_t Open(const UART::DCB_t &config)
  {
    ....
    return THwUART::ERR_OK;
  };

  static uint32_t LastError(void)
  {
    ...
  }

  static void Write(const void* data, uint_fast16_t len)
  {
    const uint8_t *ptr = static_cast<const uint8_t*>(data);
    while(len)
    {
      uint32_t ssize = TxFIFO::Write(ptr,len);
      UART_BASE()->US_IER = AT91C_US_TXBUFE;
      ptr += ssize;
      len -= ssize;
    }
  }

  static uint_fast16_t Recived(void)
  {
    return RxFIFO::Recived();
  }

  static uint_fast16_t Read(void *data, uint_fast16_t buff_size)
  {
    uint32_t tmp = RxFIFO::Read(data,buff_size);
    if ( tmp )
    {
      UART_BASE()->US_IER = AT91C_US_RXBUFF;
    }
    return tmp;
  }

};

Страннно...

C++ with templates и Embedded?

Какое-то странное сочетание...

Вполне нормально.

Вполне нормально. Вон, scmRTOS чуть менее чем полностью состоит из шаблонов :)

А что странного ?