Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: call back
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
HEX
Как известно в С++ можно получить адрес функции члена только конкретного класса, что не затрудняет реализовать обмен событиями между объектами через callback. Обсуждений этой темы было уже много, повторяться не буду:
http://www.rsdn.ru/article/cpp/fastdelegate.xml
http://forum.developing.ru/showthread.php?p=12952#post12952
http://alenacpp.blogspot.com/
http://www.newty.de/fpt/index.html
http://dobrokot.nm.ru/cpp/CppMethodsCallback.html

Из специфики встраиваемых вытикают следующие ограничения: мин. потребление памяти, минимальное время вызова, использование кучи не жалательно, переносимость.
Я лично остановился на варианте интерфейсов, плюсы и минусы:
+ прозрачная реализация в стиле ООП
+ для вызова храним только один указатель, для 32бит проц. соответственно 4 байта
+ не использем кучу
+ можно объеденить однотипные события в один интерфейс (экономим память)
- нужна потдержка множественного наследования в компиляторе
- дополнительные расходы на размер кода при множественном наследовании (но вполне допустимые)
- для каждого типа/группы событий приходиться описывать класс интерфейса
- обработка событий от разных объектов и классов получается в одном методе, приходиться использовать switch (если это кретично, то можно обрабатывать события во вложенном классе слушателе который объявлен как друг)

Выглядет это примерно так:
//Интерфейс события мыши
class IMouseEvent {
private:
protected:
public:
virtual void OnMouseDown(void);
virtual void OnMouseUp(void);
virtual void OnClick(void);
};

//Интерфейс события клавиатуры
class IKeyEvent {
private:
protected:
public:
virtual void OnKeyDown(void);
virtual void OnKeyUp(void);
virtual void OnKeyPress(void);
};

//Базовый класс
class TControl {
private:
protected:
public:
IMouseEvent* OnMouseEvent;
IKeyEvent* OnKeyEvent;
};

//Окно
class TWindow: public TControl {
private:
protected:
public:
};

//Конкретное окно, добавлены инетерфейсы для обработки события
class TConcreteWindow: public IMouseEvent, public IKeyEvent, public TForm{
private:
protected:
public:
void OnClick(void);
virtual void OnKeyPress(void);
};
DASM
Замечательно. Я так понял вопроса в посте не содержится. Это хорошо. А при чем тут ARM ?
HEX
ну да это не вопрос, я предлагую обсудить возможные реализации. ARM... я сейчас для армов пишу.
Tahoe
Цитата(HEX @ Oct 25 2007, 17:16) *
ну да это не вопрос, я предлагую обсудить возможные реализации. ARM... я сейчас для армов пишу.

Ну тогда можно просто взять, например, Атмеловский USB Framework и посмотреть, как там сделаны коллбэки на чистых Сях. smile.gif
defunct
Дурное дело нехитрое - вызвать функцию по указателю.
Что тут обсуждать?
alexander55
Цитата(HEX @ Oct 25 2007, 14:25) *
class TConcreteWindow: public IMouseEvent, public IKeyEvent, public TForm{
...
};

Этот вариант прекрасно будет работать в IAR в eec++. Проверено.
PS. Что накосячили БГ и компания лучше обсудить в форумах по VC++ и C++Builder, а вопросы по дальнейщей стандартизвции C++ лучше направлять Герберту Шилдту (если нужны явки и пароли, я Вам помогу). biggrin.gif
Shkn
Цитата(HEX @ Oct 25 2007, 14:25) *
Как известно в С++ можно получить адрес функции члена только конкретного класса, что не затрудняет реализовать обмен событиями между объектами через callback.


Захотел сделать работу с периферией посредством классов. Но возникло два вопроса: как написать один код для каждого типа периферии, но чтоб он работал независимо от ее количества (к примеру UART0, UART1..) и как потом прерывания обрабатывать. Ответ был найден в параметризации шаблона класса базовым адресом в памяти. что-то вроде template<unsigned Adr> class CUart {..}; Соответственно в нем уже работать только по смещению.

