Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.236.86.184] |
|
Сообщ.
#1
,
|
|
|
Прувет, народ! Вопрос больше для ардуинщиков, но и относится к Си.
Всю голову сломал, помогите, в поиске ответы уж очень заумные. Требуется вызвать функцию внутри класса по указателю. при попытке получить указатель компилятор ругается. uint8_t ADDR; void (*ptrFnc)(); class OWSlvDevice { public: OWSlvDevice(byte Interrupt_pin){// конструктор Itrrpt.Pin(Interrupt_pin); } void begin(uint8_addr) { ADDR = addr; ptrFnc = CreateObject; } private: void CreateObject(){ //код } }; компилятор выдает ошибку: In file included from C:\...\...\T...\TX.ino:1:0: sketch\OWSlvDevice.h: In member function 'void OWSlvDevice::begin(uint8_t)': OWSlvDevice.h:63:14: error: cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (*)()' ptrFnc=CreateObject; ^~~~~~~~~~~~ exit status 1 cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (*)()' Что не так, подскажите! |
Сообщ.
#2
,
|
|
|
Типы не совпадают. И не могут совпадать. И не смогут никогда. Тип указателя на метод кардинально отличается от типа указателя на функцию.
void (OWSlvDevice::*ptrFnc)(); Вот так работать будет. Добавлено Но и вызывать придётся иначе. Нужно указывать экземпляр класса, чей метод дёргаешь. Логично ж? Что-то типа: OWSlvDevice shelezjaka(6); /* ... */ shelezjaka.*ptrFnc(); Возможно скобки забыл. Добавлено P.S. Ну да, забыл. Вот так надо: (shelezjaka.*ptrFnc)(); |
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ Вот так работать будет. void (OWSlvDevice::*ptrFnc)(); Блин, как я об этом не подумал... Цитата Qraizer @ Но и вызывать придётся иначе. Нужно указывать экземпляр класса, чей метод дёргаешь. Логично ж? Что-то типа: OWSlvDevice shelezjaka(6); /* ... */ shelezjaka.*ptrFnc(); OWSlvDevice::ptrFnc = OWSlvDevice::CreateObject; Да, вот только вызов должен быть внутри класса, и к тому же при присвоении значения этой переменной ptrFnc = &CreateObject;OWSlvDevice::ptrFnc = OWSlvDevice::CreateObject; sketch\OWSlvDevice.h: In member function 'void OWSlvDevice::begin(uint8_t)': OWSlvDevice.h:64:41: error: cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (OWSlvDevice::*)()' OWSlvDevice::ptrFnc = OWSlvDevice::CreateObject; ^~~~~~~~~~~~ exit status 1 cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (OWSlvDevice::*)()' за то вот так: ptrFnc = &CreateObject; но, как теперь вызвать ее внутри модуля, если метод вызывающий ее находится вне класса? PIN_INT{ (OWSlvDevice.*ptrFnc)(); } |
Сообщ.
#4
,
|
|
|
Цитата SOY @ но, как теперь вызвать ее внутри модуля, если метод вызывающий ее находится вне класса? Цитата Qraizer @ (shelezjaka.*ptrFnc)(); |
Сообщ.
#5
,
|
|
|
Цитата SOY @ Сдается, вы пытаетесь в плюсах применить сишные навыки и вручную организовать виртуальные функции. Может вы опишете задачу более подробно и мы посоветуем, как обойтись вообще без укателя на функцию-член? Да, вот только вызов должен быть внутри класса |
Сообщ.
#6
,
|
|
|
Цитата Dushevny Может вы опишете задачу более подробно и мы посоветуем, как обойтись вообще без указателя на функцию-член? задача организовать аппаратное прерывание внутри класса. макросы: #define EN_INT0 {EICRA &= ~(1 << ISC00);EICRA|= (1 << ISC01);EIMSK|=(1 << INT0);} #define EN_INT1 {EICRA &= ~(1 << ISC10);EICRA|= (1 << ISC11);EIMSK|=(1<<INT1);} В зависимости от используемого пина выбираем нужную установку регистров и вектор прерывания: byte Pin(const byte Interrupt_pin) { #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) if(Interrupt_pin==2){ EN_INT0 #define PIN_INT ISR(INT0_vect) // the interrupt service routine return; } #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) if(Interrupt_pin==2){ EN_INT0 #define PIN_INT ISR(INT0_vect) // the interrupt service routine return; } #elif defined (__AVR_ATmega328P__) switch (Interrupt_pin) { case 2: EN_INT0 #define PIN_INT ISR(INT0_vect) // the interrupt service routine return; case 3: EN_INT1 #define PIN_INT ISR(INT1_vect) // the interrupt service routine return; } } } далее обработчик прерывания PIN_INT{ } надо использовать внутри класса. Однако это не удается без танцев с бубном. Либо объявлять все методы static в области public, либо вызывать методы по адресу функции, что я пытаюсь осуществить. Как вариант конечно можно использовать конструкцию attachInterrupt(pin, handler, mode);, но в моем случае это не айс, хотя работает,но, опять таки с танцем с бубном . |
Сообщ.
#7
,
|
|
|
SOY, для обработки прерывания могу предложить только синглтон, причем создание инстанса должно быть до первого срабатывания.
|
Сообщ.
#8
,
|
|
|
Цитата shm @ SOY, для обработки прерывания могу предложить только синглтон, причем создание инстанса должно быть до первого срабатывания. Class OWSlvDevice { static OWSlvDevice * instances; static void ShllFnc() { if (OWSlvDevice::instances != NULL) OWSlvDevice::instances ->INT (); } public: OWSlvDevice(byte Interrupt_pin) { // конструктор byte Vctr = Itnrrpt.Pin(Interrupt_pin); if (Vctr != 0xFF) { pinMode (Interrupt_pin, INPUT_PULLUP); attachInterrupt (Vctr, ShllFnc, FALLING); instances = this; } } private: void INT() { if (comm == SEARCH_DEVICE)CreateObject(); } void CreateObject() { . . . } }; Цитата Как вариант конечно можно использовать конструкцию attachInterrupt(pin, handler, mode);, но в моем случае это не айс, хотя работает shm,если есть другие варианты, то буду при много благодарен. |
Сообщ.
#9
,
|
|
|
SOY, я не понимаю проблему, которую ты пытаешься решить. У тебя один источник прерывания и один класс, который его обрабатывает. Что тут еще изобретать? Если у тебя несколько источников прерываний и ты каждый из них хочешь обрабатывать в своем объекте, то создай глобальный контейнер указателей на объекты и по какому-нибудь критерию (номер прерывания, id устройства, и т.п.) доставай указатель и дергай через него метод. Только не забывай корректно синхронизировать доступ к контейнеру, иначе будут проблемы. Вангую, что ты хочешь аналог std::bind для прерываний, так его нет по причине слишком низкого уровня обработчика, но ты можешь сделать что-то подобное самостоятельно.
Добавлено И приведенный тобою фрагмент кода не является синглтоном. Более того, он потенциально опасен, а в обработчике прерывания опасен^2. |
Сообщ.
#10
,
|
|
|
Я конечно соррь в ваших терках ... но, ЕМНИП, связывание идет на уровне линковки - а линкеру пофик ваши классы!
И адреса метода в классе быть попросту не может. Есть адрес метода объявленного объекта. Но есть одно "но", на этапе компиляции - можно узнать позицию(индекс) виртуального метода в VMT. На счет невиртуального метода - я ХЗ. Может кто-то разъяснит процедуру компиляции и линковки классов - я увы, пас. |
Сообщ.
#11
,
|
|
|
Цитата Это работать не будет. Препроцессор работает до компилятора, он не видит switch и выдаст здесь что-то вроде "macro PIN_INT redefined" switch (Interrupt_pin) { case 2: EN_INT0 #define PIN_INT ISR(INT0_vect) // the interrupt service routine return; case 3: EN_INT1 #define PIN_INT ISR(INT1_vect) // the interrupt service routine return; "Подвесить" функцию-член прямо на вектор прерывания невозможно. Просто потому что семантика вызова функции-члена другая - в момент вызова обработчика у ядра нет this. Для всяких Cortex-Mx можно "подвесить" на вектор статическую функцию - член, но ей все равно нужно откуда-то брать this для вызова уже собственно функции-члена для конкретного экземпляра класса. Для AVR даже это не работает из-за особого подхода к привязыванию функций-обработчиков к векторам через магические имена функций - все то, что скрыто внутри ISR(INT0_vect). На электрониксе обсуждали не так давно, в общем - не решается эта задача так, как вам хочется. Я бы использовал шаблон с номером вывода в качестве параметра. Чтобы не плодить кучу одинакового исполняемого кода - можно общую функциональность вынести в базовый класс, а в шаблоне реализовать только то, что зависит он номера вывода. Примерно так: class int_pin_base { public: inline void hander(); }; tempalte<uint8_t int_num> class int_pin : public int_pin_base { public: enum class trigger { LOW_LEVEL, ANY_EDGE, FALLING_EDGE RISING_EDGE } int_pin(trigger trigger) { EICRA = (EICRA & ~(0x03 << 2 * int_num)) | (uint8_t(trigger) << 2 * int_num); void enable() { EIMSK |= 1 << int_num; } void disable() { EIMSK &= ~(1 << int_num); } }; /* или так, если реализации сильно отличаются в зависимости от параметра шаблона template<> int_pin<0>::int_pin(trigger trigger) { EICRA = (EICRA & ~(1 << (ISC01 | ISC00))) | (uint8_t(trigger) << ISC00); } template<> int_pin<1>::int_pin(trigger trigger) { EICRA = (EICRA & ~(1 << (ISC11 | ISC10))) | (uint8_t(trigger) << ISC10); } */ int_pin<0> Int0(ANY_EDGE); int_pin<1> Int1(RISING_EDGE); ISR(INT0_vect) { Int0.handler(); } ISR(INT1_vect) { Int1.handler(); } |
Сообщ.
#12
,
|
|
|
AVR-ка да, недавно с нею разбирался. 2048 прерываний – настоящий монстр. Я-то думал, ARM жестокий, PPC по сравнению с ним просто чуточку неудобный.
SOY, простое решение вряд ли возможно будет найти. Насколько я понимаю, у тебя архитектура предусматривает по экземпляру класса на каждый обработчик. Тут надо как-то связать pin с этим экземпляром, + обеспечить корректную передачу this. Вариантов масса, включая бинд этих this в лямбды, которые уже несложно вызывать без параметров. Но я очень сомневаюсь, что компилятор сможет в исполнительном окружении обеспечить корректный вызов лямбд, учитывая, что это происходит асинхронно. Я бы всё-таки в подобных условиях сделал собственный диспетчер, который определял нужный экземпляр this по тому pin, который сработал. В той же AVR-ки иначе и никак не сделать-то. Та и в PPC архитектура внешних прерываний способствует этому же. Диспетчер же вполне можно сделать static, и пусть себе читает регистры и определяет pin. |
Сообщ.
#13
,
|
|
|
кому интересно сделал так:
файл _ISR.h #ifndef _ISR_H #define _ISR_H #if ARDUINO >= 100 #include <Arduino.h> #else #include <WProgram.h> #endif // ============================ КОНСТАНТЫ ============================== #define ISR_PIN 2 //пин(менять перед объявлением библиотеки) #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) #if (ISR_PIN == PB2) || (ISR_PIN == B2) || (ISR_PIN == 2) || defined (ISR_PIN == 7) #define EICRA _SFR_IO8(0x35) #define ISC00 0 #define ISC01 1 #define EIMSK _SFR_IO8(0x3B) #define INT0 6 #define EIFR _SFR_IO8(0x3A) #define INTF0 6 //OW Pin #define PINS PINB #define PIN PINB2 #define PORT PORTB//_SFR_IO8(0x18) #define DDR DDRB #define PIN_INT ISR(INT0_vect) // the interrupt service routine #define ISR_INIT 1 #endif #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) #if (ISR_PIN == PB2) || (ISR_PIN == B2) || (ISR_PIN == 2) || defined (ISR_PIN == 5) #define EICRA _SFR_IO8(0x35) #define ISC00 0 #define ISC01 1 #define EIMSK _SFR_IO8(0x3B) #define INT0 6 #define EIFR _SFR_IO8(0x3A) #define INTF0 6 //OW Pin #define PINS PINB #define PIN PINB2 #define PORT PORTB//_SFR_IO8(0x18) #define DDR DDRB #define PIN_INT ISR(INT0_vect) // the interrupt service routine #define ISR_INIT 1 #endif #elif defined (__AVR_ATmega328P__) #if (ISR_PIN == 2) || (ISR_PIN == D2) //OW Pin #define PINS _SFR_IO8(0x09) #define PIN PIND2 #define PORT _SFR_IO8(0x0B) #define DDR _SFR_IO8(0x0A) #define PIN_INT ISR(INT0_vect) // the interrupt service routine #define ISR_INIT 1 #endif #if (ISR_PIN == 3) || (ISR_PIN == D3) //OW Pin #define PINS _SFR_IO8(0x09) #define PIN PIND3 #define PORT _SFR_IO8(0x0B) #define DDR _SFR_IO8(0x0A) #define PIN_INT ISR(INT1_vect) // the interrupt service routine #define ISR_INIT 1 #endif #endif #if defined ISR_INIT //OW Pin #define OW_PORT PORT //1 Wire Port #define OW_PIN PINS //1 Wire Pin as number #define OW_PORTN (1<<PIN) //Pin as bit in registers #define OW_PINN (1<<PIN) #define OW_DDR DDR //регистр направления #define SET_LOW {OW_DDR|=OW_PINN;OW_PORT&=~OW_PORTN;} //установить линию 1-Wire на низкий уровень #define RESET_LOW {OW_DDR&=~OW_PINN;} //установить вывод 1-Wire в качестве входа //Pin interrupt #define EN_INT {EIMSK|=(1<<INT0);EIFR|=(1<<INTF0);} //enable interrupt #define DIS_INT EIMSK&=~(1<<INT0); //disable interrupt #define SET_RISING EICRA=(1<<ISC01)|(1<<ISC00); //set interrupt at rising edge #define SET_FALLING {EICRA &= ~(1 << ISC00);EICRA|=(1<<ISC01);} //set interrupt at falling edge #define SET_CHANGE {EICRA &= ~(1 << ISC01) ;EICRA|=(1<<ISC00);}//set interrupt at change edge #define CHK_INT_EN (EIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled class _ISR { private: void proc(); void(*CallbackCommand_)(uint8_t retv); friend void INTP(); ; public: _ISR(); void begin(void (*callback)(uint8_t retv)); }; #endif #endif файл _ISR.cpp #include "Arduino.h" #include "_ISR.h" /* Access variable for ISRs */ static _ISR *instance = NULL; void (*p_function)(); // указатель на p_function _ISR::_ISR(){}// конструктор PIN_INT {//прерывание (*p_function)(); } // спасибо Гайверу за универсальную функцию void attachFunction(void (*function)()) { // передача указателя на функцию p_function = *function; } void INTP(){ instance->proc(); } void _ISR::proc(){ CallbackCommand_(0x01); } void _ISR::begin(void (*callback)(uint8_t retv)) { cli();//откл. глоб.прер. RESET_LOW;//пин на вход SET_FALLING; EN_INT; sei(); // enable interrupts attachFunction(INTP); instance = this; CallbackCommand_ = callback; } файл tst_ISR_on_CLASS.ino #include <_ISR.h> _ISR tst; void INT(uint8_t retv); void setup() { Serial.begin(9600); tst.begin(&INT); } void loop() { // put your main code here, to run repeatedly: } void INT(uint8_t retv){ Serial.println("OK"); } код для второй ардуины для теста (просто шлем импульсы на вход прерывания второй ардуины #define INT 4 void setup() { pinMode(INT, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); } void loop() { delay(1000); digitalWrite(INT,1); digitalWrite(LED_BUILTIN,1); delay(200); digitalWrite(INT,0); digitalWrite(LED_BUILTIN,0); } тема закрыта, всем спасибо! |
Сообщ.
#14
,
|
|
|
Цитата SOY @ кому интересно сделал так: Можно вот так попробовать. Но я так не делал: Скрытый текст // -------------------------------------------------------------------------- //Определим интерфейс: class I_interrupt { public: virtual void work (int intNum)=0; }; // -------------------------------------------------------------------------- // определим указатель на интерфейс I_interrupt* pInt=NULL; // -------------------------------------------------------------------------- // установим адрес интерфейса: void Set_I_interrupt(I_interrupt* p) { pInt = p; } // -------------------------------------------------------------------------- //Далее короткие функции прерываний в стиле С: // не знаю, как для ARDUINO. Пусть для определённости будет так: interrupt [TIMER1_COMPB_vect] void t1_compb (void) { if(pInt) pInt->work(TIMER1_COMPB_vect); } // -------------------------------------------------------------------------- interrupt [TIMER1_COMPA_vect] void t1_compa (void) { if(pInt) pInt->work(TIMER1_COMPA_vect); } // -------------------------------------------------------------------------- //Пишем реализацию интерфейса для конкретного проекта: class IntProject : virtual public I_interrupt { public: virtual void work (int intNum); virtual void routine_TIMER1_COMPB_vect(); virtual void routine_TIMER1_COMPA_vect(); }; // -------------------------------------------------------------------------- void IntProject::work (int intNum) { // тут возможно что-то общее для всех, если нужно switch(intNum) { case TIMER1_COMPB_vect: routine_TIMER1_COMPB_vect(); break; case TIMER1_COMPA_vect: routine_TIMER1_COMPA_vect(); break; } // тут возможно что-то общее для всех, если нужно } // -------------------------------------------------------------------------- void IntProject::routine_TIMER1_COMPB_vect() { // ... } // -------------------------------------------------------------------------- void IntProject::routine_TIMER1_COMPA_vect() { // ... } // -------------------------------------------------------------------------- // В результате работаем с прерываниями используя классы. // Возможны варианты. // используем: void main (void) { // ... IntProject intObject; Set_I_interrupt(&intObject); // ... } После таких мероприятий реализации можно менять "как перчатки". |