Класс для чтения резисторной клавиатуры

class Resistive_keyboard {
  public:
    bool get();
  private:
    uint32_t _tmr;
    uint8_t _counts;
    bool _status;
};

bool Resistive_keyboard::get() {
  uint16_t _sensor = analogRead(BUTTONS_PIN);
  _status = false;
  if (_sensor > 80 && _sensor < 100) {//93
    if (millis() - _tmr >= 50) {
      _tmr = millis();
      _counts++;
      if (_counts >= 20) {
        _counts = 0;
        _status = true;
      }
    }
  } else {
    _counts = 0;
  }

  return _status;
}

Resistive_keyboard Qwe;



void loop() {

  if (Qwe.get()) {}


У меня if (Qwe.get()) {} не срабатывает. Этот метод класса не вызывается?

Не может быть. Откуда информация, что он не срабатывает?

Сейчас у себя попробую.

Проверяю в протеусе.

Я тоже, всё работает отлично. Вы код выложили не полный, кой-чё добавил. В итоге:

вот такой код
#define printVarLn(x) do { Serial.print(#x "="); Serial.println(x); } while (false)

class Resistive_keyboard {
  public:
    bool get();
  private:
    uint32_t _tmr;
    uint8_t _counts;
    bool _status;
    static constexpr uint8_t BUTTONS_PIN = 0; // это я добавил
};

void setup(void) {
	Serial.begin(9600);
}

bool Resistive_keyboard::get() {
  uint16_t _sensor = analogRead(BUTTONS_PIN);
  _status = false;
  printVarLn(_sensor);
  if (_sensor > 80 && _sensor < 100) {//93
    if (millis() - _tmr >= 50) {
      _tmr = millis();
      _counts++;
      printVarLn(_counts);
      if (_counts >= 20) {
        _counts = 0;
        _status = true;
      }
    }
  } else {
    _counts = 0;
  }

  return _status;
}

Resistive_keyboard Qwe;



void loop() {
	if (Qwe.get()) {
		Serial.println("Ok");
	} else {
		Serial.println("FigVam");
	}

	delay(500); // чтобы не сразу весь экран загаживало
}

выдаёт

вот такой лог сериала
_sensor=92
FigVam
_sensor=92
_counts=1
FigVam
_sensor=92
_counts=2
FigVam
_sensor=92
_counts=3
FigVam
_sensor=92
_counts=4
FigVam
_sensor=92
_counts=5
FigVam
_sensor=92
_counts=6
FigVam
_sensor=92
_counts=7
FigVam
_sensor=92
_counts=8
FigVam
_sensor=92
_counts=9
FigVam
_sensor=92
_counts=10
FigVam
_sensor=92
_counts=11
FigVam
_sensor=92
_counts=12
FigVam
_sensor=92
_counts=13
FigVam
_sensor=92
_counts=14
FigVam
_sensor=92
_counts=15
FigVam
_sensor=92
_counts=16
FigVam
_sensor=92
_counts=17
FigVam
_sensor=92
_counts=18
FigVam
_sensor=92
_counts=19
FigVam
_sensor=92
_counts=20
Ok
_sensor=92
_counts=1
FigVam
_sensor=92
_counts=2
FigVam
_sensor=92
_counts=3
FigVam
_sensor=92
_counts=4
FigVam
_sensor=92
_counts=5
FigVam

Как видите, всё работает нормально. Как счётчик добрался до 20, выдало “Ок”.

Спасибо.
Я переустановила библиотеку для платы Attiny13. Всё заработало.
Код такой

Спойлер
#define REMOTE_PIN    A2
#define BUTTONS_PIN   A3
#define MOTOR1_PIN    2//Вверх
#define MOTOR2_PIN    1//Вниз

#define MOTOR1_ON digitalWrite(MOTOR1_PIN, HIGH)
#define MOTOR1_OFF digitalWrite(MOTOR1_PIN, LOW)
#define MOTOR2_ON digitalWrite(MOTOR2_PIN, HIGH)
#define MOTOR2_OFF digitalWrite(MOTOR2_PIN, LOW)

#define MAX_TIME_UP 2000
#define MAX_TIME_DOWN 2000

bool motor_flag;
uint32_t motor_tmr;
uint32_t motor_off;


void setup() {
  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  MOTOR1_OFF;
  MOTOR2_OFF;
}


class Resistive_keyboard {
  public:
    Resistive_keyboard(uint16_t min, uint16_t max, uint8_t count);
    bool get();
  private:
    uint32_t _tmr;
    uint8_t _counts;
    uint8_t _count;
    bool _status;
    uint16_t _min;
    uint16_t _max;
};

Resistive_keyboard::Resistive_keyboard(uint16_t min, uint16_t max, uint8_t count) {
  _min = min;
  _max = max;
  _count = count;
}

bool Resistive_keyboard::get() {
  uint16_t _sensor = analogRead(BUTTONS_PIN);
  _status = false;
  if (_sensor > _min && _sensor < _max) {//93
    if (millis() - _tmr >= 50) {
      _tmr = millis();
      _counts++;
      if (_counts >= _count) {
        _counts = 0;
        _status = true;
      }
    }
  } else {
    _counts = 0;
  }
  return _status;
}

Resistive_keyboard Up(80, 100, 10);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5);   //250мс (5*50)


