Аппаратные прерывания, выдает ошибку

Добрый день! Пытаюсь написать библиотеку для драйвера моторов L298N и датчиков угловой скорости (ну энкодер оптический). Сначала делал конструктор в классе, куда вносил все 8 пинов, 6 драйвера и по одному датчику на сторону. Сразу в конструкторе настраивал входы - выходы, и аппаратные прерывания 0,1 для датчиков. IDE выдает ошибку

Compilation error: invalid use of non-static member function ‘void L298N::sensCountL()’

потом для назначения пинов и настройки прерываний создал отдельный метод, та же самая ошибка… Что я не так делаю ?

// Compilation error: invalid use of non-static member function 'void L298N::sensCountL()'

class L298N
{
  public: 
    L298N(uint8_t pwmL, uint8_t in1L, uint8_t in2L, uint8_t pwmR, uint8_t in1R, uint8_t in2R) 
    {
      _pwmL = pwmL;
      _in1L = in1L;
      _in2L = in2L;
      _pwmR = pwmR;
      _in1R = in1R;
      _in2R = in2R;
      pinMode(_pwmL, OUTPUT);
      pinMode(_in1L, OUTPUT);
      pinMode(_in2L, OUTPUT);
      pinMode(_pwmR, OUTPUT);
      pinMode(_in1R, OUTPUT);
      pinMode(_in2R, OUTPUT);
    }
 
    void setPinSensor(uint8_t sensorPinL, uint8_t sensorPinR)
    {
      _sensorPinL = sensorPinL;
      _sensorPinR = sensorPinR;
      pinMode(_sensorPinL, INPUT);
      pinMode(_sensorPinR, INPUT);
      attachInterrupt(_sensorPinL, sensCountL, RISING);
      attachInterrupt(_sensorPinR, sensCountR, RISING);
    }

    void forward(uint8_t speed)             // ЕХАТЬ ВПЕРЕД (СКОРОСТЬ) (1 для рассчета минимальной скорости в зависсимости от напряжения)
    {
      _speed = speed;
      digitalWrite(_in1L, HIGH);
      digitalWrite(_in2L, LOW);
      digitalWrite(_in1R, HIGH);
      digitalWrite(_in2R, LOW);
      _smL = 0;
      if (_speed == 1)
      {
        while (_smL < 1)
        {
          analogWrite (_pwmL, _speed)
          analogWrite (_pwmR, _speed)
          _speed ++;
        }
        _speed += 5;
      }
    }

/*

    void forward(uint8_t speed, uint16_t sm)
    {
      _speed = speed;
      _sm = sm;
      
    }

    void backward(uint8_t speed)
    {
      _speed = speed;
      analogWrite(_pwm, _sped);
      digitalWrite(_in1, LOW);
      digitalWrite(_in2, HIGH);
    }

*/

    void stop()
    {
      analogWrite(_pwmL, 0); 
      analogWrite(_pwmR, 0); 
    }

  private:
    void sensCountR()                        // ОБРАБОТКА ПРЕРЫВАНИЙ ПРАВОГО ДАТЧИКА
    {
      _smR++;
    }

    void sensCountL()                        // ОБРАБОТКА ПРЕРЫВАНИЙ ПРАВОГО ДАТЧИКА
    {
      _smR++;
    }

    uint8_t _pwmL;
    uint8_t _in1L;
    uint8_t _in2L;
    uint8_t _pwmR;
    uint8_t _in1R;
    uint8_t _in2R;
    uint8_t _sensorPinL;
    uint8_t _sensorPinR;
    uint8_t _speed;
    uint16_t _sm;
};

функцию обработчик (просто счетчик импульсов) располагал и в классе, и в loop

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

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

Ну смысл в том чтобы вызвать метод “ехать” и аргумент “количество см”, а библиотека уже сама настраивает пины, прерывания и вычисляет сколько ехать и где остановиться. Я так понял все же лучше библиотека отдельно для драйвера, а датчики и вычисления отдельно, и потом уже как аргументы драйверу отправлять ?

ты понимаешь чем различаются обычная функция и метод класса (нестатический)?

По тем нескольким видео и статьям что я смотрел понятно что проф программистом сразу не станешь, стараюсь идти постепенно, и думал что такую задачу осилю.

рано еще

Как в том анекдоте "Тяжело, тяжело, почты нэвазможна! "

я вот щас скажу тебе:

  1. заведи переменную указатель на свой класс
  2. в конструкторе запиши туда this
  3. сделай статический метод или обычную функцию
  4. приаттачь её к прерыванию.
  5. в прерывании проверь, если переменная из п.1 не NULL, по указателю вызови функцию экземпляра, которая должно реагировать на прерывание

ты поймёшь чонить? Или надо больше мультиков смотреть?

3 лайка

Не забуть, что в такой конструкции класс должен быть singleton-ом

1 лайк

Не, ну чё сразу-то! Нормально же всё вроде, а тут такие слова пошли … :frowning:

1 лайк

если он вызовет 2…N конструкторов, прерывания достанутся последнему экземпляру. :slight_smile:

1 лайк

Я не советую так делать.
Возможно, у Вас есть опыт программирования на ПК, но в данном случае есть своя специфика существенно отличающая МК от ПК.

Известно, что инициализация всех глобальных переменных осуществляется до первого исполняемого оператора.
На ПК это выглядит так:

  1. BIOS производит проверку и настройку основной части аппаратуры.
  2. BIOS читает с накопителя 0-й сектор и передает ему управление.
  3. Нулевой сектор читает с накопителя загрузчик ОС и передает ему управление.
  4. Загрузчик ОС читает с диска ядро ОС и передает ему управление.
  5. ОС читает с накопителя драйвера устройств.
  6. Драйвера устройств настраивают все необходимое оборудование.
  7. ОС дает возможность запустить прикладную задачу.
  8. Прикладная задача инициализирует глобальные переменные.
  9. Прикладная задача начинает выполнять первый оператор.

В случае МК последовательность немного другая:

  1. Если в системе установлен загрузчик, выполнение начинается с него.
  2. Загрузчик ждет около секунды, не будет ли кто-то через последовательный порт заливать новую прошивку. Если нет - переходит к следующему пункту.
  3. Начинает выполняться прошивка, если загрузчика нет - то первые два пункта пропускаются.
  4. Выполнение прошивки начинается, как это положено с инициализации глобальных переменных. В том числе - классов.
  5. Затем выполняется первый исполняемый оператор - вызов функции, которая настраивает всю аппаратуру МК.
  6. Затем следует вызов функции setup.

Из сравнения видно, что если в ПК сначала происходит настройка аппаратуры и только потом инициализация всех пользовательских классов, то в МК - наоборот: все конструкторы статических классов вызываются ДО того, как аппаратура будет настроена.
Отсюда вывод: в конструкторе класса НЕЛЬЗЯ производить настройку аппаратуры во избежание конфликтов с системой.
В Ардуино для настройки аппаратуры принято использовать методы, обычно называемые begin или init, вызываемые из setup.

PS. код не смотрел.

Я правильно понимаю что даже для таких казалось бы простых вещей как написать ардуино библиотеку для драйвера моторов нужно фундаментально изучать С++ ? Вроде изучаешь книги по ардуино, статьи вроде гайвера, а потом тысяча нюансов о которых нигде не упоминалось)

1 лайк

да булки не растут на деревьях, и в жизни не бывает простых решений сложных задач

А это-то зачем?

Вы поверили, что Арудино - это просто? Вас обманули :wink:

Я бы дальше больше сказал :frowning:

2 лайка

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

1 лайк

это следующий этап. :slight_smile: