Вызов метода класса из прерывания.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Решил освоить классы и их применение. Написал простую программу, но так и не смог добиться её работы.

// Arduino. Test Class & Interrupt.
//
#define IOPort Serial
#define PortSpeed 9600
//
class foo {
     public:
               foo(): counter(42) {}
               void up(){ counter++; }
               void dn(){ counter--; }
               int get(){return counter;}
     private:
               int counter;
};
//
foo bar;
//
void setup(){
              IOPort.begin (PortSpeed);
              attachInterrupt (0, bar.up, FALLING);
              attachInterrupt (1, bar.dn, FALLING);
}
void loop(){
              IOPort.println (bar.get());
              delay (1000);
}

Идея проста, увеличивать или уменьшать значение counter.

Помогите, пожалуйста!

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Как-то странно объявлен class foo! А вы компилировали (проверяли) скетч в Arduino IDE?

Piskunov
Offline
Зарегистрирован: 13.02.2014

Garry пишет:

Как-то странно объявлен class foo! А вы компилировали (проверяли) скетч в Arduino IDE?

Да, проверял. И если менять значения не через прерывания, то работает. А через прерывания - ошибки.

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Ошибки чего, компиляции?

foo(): counter(42) {} - подскажите, что значт? Не силен в ООП под С++. Похоже на конструктор, который инициализирует couter значением 42?

Сразу есть подозрение, что в аттаче должна быть функция void (), а не метод класса! Точнее указатель на функцию void ().

gryzov
Offline
Зарегистрирован: 21.02.2014

Для нормальной работы с переменными в прерываниях переменная должна бать описана как

volatile int counter;

см пример http://arduino.ru/Reference/AttachInterrupt

 

Piskunov
Offline
Зарегистрирован: 13.02.2014

Garry пишет:

Ошибки чего, компиляции?

foo(): counter(42) {} - подскажите, что значт? Не силен в ООП под С++. Похоже на конструктор, который инициализирует couter значением 42?

Сразу есть подозрение, что в аттаче должна быть функция void (), а не метод класса! Точнее указатель на функцию void ().

1. Ошибки компиляции, да.

2. foo(): counter(42) {} это конструктор - я по книге делал, не сам придумал :-)

3. Натолкнуло на мысль. Проверю.

Piskunov
Offline
Зарегистрирован: 13.02.2014

Получилось, НО.

То, что получилось полностью не соответствует замыслу :-(

// Arduino. Test Class & Interrupt.
//
#define IOPort Serial
#define PortSpeed 9600
#define N 10
//
class foo {
     public:
               foo(): counter(42) {}
               void up(){ counter++; }
               void dn(){ counter--; }
               int get(){ return counter; }
     private:
               volatile int counter;
};
//
foo bar[N];
//
void UP(){ bar[1].up(); }
void DN(){ bar[1].dn(); }
// И что же, теперь туеву хучу функций писать???
//
void setup(){
              IOPort.begin (PortSpeed);
}
void loop(){
              attachInterrupt (0, UP, FALLING);
              attachInterrupt (1, DN, FALLING);
//            for (i = 1; i < N ; i++) { 
              detachInterrupt (0);
              detachInterrupt (1);
//            attachInterrupt (0, bar[i].up, FALLING);
//            attachInterrupt (1, bar[i].dn, FALLING); }
//            ...
              IOPort.println (bar[1].get());
              delay (1000);
}

 

gregoryl
Offline
Зарегистрирован: 09.09.2013

Значится так, это не С, а С++.... Вообщем если коротко то учите мат.часть. 

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

class A
{
    public:
        int V;
        void M() { V++; } // void M( A * this ) { this->V++ }
}

void IntHandler()
{
    A instance;
    instance.M(); // ==> A::M( &instance )
} 

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

А attachInterrupt ни о какой передаче и ни о каком инстансе и слыхом не слыхивал, можете использовать статические методы класса, там прокатит,  но это все равно что писать внешние функции, вообщем С++ не всегда красиво стыкуется с С.

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

А я думаю, что просто подход неправильный в данном случае у автора. Зачем подстраивать класс под аттач, когда можно сделать все просто. Класс оставляем, но пишем свои функции-обертки, которые будут вызываться в аттачах:



// Arduino. Test Class & Interrupt.
//
#define IOPort Serial
#define PortSpeed 9600
//
class foo {
     public:
               foo(): counter(42) {}
               void up(){ counter++; }
               void dn(){ counter--; }
               int get(){return counter;}
     private:
               int counter;
};

//
foo bar;
//

void fUP() {bar.up();}
void fDN() {bar.dn();}

void setup(){
              IOPort.begin (PortSpeed);
              attachInterrupt (0, fUP, FALLING);
              attachInterrupt (1, fDN, FALLING);
}

void loop(){
              IOPort.println (bar.get());
              delay (1000);
}

 

gregoryl
Offline
Зарегистрирован: 09.09.2013

так и приходиться делать, есть более изящные конструкции, но они частенько сложны для понимания если С++ не родной язык :-)

Piskunov
Offline
Зарегистрирован: 13.02.2014

Спасибо за замечания и комментарии.

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

// Arduino. Test Class & Interrupt.
//
#define IOPort Serial
#define PortSpeed 9600
#define N 6
//
class ch {
    private:
            byte level;
            byte pin;
    public:
            ch(): level(127){}
            void up(){ level++;}
            void dn(){ level--;}
            byte get(){return level;}
} channel[N];
//
int i;
unsigned long tcount;
char msg[30];
//
void UP(){ channel[i].up(); }
void DN(){ channel[i].dn(); }
//
void setup(){
            IOPort.begin (PortSpeed);
            attachInterrupt (0, UP, FALLING);
            attachInterrupt (1, DN, FALLING);
}
void loop(){
            for (i = 0; i <= N; i++){
              tcount = millis();
              while ( (millis() - tcount) < 1000 ){
                sprintf (msg,"channel: %1i, level: &3i", i,channel[i].get());
                IOPort.println (msg);
              }
            }
}

Буду продолжать учить матчасть и попробую решить эту задачу поизящнее :-)

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Тихий ужас.. i используется в цикле в loop и в прерывании, натурально - обезьяна с гранатой.

Второе, после цикла у Вас i == N, если пойдет прерывание, то получите channel[N].up() или dn(), т.е. выстрел в неизвестность.

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

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

О чего нарыл - обработчик прерывания статическим методом класса с доступом к private данным.

Правда это код для ARM LeafMaple (attachInterrupt немного другой), но надо проверить под Arduino!

 

Piskunov
Offline
Зарегистрирован: 13.02.2014

Garry пишет:

О чего нарыл - обработчик прерывания статическим методом класса с доступом к private данным.

Правда это код для ARM LeafMaple (attachInterrupt немного другой), но надо проверить под Arduino!

Спасибо, почитаю и это. Я сейчас зарылся полностью в изучение "матчасти", думаю ещё на неделю-другую хватит.

Есть сомнения в работоспособности примера - статические методы работают могут работать только со статическими переменными. (ну по крайней мере по другому я не знаю)

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Работать не будет, ошибка компилятора, опять не пропускает с разными типами параметров в attach-е, да и в примере они передают 4 параметра, а в доке по libmaple attach с тремя параметрами (тоже не должно скомпилиться, но не могу проверить - сдох АРМ)!