void loop() {


  if (Up.get()) {
    MOTOR1_ON;
    MOTOR2_OFF;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_UP;
  }

  if (Down.get()) {
    MOTOR1_OFF;
    MOTOR2_ON;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_DOWN;
  }


  if (motor_flag && millis() - motor_tmr >= motor_off || Stop.get()) {
    motor_flag = false;
    MOTOR1_OFF;
    MOTOR2_OFF;
  }
}

Скетч использует 672 байт (65%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 46 байт (71%) динамической памяти, оставляя 18 байт для локальных переменных. Максимум: 64 байт.

18 байт это нормально в остатке?

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

Если хотите, я могу сегодня вечерком немного поковыряться в коде и поругаться (заодно и расход памяти заметно сократим). Тока, сразу предупреждаю, если уж я начну ругаться, я это обычно добросовестно делаю, ругани много будет. Надо? Если надо, дайте мне ссылку на поддержку Attiny13, которую Вы используете, а то я поставлю какую попало - будет несовпадение с Вашей.

Конечно надо)
https://raw.githubusercontent.com/sleemanj/optiboot/master/dists/package_gogo_diy_attiny_index.json

Хорошо, вечерком поковыряюсь.

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

Сейчас я поставлю Ваше, а у меня: https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json

Второе ядро
https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json

