C ++ обратный вызов для функции c - PullRequest
0 голосов
/ 12 марта 2020

Да, я знаю, что есть много тем об этом, и я думаю, что прочитал большинство из них, но либо я не понимаю ответы, либо я не смог адаптировать их к своему " case ".

Знайте, мой опыт работы в электронике c, а не дизайн программного обеспечения, поэтому для некоторых из вас мой вопрос может показаться глупым, но ... я застрял.

Я разработал печатную плату для некоторых целей. Он основан на модуле ESP32. У меня есть 5 кнопок, подключенных к ESP. ESP32-IDF очень сложен для меня, поэтому я попытался go для платформы Ardiuno. Теперь это начинает усложняться.

Чтобы обнаружить и дебатировать кнопки, я создал класс C ++ под названием Button. Скелет можно увидеть ниже.

class Button {
..
..

private:
  void memberCallback() {
    ...
  }



public:
  Button(const uint8_t gpio ) {
    ..
    attachInterrup(digitalPinToInterrupt(gpio), memberCallback, FALLING);
    ..
  }

..

}

Я не нашел способа определить "memberCallback", не вызывая ошибок компиляции или не работая вообще.

Это должно быть распространенной проблемой поэтому, пожалуйста, предложите мне решение:)

Редактировать. Похоже, я не выразил себя достаточно ясно, извините. - Я знаю, что если я сделаю memberCallback stati c, он по крайней мере скомпилируется. Проблема в том, что я планировал использовать 5 экземпляров этого. Статистический обратный вызов c означает, что все экземпляры будут выполнять один и тот же код. 5 экземпляров означает 5 разных прерываний. Как мне их идентифицировать.

Ответы [ 3 ]

2 голосов
/ 12 марта 2020

В ядре ESP32 Arduino есть пример того, как именно это сделать, на

https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/GPIO/FunctionalInterrupt

Я приведу здесь код:

#include <Arduino.h>
#include <FunctionalInterrupt.h>

#define BUTTON1 16
#define BUTTON2 17

class Button
{
public:
    Button(uint8_t reqPin) : PIN(reqPin){
        pinMode(PIN, INPUT_PULLUP);
        attachInterrupt(PIN, std::bind(&Button::isr,this), FALLING);
    };
    ~Button() {
        detachInterrupt(PIN);
    }

    void IRAM_ATTR isr() {
        numberKeyPresses += 1;
        pressed = true;
    }

private:
    const uint8_t PIN;
    volatile uint32_t numberKeyPresses;
    volatile bool pressed;
}

Важные вещи:

  • #include <FunctionalInterrupt.h> - это дает вам std::bind() и немного другое attachInterrupt() объявление, которое делает эту работу
  • Использование std::bind(&Button::isr,this) для привязки вашего обработчика прерываний к вашему объекту.
  • Убедитесь, что вы объявили свой обработчик прерываний как IRAM_ATTR, чтобы гарантировать, что код для него останется загруженным в ОЗУ.
  • Убедитесь, что вы объявляете любые переменные экземпляра, которые вы хотите изменить в обработчике прерываний как volatile, чтобы компилятор C ++ знал, что они вносят изменения без предупреждения.

std::bind() - это стандартная библиотечная функция C ++; есть документация об этом в Интернете .

Тем не менее, я обеспокоен тем, что вы планируете делать внутри вашего обработчика прерываний.

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

Пример в Arduino Core хорош - он просто меняет пару переменных. Делать больше - вызывать такие функции, как Serial.println(), выделять память или создавать объекты - небезопасно.

1 голос
/ 12 марта 2020

Когда вы смотрите документацию attachInterrupt, она говорит, что ISR - это функция, не имеющая аргументов. Его тип void(*)() (или, скорее всего, на самом деле extern "C" void(*)(), но я не достаточно тщательно исследовал точный тип). Ваш memberCallback выглядит , как будто он не принимает аргумент, но на самом деле это не так: в функции-члене [не- static] вы получили неявный аргумент: this, указатель на объект, который необходимо определить, какой Button объект используется. Тип memberCallback равен void (Button::*)() и несовместим с void(*)(). Это означает, что вы не сможете напрямую использовать функцию-член [не-static].

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

  1. Функция имеет доступ только к static членам (как функциям, так и переменным).
  2. Функция не будет extern "C "`.

Вам понадобится одна функция на каждые Button, которые вы хотите зарегистрировать. Только для одного Button это будет выглядеть так:

class Button
{
    // ....
public:
    void memberCallback();
    Button(int gpio, Button*& ptr, void(*isr)()) {
        ptr = this;
        attachInterrupt(digitalPinToInterrupt(gpio), isr, FALLING);
    }
};

Button* button1;
extern "C" void button1ISR() {
    button1ISR->memberCallback();
}

// create you Button somewhere, e.g.:
int main() {
    Button b1(gpio, button1, button1ISR);
    // ...
}
0 голосов
/ 12 марта 2020

Сделайте вашу memberCallback функцию stati c. Измените свой код на это:

class Button {
..
..

private:
  static void memberCallback() {
    ...
  }



public:
  Button(const uint8_t gpio ) {
    ..
    attachInterrup(digitalPinToInterrupt(gpio), memberCallback, FALLING);
    ..
  }

..

}
...