Но тут возник интересный ньюанс: а как делать обработчик прерывания? Ведь его адрес надо записать в контроллер? Сделал так: все наследуются от одного базового сласса с виртуальной функцией Isr. В конструкторе все экземпляры регистрируются в глобальном массиве. Для каждого класса (читай номера устройства) делается статическая функция, которая и регистрируется в контроллере прерываний. А уже она по своему вызову выбирает из массива адрес соответствующего ей экзепляра и дергает его Isr.

Так к чему все это? Казалось бы работает, и не надо трогать.. Но нет! Ведь можно же еще уменьшить текст кода! А именно: сделать эту статическую функцию параметризируемой номером периферии и записать всего один раз template<unsigned N> void IsrExe(){ Array[N]->Isr();} Так вот мой Keil Arm не хочет адрес так описанной функции помещать в котроллер, а под MS VC проверяю - все в порядке.

Так что речь, пока, к сожалению, можно вести не о том, как хотелось бы, а о том, что позволяет выбранный инструментарий..
alexander55
Цитата(Shkn @ Oct 26 2007, 10:14) *
Захотел сделать работу с периферией посредством классов. Но возникло два вопроса: как написать один код для каждого типа периферии, но чтоб он работал независимо от ее количества (к примеру UART0, UART1..) и как потом прерывания обрабатывать. Ответ был найден в параметризации шаблона класса базовым адресом в памяти. что-то вроде template<unsigned Adr> class CUart {..};

Это логично.

Цитата(Shkn @ Oct 26 2007, 10:14) *
Но тут возник интересный ньюанс: а как делать обработчик прерывания? Ведь его адрес надо записать в контроллер? Сделал так: все наследуются от одного базового сласса с виртуальной функцией Isr. В конструкторе все экземпляры регистрируются в глобальном массиве. Для каждого класса (читай номера устройства) делается статическая функция, которая и регистрируется в контроллере прерываний. А уже она по своему вызову выбирает из массива адрес соответствующего ей экзепляра и дергает его Isr.

Так к чему все это? Казалось бы работает, и не надо трогать.. Но нет! Ведь можно же еще уменьшить текст кода! А именно: сделать эту статическую функцию параметризируемой номером периферии и записать всего один раз template<unsigned N> void IsrExe(){ Array[N]->Isr();} Так вот мой Keil Arm не хочет адрес так описанной функции помещать в котроллер, а под MS VC проверяю - все в порядке.

a14.gif

Цитата(Shkn @ Oct 26 2007, 10:14) *
Так что речь, пока, к сожалению, можно вести не о том, как хотелось бы, а о том, что позволяет выбранный инструментарий..

Добавлю, и желательно знать глюки и фичи используемого компилятора. smile.gif
Alex03
Цитата(Shkn @ Oct 26 2007, 12:14) *
template<unsigned Adr> class CUart {..};

Или я чёта не понимаю или это оверхед по коду экономящий по одному слову RAM-a на экземпляр...
grau
Цитата(Alex03 @ Oct 26 2007, 11:37) *
Или я чёта не понимаю или это оверхед по коду экономящий по одному слову RAM-a на экземпляр...


Следует обратить внимание на фразу "что-то вроде". Такая обертка используется для определения класса каждого регистра.

Код
    template <unsigned A>    class CRxBufReg{
    public:
        CBitsRO    <A, 0,  8>    Data;
        CMixRegRO_Macro;
    };


Внутри класса устройства периферии определяется через typedef

Код
    typedef CRxBufReg        <ECurBaseAdr + 0x00>    CRxBufReg_t;


соответсвенно, базовый адрес хранит сам класс устройства

Код
template<unsigned N>
class CUartDevice : public IUartDeviceBase {
...
    enum {ECurBaseAdr = ERegBaseAdr + N*ERegStepAdr};
};


ну и как все это выглядит в использовании:

Код
template<unsigned N>
BYTE CUartDevice<N>::Take()
{
    CRxBufReg_t    RxBufReg;
    BYTE Ret = RxBufReg;
    return Ret;    
};
Alex03
Вот я и не понимаю зачем для 3-х одинаковых UART-ов иметь в коде по три набора функций.
для той же
Код
template<unsigned N>
BYTE CUartDevice<N>::Take()
будет три её реализации отличающихся одним числом - адресом регистра.