Скетч использует 526 байт (51%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 47 байт (73%) динамической памяти, оставляя 17 байт для локальных переменных. Максимум: 64 байт.

Но в протеусе не работает.

Ну, отлично, теперь у меня всё точно совпало с Вашим

Вечером тогда поковыряюсь.

Вероятно, я не понимаю, как работают МК ограниченным ресурсом, но на мой взгляд, это маловато. Собственно, на мой взгляд, и 64 - тоже маловато.
Как я считаю.
Действительно, 46 байт статической памяти расходуются так:

  1. переменные в строках 14-16 - 9 байт.
  2. 3 объекта по 11 байт - 33 байта.
  3. статическая переменная для millis - 4 байта.

18 байт остается на стек.
Каждый вызов функции: 2 байта на адрес возврата, M байт на локальные переменные, N байт на сохранение регистров.
Цепочка вызовов у нас следующая:
loop
get (+2 байта на переменную _sensor)
analogRead или millis
(прерывание по таймеру для работы millis)

Если при каждом вызове сохраняется не более 2 регистров, вроде, хватает впритык.
Если больше, могут возникнуть проблемы, которые проявятся, когда аппаратное прерывание случится во время выполнения, скажем, millis из get - когда стек и так максимально заполнен. Т.е. проблема может проявиться не сразу.
Единственный вариант анализа, как я вижу, смотреть ассемблерный листинг.
Еще можно запретить прерывания там, где стек заполняется на максимальную глубину.

Ну, поехали.

Номера строк, я буду везде приводить по последнему выложенному мною скетчу, а то я его меняю, номера плывут и чёрт их там разберёт. Поэтому, по последнему, выложенному в этом сообщении. Его-то я вижу у себя в IDE.

Изначальный скетч: (версия 672 / 46)
#define REMOTE_PIN    A2
#define BUTTONS_PIN   A3
#define MOTOR1_PIN    2//Вверх
#define MOTOR2_PIN    1//Вниз

#define MOTOR1_ON digitalWrite(MOTOR1_PIN, HIGH)
#define MOTOR1_OFF digitalWrite(MOTOR1_PIN, LOW)
#define MOTOR2_ON digitalWrite(MOTOR2_PIN, HIGH)
#define MOTOR2_OFF digitalWrite(MOTOR2_PIN, LOW)

#define MAX_TIME_UP 2000
#define MAX_TIME_DOWN 2000

bool motor_flag;
uint32_t motor_tmr;
uint32_t motor_off;

void setup() {
  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  MOTOR1_OFF;
  MOTOR2_OFF;
}

class Resistive_keyboard {
  public:
    Resistive_keyboard(uint16_t min, uint16_t max, uint8_t count);
    bool get();
  private:
    uint32_t _tmr;
    uint8_t _counts;
    uint8_t _count;
    bool _status;
    uint16_t _min;
    uint16_t _max;
};

Resistive_keyboard::Resistive_keyboard(uint16_t min, uint16_t max, uint8_t count) {
  _min = min;
  _max = max;
  _count = count;
}

bool Resistive_keyboard::get() {
  uint16_t _sensor = analogRead(BUTTONS_PIN);
  _status = false;
  if (_sensor > _min && _sensor < _max) {//93
    if (millis() - _tmr >= 50) {
      _tmr = millis();
      _counts++;
      if (_counts >= _count) {
        _counts = 0;
        _status = true;
      }
    }
  } else {
    _counts = 0;
  }
  return _status;
}

Resistive_keyboard Up(80, 100, 10);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5);   //250мс (5*50)

void loop() {
  if (Up.get()) {
    MOTOR1_ON;
    MOTOR2_OFF;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_UP;
  }
  if (Down.get()) {
    MOTOR1_OFF;
    MOTOR2_ON;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_DOWN;
  }
  if (motor_flag && millis() - motor_tmr >= motor_off || Stop.get()) {
    motor_flag = false;
    MOTOR1_OFF;
    MOTOR2_OFF;
  }
}

В строке №81, формально написано всё правильно, но я бы поставил скобки, чтобы не думалось, да и компилятор без скобок с предупреждениями лезет:

  if ((motor_flag && millis() - motor_tmr >= motor_off) || Stop.get()) {

В строке №33. Свойство _status абсолютно лишнее в классе. Оно используется только в методе get() и его использование начинается с присваивания. Так зачем тратить память на постоянное хранение старого значения? Строку №33 удаляем, а строку №46 заменяем на:

bool _status = false;

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

Едем дальше. В конструкторе класса у нас не делается ничего, кроме присваивания свойствам значений параметров конструктора. Кроме того, все экземпляры класса создаются с константными параметрами. Это сочетание просто требует исполнения конструктора на этапе компиляции! Зачем его исполнять при исполнении программы? Пусть компилятор проинициализирует переменные и всего делов. Заменяем конструктор на «constexpr конструктор» и выигрываем сразу 54 байта программной памяти!

Теперь наш скетч занимает 616 байтов программы и 43 байта переменных и выглядит вот так:

Версия 616/43
#define REMOTE_PIN    A2
#define BUTTONS_PIN   A3
#define MOTOR1_PIN    2//Вверх
#define MOTOR2_PIN    1//Вниз

#define MOTOR1_ON digitalWrite(MOTOR1_PIN, HIGH)
#define MOTOR1_OFF digitalWrite(MOTOR1_PIN, LOW)
#define MOTOR2_ON digitalWrite(MOTOR2_PIN, HIGH)
#define MOTOR2_OFF digitalWrite(MOTOR2_PIN, LOW)

#define MAX_TIME_UP 2000
#define MAX_TIME_DOWN 2000

bool motor_flag;
uint32_t motor_tmr;
uint32_t motor_off;

void setup() {
  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  MOTOR1_OFF;
  MOTOR2_OFF;
}

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count) 
    		: _tmr(0), _counts(0), _count(count), _min(min), _max(max) {}
    bool get();
  private:
    uint32_t _tmr;
    uint8_t _counts;
    uint8_t _count;
    uint16_t _min;
    uint16_t _max;
};

