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

 
 
> C++ templates и mcu peripherals
_Артём_
сообщение Mar 23 2012, 00:03
Сообщение #1


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Доброе утро.
Такой простой вопрос: сейчас многие современные контроллеры оснащаются несколькими однотипными блоками периферии. Что толкает к повторному использованию кода и templates в частности.
Как правило структура блока однотипна (имеет одинаково расположенные в памяти регистры) и каждый блок занимает в памяти некоторую непрерывную область начинающиюся с базового адреса.
Ну так вот вопрос: как правильней передать шаблону этот самый базовый адрес?
Код
template <int BASE_ADDRESS> class TIOBlock {
.....
}

И потом приводить этот адрес к тому что надо?
Или использовать enum или ещё что?
Как делать правильней?

Спасибо.
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов (1 - 10)
MALLOY2
сообщение Mar 23 2012, 09:54
Сообщение #2


Знающий
****

Группа: Validating
Сообщений: 838
Регистрация: 31-01-05
Пользователь №: 2 317



Я для SAM9XE (IAR 6.x) делал так:

Код
template <int PORT, int T_BUFF_SIZE, int R_BUFF_SIZE>
class THwUART
{
  public:
  static  OS::SEM::Handle RxSem @ ".fast_ram";
  static  OS::SEM::Handle TxSem @ ".fast_ram";
  static  uint32_t LastErr      @ ".fast_ram";
  
  typedef THwRXPDC<PORT,uint8_t,R_BUFF_SIZE> RxPDC;    
  typedef THwTXPDC<PORT,uint8_t,T_BUFF_SIZE> TxPDC;
  
