Присоединить прерывание к библиотеке

Я пытаюсь сделать библиотеку для Arduino, которая требует прерывания, но проблема в том, что когда я пробую функцию attachInterrupt внутри моей библиотеки, возникает проблема с attachInterrupt is static. Прямо сейчас в библиотеке у меня есть это в заголовочном файле:

#ifndef RegletaCMA_h
#define RegletaCMA_h

#include "Arduino.h"

class DimmerGlider {
public:
    DimmerGlider(int min, int max, int increase);

    int getMin() { return _min; }

    int getMax() { return _max; }

    int getIncrease() { return _increase; }

    void setMin(int min) { _min = min; }

    void setMax(int max) { _max = max; }

    void setIncrease(int increase) { _increase = increase; }

private:
    int _min, _max, _increase;
};

class Dimmer {

public:
    Dimmer(DimmerGlider &glider, int outputPin, int t3);

    void Dimmer::begin() {
        pinMode(_outputPin, OUTPUT);
        digitalWrite(_outputPin, false);
    }

    void Dimmer::dim() {
        delayMicroseconds(_DIMMER_counter);
        digitalWrite(_outputPin, true);  // Включаем _DIMMER_pinDimmer
        delayMicroseconds(_t3);
        digitalWrite(_outputPin, false); // Выключаем _DIMMER_pinDimmer

        if (_DIMMER_dir) _DIMMER_counter += _glider.getIncrease(); // Если dir отрицательный, добавить _DIMMER_increase
        else _DIMMER_counter -= _glider.getIncrease();                  // Если dir отрицательный, вычитаем _DIMMER_increase

        if (_DIMMER_counter > _glider.getMax()) _DIMMER_dir = false; // Изменить dir, если достигнуто дно
        if (_DIMMER_counter < _glider.getMin()) _DIMMER_dir = true;  // Изменить каталог, если достигнут верх

        _DIMMER_lastExecutionTime_micros = micros(); // Сбросить таймер
    }

    void Dimmer::force(bool status) {
        digitalWrite(_outputPin, status);
    }

private:
    DimmerGlider &_glider;

    int _outputPin;

    int _DIMMER_counter = 1000;
    bool _DIMMER_dir = true;

    unsigned long long _DIMMER_lastExecutionTime_micros = 0;

    int _t3;
};

class DimmerImplementor {
public:
    DimmerImplementor(Dimmer dimmerList[], int optoPin);

    void DimmerImplementor::begin(){
        attachInterrupt(_optoPin, dimAll, RISING);
    }

    void DimmerImplementor::dimAllDimmers() {
        for (int c = 0; c < sizeof(_dimmerList); c++)
            _dimmerList[c].dim();
    }

    void DimmerImplementor::dimAll() {
        DimmerImplementor::dimAllDimmers();
    }

private:
    int _optoPin;
    Dimmer _dimmerList[];
};

#endif

и это в cpp-файле:

#include "Arduino.h"
#include "RegletaCMA.h"

DimmerGlider::DimmerGlider(int min, int max, int increase) :
        _min(min),
        _max(max),
        _increase(increase) {
}

Dimmer::Dimmer(DimmerGlider &glider, int outputPin, int t3) :
        _glider(glider),
        _outputPin(outputPin),
        _t3(t3) {
}

DimmerImplementor::DimmerImplementor(Dimmer *dimmerList, int optoPin) :
        _dimmerList(dimmerList),
        _optoPin(optoPin) {
    for(int c = 0; c < sizeof(dimmerList); c++){
        DimmerImplementor::dimmers[c] = dimmerList[c];
    }

    for (int c = 0; c < sizeof(dimmers); c++) {
        Dimmer dimmer = dimmers[c];

        dimmer.begin();
    }
}

а когда я пытаюсь запустить библиотеку с помощью Arduino IDE, с этим кодом:

DimmerGlider dimmer1Glider = DimmerGlider(_DIMMER_1_tempMin, _DIMMER_1_tempMax, _DIMMER_1_increase);
Dimmer dimmer1 = Dimmer(
                   dimmer1Glider,
                   _DIMMER_1_pin,
                   _DIMMER_t3
                 );
DimmerGlider dimmer2Glider = DimmerGlider(_DIMMER_2_tempMin, _DIMMER_2_tempMax, _DIMMER_2_increase);
Dimmer dimmer2 = Dimmer(
                   dimmer2Glider,
                   _DIMMER_2_pin,
                   _DIMMER_t3
                 );
DimmerGlider dimmer3Glider = DimmerGlider(_DIMMER_3_tempMin, _DIMMER_3_tempMax, _DIMMER_3_increase);
Dimmer dimmer3 = Dimmer(
                   dimmer3Glider,
                   _DIMMER_3_pin,
                   _DIMMER_t3
                 );

Dimmer dimmers[] = {
  dimmer1, dimmer2, dimmer3
};

DimmerImplementor implementor = DimmerImplementor(dimmers, _DIMMER_pinOpto);

void Dimmer_Init() {
  implementor.begin();
}

Выдает следующую ошибку:

C:\Users\Arnym\Documents\Arduino\libraries\RegletaCMA/RegletaCMA.h: In member function 'void DimmerImplementor::begin()':