bool Resistive_keyboard::get() {
  uint16_t _sensor = analogRead(BUTTONS_PIN);
  bool _status = false;
  if (_sensor > _min && _sensor < _max) {//93
    if (millis() - _tmr >= 50) {
      _tmr = millis();
      _counts++;
      if (_counts >= _count) {
        _counts = 0;
        _status = true;
      }
    }
  } else {
    _counts = 0;
  }
  return _status;
}

Resistive_keyboard Up(80, 100, 10);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5);   //250мс (5*50)

void loop() {
  if (Up.get()) {
    MOTOR1_ON;
    MOTOR2_OFF;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_UP;
  }
  if (Down.get()) {
    MOTOR1_OFF;
    MOTOR2_ON;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_DOWN;
  }
  if ((motor_flag && millis() - motor_tmr >= motor_off) || Stop.get()) {
    motor_flag = false;
    MOTOR1_OFF;
    MOTOR2_OFF;
  }
}

Теперь давайте исправим небольшую ошибочку в методе get() и, заодно, причешем его в более божеский вид.

Ошибка состоит в том, что самый первый интервал в 50мс обычно не отрабатывает. Если данную кнопку давно (более 50 мс) никто не нажимал, а тут нажали, то условие в строке №42 выполнится сразу же, т.к. свойство _tmr содержит очень «древнее» значение. Если мы хотим, чтобы этот интервал честно отрабатывал, как все остальные, нам надо присваивать _tmr текущее значение millis() всякий раз, когда мы определили, что кнопка не нажата (в районе строки №51). Тогда, когда кнопку всё-таки нажмут, в _tmr у нас будет значение от последней проверки, а учитывая, что это проверки идут в loop постоянно – оно будет достаточно свежим. Итак, вставляем после строки №51 строку:

_tmr = millis();

Теперь первый интервал отрабатывает нормально, как все остальные.

Далее, заметим, что в этой маленькой функции, в которой нет никаких «долгоиграющих» операций, мы зачем-то трижды вызываем millis(). Зачем? Скорее всего, в течение этой функции она не изменится, а если и успеет измениться на 1, нам это абсолютно неважно! Давайте вызывать millis один раз в начале, а потом использовать готовое значение.

Ну, и, наконец, мы отсчитываем интервалы в 50 мс. Зачем нам значение времени аж uint32_t? При каких условиях нам такое огромное поле потребуется? Ни при каких! Нам здесь достаточно uint8_t за глаза! И для значения millis(), и для свойства _tmr.

Мелочь на закуску – переменная _sensor, получив значение, больше не изменяется. Делаем её константой.

Пора обновить текст. Кстати, заметьте, что наши последние манипуляции сократили память программ ещё на 28 байтов (стало 588), а память данных – на восемь байтов (стало 35). Скетч получился вот таким.

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

Версия 588/35 (584/35)
#define REMOTE_PIN    A2
#define BUTTONS_PIN   A3
#define MOTOR1_PIN    2//Вверх
#define MOTOR2_PIN    1//Вниз

