Метод объекта в качестве обработчика прерывания

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Там уже находятся указатели для каждой функции ISR пользователя (массив). Почему это вас не волнует? Если покопаться, то можно обнаружить, что Arduino Framework очень жирная библиотека. Пусть каждый читает документацию на avr gcc и использует прямую регистрацию обработчиков прерываний. Правда, сэкономить на этом не получится, если хотя бы один из обработчиков уже есть в этом массиве.

Какой тогда смысл во всей этой затее? Пишите всё вручную. Это вообще стандратный подход при программировании на avr. Все так пишут... нормальные программисты.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

uni пишет:

Какой тогда смысл во всей этой затее?

В какой затее? Это Вы затеяли, Вам и объяснять "какой смысл". По мне - так никакого.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Я имел в виду какой смысл с Arduino Framework? Вы либо принимаете накладные расходы при её использовании, либо пишите всё сами. Если вы пользуетесь этой библиотекой, то уж разберитесь и с ней тоже, а не только с моим кодом, если так хочется покритиковать.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

uni пишет:

... если так хочется ...

Нет, не хочется. Мне это неинтересно.

Logik
Offline
Зарегистрирован: 05.08.2014

Петь, ты снова за старое?

//Пусть каждый читает документацию .. и использует прямую регистрацию

//Пишите всё вручную.

//Вы либо принимаете .., либо пишите всё сами. 

//уж разберитесь 

Кто такой что повелеваеш кому чего делать? Без сопливых неплохо разбирались и будем далее. Знаеш поговорку, "не рассказуй что делать - не узнаеш куда пойти". Так пизуй кобыле в трещину со своими ЦУ!

Считаеш свой подход верным - твои проблемы. В лигу сексреформ уже написал - жди предсказаного ответа ;) Но есть значить мысль, что практика - она критерий истины, и если Петя четыре года не может средненький проект довести до ума, то наверно он чёт не так пишет и не слушает умных людей. А стало быть его практика порочна и подходы не верны. 

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Оказывается я не первый, кто сделал такую поддержку. Это уже обсуждалось на github'е (где содержится исходный код Arduino Framework и идёт общая над ним работа). Мне подсказали на основном форуме:

Support extra parameter on `attachInterrupt()`

Т.е. в Arduino Framework это всё-таки будет включено, насколько я понял.

П.С. Если посмотреть на первоначальный код, то видно, что наши подходы почти одинаковы. В комментах они пошли дальше, исследуя накладные расходы и возможность использования lambda функций. Последнее ардуинщики вряд ли поймут.

Насколько я вижу, западные пользователи Arduino куда более вменяемые, нежели местные неадекваты.

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

uni пишет:

западные пользователи Arduino куда более вменяемые, нежели местные неадекваты.

зато #Аллепонаш.. ой, всё

vanro
Offline
Зарегистрирован: 27.04.2021

Здравствуйте, Uni. Можете помочь мою библиотеку прерываний переделать, чтобы она могла работать с методами класса?

Вот portchange.h

#ifndef __PORTCHANGE_H
#define __PORTCHANGE_H

#include <Arduino.h>

typedef void (*interrupt_cb)(bool pinvalue);


const uint8_t PC_RISING = 0x01;
const uint8_t PC_FALLING = 0x02;
const uint8_t PC_ANY = PC_RISING | PC_FALLING;

void attachInterruptEx(uint8_t pin, interrupt_cb cb, uint8_t mode);
void detachInterruptEx(uint8_t pin);

#endif

portchange.cpp

#include "portchange.h"

struct pinchange_t {
  volatile uint8_t values;
  uint8_t conditions[8 / 4];
  interrupt_cb callbacks[8];
};

static pinchange_t *p0 = NULL, *p1 = NULL, *p2 = NULL;

ISR(PCINT0_vect) {
  if (p0) {
    uint8_t values = PINB;

    for (uint8_t i = 0; i < 6; ++i) {
      if (p0->callbacks[i] && (((values >> i) & 0x01) != ((p0->values >> i) & 0x01))) {
        uint8_t condition = (p0->conditions[i / 4] >> (i % 4) * 2) & PC_ANY;

        if (((condition & PC_RISING) && (((values >> i) & 0x01) == 1)) || ((condition & PC_FALLING) && (((values >> i) & 0x01) == 0)))
          p0->callbacks[i]((values >> i) & 0x01);
      }
    }
    p0->values = values;
  }
}

