Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: C++ и макросы имени Аскольда Волкова.
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
AHTOXA
Тема не совсем про scmRTOS, но тут много спецов по плюсамsmile.gif

Сделал такую штуку:
Код
class BasePin
{
    public:
        virtual void On() = 0;
        virtual void Off() = 0;
        virtual void Cpl() = 0;
...
};

Это базовый абстрактный класс. Нужен, чтоб можно было передавать ссылку на любую ножку куда угодно. Например, класс TDAC(spi& spiRef, BasePin& CS), конструктору передаётся ссылка на spi и ссылка на ножку чипселекта.

Дальше начинаю городить шаблоны:
Код
template<int port, int pin, char activestate = 'H'> class Pin;

template<int pin>
class Pin<1, pin>: public BasePin
{
    public:
        virtual void On() { P1OUT |= 1<<pin; }\
...
};

template<int pin>
class Pin<1, pin, 'L'>: public BasePin
{
    public:
        virtual void On(){ P1OUT &= ~(1<<pin); }
...
};


И так 5 раз (для портов 1-5). Как-то коряво...

Пробовал так:
Код
template<int port, int pin, char activestate = 'H'>
class Pin: public BasePin
{
    public:
        virtual void On() {
            if (port == 1) P1OUT |= 1<<pin;
            if (port == 2) P2OUT |= 1<<pin;
            if (port == 3) P3OUT |= 1<<pin;
            if (port == 4) P4OUT |= 1<<pin;
            if (port == 5) P5OUT |= 1<<pin;
        }
...
};


Так вроде поменьше писанины, но тоже корявенько.

Теперь внимание, вопросsmile.gif
Есть ли какая-нибудь возможность получить вот такое:
Код
template<int port, int pin, char activestate = 'H'>
class Pin: public BasePin
{
    public:
        virtual void On() {P##portOUT |= 1<<pin; }
...
};


То есть, хочу нечто вроде макроподстановки. Вроде где-то что-то встречал подобное, но никак не вспомню, куда копать.


ЗЫ. А работает вроде хорошо. То есть,

Код
Pin<1, 2, 'L'> PIN12;
#define PIN_12 1, 2, L
Pin<2, 3> PIN23;
#define PIN_23 2, 3, H
...
    PIN12.On();
    on(PIN_12);
    PIN23.On();
    on(PIN_23);


компилится в
Код
    1808:    e2 c2 21 00     bic.b    #4,    &0x0021  ;r2 As==10
    180c:    e2 c2 21 00     bic.b    #4,    &0x0021  ;r2 As==10
    1810:    f2 d2 29 00     bis.b    #8,    &0x0029  ;r2 As==11
    1814:    f2 d2 29 00     bis.b    #8,    &0x0029  ;r2 As==11


Накладные расходы - vtable+процедуры на каждую созданную ножку. За это я получаю возможность передавать ссылку на ножку. Я давно этого хотел.

При обращении по ссылке (в TDAC) вызовы уже из vtable, но это для меня приемлемо.
sergeeff
А зачем нужен параметр шаблона activestate, если он нигде не используется?
AHTOXA
Цитата(sergeeff @ Jul 29 2009, 16:17) *
А зачем нужен параметр шаблона activestate, если он нигде не используется?


Вот же он ("L"):
Код
template<int pin>
class Pin<1, pin, 'L'>: public BasePin


Он же: Pin<1, 2, 'L'> PIN12;

Суть в том, что On() переводит ногу в состояние activestate ("L" - низкий уровень, "H" - высокий). Так сделано в макросах Волкова, я к этому привык.
Сергей Борщ
Цитата(AHTOXA @ Jul 29 2009, 11:41) *
Пробовал так:
Код
template<int port, int pin, char activestate = 'H'>
class Pin: public BasePin
{
    public:
        virtual void On() {
            if (port == 1) P1OUT |= 1<<pin;
            if (port == 2) P2OUT |= 1<<pin;
            if (port == 3) P3OUT |= 1<<pin;
            if (port == 4) P4OUT |= 1<<pin;
            if (port == 5) P5OUT |= 1<<pin;
        }
...
};


Так вроде поменьше писанины, но тоже корявенько.
Я делал так. это не совсем по теме, но...:
Код
#ifndef USCIA_H__
#define USCIA_H__
#include    <io.h>

#ifndef USCIB_H__
enum usci_module_t { USCI0, USCI1 };
#endif


template <usci_module_t const module>
class uscia_t
{
public:
    class usca_ctl0_t    /* USCI Ax Control Register 0 */
    {
    public:
        uint8_t operator=(uint8_t value) { module ? UCA1CTL0 = value : UCA0CTL0 = value; return value; }
        void operator|=(uint8_t value) { module ? UCA1CTL0 |= value : UCA0CTL0 |= value; }
        void operator&=(uint8_t value) { module ? UCA1CTL0 &= value : UCA0CTL0 &= value; }
        operator uint8_t() { return module ? UCA1CTL0 : UCA0CTL0; }
    } static CTL0;
...
// pins
    class txd_bit
    {
    public:
        operator uint8_t() { return module ? (1 << 6) : (1 << 4); }
    }  static TXD_BIT;