#define MOTOR1_ON digitalWrite(MOTOR1_PIN, HIGH)
#define MOTOR1_OFF digitalWrite(MOTOR1_PIN, LOW)
#define MOTOR2_ON digitalWrite(MOTOR2_PIN, HIGH)
#define MOTOR2_OFF digitalWrite(MOTOR2_PIN, LOW)

#define MAX_TIME_UP 2000
#define MAX_TIME_DOWN 2000

bool motor_flag;
uint32_t motor_tmr;
uint32_t motor_off;

void setup() {
  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  MOTOR1_OFF;
  MOTOR2_OFF;
}

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count) 
    		: _tmr(0), _counts(0), _count(count), _min(min), _max(max) {}
    bool get();
  private:
    uint8_t _tmr;
    uint8_t _counts;
    uint8_t _count;
    uint16_t _min;
    uint16_t _max;
};

bool Resistive_keyboard::get() {
  const uint16_t _sensor = analogRead(BUTTONS_PIN);
  const uint8_t nowMillis = millis();
  bool _status = false;
  if (_sensor > _min && _sensor < _max) {//93
    if (static_cast<uint8_t>(nowMillis - _tmr) >= 50) {
      _tmr = nowMillis;
      if (++_counts >= _count) {
        _counts = 0;
        _status = true;
      }
    }
  } else {
    _counts = 0;
    _tmr = nowMillis;
  }
  return _status;
}

Resistive_keyboard Up(80, 100, 10);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5);   //250мс (5*50)

void loop() {
  if (Up.get()) {
    MOTOR1_ON;
    MOTOR2_OFF;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_UP;
  }
  if (Down.get()) {
    MOTOR1_OFF;
    MOTOR2_ON;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_DOWN;
  }
  if ((motor_flag && millis() - motor_tmr >= motor_off) || Stop.get()) {
    motor_flag = false;
    MOTOR1_OFF;
    MOTOR2_OFF;
  }
}

Обратите внимание на строки №№11 и 12. Если числа там никогда не будут превышать 65535, то из тех, же соображений, что были описаны выше, можно смело менять тип переменных motor_tmr и motor_off на uint16_t, что даст Вам ещё 4 байта экономии памяти данных.

Эти переменные (как и motor_flag, кстати) используются только в функции loop! Тогда, что они делают в глобальном контексте? Нечего им там делать, объявляем их static и переносим в loop.

Вы не поверите, но эти манипуляции дали нам ещё 50 (!!!) байтов памяти программы и четыре байта памяти данных!

Скетч теперь такой:

Версия 538/31 (518/31)
#define REMOTE_PIN    A2
#define BUTTONS_PIN   A3
#define MOTOR1_PIN    2//Вверх
#define MOTOR2_PIN    1//Вниз

#define MOTOR1_ON digitalWrite(MOTOR1_PIN, HIGH)
#define MOTOR1_OFF digitalWrite(MOTOR1_PIN, LOW)
#define MOTOR2_ON digitalWrite(MOTOR2_PIN, HIGH)
#define MOTOR2_OFF digitalWrite(MOTOR2_PIN, LOW)

#define MAX_TIME_UP 2000
#define MAX_TIME_DOWN 2000

void setup() {
  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  MOTOR1_OFF;
  MOTOR2_OFF;
}

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count) 
    		: _tmr(0), _counts(0), _count(count), _min(min), _max(max) {}
    bool get();
  private:
    uint8_t _tmr;
    uint8_t _counts;
    uint8_t _count;
    uint16_t _min;
    uint16_t _max;
};

bool Resistive_keyboard::get() {
	const uint16_t _sensor = analogRead(BUTTONS_PIN);
	const uint8_t nowMillis = millis();
	bool _status = false;
	if (_sensor > _min && _sensor < _max) {//93
		if (static_cast<uint8_t>(nowMillis - _tmr) >= 50) {
			_tmr = nowMillis;
			if (++_counts >= _count) {
				_counts = 0;
				_status = true;
			}
		}
	} else {
		_counts = 0;
		_tmr = nowMillis;
	}
	return _status;
}