ISR(PCINT1_vect) {
  if (p1) {
    uint8_t values = PINC;

    for (uint8_t i = 0; i < 6; ++i) {
      if (p1->callbacks[i] && (((values >> i) & 0x01) != ((p1->values >> i) & 0x01))) {
        uint8_t condition = (p1->conditions[i / 4] >> (i % 4) * 2) & PC_ANY;

        if (((condition & PC_RISING) && (((values >> i) & 0x01) == 1)) || ((condition & PC_FALLING) && (((values >> i) & 0x01) == 0)))
          p1->callbacks[i]((values >> i) & 0x01);
      }
    }
    p1->values = values;
  }
}

ISR(PCINT2_vect) {
  if (p2) {
    uint8_t values = PIND;

    for (uint8_t i = 0; i < 8; ++i) {
      if (p2->callbacks[i] && (((values >> i) & 0x01) != ((p2->values >> i) & 0x01))) {
        uint8_t condition = (p2->conditions[i / 4] >> (i % 4) * 2) & PC_ANY;

        if (((condition & PC_RISING) && (((values >> i) & 0x01) == 1)) || ((condition & PC_FALLING) && (((values >> i) & 0x01) == 0)))
          p2->callbacks[i]((values >> i) & 0x01);
      }
    }
    p2->values = values;
  }
}

void attachInterruptEx(uint8_t pin, interrupt_cb cb, uint8_t mode) {
  if (pin < 8) { // D0..D7
    if (! p2) {
      p2 = (pinchange_t*)malloc(sizeof(pinchange_t));
      memset(p2, 0, sizeof(pinchange_t));
      p2->values = PIND;
    }
    p2->conditions[pin / 4] &= ~(uint8_t)(PC_ANY << (pin % 4) * 2);
    p2->conditions[pin / 4] |= ((mode & PC_ANY) << (pin % 4) * 2);
    p2->callbacks[pin] = cb;
    PCIFR |= (1 << PCIF2);
    PCICR |= (1 << PCIE2);
    PCMSK2 |= (1 << pin);
  } else if (pin < A0) { // D8..D13
    pin -= 8;
    if (! p0) {
      p0 = (pinchange_t*)malloc(sizeof(pinchange_t) - sizeof(interrupt_cb) * 2);
      memset(p0, 0, sizeof(pinchange_t) - sizeof(interrupt_cb) * 2);
      p0->values = PINB;
    }
    p0->conditions[pin / 4] &= ~(uint8_t)(PC_ANY << (pin % 4) * 2);
    p0->conditions[pin / 4] |= ((mode & PC_ANY) << (pin % 4) * 2);
    p0->callbacks[pin] = cb;
    PCIFR |= (1 << PCIF0);
    PCICR |= (1 << PCIE0);
    PCMSK0 |= (1 << pin);
  } else if (pin <= A5) { // A0..A5
    pin -= A0;
    if (! p1) {
      p1 = (pinchange_t*)malloc(sizeof(pinchange_t) - sizeof(interrupt_cb) * 2);
      memset(p1, 0, sizeof(pinchange_t) - sizeof(interrupt_cb) * 2);
      p1->values = PINC;
    }
    p1->conditions[pin / 4] &= ~(uint8_t)(PC_ANY << (pin % 4) * 2);
    p1->conditions[pin / 4] |= ((mode & PC_ANY) << (pin % 4) * 2);
    p1->callbacks[pin] = cb;
    PCIFR |= (1 << PCIF1);
    PCICR |= (1 << PCIE1);
    PCMSK1 |= (1 << pin);
  }
}

static bool isEmpty(const pinchange_t *pc, uint8_t len = 8) {
  for (uint8_t i = 0; i < len; ++i) {
    if (pc->callbacks[i])
      return false;
  }

  return true;
}