C:\Users\Arnym\Documents\Arduino\libraries\RegletaCMA/RegletaCMA.h:73:44: error: invalid use of non-static member function

         attachInterrupt(_optoPin, dimAll, 3); // 3 is the same as RISING

                                            ^

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

, 👍1


2 ответа


Лучший ответ:

2

В C++ необходимо различать две отдельные копии объекта или наличие ссылки на существующий объект. @chrisl правильно понял, когда определил, что вы пытаетесь создать вторую копию DimmerGlider, поэтому он предложил использовать ссылку, а затем показал, что вместо этого можно использовать указатель (*).

Он был прав: Dimmer должен содержать ссылку на объект DimmerGlider, но это делается с помощью &. Вам нужно внести следующие изменения в данный код:

В .h:

class Dimmer {

public:
    Dimmer(DimmerGlider &glider, int outputPin, int t3);
.
.
.
private:
    DimmerGlider &_glider;
.
.

И в конечном .cpp:

DimmerGlider::DimmerGlider(int min, int max, int increase) :
              _min(min),
              _max(max),
              _increase(increase) {
}

Dimmer::Dimmer(DimmerGlider &glider, int outputPin, int t3) :
        _glider(glider),
        _outputPin(outputPin),
        _t3(t3) {
}

Синтаксис для конструктора Dimmer пришлось изменить. Вы присваивали членам класса значения из параметров конструктора. Ссылки нужно инициализировать, а не назначать. Это тот синтаксис, который вам нужен (обратите внимание, что они находятся перед {?)

Я исправил код конструктора DimmerGlider, пока был занят...

Также обратите внимание на разницу между использованием ссылки и указателя. Указатель необходимо разыменовать, поэтому вам нужно использовать *ptr и ptr->_min для ссылки на исходный объект. Ссылка уже разыменована, поэтому вы можете просто использовать ее как исходный объект: ref и ref._min.


Изменение: если вы хотите использовать функцию dimAll() как функцию обработчика прерываний, то она не может быть (нестатическим) членом класса. Все функции класса имеют скрытый указатель this в качестве первого параметра в списке аргументов, поскольку он должен знать, какие члены DimmerImplementor изменять. Таким образом, вы должны объявить функцию как static. И как только вы это сделаете, вы не сможете вызвать dimAllDimmers(), потому что эта функция обращается к экземпляру переменных DimmerImplementor. К счастью, у вас есть глобальный объект с именем implementor, поэтому вы можете использовать его:

static void DimmerImplementor::dimAll() {
    implementor.dimAllDimmers();
}

Но обычно эту переменную делают статическим членом самого класса:

class DimmerImplementor {
    .
    .
public: // Статические переменные
    static DimmerImplementor implementor;
    .
    .
}; // Реализатор диммера

Вы бы инициализировали это следующим образом:

DimmerImplementor DimmerImplementor::implementor(dimmers, _DIMMER_pinOpto);
,

Спасибо, это решило мою первую проблему, но у меня все еще возникают проблемы с attachInterrupt, компилятор выдает error: invalid use of non-static member function, см. измененный вопрос для полной программы, @Arnyminer Z

@ArnyminerZ Проверьте редактирование моего ответа., @John Burger


0

В сообщении об ошибке говорится, что в конструкторе Dimmer не удается найти соответствующий конструктор DimmerGlider. Проблема здесь в том, что вы передаете параметры по значению, что создает новую переменную указанного типа и содержимого для вызова функции/конструктора. Поэтому, когда вы создаете новую переменную Dimmer, вы вызываете конструктор класса. Поскольку параметры конструктора содержат экземпляр DimmerGlider, он также должен быть создан для вас, что означает вызов конструктора. При передаче переменных по значению используется конструктор по умолчанию (без каких-либо параметров). Но у DimmerGlider нет конструктора по умолчанию (так как вы его не определили). Затем компилятор сообщает вам, что для создания экземпляра DimmerGlider конструктору нужны некоторые параметры.

Я не знаю способа передать эти параметры для конструктора DimmerGlider напрямую через конструктор Dimmer. Также я не думаю, что это желательно для вас. Вы сначала создаете DimmerGlider и хотите, чтобы объекты Dimmer ссылались именно на этот экземпляр, а не на другой (клонированный) экземпляр, созданный с помощью конструктора Dimmer.

Поэтому я предлагаю следующее: Передайте DimmerGlider по ссылке, то есть вы передаете только указатель на объект DimmerGlider. В определении конструктора Dimmer измените на следующее:

Dimmer::Dimmer(DimmerGlider *glider, int outputPin, int t3);

* говорит, что эта переменная не будет содержать экземпляр класса DimmerGlider, а только указатель на него. Указатель создать просто, потому что это, по сути, unsigned int (16-битный адрес памяти).

Затем в вашем скетче вы передаете не саму переменную, а только адрес в памяти (указатель на объект) с помощью &:

Dimmer dimmer1 = Dimmer(
                   &dimmer1Glider,
                   _DIMMER_1_pin,
                   _DIMMER_t3
                 );

Теперь конструктор Dimmer не должен создавать экземпляр DimmerGlider, а только простой указатель типа Dimmerglider. & извлекает адрес памяти (указатель) экземпляра объекта. Конструктор теперь просто записывает указатель, который он получил как параметр, в переменную glider типа DimmerGlider-указатель.

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

,