Resistive_keyboard Up(80, 100, 10);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5);   //250мс (5*50)

void loop() {
	static bool motor_flag;
	static uint16_t motor_tmr;
	static uint16_t motor_off;

  if (Up.get()) {
    MOTOR1_ON;
    MOTOR2_OFF;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_UP;
  }
  if (Down.get()) {
    MOTOR1_OFF;
    MOTOR2_ON;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_DOWN;
  }
  if ((motor_flag && static_cast<uint16_t>(millis() - motor_tmr) >= motor_off) || Stop.get()) {
    motor_flag = false;
    MOTOR1_OFF;
    MOTOR2_OFF;
  }
}

Теперь, давайте посмотрим на класс. Вы предлагаете хранить _min и _max как двухбайтовые целые и это правильно. Но, давайте подумаем:

Мысля №1. Разница между _max и _min по смыслу задачи не очень большая и уж в байт точно влезет! Значит, можно хранить только _min (uint16_t) и разницу между ними (_diff - uint8_t). Экономим три байтика + ещё один от выравнивания, итого – четыре. Вот скетч:

Версия 538/27 (518/27)
#define REMOTE_PIN    A2
#define BUTTONS_PIN   A3
#define MOTOR1_PIN    2//Вверх
#define MOTOR2_PIN    1//Вниз

#define MOTOR1_ON digitalWrite(MOTOR1_PIN, HIGH)
#define MOTOR1_OFF digitalWrite(MOTOR1_PIN, LOW)
#define MOTOR2_ON digitalWrite(MOTOR2_PIN, HIGH)
#define MOTOR2_OFF digitalWrite(MOTOR2_PIN, LOW)

#define MAX_TIME_UP 2000
#define MAX_TIME_DOWN 2000

void setup() {
  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  MOTOR1_OFF;
  MOTOR2_OFF;
}

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count) 
    		: _min(min), _diff(max - min), _tmr(0), _counts(0), _count(count) {}
    bool get();
  private:
    const uint16_t _min;
    const uint8_t _diff;
    uint8_t _tmr;
    uint8_t _counts;
    uint8_t _count;
};

bool Resistive_keyboard::get() {
	const uint16_t _sensor = analogRead(BUTTONS_PIN);
	const uint8_t nowMillis = millis();
	bool _status = false;
	if (_sensor > _min && _sensor < (_min + _diff)) {//93
		if (static_cast<uint8_t>(nowMillis - _tmr) >= 50) {
			_tmr = nowMillis;
			if (++_counts >= _count) {
				_counts = 0;
				_status = true;
			}
		}
	} else {
		_counts = 0;
		_tmr = nowMillis;
	}
	return _status;
}

Resistive_keyboard Up(80, 100, 10);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5);   //250мс (5*50)

void loop() {
	static uint16_t motor_tmr;
	static uint16_t motor_off;
	static bool motor_flag;

  if (Up.get()) {
    MOTOR1_ON;
    MOTOR2_OFF;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_UP;
  }
  if (Down.get()) {
    MOTOR1_OFF;
    MOTOR2_ON;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_DOWN;
  }
  if ((motor_flag && static_cast<uint16_t>(millis() - motor_tmr) >= motor_off) || Stop.get()) {
    motor_flag = false;
    MOTOR1_OFF;
    MOTOR2_OFF;
  }
}

Мысля №2. А давайте пойдём дальше! Нас ведь интересует не конкретное значение, а попадает ли оно в интервал между _min и _max, так ведь? Причём этот интервал, заведомо больше четырёх (точность АЦП этого требует). Но тогда, мы ведь можем хранить не _min и _max, а их же делёнными на 4 (тогда они обе в байт поместятся!), а результат analogRead сразу же делить на 4! В итоге, получим то же самое, но несколько байтиков «захомячим» (вообще-то три байтика, но там один опять вылезет на выравнивание). Смотрите, как это выглядит:

Версия 538/25 (518/25)
#define REMOTE_PIN    A2
#define BUTTONS_PIN   A3
#define MOTOR1_PIN    2//Вверх
#define MOTOR2_PIN    1//Вниз

#define MOTOR1_ON digitalWrite(MOTOR1_PIN, HIGH)
#define MOTOR1_OFF digitalWrite(MOTOR1_PIN, LOW)
#define MOTOR2_ON digitalWrite(MOTOR2_PIN, HIGH)
#define MOTOR2_OFF digitalWrite(MOTOR2_PIN, LOW)

#define MAX_TIME_UP 2000
#define MAX_TIME_DOWN 2000

void setup() {
  pinMode(MOTOR1_PIN, OUTPUT);
  pinMode(MOTOR2_PIN, OUTPUT);
  MOTOR1_OFF;
  MOTOR2_OFF;
}

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count) 
    		: _min(min / 4), _max(max / 4), _tmr(0), _counts(0), _count(count) {}
    bool get();
  private:
    const uint8_t _min;
    const uint8_t _max;
    uint8_t _tmr;
    uint8_t _counts;
    uint8_t _count;
};

bool Resistive_keyboard::get() {
	const uint16_t _sensor = analogRead(BUTTONS_PIN) / 4;
	const uint8_t nowMillis = millis();
	bool _status = false;
	if (_sensor > _min && _sensor < _max) {//93
		if (static_cast<uint8_t>(nowMillis - _tmr) >= 50) {
			_tmr = nowMillis;
			if (++_counts >= _count) {
				_counts = 0;
				_status = true;
			}
		}
	} else {
		_counts = 0;
		_tmr = nowMillis;
	}
	return _status;
}

Resistive_keyboard Up(80, 100, 10);     //500мс (10*50)
Resistive_keyboard Down(270, 300, 10);  //500мс (10*50)
Resistive_keyboard Stop(350, 380, 5);   //250мс (5*50)

void loop() {
	static uint16_t motor_tmr;
	static uint16_t motor_off;
	static bool motor_flag;

  if (Up.get()) {
    MOTOR1_ON;
    MOTOR2_OFF;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_UP;
  }
  if (Down.get()) {
    MOTOR1_OFF;
    MOTOR2_ON;
    motor_flag = true;
    motor_tmr = millis();
    motor_off = MAX_TIME_DOWN;
  }
  if ((motor_flag && static_cast<uint16_t>(millis() - motor_tmr) >= motor_off) || Stop.get()) {
    motor_flag = false;
    MOTOR1_OFF;
    MOTOR2_OFF;
  }
}

Ну, вот, такими нехитрыми шагами, мы уменьшили расход памяти с 672/46, до 538/25. Свободной памяти данных у Вас стало не 18 байтов, а 39, т.е. вдвое больше.

Теперь позвольте мне пройтись просто по коду (номера строк по последнему скетчу) и поворчать по-стариковски, уже без правок.

Строки №№ 6-9 – просто аплодирую! Все бы так делали. Чисто технически я бы советовал делать это inline функциями, а не макросами, но сама идея дать операциям осмысленные названия – нашим новичкам это надо на лбу в граните отливать!

Строки №№53-55. Эти переменные используются только в функции loop. Я бы их там и объявлял со словом static. Зачем им светиться на весь код? Не стоит расширять область видимости без крайней нужды. В смысле не стоит привыкать к этому.

Дополнение (вчера думал об этом, но забыл написать). Строки №№31 и 31 по последнему коду. Через месяц Вы ни в жисть не вспомните, что там для чего. Ну, обзовите их типа counter и countLimit - сразу понятно.

Это же касается MOTOR(1/2). Моторы же у Вас “вверх” и “вниз”. Ну, и называйте так, зачем 1 и 2? Чтобы потом думать что и где?

Ещё дополнение: раз уж мы делим на 4 с отбрасыванием остатка, наверное, надёжнее будет в строке №38 для нижней границы заменить “больше” на “больше или равно” (_sensor >= _min). Думаю, так лучше будет.