void detachInterruptEx(uint8_t pin) {
  if (pin < 8) { // D0..D7
    if (p2) {
      p2->conditions[pin / 4] &= ~(uint8_t)(PC_ANY << (pin % 4) * 2);
      p2->callbacks[pin] = NULL;
      PCMSK2 &= ~(uint8_t)(1 << pin);
      if (isEmpty(p2)) {
        PCICR &= ~(uint8_t)(1 << PCIE2);
        free(p2);
        p2 = NULL;
      }
    }
  } else if (pin < A0) { // D8..D13
    pin -= 8;
    if (p0) {
      p0->conditions[pin / 4] &= ~(uint8_t)(PC_ANY << (pin % 4) * 2);
      p0->callbacks[pin] = NULL;
      PCMSK0 &= ~(uint8_t)(1 << pin);
      if (isEmpty(p0, 6)) {
        PCICR &= ~(uint8_t)(1 << PCIE0);
        free(p0);
        p0 = NULL;
      }
    }
  } else if (pin <= A5) { // A0..A5
    pin -= A0;
    if (p1) {
      p1->conditions[pin / 4] &= ~(uint8_t)(PC_ANY << (pin % 4) * 2);
      p1->callbacks[pin] = NULL;
      PCMSK1 &= ~(uint8_t)(1 << pin);
      if (isEmpty(p1, 6)) {
        PCICR &= ~(uint8_t)(1 << PCIE1);
        free(p1);
        p1 = NULL;
      }
    }
  }
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vanro пишет:

Здравствуйте, Uni. Можете помочь мою библиотеку прерываний переделать, чтобы она могла работать с методами класса?

vanro
Offline
Зарегистрирован: 27.04.2021

Да я сам думал, что лучше на почту написать, тема то 5 лет назад была создана. Когда читал, так орал со срача

vanro
Offline
Зарегистрирован: 27.04.2021

Евгений, можно к вам тогда обратиться? Я просто новичёк. У меня есть проект на Arduino где мне нужно снимать показания с энкодеров, которые стоят на колёсах. Если счётчик прерываний делать статическим в классе Encoder , то он становится как бы общим для обоих колёс и выходит лажа. Мне нужно, чтобы pulses принадлежал конкретным объектам класса Encoder, но чтобы так сделать нужно передавать метод counter, где происходит итерация pulses, в качестве обработчика. Можно ли вообще другой способ организовать логику работы? А то закритиковали Uni много лет назад, я уже ничего не понял под конец чтива.

Вот объявление класса

class Encoder {
  public:
      Encoder();
      Encoder(uint8_t _encoderPin);
      double getRps(void);
      void setPin(uint8_t _encoderPin);
      
      friend void counter(bool pinvalue);
  private:
      static volatile uint32_t pulses;
      const unsigned int quantization_period = 500; //период квантования энкодера
      unsigned long prevTime = 0; 
      uint8_t encoderPin;
      double rps;
};

Вот определение метода

volatile uint32_t Encoder::pulses = 0;

void counter(bool pinvalue) {
  Encoder::pulses++;
};

 

b707
Offline
Зарегистрирован: 26.05.2017

vanro пишет:

А то закритиковали Uni много лет назад, я уже ничего не понял под конец чтива.

вместо кода Уни посмотрите пример Логика в #39. Создайте для каждого экземпяра энкодера свой обработчик-обертку и вызывайте из нее счетчик нужного энкодера, да и все.

Может не очень изящно. зато просто и надежно

А то, что предложил Уни - ну уж слишком кривое.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vanro,

зачем плодить сущности? Такие вещи нужны когда у Вас до хрена объектов и они постоянно то создаются, то уничтожаются.

У Вас же всего два объекта и наверняка они объявлены глобально (по крайней мере ничто не мешает их так объявить), типа:

Encoder levoeKoleso(LEFT_WHEEL_PIN), pravoeKoleso(RIGHT_WHEEL_PIN);

И  функции-обработчики прерываний у Вас наверняка разные. А даже если одна (на PCINT), то легко определить с какого именно пина прерывание.

Ну а раз так, то кто Вам мешает при прерывании для левого колеса так прямо и вызывать

levoeKoleso.counter(pinVal);

ну и для правого соответственно.

Но это будет верно, если Вы сделаете counter методом, а pulses нормальным (нестатическим) членом. Сейчас counter у Вас не метод, а друг.

Неэстетично, конечно, зато дешево, надёжно и практично.

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

class Encoder {
  public:
      Encoder();
      Encoder(uint8_t _encoderPin);
      ........
      void counter(bool pinvalue) volatile { pulses++; }
};

  private:
      volatile uint32_t pulses = 0;
       ....
};

Оно, конечно, умный компилятор по идее и сам это сделает, но я предпочитаю писать сам как надо.