Ну и ИМХО применение шаблонов тут ведёт только к усложнению понимания кода.
Мож я чего не вижу? Просвятите?
grau
Цитата(Alex03 @ Oct 29 2007, 12:10) *
Вот я и не понимаю зачем для 3-х одинаковых UART-ов иметь в коде по три набора функций.
для той же
Код
template<unsigned N>
BYTE CUartDevice<N>::Take()
будет три её реализации отличающихся одним числом - адресом регистра.


А какая альтернатива? Делать switch внутри функции? Вычислять адрес во время исполнения?
И еще стоит отметить, что шаблоны превращаются в код только если их вызвать явно. Т.е. если один из Uart-ов, к примеру, не используется, то кода для него и не сгенерируется. Это, кстати, является весьма трудной задачей для Keil. Он может просто не заметить такого вызова, а потом сиди-ищи, куда код делся.
А еще он не любит прерывания на параметризируемые классы в IDE ставить, хотя по asm-коду ходит

Цитата(Alex03 @ Oct 29 2007, 12:10) *
Ну и ИМХО применение шаблонов тут ведёт только к усложнению понимания кода.
Мож я чего не вижу? Просвятите?


Пример использования:

CUartDevice<0> _Uart;
_Uart.Start();

А все остальное пользователя уже и не касается.
Alex03
Цитата(grau @ Oct 29 2007, 13:28) *
А какая альтернатива? Делать switch внутри функции? Вычислять адрес во время исполнения?

Зачем свич? Например храним в экземпляре класса базовый адрес, вычисления не такие уж громоздкие, а учитывая как ARM грузит константы в регистры и оверхеда на вычисления может не понадобиться. smile.gif
А код переделываем в такой:

CUartDevice _Uart(0);
_Uart.Start();

И также все остальное пользователя уже и не касается.
Впрочем спорить не собираюсь, думал мож я чего не вижу/замечаю/понимаю.
scout
Читал архивы и наткнулся... Стало интересно, прочитал, кое - что взял на вооружение. Но непонятен один момент - как устроен класс "CBitsRO", а конкретно каким образом он осуществляет доступ к регистру с адресом, определяемым параметром шаблона?

Цитата
template <unsigned A> class CRxBufReg{
public:
CBitsRO <A, 0, 8> Data;
CMixRegRO_Macro;
};
landrey
Цитата(scout @ Nov 30 2007, 13:48) *
Читал архивы и наткнулся... Стало интересно, прочитал, кое - что взял на вооружение. Но непонятен один момент - как устроен класс "CBitsRO", а конкретно каким образом он осуществляет доступ к регистру с адресом, определяемым параметром шаблона?


Меня тоже заинтересовала эта тема! Уважаемый grau, нельзя ли поподробней?
Вот пишу (IAR AVR):

Код
typedef uint8_t volatile __tiny IO_REG;

template <IO_REG &r>
void f(uint8_t mask)
{
    r |= mask;
}

int main()
{
    f<PORTB>(0x77);    
    return 0;
}


На это ругается компилятор: Internal Error: [any]: Unexpected exception
Что я неправильно написал?
grau
Цитата(scout @ Nov 30 2007, 13:48) *
Читал архивы и наткнулся... Стало интересно, прочитал, кое - что взял на вооружение. Но непонятен один момент - как устроен класс "CBitsRO", а конкретно каким образом он осуществляет доступ к регистру с адресом, определяемым параметром шаблона?


Сорри, что долго не отвечал..

Класс CBitsRO является фактически обычным макросом. Его задача обеспечить корректность возращаемого значения. Т.е. прозрачно читаем значение битов по маске и сдвигаем их.


Код
template <unsigned A, unsigned P, unsigned L>
class CBitsRO{
public:
    operator unsigned(){
        return ((*((volatile unsigned*) A))>>P)&((1<<L)-1);
    }
};





Цитата(landrey @ Feb 17 2008, 16:09) *
Код
template <IO_REG &r>
void f(uint8_t mask)


int main(){
    f<PORTB>(0x77);    
    return 0;
}