Ну, вот, как-то так. Простите за много букв, я уже говорил, что мой талант один у родителей, и сестёр у него нет :frowning:

P.S. Вроде, в протеусе всё работает

4 лайка

Не, это надо на ночь читаь, :+1:

Не знаю, сильно ли меня будет чихвостить ЕП, но сунусь тоже.

class Resistive_keyboard {
  public:
    constexpr Resistive_keyboard(const uint16_t min, const uint16_t max) 
    		: _min(min), _diff(max - min), _tmr(0)) {}
    bool get();
  private:
    const uint16_t _min;
    const uint8_t _diff;
    uint8_t _tmr;
    uint8_t _statusTest;
    bool _status;
};

bool Resistive_keyboard::get() {
	const uint8_t nowMillis = millis();
        // Every millisecond (or two, three...) check the input state
   	if (nowMillis - _tmr >= 0x01) { 
   	  const uint16_t _sensor = analogRead(BUTTONS_PIN) / 4;
         _tmr = nowMillis;
         _statusTest = (adcState << 0x01) | (_sensor > _min && _sensor < (_min + _diff)); 
         if (0xFF == _statusTest) { _status = true;  } // 100 % in range for 8 reads/8 ms (8-bit _statusTest "counter var")
         if (0x00 == _statusTest) { _status = false; } // 100 % not in range for 8 reads/8 ms
         // Otherwize we have unstable state, and do nothing
        }
 	return _status;
}

Нааверное на ггранитном лбу бронзой высекать, так ближе к классике

Настал бронзовый век программирования?

Не просто кто-то из веиких говорил про латынь что-то типа этот язык высечен бронзой на граните

ОГО. Огромное спасибо. Буду разбираться, думаю вопросов будет немало.
Схема вот такая:

Спойлер

Я вставила Ваш код, еще не разбиралась в нём, в протеусе не работает.

Я начну задавать вопросы издалека…)
inline функциями - это вот так?

inline void MOTOR5_ON(void){digitalWrite(MOTOR1_PIN, HIGH);}
inline void MOTOR5_OFF(void){digitalWrite(MOTOR1_PIN, LOW);}

  MOTOR5_ON();
  delay(1000);
  MOTOR5_OFF();
  delay(1000);

Я недавнее время приучилась делать вот так

#define MOTOR_OUT   DDRB  |=  1 << 2
#define MOTOR_ON    PORTB |=  1 << 2
#define MOTOR_OFF   PORTB &= ~1 << 2

Но кто-то из участников, ещё на старом форуме, сказали что это “бессмысленно”, что разница будет незаметна.

constexpr Resistive_keyboard(const uint16_t min, const uint16_t max, const uint8_t count) 
    		: _tmr(0), _counts(0), _count(count), _min(min), _max(max) {}

Не понимаю что такое constexpr

Для человека разницы не будет.
А для МК - будет. И по скорости и по флешу. Другое дело - нужна ли скорость для вызова раз в полгода.

1 лайк

Евгений Петрович, у меня пара соображений (или непоняток), если позволите.

  1. С “переполнением millis” все хорошо, если оно происходит не более одного раза. В данном коде, очевидно, проблем быть не должно, но классы обычно разрабатываются, чтобы их, особенно не задумываясь, использовать в будущих проектах, а также в случае развития данного проекта.
    Можно ведь допустить, что продолжительность loop превысит четверть секунды и строка 39 сработает не так, как нам бы хотелось. Конечно, в этом случае с клавиатурой будет работать не слишком комфортно (нажатия короче четверти секунды будут пропускаться), но проблема в том, что точно так же могут пропускаться и нажатия 0.5 секунды, 0.75 секунды и пр. Один байт (четверть секунды) - это не слишком мало для хранения времени?

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

  3. Не проигрываем ли мы за счет стека, если вместо троекратного вызова millis создаем лишнюю локальную переменную? У нас ведь основной ограничивающий ресурс не время выполнения, а расход памяти.