  inline static 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 YieldRequired = OS::SEM::YIELD_NOTREQUIRED;
    uint32_t imr = UART_BASE()->US_IMR;
    uint32_t status = UART_BASE()->US_CSR & imr;
    if( status &  AT91C_US_TIMEOUT)
    {
      UART_BASE()->US_CR = AT91C_US_STTTO;
      OS::SEM::SignalFromISR(RxSem, &YieldRequired);
    };
...
Go to the top of the page
 
+Quote Post
neiver
сообщение Mar 23 2012, 11:06
Сообщение #3


Местный
***

Группа: Участник
Сообщений: 214
Регистрация: 22-03-10
Из: Саратов
Пользователь №: 56 123



Сложность здесь заключается в том, что эти структуры регистров периферии часто объявляются очень разными способами. Это может быть дефайн, в котором целочисленный литерал приводится к указателю на соответствующую структуру. А может быть хитрым образом переменная, размещенная по нужному адресу с помощью каких-то компиляторных расширений. Просто взять адрес такой структуры и передать его как параметр шаблона в большинстве случаев не получится, так как компилятор может не посчитать его пригоднам для параметра шаблона. Самый надёжный, на мой взгляд, способ - обращаться к этой структуре по имени. Для этого делаем макрос, который объявляет хитрую структуру-обёртку:
Код
#define IO_STRUCT_WRAPPER(STRUCT_PTR, CLASS_NAME, STRUCT_TYPE) \
    struct CLASS_NAME\
    {\
        typedef STRUCT_TYPE DataT;\
        STRUCT_TYPE* operator->(){return ((STRUCT_TYPE *)(STRUCT_PTR));}\
    }

В ней определён оператор ->, который позволяет прозрачно обращаться к отдельным регистрам в структуре. С помощью этого макроса объявляем обёртки:
Код
IO_STRUCT_WRAPPER(DMA1, Dma1, DMA_TypeDef);
IO_STRUCT_WRAPPER(DMA1_Channel1, Dma1Channel1, DMA_Channel_TypeDef);
...
IO_STRUCT_WRAPPER(DMA1_Channel7, Dma1Channel7, DMA_Channel_TypeDef);


Код
template<class DmaRegs, class Clock, int Channels>
class DmaModule :public DmaBase
{
...
};
В шаблон структура-обёртка передаётся как обычный типовой параметр:
template<class Module, class ChannelRegs, int Channel>
class DmaChannel :public DmaBase
{
    public:        
    static void Init(Mode mode, void *buffer, void *periph, uint32_t bufferSize)
    {
        Module::Enable();
        ChannelRegs()->CCR = 0;
        ChannelRegs()->CNDTR = bufferSize;
        ChannelRegs()->CPAR = reinterpret_cast<uint32_t>(periph);
        ChannelRegs()->CMAR = reinterpret_cast<uint32_t>(buffer);
        ChannelRegs()->CCR = mode | DMA_CCR1_EN;
    }
...
};

typedef DmaModule<Private::Dma1, Clock::Dma1Clock, 7> Dma1;
typedef DmaChannel<Dma1, Private::Dma1Channel1, 1> Dma1Channel1;

Для доступа к отдельным регистрам в структуре используестя стрелка:
Код
ChannelRegs()->CCR = 0;

Go to the top of the page
 
+Quote Post
_Артём_
сообщение Mar 23 2012, 14:16
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(MALLOY2 @ Mar 23 2012, 11:54) *
Я для SAM9XE (IAR 6.x) делал так:

[code]template <int PORT, int T_BUFF_SIZE, int R_BUFF_SIZE>
class THwUART

[code]


То есть порты с номером от 0...сколько их есть-1.
Спасибо.

Что за ОС кстати? Вроде что-то знакомое но не совсем.

Цитата(neiver @ Mar 23 2012, 13:06) *
Сложность здесь заключается в том, что эти структуры регистров периферии часто объявляются очень разными способами. Это может быть дефайн, в котором целочисленный литерал приводится к указателю на соответствующую структуру. А может быть хитрым образом переменная, размещенная по нужному адресу с помощью каких-то компиляторных расширений. Просто взять адрес такой структуры и передать его как параметр шаблона в большинстве случаев не получится, так как компилятор может не посчитать его пригоднам для параметра шаблона. Самый надёжный, на мой взгляд, способ - обращаться к этой структуре по имени. Для этого делаем макрос, который объявляет хитрую структуру-обёртку:

Спасибо за пример.
Попробую вечером.
Go to the top of the page
 
+Quote Post
MALLOY2
сообщение Mar 23 2012, 14:52
Сообщение #5


Знающий
****

Группа: Validating
Сообщений: 838
Регистрация: 31-01-05
Пользователь №: 2 317



Цитата
Сложность здесь заключается в том, что эти структуры регистров периферии часто объявляются очень разными способами.


Это если вы используете какие то левые или от производителя, я всегда переписываю их под себя, да вечер потерян но все одинаково и мне привычно.


Цитата
Что за ОС кстати? Вроде что-то знакомое но не совсем.


Это моя обертка С++ для FREERTOS
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Mar 23 2012, 15:04
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(MALLOY2 @ Mar 23 2012, 16:52) *
я всегда переписываю их под себя

Интересно.
Пример бы увидеть(не всё что есть конечно).
Например как AT91C_ID_US0... както сами определяли?

Цитата(MALLOY2 @ Mar 23 2012, 16:52) *
Это моя обертка С++ для FREERTOS

Тогда показалось что знакомое.
Напомнило чтото.
Go to the top of the page
 
+Quote Post
MALLOY2
сообщение Mar 23 2012, 16:44
Сообщение #7


Знающий
****

Группа: Validating
Сообщений: 838
Регистрация: 31-01-05
Пользователь №: 2 317



Есть хидеры к периферии, обычно они идут с компиляторами причем каждого свой стиль, есть которые дает производитель это еще один стиль, я обычно их беру и правлю под себя.

Цитата
Например как AT91C_ID_US0... както сами определяли?


Вот так sm.gif
Код
// *****************************************************************************
//               PERIPHERAL ID DEFINITIONS FOR AT91SAM9XE512
// *****************************************************************************
#define AT91C_ID_FIQ    ( 0) // Advanced Interrupt Controller (FIQ)
#define AT91C_ID_SYS    ( 1) // System Controller
#define AT91C_ID_PIOA   ( 2) // Parallel IO Controller A
#define AT91C_ID_PIOB   ( 3) // Parallel IO Controller B
#define AT91C_ID_PIOC   ( 4) // Parallel IO Controller C
#define AT91C_ID_ADC    ( 5) // ADC
#define AT91C_ID_US0    ( 6) // USART 0
#define AT91C_ID_US1    ( 7) // USART 1
#define AT91C_ID_US2    ( 8) // USART 2
#define AT91C_ID_MCI    ( 9) // Multimedia Card Interface 0
#define AT91C_ID_UDP    (10) // USB Device Port
#define AT91C_ID_TWI0   (11) // Two-Wire Interface 0
#define AT91C_ID_SPI0   (12) // Serial Peripheral Interface 0
#define AT91C_ID_SPI1   (13) // Serial Peripheral Interface 1
#define AT91C_ID_SSC0   (14) // Serial Synchronous Controller 0
#define AT91C_ID_TC0    (17) // Timer Counter 0
#define AT91C_ID_TC1    (18) // Timer Counter 1
#define AT91C_ID_TC2    (19) // Timer Counter 2
#define AT91C_ID_UHP    (20) // USB Host Port
#define AT91C_ID_EMAC   (21) // Ethernet Mac
#define AT91C_ID_ISI    (22) // Image Sensor Interface
#define AT91C_ID_US3    (23) // USART 3
#define AT91C_ID_US4    (24) // USART 4
#define AT91C_ID_TWI1   (25) // Two-Wire Interface 1
#define AT91C_ID_TC3    (26) // Timer Counter 3
#define AT91C_ID_TC4    (27) // Timer Counter 4
#define AT91C_ID_TC5    (28) // Timer Counter 5
#define AT91C_ID_IRQ0   (29) // Advanced Interrupt Controller (IRQ0)
#define AT91C_ID_IRQ1   (30) // Advanced Interrupt Controller (IRQ1)
#define AT91C_ID_IRQ2   (31) // Advanced Interrupt Controller (IRQ2)
#define AT91C_ALL_INT   (0xFFFE7FFF) // ALL VALID INTERRUPTS
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Mar 23 2012, 17:16
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(MALLOY2 @ Mar 23 2012, 18:44) *
Есть хидеры к периферии, обычно они идут с компиляторами причем каждого свой стиль, есть которые дает производитель это еще один стиль, я обычно их беру и правлю под себя.
Вот так sm.gif
Код
// *****************************************************************************
//               PERIPHERAL ID DEFINITIONS FOR AT91SAM9XE512
// *****************************************************************************
#define AT91C_ID_FIQ    ( 0) // Advanced Interrupt Controller (FIQ)
#define AT91C_ID_SYS    ( 1) // System Controller
.....

Особых отличий от IAR не заметил. Зачем тогда самому делать?
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Mar 24 2012, 20:30
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Несколько в сторону от темы шаблонов.

Чтоб не плодить топики спрошу здесь.
Насколько применим такой способ работы с периферией:
CODE

class TLed {
LPC_GPIO_TypeDef *Port;
uint32_t PortMask;
public:
TOutControl(LPC_GPIO_TypeDef *port, unsigned char pin) {
Port=port;
PortMask=1<<pin;
Port->DIR|=PortMask;
Port->DATA&=~PortMask;
}
void On() {
Port->DATA|=PortMask;
}
void Off() {
Port->DATA&=~PortMask;
}
};

TLed Led(LPC_GPIO0, 7);

int main (void) {
Led.On();
///
Led.Off();
}


Не кроется ли тут каких-то невидимых мне потенциальных ошибок и недостатков(для Cortex-M)?
Go to the top of the page
 
+Quote Post
MALLOY2
сообщение Mar 26 2012, 07:33
Сообщение #10


Знающий
****

Группа: Validating
Сообщений: 838
Регистрация: 31-01-05
Пользователь №: 2 317



Работать будет, но избыточно, через шаблоны будет лучше и не будет никаких дополнительных расходов.
Здесь почитайте



Цитата
Особых отличий от IAR не заметил. Зачем тогда самому делать?

ну то что я вам показал взято с IAR. Но вы же не все видите.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Mar 26 2012, 09:47
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(MALLOY2 @ Mar 26 2012, 10:33) *
ну то что я вам показал взято с IAR. Но вы же не все видите.


Всё не вижу. И соответственно не понимаю ничего.
Go to the top of the page
 
+Quote Post

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

 


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


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