Ну, поехали.
Номера строк, я буду везде приводить по последнему выложенному мною скетчу, а то я его меняю, номера плывут и чёрт их там разберёт. Поэтому, по последнему, выложенному в этом сообщении. Его-то я вижу у себя в 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
). Думаю, так лучше будет.
Ну, вот, как-то так. Простите за много букв, я уже говорил, что мой талант один у родителей, и сестёр у него нет 
P.S. Вроде, в протеусе всё работает