На это ругается компилятор: Internal Error: [any]: Unexpected exception
Что я неправильно написал?


Для начала считаю необходимым заметить, что приводил пример с классами, а не с функциями, с которыми все немного по-другому. Ну а если конкретнее, то мне всегда казалось, что параметризируется только тип аргумента функции, но никак не внутренние переменные. Иначе как ее вообще объявлять?
SpiritDance
Цитата(Shkn @ Oct 26 2007, 09:14) *
Захотел сделать работу с периферией посредством классов. Но возникло два вопроса: как написать один код для каждого типа периферии, но чтоб он работал независимо от ее количества (к примеру UART0, UART1..) и как потом прерывания обрабатывать. Ответ был найден в параметризации шаблона класса базовым адресом в памяти. что-то вроде template<unsigned Adr> class CUart {..}; Соответственно в нем уже работать только по смещению.

Но тут возник интересный ньюанс: а как делать обработчик прерывания? Ведь его адрес надо записать в контроллер? Сделал так: все наследуются от одного базового сласса с виртуальной функцией Isr. В конструкторе все экземпляры регистрируются в глобальном массиве. Для каждого класса (читай номера устройства) делается статическая функция, которая и регистрируется в контроллере прерываний. А уже она по своему вызову выбирает из массива адрес соответствующего ей экзепляра и дергает его Isr.

Так к чему все это? Казалось бы работает, и не надо трогать.. Но нет! Ведь можно же еще уменьшить текст кода! А именно: сделать эту статическую функцию параметризируемой номером периферии и записать всего один раз template<unsigned N> void IsrExe(){ Array[N]->Isr();} Так вот мой Keil Arm не хочет адрес так описанной функции помещать в котроллер, а под MS VC проверяю - все в порядке.

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


Почитав это можно только спросить и к чему так е-ть себе мозги? smile.gif)
grau
Цитата(SpiritDance @ Feb 19 2008, 14:04) *
Почитав это можно только спросить и к чему так е-ть себе мозги? smile.gif)


Как поуказывает практика, у большинства людей мозги чаще ржавеют, чем истираются.
Так что не спи - замерзнешьsmile.gif
landrey
Цитата(grau @ Feb 18 2008, 11:54) *
Для начала считаю необходимым заметить, что приводил пример с классами, а не с функциями, с которыми все немного по-другому. Ну а если конкретнее, то мне всегда казалось, что параметризируется только тип аргумента функции, но никак не внутренние переменные. Иначе как ее вообще объявлять?


Прошу прощения, я сморозил глупость. Все-таки я вначале расписал класс, потом из-за ошибок компиляции начал поочередно все откидывать, потом по старой русской традиции решил все-таки заглянуть в EVAVR_CompilerReference.pdf. вот что я там нашел (цитирую):
Цитата
Non-type template parameters
It is allowed to have a reference to a memory type as a template parameter, even if
pointers to that memory type are not allowed.
Example
extern int __io x;
template<__io int &y>
void foo()
{
y = 17;
}
void bar()
{
foo<x>();
}

Пробую этот кусок - та же ошибка. Поэтому и в своем посте я изобразил подобное.
Но вот вырезка из моего класса:
Код
typedef uint8_t volatile __tiny IO_REG;

template <IO_REG &UDR, IO_REG &UBRRH, IO_REG &UBRRL>
class CBaseUart  
{

private:

    enum
    {
        DEFAULT_BAUDRATE    = 115200,
    };


public:
    
    CBaseUart(uint32_t baudRate=DEFAULT_BAUDRATE)
    {
        uint16_t tmp_baud = CPU_CLK_Hz / (16 * baudRate) - 1;
        UBRRH = (uint8_t)(tmp_baud >> 8);
        UBRRL = (uint8_t)tmp_baud;
    }
    
    virtual ~CBaseUart();
    
    
    inline void transmit(uint8_t data)
    {
        UDR = data;
    }

};


Вот так я пытаюсь использовать:
Код
int main()
{
    
    CBaseUart <UDR0, UBRR0H, UBRR0L> uart;
    
    uart.transmit(0x55);
    
    return 0;
}

Опять та же ошибка. Обидно
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.