    class rxd_bit
    {
    public:
        operator uint8_t() { return module ? (1 << 7) : (1 << 5); }
    }  static RXD_BIT;
};

template <usci_module_t module>
class uart_t
{
    typedef uscia_t<module> UCA;
public:
    static INLINE inline void init(uint32_t const divider);
....
};

template <usci_module_t module>
void uart_t<module>::init(uint32_t const divider)
{
    UCA::CTL1 |= (1  * UCSWRST);
    UCA::CTL1 = 0
        |(0 * UCRXEIE)|(0 * UCBRKIE)|(0 * UCTXADDR)
        |(1 * UCSWRST)
        |(1 * UCSSEL1)|(0 * UCSSEL0)
      ;
    UCA::CTL0 = (0 * UCPEN)|(0 * UCMSB)|(0 * UC7BIT)|(0 * UCSPB)|(0 * UCMODE1)|(0 * UCMODE0)|(0 * UCSYNC);

    UCA::BR0 = (divider / 16) & 0xFF;
    UCA::BR1 = (divider / 16) >> 8;
    UCA::MCTL = ((divider - (divider / 16) * 16) * UCBRF0)
        |(0 * UCBRS0)|(1 * UCOS16);
    UCA::CTL1 &= ~(1  * UCSWRST);
    P3SEL |= UCA::RXD_BIT | UCA::TXD_BIT;
    UCA::IFG |= UCA::TXIFG;
    UCA::IE |= UCA::RXIE;
}
AHTOXA
Цитата(Сергей Борщ @ Jul 29 2009, 16:50) *
Я делал так. это не совсем по теме, но...:


Да, это немножко другое. Выбор из пары возможных вариантов. Вы, кстати, зря избегаете виртуализации. С ней то же самое можно сделать гораздо изящнее:
Код
enum uart_num_t {UART0, UART1};

class TBaseUart
{
private:
    static const int RxSize = 32;
    static const int TxSize = 32;
protected:
    bool tx_pending;
    RingBuf<RxSize> RxBuf;
    RingBuf<TxSize> TxBuf;
    virtual void write_tx_reg(char ch) = 0;
    virtual void disable_tx_interrupt() = 0;
    virtual void enable_tx_interrupt() = 0;
public:
    TBaseUart() : RxBuf(), TxBuf(), tx_pending(false) {}
    char getch(void) { return RxBuf.get(); }
    bool keypressed(void) { return RxBuf.count(); }
//    ... весь остальной полезный фарш
};

template<uart_num_t uart_num> class TUart;

template<>
class TUart<UART0> : public TBaseUart
{
private:
    void hw_init(uint32_t baudrate);
protected:
    virtual void write_tx_reg(char ch) { TXBUF0 = ch; }
    virtual void disable_tx_interrupt() { IE1 &= ~UTXIE0; }
    virtual void enable_tx_interrupt() { IE1 |= UTXIE0; }
public:
    TUart(uint32_t baudrate) : TBaseUart() { hw_init(baudrate); }
};

template<>
class TUart<UART1> : public TBaseUart
...


В плюсе - оба UARTа имеют тип TBaseUart, весь фарш типа gets, puts, << и проч. - реализован один раз (в TBaseUart). В минусе - небольшие накладные расходы на вызов виртуальных фанкций. По моим наблюдениям они невеликиsmile.gif

ЗЫ. Пока решил проблему вот так:
Код
#define DECLARE_PORT(portN)                                                    \
template<int pin>                                                            \
class Pin<portN, pin>: public BasePin                                        \
{                                                                            \
    public:                                                                 \
        virtual void On() { P##portN##OUT |= 1<<pin; }                        \
...
};                                                                            \
                                                                            \
template<int pin>                                                            \
class Pin<portN, pin, 'L'>: public BasePin                                    \
{                                                                            \
    public:                                                                    \
        virtual void On(){ P##portN##OUT &= ~(1<<pin); }                    \
...
};                                                                            \

DECLARE_PORT(1)
DECLARE_PORT(2)
DECLARE_PORT(3)
DECLARE_PORT(4)
DECLARE_PORT(5)


Всё же без макросов пока никудаsmile.gif
Сергей Борщ
Цитата(AHTOXA @ Jul 29 2009, 14:32) *
Да, это немножко другое. Выбор из пары возможных вариантов.
Можно и из бОльшего количества:
Код
    class dma_ctl_t    /* DMA channel x control */
    {
    public:
        uint16_t operator=(uint16_t value)
                {
                      channel == DMA0 ? DMA0CTL = value
                    : channel == DMA1 ? DMA1CTL = value
                    : DMA2CTL = value;
                    return value;
                }
Это я к тому, что тернарный оператор короче if в записи. Ну и еще его можно использовать там, где if использовать нельзя:
Код
    static uint8_t const RXIFG = module == USCI0 ? UCB0RXIFG : UCB1RXIFG;

Цитата(AHTOXA @ Jul 29 2009, 14:32) *
Вы, кстати, зря избегаете виртуализации. С ней то же самое можно сделать гораздо изящнее:
Есть такой недостаток. Буду работать над собой.
sergeeff
А в чем навар в объявлении

Код
template<>
class TUart<UART0> : public TBaseUart


по сравнению с
Код
class TUart0 : public TBaseUart


Вы же все равно в каждом шаблонном классе все руками прописываете?
AHTOXA
Цитата(Сергей Борщ @ Jul 29 2009, 18:35) *
Это я к тому, что тернарный оператор короче if в записи.


А, понял. Но при пяти портах уже и то и то выглядит громоздко.


Цитата(sergeeff @ Jul 29 2009, 19:16) *
А в чем навар в объявлении


Ну я урезАл пример, и перестаралсяsmile.gif Но даже в этом, минимальном примере, вариант с шаблонами будет компилироваться при наличии только одного UART, а вариант с прямым прописыванием - нет.

А если применить подход Сергея из поста номер 4, то можно обойтись всего одной реализацией на все UART-ы. Но тут опять же, при возрастании числа UART-ов изящность решения снижается.
sergeeff
Цитата(AHTOXA @ Jul 29 2009, 17:29) *
Ну я урезАл пример, и перестаралсяsmile.gif Но даже в этом, минимальном примере, вариант с шаблонами будет компилироваться при наличии только одного UART, а вариант с прямым прописыванием - нет.


Что-то вы любезный какими-то загадками все время говорите. Вам надо какое-то псевдоизящество, или эффективная работа с периферией?

Почему не будет компилироваться вариант с прямым прописыванием? С чего-бы это?
MrYuran
Код
virtual void On() {
            if (port == 1) P1OUT |= 1<<pin;
            if (port == 2) P2OUT |= 1<<pin;
            if (port == 3) P3OUT |= 1<<pin;
            if (port == 4) P4OUT |= 1<<pin;
            if (port == 5) P5OUT |= 1<<pin;
        }

5 ифов подряд - по-моему, коряво...
Причём, это ведь для каждой операции?

В mspgcc есть iostructures.h, там порты объявлены в виде структур.
Тогда можно просто подставить адрес нужного порта, а дальше действовать в манере
Код
#define SPI         port1

#define RST_PIN     pin2
#define DI_PIN      pin3    // DI - вход мк, выход АЦП DO
#define RDY_PIN     pin5
#define CLK_PIN     pin6
#define DO_PIN      pin7    // DO - выход со стороны мк, вход АЦП DI

#define CLR_SCLK    SPI.out.CLK_PIN = 0
#define SET_SCLK    SPI.out.CLK_PIN = 1
AHTOXA
Цитата(sergeeff @ Jul 30 2009, 03:45) *
Что-то вы любезный какими-то загадками все время говорите. Вам надо какое-то псевдоизящество, или эффективная работа с периферией?

Почему не будет компилироваться вариант с прямым прописыванием? С чего-бы это?


Если вам что-то непонятно, просто перечитайте. Снова не поймёте - переспросите. К чему этот надрыв? smile.gif

Я же написал: "вариант с шаблонами будет компилироваться при наличии только одного UART, а вариант с прямым прописыванием - нет". Вы это отцитировали. Значит, прочитали? И не поняли? Хорошо, я переформулирую: "при наличии только одного UART" = "при отсутствии второго UART". В системе. Так яснее? Нет? Хорошо, поясню на примере.

Код
class TUart1 : public TBaseUart
{
protected:
     virtual void write_tx_reg(char ch) { TXBUF0 = ch; }
...
};

class TUart2 : public TBaseUart
{
protected:
     virtual void write_tx_reg(char ch) { TXBUF1 = ch; }
};


Если у проца нет второго UART, то на последней строчке компилятор скажет, что не знает, что такое TXBUF1.

В случае с шаблонами - не скажет, пока вы не попытаетесь создать экземпляр TUart<UART2>.

Цитата(MrYuran @ Jul 30 2009, 10:48) *
5 ифов подряд - по-моему, коряво...
Причём, это ведь для каждой операции?


Да не, это всё выкидывается компилятором, ибо сравнение константы с константой. Остаётся один bis.
sergeeff
Цитата(AHTOXA @ Jul 30 2009, 07:57) *
Если вам что-то непонятно, просто перечитайте. Снова не поймёте - переспросите. К чему этот надрыв? smile.gif

Я же написал: "вариант с шаблонами будет компилироваться при наличии только одного UART, а вариант с прямым прописыванием - нет". Вы это отцитировали. Значит, прочитали? И не поняли? Хорошо, я переформулирую: "при наличии только одного UART" = "при отсутствии второго UART". В системе. Так яснее? Нет?


Надрыва никакого нет. Просто вы приводите в качестве примеров куски кода, в которых отсутствуют отдельные фрагменты и мы должны догадываться, что вы имели ввиду.

По поводу "при наличии только одного UART" = "при отсутствии второго UART" - это тоже пример того, что вы хотите нам сказать и что за этим вы сами подразумеваете. Четче надо свои мысли излагать.
AHTOXA
Цитата(sergeeff @ Jul 30 2009, 13:02) *
Надрыва никакого нет. Просто вы приводите в качестве примеров куски кода, в которых отсутствуют отдельные фрагменты и мы должны догадываться, что вы имели ввиду.


Ой да ладно. Тот мой пост (с кусками кода) был адресован Сергею Борщу, и я уверен, что он там всё понял. Для иллюстрации моей мысли кода там достаточно. Если вы не догадались - сочувствую.

Цитата(sergeeff @ Jul 30 2009, 13:02) *
По поводу "при наличии только одного UART" = "при отсутствии второго UART" - это тоже пример того, что вы хотите нам сказать и что за этим вы сами подразумеваете. Четче надо свои мысли излагать.


Хм. Приведён код для двух UART. Разве в этом случае фраза "наличие только одного UART" не тождественна фразе "отсутствие второго UART"? Так что верну вам упрёк - внимательнее (вдумчивее) надо читать.
sergeeff
Цитата(AHTOXA @ Jul 30 2009, 10:55) *
Так что верну вам упрёк - внимательнее (вдумчивее) надо читать.


1. Жизненная позиция, основанная на принципе "все вокруг недоделанные идиоты, а я самый крутой", не есть правильная.
2. Вдумчивее читать то, чего не написано - это разгадывание ребусов. Дело в принципе тоже полезное, но, как мне сдается, наш форум не для этого создан.
AHTOXA
Цитата(sergeeff @ Jul 31 2009, 03:27) *
1. Жизненная позиция, основанная на принципе "все вокруг недоделанные идиоты, а я самый крутой", не есть правильная.
2. Вдумчивее читать то, чего не написано - это разгадывание ребусов. Дело в принципе тоже полезное, но, как мне сдается, наш форум не для этого создан.


Интересная беседа получаетсяsmile.gif

1. Каким образом из того, что вы лично что-то не поняли, следует вывод, что я всех считаю идиотами? И кто начал всю эту бодягу? Вы спокойно спросили про activestate, - я спокойно ответил. Вы спокойно спросили, в чём навар в использовании шаблонов по сравнению с явным прописыванием - я спокойно ответил. Вы не поняли ответа, и спокойствие исчезло из нашего диалога. Сразу я стал "любезный", стал "говорить загадками" и делать "псевдоизящество"... Не поняли - разозлились. Это, извините, ваши комплексы. Я тут не при чём.

2. Не всё, что вы не понимаете - ребус.

ЗЫ. Вы, кстати, испортили мне тему.
sergeeff
Вы внимательно пересмотрите свой топик. Про эту всю "бодягу" я и не начинал. Не можете поддерживать в спокойном тоне дискуссию - не мои проблемы. Не можете ясно излагать свои идеи - тоже не ко мне.

Напоследок хочу пожелать быть более уравновешенным и уважительным. Я это и раньше замечал в других ваших сообщениях.

Если я вас чем-то обидел - прощу прощения и на том закончим.
AHTOXA
Цитата(sergeeff @ Jul 31 2009, 12:48) *
Вы внимательно пересмотрите свой топик.


Пересмотрел. Не увидел, в чём я неправ. Я лишь симметрично отвечал.

Цитата
Не можете поддерживать в спокойном тоне дискуссию - не мои проблемы.


Могу. Пока кто-то не выведет дискуссию из спокойного русла.

Цитата
Не можете ясно излагать свои идеи - тоже не ко мне.


Могу. Я излагаю свои идеи на форуме разработчиков электроники. Это подразумевает определённый уровень участников. Если кто-то не понимает - не мои проблемы.

Цитата
Напоследок хочу пожелать быть более уравновешенным и уважительным. Я это и раньше замечал в других ваших сообщениях.


Ах, вот оно что. Вы решили меня повоспитывать? Спасибо, не надо.

Цитата
Если я вас чем-то обидел - прощу прощения и на том закончим.


Ок.
AHTOXA
Подниму тему. У меня сейчас мысль не про макросы, а вот про это изящное решение с шаблонизацией регистров:

Цитата(Сергей Борщ @ Jul 29 2009, 16:50) *
Я делал так. это не совсем по теме, но...:
Код
enum usci_module_t { USCI0, USCI1 };

template <usci_module_t const module>
class uscia_t
{
public:
     class usca_ctl0_t    /* USCI Ax Control Register 0 */
     {
     public:
         uint8_t operator=(uint8_t value) { module ? UCA1CTL0 = value : UCA0CTL0 = value; return value; }
         void operator|=(uint8_t value) { module ? UCA1CTL0 |= value : UCA0CTL0 |= value; }
         void operator&=(uint8_t value) { module ? UCA1CTL0 &= value : UCA0CTL0 &= value; }
         operator uint8_t() { return module ? UCA1CTL0 : UCA0CTL0; }
     } static CTL0;
...
// pins
     class txd_bit
     {
     public:
         operator uint8_t() { return module ? (1 << 6) : (1 << 4); }
     }  static TXD_BIT;
...
};

template <usci_module_t module>
class uart_t
{
     typedef uscia_t<module> UCA;
public:
     static INLINE inline void init(uint32_t const divider);
....
};

template <usci_module_t module>
void uart_t<module>::init(uint32_t const divider)
{
     UCA::CTL1 |= (1  * UCSWRST);
     UCA::CTL1 = 0
...
}


Здесь ведь можно сделать ещё красивее, если унаследовать uart_t от uscia_t:
Код
template <usci_module_t module>
class uart_t: public uscia_t<module>
{
public:
     static INLINE inline void init(uint32_t const divider);
....
};


Тогда не потребуется указывать UCA:: при обращении к регистрам:

Код
template <usci_module_t module>
void uart_t<module>::init(uint32_t const divider)
{
     CTL1 |= (1  * UCSWRST);
     CTL1 = 0
...
}
Сергей Борщ
Цитата(AHTOXA @ Aug 19 2009, 08:09) *
Тогда не потребуется указывать UCA:: при обращении к регистрам:
А это мысль! Нет предела совершнству!
AHTOXA
Цитата(Сергей Борщ @ Aug 19 2009, 15:07) *
А это мысль! Нет предела совершнству!


Оказывается, есть... Эта красота не работает в более свежих gcc. Ругается, что мембер из базового класса "was not declared in this scope" в классе-потомке.
Я покопался в интернете, оказывается, что стандарт на этот счёт гласит:

14.6.2/3
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

И даже есть специальный баг, из-за которого, возможно, это работает в msp-gcc smile.gif

Я опечален sad.gif
AHTOXA
Короче, вот что получилось в итоге: Нажмите для просмотра прикрепленного файла

От общего виртуального предка я отказался, всё-таки оказалось накладно. Придумал другой способ передачи ножек в объект - через параметр шаблона.
Например, библиотечный файл adc.h:
Код
template <typename cs_pin>class adc_t
{
private:
    spi_t& spi;
    cs_pin CS;
    void select() { CS.On(); }
    void deselect() { CS.Off(); }
public:
    adc_t(spi_t& spi_ref): spi(spi_ref) { CS.Mode(OUTPUT); deselect(); }
}


А в приложении:
Код
typedef Pin<'A', 2, 'L'> adc_cs_t;
typedef adc_t<adc_cs_t> adc1_t;

и, наконец,
Код
adc1_t adc(spi2);


Как-то такsmile.gif Конструктивная критика и предложения по улучшению - приветствуютсяsmile.gif

ЗЫ. Есть ещё такая-же, но для MSP, на днях выложу.
Waso
На днях опять пришлось иметь дело с проектом, где с разными ножками контроллера поочередно выполняются одинаковые действия. Снова вернулся к проблеме обращения к ногам через ссылки в массиве. И хотя главную проблему так и не решил (неоправданное разрастание кода при использовании виртуальных функций в шаблонных классах), однако получилось написать класс, который понимает Аскольдовские объявления ножек, а также имеется возможность создать порты на разные контроллеры. В данном случае выкладываю порт на ADuc702x, который можно переделать например под AVR переписав небольшой класс BaseIO, или еще лучше добавить условную компиляцию...
Код
#define Req1      1, 0, L
#define Req2      1, 1, L
//...
#define ChipSel1  1, 7, L
#define ChipSel2  0, 5, L
//...
#include "ascold.h"
#include "pin.hpp"
Pin<ChipSel1> CS1;
Pin<ChipSel2> CS2;

Pin<Req1> RQ1;
Pin<Req2,OUTPUT_OK> RQ2;

int main()
{
on(ChipSel1);  // макрос Аскольда Волкова
CS1.On();       // гибрид на С++
}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.