Классы Ардуино по qwone для чайников.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

ссылка на указатель функции . Само сочетание слов  уже сбивает с толка.

void func() { // это просто функция
  Serial.println("qwqewqewq");
}
void (*pFun)() = &func;// это указатель на функцию
void (*&pA)() = pFun;// это ссылка на указатель функции

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

 

5N62V
Онлайн
Зарегистрирован: 25.02.2016

qwone пишет:

Следующий этап Пример 1

/* Пример 1

*/
//---------------классы---------
// пример класса Cl_AAA
class Cl_AAA {
   int data;// параметры класса
  public:
    // конструктор класса
    Cl_AAA(int _data):data(_data) {}

Как раз читаю книгу по С++. И вроде конструкторы уже позади, но эта форма конструктора не понятна. Во всех примерах конструктор выглядит ну где-то так: 

Cl_AAA(int k){data = k;}

Не сочтите за труд направить где почитать про такую форму?

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

5N62V !Цитата от сюда https://msdn.microsoft.com/ru-ru/library/s16xw1a8.aspx#member_lists

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

ПС: Си(Си++) не стоит на месте, он развивается. А значит находятся новые приемы, чем описаны в более старых источниках.

eXecutioner
eXecutioner аватар
Offline
Зарегистрирован: 09.01.2018

qwone пишет:

Вот еще код под RFID. Переделал свой старый код под существующую концепцию



 

а есть вариант что бы работало с Двумя ( для карт(MFRC522) и в виде брелков(металических))

пытаюсь с CLASS сделать но что то не выходит

> Дубликатор <

 

 

Заранее благоарю)

 

ЗЫ. не получается совместить всё и смена режима считывания на кнопку отдельную.

а второй кнопкой делать (запись ->считывание) 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Ну нет у меня двух RFID приемников. Что в наборе было, тем и пользовался . Был брелок там и карточка. Но приемник там был один на все и работал.

eXecutioner
eXecutioner аватар
Offline
Зарегистрирован: 09.01.2018

у меня выходит что нужно совместить грубо говоря.

#include <OneWire.h>

#define pin 4
OneWire ibutton (pin); // Пин D11 для подлючения iButton (Data)
byte addr[8];
byte ReadID[8] = { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F }; // "Универсальный" ключ. Прошивается последовательность 01:FF:FF:FF:FF:FF:FF:2F

const int buttonPin = 5;
const int buttonPin1 = 6;

const int ledPin = 7;
const int ledPin1 = 8;

int buttonState = 0;
int writeflag = 0;
int readflag = 0;
int val = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  
  pinMode(ledPin1, OUTPUT);
  pinMode(buttonPin1, INPUT);
  
  Serial.begin(115200);
}

void loop() {

  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    readflag = 1;
    writeflag = 1;
    digitalWrite(ledPin, HIGH);
    val++;
    if (val > 6) val = 6;
    Serial.print(val);
    delay(500);
  }
  else {
    val = 0;
  }


  if (!ibutton.search (addr)) {
    ibutton.reset_search();
    delay(50);
    if (val <= 5) return;
    val = 0;
  }

  digitalWrite(ledPin, HIGH);
  delay(50);

  for (byte x = 0; x < 8; x++) {
    Serial.print(addr[x], HEX);
    if (readflag == 0) {
      ReadID[x] = (addr[x]);
    }
    Serial.print(":");
  }

  byte crc; // Проверка контрольной суммы
  crc = ibutton.crc8(addr, 7);
  Serial.print("CRC: ");
  Serial.println(crc, HEX);
  digitalWrite(ledPin, LOW);

  if ((writeflag == 1) or (Serial.read() == 'w')) {
    ibutton.skip(); ibutton.reset(); ibutton.write(0x33);
    Serial.print("  ID before write:");
    for (byte x = 0; x < 8; x++) {
      Serial.print(' ');
      Serial.print(ibutton.read(), HEX);
    }
    // send reset
    ibutton.skip();
    ibutton.reset();
    // send 0xD1
    ibutton.write(0xD1);
    // send logical 0
    digitalWrite(pin, LOW); pinMode(pin, OUTPUT); delayMicroseconds(60);
    pinMode(pin, INPUT); digitalWrite(pin, HIGH); delay(10);

    Serial.print('\n');
    Serial.print("  Writing iButton ID:\n    ");
    byte newID[8] = { (ReadID[0]), (ReadID[1]), (ReadID[2]), (ReadID[3]), (ReadID[4]), (ReadID[5]), (ReadID[6]), (ReadID[7]) };
    ibutton.skip();
    ibutton.reset();
    ibutton.write(0xD5);
    for (byte x = 0; x < 8; x++) {
      writeByte(newID[x]);
      Serial.print('*');
    }
    Serial.print('\n');
    ibutton.reset();
    // send 0xD1
    ibutton.write(0xD1);
    //send logical 1
    digitalWrite(pin, LOW); pinMode(pin, OUTPUT); delayMicroseconds(10);
    pinMode(pin, INPUT); digitalWrite(pin, HIGH); delay(10);
    writeflag = 0;
    readflag = 0;
    digitalWrite(ledPin, LOW);
  }
}

int writeByte(byte data) {
  int data_bit;
  for (data_bit = 0; data_bit < 8; data_bit++) {
    if (data & 1) {
      digitalWrite(pin, LOW); pinMode(pin, OUTPUT);
      delayMicroseconds(60);
      pinMode(pin, INPUT); digitalWrite(pin, HIGH);
      delay(10);
    } else {
      digitalWrite(pin, LOW); pinMode(pin, OUTPUT);
      pinMode(pin, INPUT); digitalWrite(pin, HIGH);
      delay(10);
    }
    data = data >> 1;
  }
  return 0;
}

и второй для MFRC522

вот для него еще не сделал(не нашел скейтч)

есть только считывание 

записать только не получается с скопированного

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016
eXecutioner
eXecutioner аватар
Offline
Зарегистрирован: 09.01.2018

у меня выходит что нужно совместить грубо говоря.

#include <OneWire.h>

#define pin 4
OneWire ibutton (pin); // Пин D11 для подлючения iButton (Data)
byte addr[8];
byte ReadID[8] = { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F }; // "Универсальный" ключ. Прошивается последовательность 01:FF:FF:FF:FF:FF:FF:2F

const int buttonPin = 5;
const int buttonPin1 = 6;

const int ledPin = 7;
const int ledPin1 = 8;

int buttonState = 0;
int writeflag = 0;
int readflag = 0;
int val = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
  
  pinMode(ledPin1, OUTPUT);
  pinMode(buttonPin1, INPUT);
  
  Serial.begin(115200);
}

void loop() {

  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    readflag = 1;
    writeflag = 1;
    digitalWrite(ledPin, HIGH);
    val++;
    if (val > 6) val = 6;
    Serial.print(val);
    delay(500);
  }
  else {
    val = 0;
  }


  if (!ibutton.search (addr)) {
    ibutton.reset_search();
    delay(50);
    if (val <= 5) return;
    val = 0;
  }

  digitalWrite(ledPin, HIGH);
  delay(50);

  for (byte x = 0; x < 8; x++) {
    Serial.print(addr[x], HEX);
    if (readflag == 0) {
      ReadID[x] = (addr[x]);
    }
    Serial.print(":");
  }

  byte crc; // Проверка контрольной суммы
  crc = ibutton.crc8(addr, 7);
  Serial.print("CRC: ");
  Serial.println(crc, HEX);
  digitalWrite(ledPin, LOW);

  if ((writeflag == 1) or (Serial.read() == 'w')) {
    ibutton.skip(); ibutton.reset(); ibutton.write(0x33);
    Serial.print("  ID before write:");
    for (byte x = 0; x < 8; x++) {
      Serial.print(' ');
      Serial.print(ibutton.read(), HEX);
    }
    // send reset
    ibutton.skip();
    ibutton.reset();
    // send 0xD1
    ibutton.write(0xD1);
    // send logical 0
    digitalWrite(pin, LOW); pinMode(pin, OUTPUT); delayMicroseconds(60);
    pinMode(pin, INPUT); digitalWrite(pin, HIGH); delay(10);

    Serial.print('\n');
    Serial.print("  Writing iButton ID:\n    ");
    byte newID[8] = { (ReadID[0]), (ReadID[1]), (ReadID[2]), (ReadID[3]), (ReadID[4]), (ReadID[5]), (ReadID[6]), (ReadID[7]) };
    ibutton.skip();
    ibutton.reset();
    ibutton.write(0xD5);
    for (byte x = 0; x < 8; x++) {
      writeByte(newID[x]);
      Serial.print('*');
    }
    Serial.print('\n');
    ibutton.reset();
    // send 0xD1
    ibutton.write(0xD1);
    //send logical 1
    digitalWrite(pin, LOW); pinMode(pin, OUTPUT); delayMicroseconds(10);
    pinMode(pin, INPUT); digitalWrite(pin, HIGH); delay(10);
    writeflag = 0;
    readflag = 0;
    digitalWrite(ledPin, LOW);
  }
}

int writeByte(byte data) {
  int data_bit;
  for (data_bit = 0; data_bit < 8; data_bit++) {
    if (data & 1) {
      digitalWrite(pin, LOW); pinMode(pin, OUTPUT);
      delayMicroseconds(60);
      pinMode(pin, INPUT); digitalWrite(pin, HIGH);
      delay(10);
    } else {
      digitalWrite(pin, LOW); pinMode(pin, OUTPUT);
      pinMode(pin, INPUT); digitalWrite(pin, HIGH);
      delay(10);
    }
    data = data >> 1;
  }
  return 0;
}

и второй для MFRC522

/**
 * ----------------------------------------------------------------------------
 * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid
 * for further details and other examples.
 * 
 * NOTE: The library file MFRC522.h has a lot of useful info. Please read it.
 * 
 * Released into the public domain.
 * ----------------------------------------------------------------------------
 * This sample shows how to read and write data blocks on a MIFARE Classic PICC
 * (= card/tag).
 * 
 * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7).
 * 
 * 
 * Typical pin layout used:
 * -----------------------------------------------------------------------------------------
 *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
 *             Reader/PCD   Uno           Mega      Nano v3    Leonardo/Micro   Pro Micro
 * Signal      Pin          Pin           Pin       Pin        Pin              Pin
 * -----------------------------------------------------------------------------------------
 * RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST
 * SPI SS      SDA(SS)      10            53        D10        10               10
 * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
 * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
 * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15
 * 
 */

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9           // Configurable, see typical pin layout above
#define SS_PIN          10          // Configurable, see typical pin layout above

MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance.

MFRC522::MIFARE_Key key;

/**
 * Initialize.
 */
void setup() {
    Serial.begin(9600); // Initialize serial communications with the PC
    while (!Serial);    // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
    SPI.begin();        // Init SPI bus
    mfrc522.PCD_Init(); // Init MFRC522 card

    // Prepare the key (used both as key A and as key B)
    // using FFFFFFFFFFFFh which is the default at chip delivery from the factory
    for (byte i = 0; i < 6; i++) {
        key.keyByte[i] = 0xFF;
    }

    Serial.println(F("Scan a MIFARE Classic PICC to demonstrate read and write."));
    Serial.print(F("Using key (for A and B):"));
    dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);
    Serial.println();
    
    Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1"));
}

/**
 * Main loop.
 */
void loop() {
    // Look for new cards
    if ( ! mfrc522.PICC_IsNewCardPresent())
        return;

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial())
        return;

    // Show some details of the PICC (that is: the tag/card)
    Serial.print(F("Card UID:"));
    dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);
    Serial.println();
    Serial.print(F("PICC type: "));
    byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
    Serial.println(mfrc522.PICC_GetTypeName(piccType));

    // Check for compatibility
    if (    piccType != MFRC522::PICC_TYPE_MIFARE_MINI
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
        Serial.println(F("This sample only works with MIFARE Classic cards."));
        return;
    }

    // In this sample we use the second sector,
    // that is: sector #1, covering block #4 up to and including block #7
    byte sector         = 1;
    byte blockAddr      = 4;
    byte dataBlock[]    = {
        0x01, 0x02, 0x03, 0x04, //  1,  2,   3,  4,
        0x05, 0x06, 0x07, 0x08, //  5,  6,   7,  8,
        0x08, 0x09, 0xff, 0x0b, //  9, 10, 255, 12,
        0x0c, 0x0d, 0x0e, 0x0f  // 13, 14,  15, 16
    };
    byte trailerBlock   = 7;
    byte status;
    byte buffer[18];
    byte size = sizeof(buffer);

    // Authenticate using key A
    Serial.println(F("Authenticating using key A..."));
    status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("PCD_Authenticate() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
        return;
    }

    // Show the whole sector as it currently is
    Serial.println(F("Current data in sector:"));
    mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
    Serial.println();

    // Read data from the block
    Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
    Serial.println(F(" ..."));
    status = mfrc522.MIFARE_Read(blockAddr, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("MIFARE_Read() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
    }
    Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
    dump_byte_array(buffer, 16); Serial.println();
    Serial.println();

    // Authenticate using key B
    Serial.println(F("Authenticating again using key B..."));
    status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("PCD_Authenticate() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
        return;
    }

    // Write data to the block
    Serial.print(F("Writing data into block ")); Serial.print(blockAddr);
    Serial.println(F(" ..."));
    dump_byte_array(dataBlock, 16); Serial.println();
    status = mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("MIFARE_Write() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
    }
    Serial.println();

    // Read data from the block (again, should now be what we have written)
    Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
    Serial.println(F(" ..."));
    status = mfrc522.MIFARE_Read(blockAddr, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
        Serial.print(F("MIFARE_Read() failed: "));
        Serial.println(mfrc522.GetStatusCodeName(status));
    }
    Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
    dump_byte_array(buffer, 16); Serial.println();
        
    // Check that data in block is what we have written
    // by counting the number of bytes that are equal
    Serial.println(F("Checking result..."));
    byte count = 0;
    for (byte i = 0; i < 16; i++) {
        // Compare buffer (= what we've read) with dataBlock (= what we've written)
        if (buffer[i] == dataBlock[i])
            count++;
    }
    Serial.print(F("Number of bytes that match = ")); Serial.println(count);
    if (count == 16) {
        Serial.println(F("Success :-)"));
    } else {
        Serial.println(F("Failure, no match :-("));
        Serial.println(F("  perhaps the write didn't work properly..."));
    }
    Serial.println();
        
    // Dump the sector data
    Serial.println(F("Current data in sector:"));
    mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
    Serial.println();

    // Halt PICC
    mfrc522.PICC_HaltA();
    // Stop encryption on PCD
    mfrc522.PCD_StopCrypto1();
}

/**
 * Helper routine to dump a byte array as hex values to Serial.
 */
void dump_byte_array(byte *buffer, byte bufferSize) {
    for (byte i = 0; i < bufferSize; i++) {
        Serial.print(buffer[i] < 0x10 ? " 0" : " ");
        Serial.print(buffer[i], HEX);
    }
}

мне нужно то бы смена режима была на кнопку.

ну по типу .

Включил (ожидание)

нажал одну из 2-ух кнопок для выбора цикла(режима >  скейтча)

мне сама суть нужна смены (выбор).

по сути 2 кнопки всего

-------------------------------------------->      :-)

выбора цикла по идее

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Для тех кого раздражает глобальный писец  mill  могу предложить такой вариант 

/**/
//--------------------------------
class Cl_led {
  protected:
    const byte pin;
    const unsigned long time;
    unsigned long past;
    bool led = 0;
    /*инверсия состояния*/
    void invert() {
      digitalWrite(pin, led = !led);
    }
  public:
    /*конструктор*/
    Cl_led(byte p, unsigned long t = 200)
      : pin(p), time(t) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, OUTPUT);
      invert();
    }
    /*работа-вставить в loop()*/
    void run(unsigned long &mill) {
      if (mill - past >= time) {
        past = mill;
        invert();
      }
    }
};
//---Компоновка-----------------------------
Cl_led led(/*пин*/13);
//---main-----------------------------
void setup() {
  led.init();
}
void loop() {
  unsigned long  mill = millis();
  led.run(mill);
}
/*Скетч использует 938 байт (2%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 19 байт (0%) динамической памяти, оставляя 2029 байт для локальных переменных. Максимум: 2048 байт.
*/

Но лично мне такой вариант не нравится из-за отсутствия симметричности .

В начальном ардуине скетче есть void setup(void)  и симметричная ей void loop(void) . А в этот варианте на   void init(void) идет не симметричный ответ void run(unsigned long &mill)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

qwone пишет:

В начальном ардуине скетче есть void setup(void)  и симметричная ей void loop(void) .

ох, Пух, наерна я, на старости лет, чота не понимаю в симметрии... 

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

Это суперсимметрия. Появление параметра вызвано наличием его обявления ;) Восстановить симетрию можна доработав void init(void) с аналогичным параметром.  А вот передача параметра по ссылке может вызвать глобальное искажение времени. И даже его обратный ход.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Logik пишет:
А вот передача параметра по ссылке может вызвать глобальное искажение времени. И даже его обратный ход.
Если вы заметили ошибку, то укажите и метод решения. Или вы живете по принципу "чукча не читатель- чукча писатель, поэтому чукча не читает, что сам пишет" 

ПС: Не нравится посто ссылка, замените на константную ссылку. 

    /*работа-вставить в loop()*/
    void run(const unsigned long &mill) {
      if (mill - past >= time) {
        past = mill;
        invert();
      }
    }

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Статья :: Обработка событий в С++ : Александр Клюев http://programming-lang.com/ru/comp_programming/klyuev/0/j0.html

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Событийное программирование для "бедных". Во компьютерах очень часто упоминается о собитиях и их обработчиках. Но как очень просто организовать такой подход на Ардуине.  Разумеется можно использовать аппаратное прерывание. Но в некоторых случаях хватает и программного прерывания.

/**/
//---событие A---------------
bool eventA = 0; // 0-не произошло /1- произошло
/*обработчик события*/
void DoA() {}
//---main()------------
void setup() {
}
void loop() {
  if (eventA) DoA();
}

Недостаток такого кода в том, что обработчик вызывается каждый раз, когда проходит loop . Так что есть такой вариант. Выпольнился обработчик и сбросился

/**/
//---событие A---------------
bool eventA = 0; // 0-не произошло /1- произошло
/*обработчик события*/
void DoA() {}
//---main()------------
void setup() {
}
void loop() {
  if (eventA) {
    eventA = 0;
    DoA();
  }
}

Если в процессе надо менять функции обработчики на различные функции, то может пойти такой вариант

/**/
typedef void (*pDo)();
//---событие A---------------
bool eventA = 0; // 0-не произошло /1- произошло
pDo handleA = NULL;
/*обработчик события*/
void DoA() {}
//---main()------------
void setup() {
  /*подключить обработчик*/
  handleA = DoA;
}
void loop() {
  if (eventA) {
    eventA = 0;
    if (handleA) handleA();
  }
}

ПС: Там есть защита от вызова, если функция не подключена

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

А как это выглядит "для богатых"? Вместо "typedef void (*pDo)();" использовать using или как-то по-другому?

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

qwone пишет:

Если в процессе надо менять функции обработчики на различные функции, то может пойти такой вариант

/**/
typedef void (*pDo)();
//---событие A---------------
bool eventA = 0; // 0-не произошло /1- произошло
pDo handleA = NULL;
/*обработчик события*/
void DoA() {}
//---main()------------
void setup() {
  /*подключить обработчик*/
  handleA = DoA;
}
void loop() {
  if (eventA) {
    eventA = 0;
    if (handleA) handleA();
  }
}

и где тут замена обработчиков на разные функци?

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

Код у Вас какойто слишком философский. Нигде eventA в 1 не устанавливается. А раз нет такого - зачем вобще все остальное?  ИМХО рассматривать единичное событие (а я бы сказал что это скорей флаг у Вас чем событие) - пустая трата времени. Запилите сразу очередь событий, да позабористей! Чтоб обработчик событий не просто так, а обрабатывая событие  добавлял пару-тройку новых в очередь. Например, рассмотрим пример, на примере кнопки -  с удержаниями, даблкликами  и прочими наворотами.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Скину сюда учебный скетч Меню , который вызывает у меня кучу вопросов в своей неокоченности

//**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
typedef struct { // тип строка меню
  pDo viev;
  pDo inc;
  pDo dec;
} NoteMenu;
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    unsigned long interval, past;
  public:
    /*конструктор*/
    Cl_Display(unsigned long i, pDo D): interval(i), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (mill - past >= interval) {
        past = mill;
        Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
// класс кнопка с повтором при удерж кнопки
class Cl_BtnR {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//-----Компоновка----------------------
//переменая var1
const int minVar1 = 10;
const int maxVar1 = 100;
int var1 = minVar1;
/*вывевети на экран var1*/
void vievVar1() {
  Serial.print("var1: ");
  Serial.print(var1);
  Serial.println();
}
/*прочитать var1*/
void loadVar1() {}
/*записать var1*/
void saveVar1() {}
/*увеличить var1*/
void incVar1() {
  ++var1;
  if (var1 > maxVar1) var1 = maxVar1;
}
/*уменьшить var1*/
void decVar1() {
  --var1;
  if (var1 < minVar1) var1 = minVar1;
}
//переменая var2
const int minVar2 = 10;
const int maxVar2 = 100;
int var2 = minVar2;
/*вывевети на экран var2*/
void vievVar2() {
  Serial.print("var2: ");
  Serial.print(var2);
  Serial.println();
}
/*прочитать var2*/
void loadVar2() {}
/*записать var2*/
void saveVar2() {}
/*увеличить var2*/
void incVar2() {
  ++var2;
  if (var2 > maxVar2) var2 = maxVar2;
}
/*уменьшить var2*/
void decVar2() {
  --var2;
  if (var2 < minVar2) var2 = minVar2;
}
// меню
int maxScreen = 2;
int screen = 0;
NoteMenu NM[] = {
  {vievVar1, incVar1, decVar1}, /*1 экран*/
  {vievVar2, incVar2, decVar2}  /*2 экран*/
};
// дисплей
Cl_Display Display(/*интервал*/500,/*обработчик*/NM[screen].viev);
// кнопки
Cl_BtnR Btn1(/*пин*/2,/*обработчик*/NM[screen].inc);/*инкрем*/
Cl_BtnR Btn2(/*пин*/3,/*обработчик*/NM[screen].dec);/*декримент*/
void DoBtn3() {
  ++screen;
  if (screen >= maxScreen) screen = 0;
  Display.write(NM[screen].viev);
  Btn1.write(NM[screen].inc);
  Btn2.write(NM[screen].dec);
}
Cl_Btn Btn3(/*пин*/4,/*обработчик*/DoBtn3);/*Next*/
//-----main----------------------
void setup() {
  Serial.begin(9600);
  Display.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Display.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}

/*Скетч использует 3090 байт (10%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 262 байт (12%) динамической памяти, оставляя 1786 байт для локальных переменных. Максимум: 2048 байт.
*/

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Вот немного доработал код Меню. Так немного погонял. Но скорее там надо еще PROGMEM использовать.

//**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//----------------------------------------
#include <EEPROM.h>
// чтение
int EEPROM_int_read(int addr) {
  byte raw[2];
  for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i);
  int &num = (int&)raw;
  return num;
}
// запись
void EEPROM_int_write(int addr, int num) {
  byte raw[2];
  (int&)raw = num;
  for (byte i = 0; i < 2; i++) EEPROM.write(addr + i, raw[i]);
}
// обновить
void EEPROM_int_update(int addr, int num) {
  if (EEPROM_int_read(addr) != num) EEPROM_int_write(addr, num);
}
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    unsigned long interval, past;
  public:
    /*конструктор*/
    Cl_Display(unsigned long i, pDo D): interval(i), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (mill - past >= interval) {
        past = mill;
        Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
// класс кнопка с повтором при удерж кнопки
class Cl_BtnR {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//-----Компоновка----------------------
// база данных настроечных переменных
const int maxNumber = 5;
int number = 0;
int var1, var2 , var3 , var4 , var5 = 10; /*настроечные переменные !!!!!*/
int *pVar[maxNumber] = {&var1, &var2, &var3, &var4, &var5};/*указатели на переменные*/
const char *name[maxNumber] = {"var1", "var2", "var3", "var4", "var5"};/*имя*/
const int addr[maxNumber] = {0, 2, 4, 6, 8};/*адресс*/
const int minVar[maxNumber] = {10, 10, 10, 10, 10};/*минимум*/
const int maxVar[maxNumber] = {100, 100, 100, 100, 100};/*максимум*/
//
int var_, minVar_, maxVar_;
char *name_;
/*вывевети на экран*/
void vievVar() {
  Serial.print(name_);
  Serial.print(": ");
  Serial.print(var_);
  Serial.println();
}
/*прочитать var*/
void loadVar() {
  var_ = *pVar[number];
  name_ = name[number];
  minVar_ = minVar[number];
  maxVar_ = maxVar[number];
}
/*прочитать все из EEPROM*/
void loadAll() {
  for (int i = 0; i < maxNumber; ++i) {
    *pVar[i] = EEPROM_int_read(addr[i]);
  }
  loadVar();
};
/*записать var*/
void saveVar() {
  *pVar[number] = var_;
  EEPROM_int_update(addr[number], var_);
}
/*увеличить var1*/
void incVar() {
  ++var_;
  if (var_ > maxVar_) var_ = maxVar_;
}
/*уменьшить var1*/
void decVar() {
  --var_;
  if (var_ < minVar_) var_ = minVar_;
}
// дисплей
Cl_Display Display(/*интервал*/500,/*обработчик*/vievVar);
// кнопки
Cl_BtnR Btn1(/*пин*/2,/*обработчик*/incVar);/*инкрем*/
Cl_BtnR Btn2(/*пин*/3,/*обработчик*/decVar);/*декримент*/
void DoBtn3() {
  saveVar();
  ++number;
  if (number >= maxNumber) number = 0;
  loadVar();
}
Cl_Btn Btn3(/*пин*/4,/*обработчик*/DoBtn3);/*Next*/
//-----main----------------------
void setup() {
  Serial.begin(9600);
  loadAll();
  Display.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Display.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}

/*Скетч использует 3264 байт (10%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 328 байт (16%) динамической памяти, оставляя 1720 байт для локальных переменных. Максимум: 2048 байт.
*/

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Вот это уже можно применять в ваших поделках. Обновление не по времени, а по щелчку кнопки

/**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//----------------------------------------
#include <EEPROM.h>
// чтение
int EEPROM_int_read(int addr) {
  byte raw[2];
  for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i);
  int &num = (int&)raw;
  return num;
}
// запись
void EEPROM_int_write(int addr, int num) {
  byte raw[2];
  (int&)raw = num;
  for (byte i = 0; i < 2; i++) EEPROM.write(addr + i, raw[i]);
}
// обновить
void EEPROM_int_update(int addr, int num) {
  if (EEPROM_int_read(addr) != num) EEPROM_int_write(addr, num);
}
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
// класс кнопка с повтором при удерж кнопки
class Cl_BtnR {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//-----Компоновка----------------------
// база данных настроечных переменных
const int maxNumber = 5;
int number = 0;
int var1, var2 , var3 , var4 , var5; /*настроечные переменные !!!!!*/
int * const pVar[maxNumber] PROGMEM = {&var1, &var2, &var3, &var4, &var5}; /*указатели на переменные*/
const char string_0[] PROGMEM = "var1"; /*имя*/
const char string_1[] PROGMEM = "var2";
const char string_2[] PROGMEM = "var3";
const char string_3[] PROGMEM = "var4";
const char string_4[] PROGMEM = "var5";
const char* const name[maxNumber] PROGMEM = {string_0, string_1, string_2, string_3, string_4};
const int   addr[maxNumber] PROGMEM = {0  , 2  , 4  , 6  , 8  };/*адресс*/
const int minVar[maxNumber] PROGMEM = {10 , 10 , 10 , 10 , 10 };/*минимум*/
const int maxVar[maxNumber] PROGMEM = {100, 100, 100, 100, 100};/*максимум*/
//
char buffer[10];
int var_, minVar_, maxVar_;
char *name_;
/*вывевети на экран*/
void vievVar() {
  Serial.print(buffer);
  Serial.print(": ");
  Serial.print(var_);
  Serial.println();
}
/*прочитать var*/
void loadVar() {
  var_ = *(int*)pgm_read_word(&pVar[number]);
  strcpy_P(buffer, (char*)pgm_read_word(&(name[number])));
  minVar_ = pgm_read_byte_near(minVar + number);
  maxVar_ = pgm_read_byte_near(maxVar + number);
}
/*прочитать все из EEPROM*/
void loadAll() {
  for (int i = 0; i < maxNumber; ++i) {
    *(int*)pgm_read_word(&pVar[i]) = EEPROM_int_read(pgm_read_byte_near(addr + i));
  }
  loadVar();
};
/*записать var*/
void saveVar() {
  *(int*)pgm_read_word(&pVar[number]) = var_;
  EEPROM_int_update(pgm_read_byte_near(addr + number), var_);
}

// дисплей
Cl_Display Display(/*обработчик*/vievVar);
/*увеличить var1*/
void incVar() {
  Display.refresh();
  ++var_;
  if (var_ > maxVar_) var_ = maxVar_;
}
/*уменьшить var1*/
void decVar() {
  Display.refresh();
  --var_;
  if (var_ < minVar_) var_ = minVar_;
}
// кнопки
Cl_BtnR Btn1(/*пин*/2,/*обработчик*/incVar);/*инкрем*/
Cl_BtnR Btn2(/*пин*/3,/*обработчик*/decVar);/*декримент*/
void DoBtn3() {
  Display.refresh();
  saveVar();
  ++number;
  if (number >= maxNumber) number = 0;
  loadVar();
}
Cl_Btn Btn3(/*пин*/4,/*обработчик*/DoBtn3);/*Next*/
//-----main----------------------
void setup() {
  Serial.begin(9600);
  loadAll();
  Display.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Display.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}

/*Скетч использует 3230 байт (10%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 253 байт (12%) динамической памяти, оставляя 1795 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

Стремно это все. Скажите qwone, Вы мазохист или просто засрать флеш повторами кода хотите? Посмотрите на классы  Cl_Btn и Cl_BtnR. Их отличает три строки 116, 117 и 118 . Они есть во втором классе и отсутствуют в первом. Сколько кода и сколько раз Вы готовы нагло продублировать, чтоб не разбиратся с наследованием классов? 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Logik. Если бы смысл был в данном случае от наследовании, то да. а так крутить код ради этого не стоит. Это как экономить спички паля бумажные банкноты. Да и этот скеч заглатывает много переменных, но ест похоже мало памяти. Можно хоть на 100 переменных закатать.

//**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//----------------------------------------
#include <EEPROM.h>
// чтение
int EEPROM_int_read(int addr) {
  byte raw[2];
  for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i);
  int &num = (int&)raw;
  return num;
}
// запись
void EEPROM_int_write(int addr, int num) {
  byte raw[2];
  (int&)raw = num;
  for (byte i = 0; i < 2; i++) EEPROM.write(addr + i, raw[i]);
}
// обновить
void EEPROM_int_update(int addr, int num) {
  if (EEPROM_int_read(addr) != num) EEPROM_int_write(addr, num);
}
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
// класс кнопка с повтором при удерж кнопки
class Cl_BtnR {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//-----Компоновка----------------------
// база данных настроечных переменных
const int maxNumber = 20;
int number = 0;
int  var0,  var1,  var2,  var3,  var4,  var5,  var6,  var7,  var8, var9; /*настроечные переменные !!!!!*/
int var10, var11, var12, var13, var14, var15, var16, var17, var18, var19;
int * const pVar[maxNumber] PROGMEM = {
  &var0 ,  &var1,  &var2,  &var3,  &var4,  &var5,  &var6,  &var7,  &var8,  &var9,
  &var10, &var11, &var12, &var13, &var14, &var15, &var16, &var17, &var18, &var19
}; /*указатели на переменные*/
const char name0[] PROGMEM = "var0"; /*имя*/
const char name1[] PROGMEM = "var1";
const char name2[] PROGMEM = "var2";
const char name3[] PROGMEM = "var3";
const char name4[] PROGMEM = "var4";
const char name5[] PROGMEM = "var5";
const char name6[] PROGMEM = "var6";
const char name7[] PROGMEM = "var7";
const char name8[] PROGMEM = "var8";
const char name9[] PROGMEM = "var9";
const char name10[] PROGMEM = "var10";
const char name11[] PROGMEM = "var11";
const char name12[] PROGMEM = "var12";
const char name13[] PROGMEM = "var13";
const char name14[] PROGMEM = "var14";
const char name15[] PROGMEM = "var15";
const char name16[] PROGMEM = "var16";
const char name17[] PROGMEM = "var17";
const char name18[] PROGMEM = "var18";
const char name19[] PROGMEM = "var19";
const char* const name[maxNumber] PROGMEM = {
  name0 , name1 ,  name2,  name3,  name4,  name5,  name6,  name7,  name8,  name9,
  name10, name11, name12, name13, name14, name15, name16, name17, name18, name19
};
const int   addr[maxNumber] PROGMEM = { /*адресс*/
  0  , 2  , 4  , 6  , 8  , 10 , 12 , 14 , 16 , 18,
  20 , 22 , 24 , 26 , 28 , 30 , 32 , 34 , 36 , 38
};
const int minVar[maxNumber] PROGMEM = { /*минимум*/
  10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10,
  10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10
};
const int maxVar[maxNumber] PROGMEM = { /*максимум*/
  100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
  100, 100, 100, 100, 100, 100, 100, 100, 100, 100
};
//
char buffer[10];
int var_, minVar_, maxVar_;
char *name_;
/*вывевети на экран*/
void vievVar() {
  Serial.print(buffer);
  Serial.print(": ");
  Serial.print(var_);
  Serial.println();
}
/*прочитать var*/
void loadVar() {
  var_ = *(int*)pgm_read_word(&pVar[number]);
  strcpy_P(buffer, (char*)pgm_read_word(&(name[number])));
  minVar_ = pgm_read_byte_near(minVar + number);
  maxVar_ = pgm_read_byte_near(maxVar + number);
}
/*прочитать все из EEPROM*/
void loadAll() {
  for (int i = 0; i < maxNumber; ++i) {
    *(int*)pgm_read_word(&pVar[i]) = EEPROM_int_read(pgm_read_byte_near(addr + i));
  }
  loadVar();
};
/*записать var*/
void saveVar() {
  *(int*)pgm_read_word(&pVar[number]) = var_;
  EEPROM_int_update(pgm_read_byte_near(addr + number), var_);
}

// дисплей
Cl_Display Display(/*обработчик*/vievVar);
/*увеличить var1*/
void incVar() {
  Display.refresh();
  ++var_;
  if (var_ > maxVar_) var_ = maxVar_;
}
/*уменьшить var1*/
void decVar() {
  Display.refresh();
  --var_;
  if (var_ < minVar_) var_ = minVar_;
}
// кнопки
Cl_BtnR Btn1(/*пин*/2,/*обработчик*/incVar);/*инкрем*/
Cl_BtnR Btn2(/*пин*/3,/*обработчик*/decVar);/*декримент*/
void DoBtn3() {
  Display.refresh();
  saveVar();
  ++number;
  if (number >= maxNumber) number = 0;
  loadVar();
}
Cl_Btn Btn3(/*пин*/4,/*обработчик*/DoBtn3);/*Next*/
//-----main----------------------
void setup() {
  Serial.begin(9600);
  loadAll();
  Display.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Display.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}

/*Скетч использует 3464 байт (11%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 283 байт (13%) динамической памяти, оставляя 1765 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

// Если бы смысл был в данном случае от наследовании, то да. а так крутить код ради этого не стоит. Это как экономить спички паля бумажные банкноты.

Даже незнаю, плакать или смеятся над Вашим упрямством. Попробуем так:

Вариант1. Без наследования, как у Вас. В исходниках 2 класса, их код, с мизерным отличием записан 2 раза. Все доработки, правки и т.д необходимо не забывать дублировать. В флеше код также будет присутствовать в 2-х экземплярах занимая в два раз больше места чем возможно.

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

Какие спички, какие ассигнации?! Очевидно что вариант2 наголову превосходит вариант1. Тем более что все механизмы наследования скрыты от разработчика и автоматом реализуются компилятором.  Вашему коду пока очень далеко до состояния когда "крутить код ради этого не стоит". Увы. Вы и на 3% возможности ООП не освоили и не раскрываете. Хотя и выгодно отличаетесь от общей массы форума тем что хоть пробуете. Но Вам пока не учить и примеры выкладывать, а учится и  по сто раз переписывать впору. Не обижайтесь, но Ваше упрямство совершенно ирационально, больше всего оно вредит именно Вам.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Тут я с Logic согласно, Пух. 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Проводим эксперимент. Берем 2 класса с отличающими методами.

/**/
// -----------------------
// класс ААА
class Cl_AAA {
  protected:
    int i = 10;
  public:
    Cl_AAA(int j): i(j) {}
    /*некий повторяющий метод*/
    int noDo() {
      return i + 10;
    }
    /*некий отличный метод*/
    int Do() {
      i++;
      return i;
    }
};
// -----------------------
// класс BBB
class Cl_BBB {
  protected:
    int i = 10;
  public:
    Cl_BBB(int j): i(j) {}
    /*некий повторяющий метод*/
    int noDo() {
      return i + 10;
    }
    /*некий отличный метод*/
    int Do() {
      i--;
      return i;
    }
};
//----------------------------
Cl_BBB BBB(10);
Cl_AAA AAA(10);
//------------------------
void setup() {
  Serial.begin(9600);
  Serial. println(AAA.noDo());
  Serial. println(BBB.noDo());
  Serial. println(AAA.Do());
  Serial. println(BBB.Do());
}

void loop() {
}
/*Скетч использует 1838 байт (5%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 190 байт (9%) динамической памяти, оставляя 1858 байт для локальных переменных. Максимум: 2048 байт.
*/

и 

/**/
// -----------------------
// класс ААА
class Cl_AAA {
  protected:
    int i = 10;
  public:
    Cl_AAA(int j): i(j) {}
    /*некий повторяющий метод*/
    int noDo() {
      return i + 10;
    }
    /*некий отличный метод*/
    int Do() {
      i++;
      return i;
    }
};
// -----------------------
// класс BBB
class Cl_BBB : public Cl_AAA {
  protected:
  public:
    Cl_BBB(int j): Cl_AAA(j) {}
    /*некий отличный метод*/
    int Do() {
      i--;
      return i;
    }
};
//----------------------------
Cl_BBB BBB(10);
Cl_AAA AAA(10);
//------------------------
void setup() {
  Serial.begin(9600);
  Serial. println(AAA.noDo());
  Serial. println(BBB.noDo());
  Serial. println(AAA.Do());
  Serial. println(BBB.Do());
}

void loop() {

}
/*Скетч использует 1838 байт (5%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 190 байт (9%) динамической памяти, оставляя 1858 байт для локальных переменных. Максимум: 2048 байт.
*/

Ура мы ЭКОНОМИМ СТРОЧКИ ИСХОДНИКА, но нафиг это мне сдалось, так как программа занимает один и тот же размер.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Для любителей экономить строчки исходника переписал код #222  Ну может у них в экран вся программа не помещается. А а скролл крутить религия не дает . размер такой же.

//**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//----------------------------------------
#include <EEPROM.h>
// чтение
int EEPROM_int_read(int addr) {
  byte raw[2];
  for (byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr + i);
  int &num = (int&)raw;
  return num;
}
// запись
void EEPROM_int_write(int addr, int num) {
  byte raw[2];
  (int&)raw = num;
  for (byte i = 0; i < 2; i++) EEPROM.write(addr + i, raw[i]);
}
// обновить
void EEPROM_int_update(int addr, int num) {
  if (EEPROM_int_read(addr) != num) EEPROM_int_write(addr, num);
}
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
// класс кнопка с повтором при удерж кнопки
class Cl_BtnR : public Cl_Btn {
  protected:
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): Cl_Btn(p, D) {}
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
};
//-----Компоновка----------------------
// база данных настроечных переменных
const int maxNumber = 20;
int number = 0;
int  var0,  var1,  var2,  var3,  var4,  var5,  var6,  var7,  var8, var9; /*настроечные переменные !!!!!*/
int var10, var11, var12, var13, var14, var15, var16, var17, var18, var19;
int * const pVar[maxNumber] PROGMEM = {
  &var0 ,  &var1,  &var2,  &var3,  &var4,  &var5,  &var6,  &var7,  &var8,  &var9,
  &var10, &var11, &var12, &var13, &var14, &var15, &var16, &var17, &var18, &var19
}; /*указатели на переменные*/
const char name0[] PROGMEM = "var0"; /*имя*/
const char name1[] PROGMEM = "var1";
const char name2[] PROGMEM = "var2";
const char name3[] PROGMEM = "var3";
const char name4[] PROGMEM = "var4";
const char name5[] PROGMEM = "var5";
const char name6[] PROGMEM = "var6";
const char name7[] PROGMEM = "var7";
const char name8[] PROGMEM = "var8";
const char name9[] PROGMEM = "var9";
const char name10[] PROGMEM = "var10";
const char name11[] PROGMEM = "var11";
const char name12[] PROGMEM = "var12";
const char name13[] PROGMEM = "var13";
const char name14[] PROGMEM = "var14";
const char name15[] PROGMEM = "var15";
const char name16[] PROGMEM = "var16";
const char name17[] PROGMEM = "var17";
const char name18[] PROGMEM = "var18";
const char name19[] PROGMEM = "var19";
const char* const name[maxNumber] PROGMEM = {
  name0 , name1 ,  name2,  name3,  name4,  name5,  name6,  name7,  name8,  name9,
  name10, name11, name12, name13, name14, name15, name16, name17, name18, name19
};
const int   addr[maxNumber] PROGMEM = { /*адресс*/
  0  , 2  , 4  , 6  , 8  , 10 , 12 , 14 , 16 , 18,
  20 , 22 , 24 , 26 , 28 , 30 , 32 , 34 , 36 , 38
};
const int minVar[maxNumber] PROGMEM = { /*минимум*/
  10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10,
  10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10 , 10
};
const int maxVar[maxNumber] PROGMEM = { /*максимум*/
  100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
  100, 100, 100, 100, 100, 100, 100, 100, 100, 100
};
//
char buffer[10];
int var_, minVar_, maxVar_;
char *name_;
/*вывевети на экран*/
void vievVar() {
  Serial.print(buffer);
  Serial.print(": ");
  Serial.print(var_);
  Serial.println();
}
/*прочитать var*/
void loadVar() {
  var_ = *(int*)pgm_read_word(&pVar[number]);
  strcpy_P(buffer, (char*)pgm_read_word(&(name[number])));
  minVar_ = pgm_read_byte_near(minVar + number);
  maxVar_ = pgm_read_byte_near(maxVar + number);
}
/*прочитать все из EEPROM*/
void loadAll() {
  for (int i = 0; i < maxNumber; ++i) {
    *(int*)pgm_read_word(&pVar[i]) = EEPROM_int_read(pgm_read_byte_near(addr + i));
  }
  loadVar();
};
/*записать var*/
void saveVar() {
  *(int*)pgm_read_word(&pVar[number]) = var_;
  EEPROM_int_update(pgm_read_byte_near(addr + number), var_);
}

// дисплей
Cl_Display Display(/*обработчик*/vievVar);
/*увеличить var1*/
void incVar() {
  Display.refresh();
  ++var_;
  if (var_ > maxVar_) var_ = maxVar_;
}
/*уменьшить var1*/
void decVar() {
  Display.refresh();
  --var_;
  if (var_ < minVar_) var_ = minVar_;
}
// кнопки
Cl_BtnR Btn1(/*пин*/2,/*обработчик*/incVar);/*инкрем*/
Cl_BtnR Btn2(/*пин*/3,/*обработчик*/decVar);/*декримент*/
void DoBtn3() {
  Display.refresh();
  saveVar();
  ++number;
  if (number >= maxNumber) number = 0;
  loadVar();
}
Cl_Btn Btn3(/*пин*/4,/*обработчик*/DoBtn3);/*Next*/
//-----main----------------------
void setup() {
  Serial.begin(9600);
  loadAll();
  Display.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Display.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}

/*Скетч использует 3464 байт (11%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 283 байт (13%) динамической памяти, оставляя 1765 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

qwone пишет:

Проводим эксперимент. Берем 2 класса с отличающими методами.

Ура мы ЭКОНОМИМ СТРОЧКИ ИСХОДНИКА, но нафиг это мне сдалось, так как программа занимает один и тот же размер.

Кривые руки дают кривой опыт. Собираю оба Ваших кода, имею одинаковое

 
Sketch uses 2 262 bytes (7%) of program storage space. Maximum is 30 720 bytes.
Global variables use 204 bytes (9%) of dynamic memory, leaving 1 844 bytes for local variables. Maximum is 2 048 bytes.
 
Однако стоит только усложнить  int noDo() и чудеса заканчиваются ))

 

И чем сложней - код тем больше наследование будет выигрывать.

Надеюсь все понятно? Не проверяйте на коротком коде, оптимизация и мелкие издержки исказят результат. 

 

ПС. Не устраюйте срач отрицая очевидное. Дураку понятно что меньше повторов в исходнике - лучше. Меньше флеша занято - лучше. Вы взялись учить ООП - так слушаете тех кто его знает.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Еще вариант использования PROGMEM . Так для пробы

/**/
//-------------------------------
typedef struct {
  uint16_t reg;
  uint16_t out;
  uint16_t in;
  uint8_t bt;
} pin_t;
//-----------------------------------
const pin_t PROGMEM mapPin[]  = {
  /*          reg,              out,              in,     bit*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 0},/*pin0*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 1},/*pin1*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 2},/*pin2*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 3},/*pin3*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 4},/*pin4*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 5},/*pin5*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 6},/*pin6*/
  {(uint16_t)&DDRD, (uint16_t) &PORTD, (uint16_t) &PIND, 1 << 7},/*pin7*/
  {(uint16_t)&DDRB, (uint16_t) &PORTB, (uint16_t) &PINB, 1 << 0},/*pin8*/
  {(uint16_t)&DDRB, (uint16_t) &PORTB, (uint16_t) &PINB, 1 << 1},/*pin9*/
  {(uint16_t)&DDRB, (uint16_t) &PORTB, (uint16_t) &PINB, 1 << 2},/*pin10*/
  {(uint16_t)&DDRB, (uint16_t) &PORTB, (uint16_t) &PINB, 1 << 3},/*pin11*/
  {(uint16_t)&DDRB, (uint16_t) &PORTB, (uint16_t) &PINB, 1 << 4},/*pin12*/
  {(uint16_t)&DDRB, (uint16_t) &PORTB, (uint16_t) &PINB, 1 << 5},/*pin13*/
  {(uint16_t)&DDRC, (uint16_t) &PORTC, (uint16_t) &PINC, 1 << 0},/*pinA0*/
  {(uint16_t)&DDRC, (uint16_t) &PORTC, (uint16_t) &PINC, 1 << 1},/*pinA1*/
  {(uint16_t)&DDRC, (uint16_t) &PORTC, (uint16_t) &PINC, 1 << 2},/*pinA2*/
  {(uint16_t)&DDRC, (uint16_t) &PORTC, (uint16_t) &PINC, 1 << 3},/*pinA3*/
  {(uint16_t)&DDRC, (uint16_t) &PORTC, (uint16_t) &PINC, 1 << 4},/*pinA4*/
  {(uint16_t)&DDRC, (uint16_t) &PORTC, (uint16_t) &PINC, 1 << 5} /*pinA5*/
};
//-------------------------------------------------------
class Cl_Led {
  protected:
    uint8_t pin;
  public:
    /**/
    Cl_Led(uint8_t p): pin(p) {}
    /**/
    void init() {
      *(uint16_t*) pgm_read_word_near(&mapPin[pin].reg) |= (uint8_t)pgm_read_byte_near(&mapPin[pin].bt);
    }
    /**/
    void ON() {
      //Serial.println((uint8_t)pgm_read_byte_near(mapPin[pin].bt),2);
      *(uint16_t*)pgm_read_word_near(&mapPin[pin].out) |= (uint8_t)pgm_read_byte_near(&mapPin[pin].bt);
    }
    /**/
    void OFF() {
      *(uint16_t*)pgm_read_word_near(&mapPin[pin].out) &= ~(uint8_t)pgm_read_byte_near(&mapPin[pin].bt);
    }
};
//------Компоновка-----------------------------
Cl_Led Led(/*пин*/13);
//---main()----------------------------
void setup() {
  Led.init();
}

void loop() {
  Led.ON();
  delay(200);
  Led.OFF();
  delay(200);
}
/*Скетч использует 956 байт (3%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 10 байт (0%) динамической памяти, оставляя 2038 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

qwone пишет:

Еще вариант использования PROGMEM . Так для пробы

и чЁ?!  "ОйНуВсеПроехали"?  :))  По бабски это както, qwone, не по мужски ;)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Пух, пей лучше простую вотку, без добавок. 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Очередной вариант скетча Меню.

/**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//-------------------------------
typedef struct {
  uint16_t Name;/*текст страницы*/
  uint8_t  ThisLine;/*текущая строка*/
  uint8_t  TopLine;/*верхняя строка*/
  uint8_t  LowerLine;/*нижняя строка*/
  uint8_t  UpPg;/*верхняя страница*/
  uint8_t  DnPg;/*нижняя страница*/
  uint16_t DoExe;/*обработчик привязанный к странице*/
} MenuNote;
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//--------------------------------------
const byte maxScreen = 6;
byte screen = 0;// текущий экран
void DoExe0() { /*обработчик 0 экрана*/
  Serial.println();
  Serial.print("DoExe0");
}
void DoExe1() { /*обработчик 1 экрана*/
  Serial.println();
  Serial.print("DoExe1");
}
void DoExe2() { /*обработчик 2 экрана*/
  Serial.println();
  Serial.print("DoExe2");
}
void DoExe3() { /*обработчик 3 экрана*/
  Serial.println();
  Serial.print("DoExe3");
}
void DoExe4() {/*обработчик 4 экрана*/
  Serial.println();
  Serial.print("DoExe4");
}
void DoExe5() { /*обработчик 5 экрана*/
  Serial.println();
  Serial.print("DoExe5");
}
const char txt0[] PROGMEM = "Alarm";
const char txt1[] PROGMEM = "Time";
const char txt2[] PROGMEM = "Setting";
const char txt3[] PROGMEM = "Setting2";
const char txt4[] PROGMEM = "Setting3";
const char txt5[] PROGMEM = "Back";
const MenuNote PROGMEM Menu[maxScreen]  = {
  /*Name,ThisLine,TopLine,LowerLine,UpPg,DnPg,DoExe*/
  {txt0  , 0     , 5     , 0     , 0  , 1 , (uint16_t)DoExe0}, /*0-экран*/
  {txt1  , 1     , 5     , 0     , 0  , 2 , (uint16_t)DoExe1}, /*1-экран*/
  {txt2  , 2     , 5     , 0     , 1  , 3 , (uint16_t)DoExe2}, /*2-экран*/
  {txt3  , 3     , 5     , 0     , 2  , 4 , (uint16_t)DoExe3}, /*3-экран*/
  {txt4  , 4     , 5     , 0     , 3  , 5 , (uint16_t)DoExe4}, /*4-экран*/
  {txt5  , 5     , 5     , 0     , 4  , 5 , (uint16_t)DoExe5}  /*5-экран*/
};
// дисплей
/*вывеcти страницу на дисплей*/
void vievPg() {
  byte ThisLine = (uint8_t)pgm_read_byte_near(&Menu[screen].ThisLine);
  byte TopLine = (uint8_t)pgm_read_byte_near(&Menu[screen].TopLine);
  byte LowerLine = (uint8_t)pgm_read_byte_near(&Menu[screen].LowerLine);
  Serial.println();
  for (byte i = LowerLine; i <= TopLine; ++i) {
    char buffer[15];
    strcpy_P(buffer, (char*)pgm_read_word(&Menu[i].Name));
    Serial.println();
    if (i == ThisLine) Serial.print(">");
    else Serial.print(" ");
    Serial.print(buffer);
  }
}
Cl_Display Display(/*обработчик*/vievPg);
/*перейти на верхнюю строчку*/
void GoTopLine() {
  screen = (uint8_t)pgm_read_byte_near(&Menu[screen].UpPg);
  Display.refresh();
}
/*перейти на нижнюю строчку*/
void GoLowerLine() {
  screen = (uint8_t)pgm_read_byte_near(&Menu[screen].DnPg);
  Display.refresh();
}
Cl_Btn BtnUp  (/*пин*/2,/*обработчик*/GoTopLine);/*Кнопка строка вверх*/
Cl_Btn BtnDown(/*пин*/3,/*обработчик*/GoLowerLine);/*Кнопка строка вниз*/
/*Выполнить обработчик строки*/
void DoExe() {
  pDo Do = (pDo)pgm_read_word_near(&Menu[screen].DoExe);
  Do();
  //Display.refresh();
}
Cl_Btn BtnExe(/*пин*/4,/*обработчик*/DoExe);/*Кнопка Выполнить*/
//---main()-------------------------------------
void setup() {
  Serial.begin(9600);
  Display.init();
  BtnUp.init();
  BtnDown.init();
  BtnExe.init();
}
void loop() {
  mill = millis();
  Display.run();
  BtnUp.run();
  BtnDown.run();
  BtnExe.run();
}
/*Скетч использует 2662 байт (8%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 270 байт (13%) динамической памяти, оставляя 1778 байт для локальных переменных. Максимум: 2048 байт.
*/

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Ну и собственно  скетч.

/*Menu 15.02*/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//-------------------------------
typedef struct {
  const char *Name;/*текст страницы*/
  const uint8_t  ThisLine;/*текущая строка*/
  const uint8_t  FromLine;/*начальняя строка*/
  const uint8_t  ToLine;  /*конечная строка*/
  const uint8_t  DnLn;/*верхняя страница*/
  const uint8_t  UpLn;/*нижняя страница*/
  const uint8_t  GoPg;/*перейти на строку*/
  const uint16_t DoExe;/*обработчик привязанный к странице*/
} MenuNote;
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//--------------------------------------
const byte maxScreen = 16;
byte screen = 0;// текущий экран
void DoNone() { /*пустой обработчик*/
}
void DoExe31AAA() { /*обработчик 3.1.AAA*/
  Serial.println();
  Serial.print("Do 3.1.AAA");
}
void DoExe32AAA() { /*обработчик 3.2.AAA*/
  Serial.println();
  Serial.print("Do 3.2.AAA");
}
void DoExe33AAA() { /*обработчик 3.3.AAA*/
  Serial.println();
  Serial.print("Do 3.3.AAA");
}
void DoExe31BBB() { /*обработчик 3.1.BBB*/
  Serial.println();
  Serial.print("Do 3.1.BBB");
}
void DoExe32BBB() { /*обработчик 3.2.BBB*/
  Serial.println();
  Serial.print("Do 3.2.BBB");
}
void DoExe33BBB() { /*обработчик 3.3.BBB*/
  Serial.println();
  Serial.print("Do 3.3.BBB");
}
void DoExe31CCC() { /*обработчик 3.1.CCC*/
  Serial.println();
  Serial.print("Do 3.1.CCC");
}
void DoExe32CCC() { /*обработчик 3.2.CCC*/
  Serial.println();
  Serial.print("Do 3.2.CCC");
}
void DoExe33CCC() { /*обработчик 3.3.CCC*/
  Serial.println();
  Serial.print("Do 3.3.CCC");
}
const char txt0[] PROGMEM = "Back";
const char txt1[] PROGMEM = "1.AAA";
const char txt2[] PROGMEM = "2.BBB";
const char txt3[] PROGMEM = "3.CCC";
const char txt4[] PROGMEM = "1.1.AAA";
const char txt5[] PROGMEM = "1.2.AAA";
const char txt6[] PROGMEM = "1.3.AAA";
const char txt7[] PROGMEM = "2.1.BBB";
const char txt8[] PROGMEM = "2.2.BBB";
const char txt9[] PROGMEM = "2.3.BBB";
const char txt10[] PROGMEM = "3.1.CCC";
const char txt11[] PROGMEM = "3.2.CCC";
const char txt12[] PROGMEM = "3.3.CCC";
const MenuNote PROGMEM Menu[maxScreen]  = {
  /*Name,ThisLine,ToLine,FromLine,DnLn,UpLn,GoPg,DoExe*/
  {txt0  , 0     , 0     , 3     , 0  , 1 , 0  , (uint16_t)DoNone}, /*0  1-экран 0-строка*/
  {txt1  , 1     , 0     , 3     , 0  , 2 , 4  , (uint16_t)DoNone}, /*1  1-экран 1-строка*/
  {txt2  , 2     , 0     , 3     , 1  , 3 , 8  , (uint16_t)DoNone}, /*2  1-экран 2-строка*/
  {txt3  , 3     , 0     , 3     , 2  , 3 , 12 , (uint16_t)DoNone}, /*3  1-экран 3-строка*/

  {txt0  , 4     , 4     , 7     , 4  , 5 , 0  , (uint16_t)DoNone}    , /*4  2-экран 0-строка*/
  {txt4  , 5     , 4     , 7     , 4  , 6 , 255, (uint16_t)DoExe31AAA}, /*5  2-экран 1-строка*/
  {txt5  , 6     , 4     , 7     , 5  , 7 , 255, (uint16_t)DoExe32AAA}, /*6  2-экран 2-строка*/
  {txt6  , 7     , 4     , 7     , 6  , 7 , 255, (uint16_t)DoExe33AAA}, /*7  2-экран 3-строка*/

  {txt0  , 8     , 8     , 11    , 8  , 9  , 0   , (uint16_t)DoNone}    , /*8   3-экран 0-строка*/
  {txt7  , 9     , 8     , 11    , 8  , 10 , 255, (uint16_t)DoExe31BBB}, /*9   3-экран 1-строка*/
  {txt8  , 10    , 8     , 11    , 9  , 11 , 255, (uint16_t)DoExe32BBB}, /*10  3-экран 2-строка*/
  {txt9  , 11    , 8     , 11    , 10 , 11 , 255, (uint16_t)DoExe33BBB}, /*11  3-экран 3-строка*/

  {txt0  , 12     , 12   , 15    , 12  , 13 , 0   , (uint16_t)DoNone}   , /*12  4-экран 0-строка*/
  {txt10 , 13     , 12   , 15    , 12  , 14 , 255, (uint16_t)DoExe31CCC}, /*13  4-экран 1-строка*/
  {txt11 , 14     , 12   , 15    , 13  , 15 , 255, (uint16_t)DoExe32CCC}, /*14  4-экран 2-строка*/
  {txt12 , 15     , 12   , 15    , 14  , 15 , 255, (uint16_t)DoExe33CCC} /*15  4-экран 3-строка*/
};
// дисплей
/*вывеcти страницу на дисплей*/
void vievPg() {
  byte ThisLine  = pgm_read_byte_near(&Menu[screen].ThisLine);
  byte ToLine   = pgm_read_byte_near(&Menu[screen].ToLine);
  byte FromLine = pgm_read_byte_near(&Menu[screen].FromLine);
  Serial.println();
  for (byte i = FromLine; i <= ToLine; ++i) {
    char buffer[15];
    strcpy_P(buffer, (char*)pgm_read_word(&Menu[i].Name));
    Serial.println();
    if (i == ThisLine) Serial.print(">");
    else Serial.print(" ");
    Serial.print(buffer);
  }
}
Cl_Display Display(/*обработчик*/vievPg);
/*перейти на верхнюю строчку*/
void GoToLine() {
  screen = pgm_read_byte_near(&Menu[screen].DnLn);
  Display.refresh();
}
/*перейти на нижнюю строчку*/
void GoFromLine() {
  screen = pgm_read_byte_near(&Menu[screen].UpLn);
  Display.refresh();
}
Cl_Btn BtnUp  (/*пин*/2,/*обработчик*/GoToLine);/*Кнопка строка вверх*/
Cl_Btn BtnDown(/*пин*/3,/*обработчик*/GoFromLine);/*Кнопка строка вниз*/
/*Выполнить обработчик строки*/
void DoExe() {

  uint8_t GoPg = pgm_read_byte_near(&Menu[screen].GoPg);
  if (GoPg == 255) {/*если команда перейти на новую строку*/
    pDo Do = (pDo)pgm_read_word_near(&Menu[screen].DoExe);
    Do();
  }
  else { /*иначе сделать обработчик строки*/
    screen = GoPg;
    Display.refresh();
  }
}
Cl_Btn BtnExe(/*пин*/4,/*обработчик*/DoExe);/*Кнопка Выполнить*/
//---------------------------
void setup() {
  Serial.begin(9600);
  Display.init();
  BtnUp.init();
  BtnDown.init();
  BtnExe.init();
}

void loop() {
  mill = millis();
  Display.run();
  BtnUp.run();
  BtnDown.run();
  BtnExe.run();
}
/*Скетч использует 2942 байт (9%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 326 байт (15%) динамической памяти, оставляя 1722 байт для локальных переменных. Максимум: 2048 байт.
*/

ПС: Кроме блока MainScreen

adread
Offline
Зарегистрирован: 23.01.2018

как подписоваться без комментария не знаю, потому оставлю ) 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

И собственно сам код

/*Menu 16.02*/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//----------------------------------------

//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
// класс кнопка с повтором при удерж кнопки
class Cl_BtnR : public Cl_Btn {
  protected:
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): Cl_Btn(p, D) {}
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
};
//--------------------------------------------------------------------------------
const byte maxLine = 28;/*суммарное колличество строк в меню*/
const byte valLine = 22;/*номер начало строки  блока переменных*/
byte line = 0; /* текущая  строка */
/*блок записей*/
void DoNone() { /*пустой обработчик*/
}
void DoExe31AAA() { /*обработчик 3.1.AAA*/
  Serial.println();
  Serial.print("Do 3.1.AAA");
}
void DoExe32AAA() { /*обработчик 3.2.AAA*/
  Serial.println();
  Serial.print("Do 3.2.AAA");
}
void DoExe33AAA() { /*обработчик 3.3.AAA*/
  Serial.println();
  Serial.print("Do 3.3.AAA");
}
void DoExe31BBB() { /*обработчик 3.1.BBB*/
  Serial.println();
  Serial.print("Do 3.1.BBB");
}
void DoExe32BBB() { /*обработчик 3.2.BBB*/
  Serial.println();
  Serial.print("Do 3.2.BBB");
}
void DoExe33BBB() { /*обработчик 3.3.BBB*/
  Serial.println();
  Serial.print("Do 3.3.BBB");
}
void DoExe31CCC() { /*обработчик 3.1.CCC*/
  Serial.println();
  Serial.print("Do 3.1.CCC");
}
void DoExe32CCC() { /*обработчик 3.2.CCC*/
  Serial.println();
  Serial.print("Do 3.2.CCC");
}
void DoExe33CCC() { /*обработчик 3.3.CCC*/
  Serial.println();
  Serial.print("Do 3.3.CCC");
}
const char txt0[] PROGMEM = "Back";
const char txt1[] PROGMEM = "1.AAA";
const char txt2[] PROGMEM = "2.BBB";
const char txt3[] PROGMEM = "3.CCC";
const char txt4[] PROGMEM = "1.1.AAA";
const char txt5[] PROGMEM = "1.2.AAA";
const char txt6[] PROGMEM = "1.3.AAA";
const char txt7[] PROGMEM = "2.1.BBB";
const char txt8[] PROGMEM = "2.2.BBB";
const char txt9[] PROGMEM = "2.3.BBB";
const char txt10[] PROGMEM = "3.1.CCC";
const char txt11[] PROGMEM = "3.2.CCC";
const char txt12[] PROGMEM = "3.3.CCC";
const char txt13[] PROGMEM = "val 1";
const char txt14[] PROGMEM = "val 2";
const char txt15[] PROGMEM = "val 3";
const char txt16[] PROGMEM = "val 4";
const char txt17[] PROGMEM = "val 5";
const char txt18[] PROGMEM = "val 6";

typedef struct { /*структура строки меню-запись*/
  const char     *Name;/*текст строки*/
  const uint8_t  FrmLn;/*начальняя строка*/
  const uint8_t  ToLn; /*конечная строка*/
  const uint8_t  DnLn; /*верхняя страница*/
  const uint8_t  UpLn; /*нижняя страница*/
  const uint8_t  GoPg; /*перейти на строку*/
  const uint16_t DoExe;/*обработчик привязанный к странице*/
} strMenuNote;

const strMenuNote PROGMEM MenuNote[22]  = {/*блок для записей*/
  /*Name,ToLn,FrmLn,DnLn,UpLn,GoPg,DoExe*/
  {txt0 , 0  , 3   , 0  , 1  , 0  , (uint16_t)DoNone}       , /*0   Back */
  {txt1 , 0  , 3   , 0  , 2  , 4  , (uint16_t)DoNone}       , /*1  1.AAA */
  {txt2 , 0  , 3   , 1  , 3  , 10  , (uint16_t)DoNone}      , /*2  2.BBB */
  {txt3 , 0  , 3   , 2  , 3  , 16 , (uint16_t)DoNone}       , /*3  3.CCC */

  {txt0 , 4  , 9   , 4  , 5 , 0  , (uint16_t)DoNone}        , /*4  Back   */
  {txt4 , 4  , 9   , 4  , 6 , 255, (uint16_t)DoExe31AAA}    , /*5  1.1.AAA*/
  {txt5 , 4  , 9   , 5  , 7 , 255, (uint16_t)DoExe32AAA}    , /*6  1.2.AAA*/
  {txt6 , 4  , 9   , 6  , 8 , 255, (uint16_t)DoExe33AAA}    , /*7  1.3.AAA*/
  {txt13, 4  , 9   , 7  , 9 , 22 , (uint16_t)DoNone}        , /*8  val1   */
  {txt14, 4  , 9   , 8  , 9 , 23 , (uint16_t)DoNone}        , /*9  val2   */

  {txt0 , 10 , 15  , 10  , 11 , 0  , (uint16_t)DoNone}      , /*10  Back*/
  {txt7 , 10 , 15  , 10  , 12 , 255, (uint16_t)DoExe31BBB}  , /*11  2.1.BBB*/
  {txt8 , 10 , 15  , 11  , 13 , 255, (uint16_t)DoExe32BBB}  , /*12  2.2.BBB*/
  {txt9 , 10 , 15  , 12  , 14 , 255, (uint16_t)DoExe33BBB}  , /*13  2.3.BBB*/
  {txt15, 10 , 15  , 13  , 15 , 24 , (uint16_t)DoNone}      , /*14  val3   */
  {txt16, 10 , 15  , 14  , 15 , 25 , (uint16_t)DoNone}      , /*15  val4   */

  {txt0 , 16 , 21   , 16  , 17 , 0  , (uint16_t)DoNone}     , /*16  Back*/
  {txt10, 16 , 21   , 16  , 18 , 255, (uint16_t)DoExe31CCC} , /*17  3.1.CCC*/
  {txt11, 16 , 21   , 17  , 19 , 255, (uint16_t)DoExe32CCC} , /*18  3.2.CCC*/
  {txt12, 16 , 21   , 18  , 20 , 255, (uint16_t)DoExe33CCC} , /*19  3.3.CCC*/
  {txt17, 16 , 21   , 19  , 21 , 26 , (uint16_t)DoNone}     , /*20  val5   */
  {txt18, 16 , 21   , 20  , 21 , 27 , (uint16_t)DoNone}       /*21  val6   */
};
/*блок переменных*/
int val1, val2, val3, val4, val5, val6; /*настроечные переменные !!!!!*/
int val, maxVa1, minVa1;
typedef struct {           /*структура строки меню-переменная*/
  const char        *Name; /*текст строки*/
  const int      *pointer; /*указатель на переменную*/
  const byte         addr; /*адресс в EEPROM*/
  const int        maxVa1; /*максимальное значение переменной*/
  const int        minVa1; /*минимальной значение переменной*/
  const uint8_t      GoPg; /*перейти на строку*/
} strMenuVal;
const strMenuVal PROGMEM MenuVal[6]  = {/*блок для переменных*/
  /*Name,pointer,addr,maxVal,minVal,GoPg*/
  {txt13, &val1 , 0   , 100  , 10 , 8}    , /*22  val1   */
  {txt14, &val2 , 2   , 100  , 10 , 9}    , /*23  val2   */
  {txt15, &val3 , 4   , 100  , 10 , 14}   , /*24  val3   */
  {txt16, &val4 , 8   , 100  , 10 , 15}   , /*25  val4   */
  {txt17, &val5 , 10  , 100  , 10 , 20}   , /*26  val5   */
  {txt18, &val6 , 12  , 100  , 10 , 21}     /*27  val6   */
};
/*прочитать значение переменной из указателя*/
void readVal() {
  val = *(int*)pgm_read_word(&MenuVal[line - valLine].pointer) ;
  maxVa1 = pgm_read_word(&MenuVal[line - valLine].maxVa1) ;
  minVa1 = pgm_read_word(&MenuVal[line - valLine].minVa1) ;
}
/*записать значение переменной по указателю */
void saveVal(byte line) {
  *(int*)pgm_read_word(&MenuVal[line - valLine].pointer) = val ;
}
/*+1 к переменной */
void plusVal() {
  ++val;
  if (val > maxVa1) val = maxVa1;
}
/*-1 к переменной*/
void minusVal() {
  --val;
  if (val < minVa1) val = minVa1;
}
#include <EEPROM.h>
/*записать значение переменной в EEPROM */
void saveValEEPROM(byte line) {
  EEPROM.put(pgm_read_byte_near(&MenuVal[line - valLine].addr), val);
}
/*прочитать значение переменной из EEPROM*/
void readValEEPROM() {
  EEPROM.get(pgm_read_byte_near(&MenuVal[line - valLine].addr), val);
}
/*прочитать все значения переменнох из EEPROM*/
void readAllEEPROM() {
  for (byte i = 0; i < maxLine - valLine; ++i) {
    EEPROM.get(pgm_read_byte_near(&MenuVal[i].addr), val);
    *(int*)pgm_read_word(&MenuVal[i].pointer) = val ;
  }
}
/* РАБОТА ДИСПЛЕЯ */
/*вывод строки на дисплей*/
void printLn( byte line) {
  char buffer[15];
  strcpy_P(buffer, (char*)pgm_read_word(&MenuNote[line].Name));
  Serial.print(buffer);
}
/*вывеcти информацию на дисплей*/
void vievToDisplay() {
  static byte oldLine = 0;
  Serial.println();
  if (line < valLine) {
    if (oldLine >=  valLine) { /*переход из блока переменных на блок записей*/
      saveVal(oldLine);
      saveValEEPROM(oldLine);
    }
    byte ToLn   = pgm_read_byte_near(&MenuNote[line].ToLn);/*начало*/
    byte FrmLn = pgm_read_byte_near(&MenuNote[line].FrmLn);/*конец*/
    for (byte i = FrmLn; i <= ToLn; ++i) {
      Serial.println();
      if (i == line) Serial.print(">");
      else Serial.print(" ");
      printLn(i);
    }
  }
  else {
    if (oldLine < valLine)  /*переход из блока записей на блок переменных*/
      readVal();
    char buffer[15];
    strcpy_P(buffer, (char*)pgm_read_word(&MenuVal[line - valLine].Name));
    Serial.print(buffer);
    Serial.print("=");
    Serial.print(val);
  }
  oldLine = line;
}
Cl_Display Display(/*обработчик*/vievToDisplay);
/*перейти на верхнюю строчку*/
void DoMinus() {
  if (line < valLine)
    line = pgm_read_byte_near(&MenuNote[line].DnLn);
  else minusVal();
  Display.refresh();
}
/*перейти на нижнюю строчку*/
void DoPlus() {
  if (line < valLine)
    line = pgm_read_byte_near(&MenuNote[line].UpLn);
  else plusVal();
  Display.refresh();
}
Cl_BtnR BtnUp  (/*пин*/2,/*обработчик*/DoMinus);/*Кнопка строка вверх*/
Cl_BtnR BtnDown(/*пин*/3,/*обработчик*/DoPlus);/*Кнопка строка вниз*/
/*Выполнить обработчик строки*/
void DoExe() {
  if (line < valLine) {
    uint8_t GoPg = pgm_read_byte_near(&MenuNote[line].GoPg);
    if (GoPg == 255) {/*если команда перейти на новую строку*/
      pDo Do = (pDo)pgm_read_word_near(&MenuNote[line].DoExe);
      Do();
    }
    else { /*иначе сделать обработчик строки*/
      line = GoPg;
      Display.refresh();
    }
  }
  else {
    line =  pgm_read_byte_near(&MenuVal[line - valLine].GoPg);
    Display.refresh();
  }
}
Cl_Btn BtnExe(/*пин*/4,/*обработчик*/DoExe);/*Кнопка Выполнить*/
//---------------------------
void setup() {
  Serial.begin(9600);
  readAllEEPROM();
  Display.init();
  BtnUp.init();
  BtnDown.init();
  BtnExe.init();
}

void loop() {
  mill = millis();
  Display.run();
  BtnUp.run();
  BtnDown.run();
  BtnExe.run();
}
/*Скетч использует 4004 байт (13%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 347 байт (16%) динамической памяти, оставляя 1701 байт для локальных переменных. Максимум: 2048 байт.
*/

ПС: Код сыроват. Требует отладки не только на уровне кода, а так же структуры программы и удобства для пользователя. + - можно менять долго удерживая нужные кнопки. вывод на главный экран не организован.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Ну и сам скетч.

/*Cl_Controle*/
unsigned long mill; // переменная под millis()
typedef void (*pDo)() ;// тип -функция обработчик
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
// класс кнопка с повтором при удерж кнопки
class Cl_BtnR : public Cl_Btn {
  protected:
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): Cl_Btn(p, D) {}
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
};
//--------------------------------------------------------------------------------
typedef struct {    /*структура строки обработчиков управления*/
  const uint16_t plus;/*обработчик привязанный к странице*/
  const uint16_t minus;/*обработчик привязанный к странице*/
  const uint16_t exe;/*обработчик привязанный к странице*/
  const uint16_t reviev;/*обработчик привязанный к странице*/
} strControl;

class Cl_Control {
  protected:
    const strControl * sCtrl;
    pDo DoReviev;    /*обработчик на обновл экрана*/
    bool refreshON = 1; /*1-обновить экран  0 нет*/
    Cl_BtnR BtnUp;
    Cl_BtnR BtnDown;
    Cl_Btn  BtnExe;
    byte state = 1; /*0 устройство 1 меню*/
    void stand(byte s) { /*установить*/
      if (state == s) return;
      state = s;
      DoReviev =   (pDo)pgm_read_word_near(&sCtrl[state].reviev);
      BtnUp.write  ((pDo)pgm_read_word_near(&sCtrl[state].plus));
      BtnDown.write((pDo)pgm_read_word_near(&sCtrl[state].minus));
      BtnExe.write ((pDo)pgm_read_word_near(&sCtrl[state].exe));
    }
  public:
    /*конструктор*/
    Cl_Control(const strControl * sCtrl_, byte p1, byte p2, byte p3)
      : sCtrl(sCtrl_), BtnUp(p1, NULL), BtnDown(p2, NULL), BtnExe(p3, NULL) {
      stand(0);
    }
    /*инициализация-вставить в setup()*/
    void init() {
      BtnUp  .init();
      BtnDown.init();
      BtnExe .init();
    }
    /*работа-вставить в loop()*/
    void run() {
      BtnUp  .run();
      BtnDown.run();
      BtnExe .run();
      if (refreshON == 1) {
        DoReviev();
        refreshON = 0;
      }
    }
    /*перейти на устройство*/
    void setDevice() {
      stand(0);
      refreshON = 1;
    }
    /*перейти на меню*/
    void setMenu() {
      stand(1);
      refreshON = 1;
    }
    void refresh() {
      refreshON = 1;
    }
};
//---Компоновка-----------------------------
/*предварительное объявление обработчиков*/
void DoPlus1() ;
void DoMinus1();
void DoExe1() ;
void DoViev1() ;

void DoPlusMenu() ;
void DoMinusMenu();
void DoExeMenu();
void DoVievMenu();

const strControl sCtrl[2]  PROGMEM = {/*блок для записей*/
  /*plus               ,minus                 ,exe                 ,reviev   */
  {(uint16_t)DoPlus1   , (uint16_t)DoMinus1   , (uint16_t)DoExe1   , (uint16_t) DoViev1   },  /*устройство*/
  {(uint16_t)DoPlusMenu, (uint16_t)DoMinusMenu, (uint16_t)DoExeMenu, (uint16_t) DoVievMenu}   /*меню*/
};

Cl_Control Ctrl(/*структура*/sCtrl,/*пин+*/2,/*пин-*/3,/*пин exe*/4);
//--------------------------------------
/*обработчики*/
void DoPlus1() {                  /*обработчик устройства +*/
  Serial.println();
  Serial.print("Device +1");
  Ctrl.refresh();
}
void DoMinus1() {                  /*обработчик устройства -*/
  Serial.println();
  Serial.print("Device -1");
  Ctrl.refresh();
}
void DoExe1() {                  /*обработчик устройства выполнить*/
  Ctrl.setMenu();
}
void DoViev1() {                  /*обработчик устройства показать*/
  Serial.println();
  Serial.print("Device Viev");
}
void DoPlusMenu() {                  /*обработчик Меню +*/
  Serial.println();
  Serial.print("Menu +1");
  Ctrl.refresh();
}
void DoMinusMenu() {                  /*обработчик Меню -*/
  Serial.println();
  Serial.print("Menu -1");
  Ctrl.refresh();
}
void DoExeMenu() {                  /*обработчик Меню выполнить*/
  Ctrl.setDevice();
}
void DoVievMenu() {                  /*обработчик Меню показать*/
  Serial.println();
  Serial.print("Menu Viev");
}
//---main-----------------------------
void setup() {
  Serial.begin(9600);
  Ctrl.init();
}

void loop() {
  mill = millis();
  Ctrl.run();
}
/**/

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

/*Menu17_02.ino*/
unsigned long mill; // переменная под millis()
typedef void (*pDo)() ;// тип -функция обработчик
//------Cl_Btn----------------------
class Cl_Btn {                      /* класс кнопка*/
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
class Cl_BtnR : public Cl_Btn {      /* класс кнопка с повтором при удерж кнопки*/
  protected:
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): Cl_Btn(p, D) {}
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
};
//-------Cl_Control-------------------------------------------------------------------------
typedef struct {    /*структура строки обработчиков управления*/
  const uint16_t plus;/*обработчик привязанный к странице*/
  const uint16_t minus;/*обработчик привязанный к странице*/
  const uint16_t exe;/*обработчик привязанный к странице*/
  const uint16_t reviev;/*обработчик привязанный к странице*/
} strControl;
class Cl_Control {                   /* класс общее управление*/
  protected:
    const strControl * sCtrl;
    pDo DoReviev;    /*обработчик на обновл экрана*/
    bool refreshON = 1; /*1-обновить экран  0 нет*/
    Cl_BtnR BtnUp;
    Cl_BtnR BtnDown;
    Cl_Btn  BtnExe;
    byte state = 1; /*0 устройство 1 меню*/
    void stand(byte s) { /*установить*/
      if (state == s) return;
      state = s;
      DoReviev =   (pDo)pgm_read_word_near(&sCtrl[state].reviev);
      BtnUp.write  ((pDo)pgm_read_word_near(&sCtrl[state].plus));
      BtnDown.write((pDo)pgm_read_word_near(&sCtrl[state].minus));
      BtnExe.write ((pDo)pgm_read_word_near(&sCtrl[state].exe));
    }
  public:
    /*конструктор*/
    Cl_Control(const strControl * sCtrl_, byte p1, byte p2, byte p3)
      : sCtrl(sCtrl_), BtnUp(p1, NULL), BtnDown(p2, NULL), BtnExe(p3, NULL) {
      stand(0);
    }
    /*инициализация-вставить в setup()*/
    void init() {
      BtnUp  .init();
      BtnDown.init();
      BtnExe .init();
    }
    /*работа-вставить в loop()*/
    void run() {
      BtnUp  .run();
      BtnDown.run();
      BtnExe .run();
      if (refreshON == 1) {
        DoReviev();
        refreshON = 0;
      }
    }
    /*перейти на устройство*/
    void setDevice() {
      stand(0);
      refreshON = 1;
    }
    /*перейти на меню*/
    void setMenu() {
      stand(1);
      refreshON = 1;
    }
    void refresh() {
      refreshON = 1;
    }
};
//---Компоновка-----------------------------
/*предварительное объявление обработчиков меню и устройства*/
void DoPlus1()   ; void DoMinus1()   ; void DoExe1()   ; void DoViev1()   ;
void DoPlusMenu(); void DoMinusMenu(); void DoExeMenu(); void DoVievMenu();

const strControl sCtrl[2]  PROGMEM = {/*блок для записей*/
  /*plus               ,minus                 ,exe                 ,reviev   */
  {(uint16_t)DoPlus1   , (uint16_t)DoMinus1   , (uint16_t)DoExe1   , (uint16_t) DoViev1   },  /*устройство*/
  {(uint16_t)DoMinusMenu, (uint16_t)DoPlusMenu, (uint16_t)DoExeMenu, (uint16_t) DoVievMenu}   /*меню*/
};

Cl_Control Ctrl(/*структура*/sCtrl,/*пин+*/2,/*пин-*/3,/*пин exe*/4);
//--------------------------------------
/* ОБРАБОТЧИКИ УСТРОЙСТВА */
void DoPlus1()  {                  /*обработчик устройства +*/
  Serial.println();
  Serial.print("Device +1");
  Ctrl.refresh();
}
void DoMinus1() {                  /*обработчик устройства -*/
  Serial.println();
  Serial.print("Device -1");
  Ctrl.refresh();
}
void DoExe1()   {                  /*обработчик устройства выполнить*/
  Ctrl.setMenu();
}
void DoViev1()  {                  /*обработчик устройства показать*/
  Serial.println();
  Serial.print("MainScreen");
}
//--------------------------------------------------------------------------------
const byte maxLine = 28;/*суммарное колличество строк в меню*/
const byte valLine = 22;/*номер начало строки  блока переменных*/
byte line = 0; /* текущая  строка */
/*блок записей*/
void DoNone() { /*пустой обработчик*/
}
void DoMenu() { /*возврат на устройство*/
  Ctrl.setDevice();
}
void DoExe31AAA() { /*обработчик 3.1.AAA*/
  Serial.println();
  Serial.print("Do 3.1.AAA");
}
void DoExe32AAA() { /*обработчик 3.2.AAA*/
  Serial.println();
  Serial.print("Do 3.2.AAA");
}
void DoExe33AAA() { /*обработчик 3.3.AAA*/
  Serial.println();
  Serial.print("Do 3.3.AAA");
}
void DoExe31BBB() { /*обработчик 3.1.BBB*/
  Serial.println();
  Serial.print("Do 3.1.BBB");
}
void DoExe32BBB() { /*обработчик 3.2.BBB*/
  Serial.println();
  Serial.print("Do 3.2.BBB");
}
void DoExe33BBB() { /*обработчик 3.3.BBB*/
  Serial.println();
  Serial.print("Do 3.3.BBB");
}
void DoExe31CCC() { /*обработчик 3.1.CCC*/
  Serial.println();
  Serial.print("Do 3.1.CCC");
}
void DoExe32CCC() { /*обработчик 3.2.CCC*/
  Serial.println();
  Serial.print("Do 3.2.CCC");
}
void DoExe33CCC() { /*обработчик 3.3.CCC*/
  Serial.println();
  Serial.print("Do 3.3.CCC");
}
const char txt0[] PROGMEM = "Back";
const char txt1[] PROGMEM = "1.AAA";
const char txt2[] PROGMEM = "2.BBB";
const char txt3[] PROGMEM = "3.CCC";
const char txt4[] PROGMEM = "1.1.AAA";
const char txt5[] PROGMEM = "1.2.AAA";
const char txt6[] PROGMEM = "1.3.AAA";
const char txt7[] PROGMEM = "2.1.BBB";
const char txt8[] PROGMEM = "2.2.BBB";
const char txt9[] PROGMEM = "2.3.BBB";
const char txt10[] PROGMEM = "3.1.CCC";
const char txt11[] PROGMEM = "3.2.CCC";
const char txt12[] PROGMEM = "3.3.CCC";
const char txt13[] PROGMEM = "val 1";
const char txt14[] PROGMEM = "val 2";
const char txt15[] PROGMEM = "val 3";
const char txt16[] PROGMEM = "val 4";
const char txt17[] PROGMEM = "val 5";
const char txt18[] PROGMEM = "val 6";
const char txt19[] PROGMEM = "MainScreen";

typedef struct { /*структура строки меню-запись*/
  const char     *Name;/*текст строки*/
  const uint8_t  FrmLn;/*начальняя строка*/
  const uint8_t  ToLn; /*конечная строка*/
  const uint8_t  DnLn; /*верхняя страница*/
  const uint8_t  UpLn; /*нижняя страница*/
  const uint8_t  GoPg; /*перейти на строку*/
  const uint16_t DoExe;/*обработчик привязанный к странице*/
} strMenuNote;

const strMenuNote PROGMEM MenuNote[22]  = {/*блок для записей*/
  /*Name,ToLn,FrmLn,DnLn,UpLn,GoPg,DoExe*/
  {txt19, 0  , 3   , 0  , 1  , 255  , (uint16_t)DoMenu}  , /*0   MainScreen */
  {txt1 , 0  , 3   , 0  , 2  , 4    , (uint16_t)DoNone}  , /*1  1.AAA */
  {txt2 , 0  , 3   , 1  , 3  , 10   , (uint16_t)DoNone}  , /*2  2.BBB */
  {txt3 , 0  , 3   , 2  , 3  , 16   , (uint16_t)DoNone}  , /*3  3.CCC */

  {txt0 , 4  , 9   , 4  , 5 , 0  , (uint16_t)DoNone}        , /*4  Back   */
  {txt4 , 4  , 9   , 4  , 6 , 255, (uint16_t)DoExe31AAA}    , /*5  1.1.AAA*/
  {txt5 , 4  , 9   , 5  , 7 , 255, (uint16_t)DoExe32AAA}    , /*6  1.2.AAA*/
  {txt6 , 4  , 9   , 6  , 8 , 255, (uint16_t)DoExe33AAA}    , /*7  1.3.AAA*/
  {txt13, 4  , 9   , 7  , 9 , 22 , (uint16_t)DoNone}        , /*8  val1   */
  {txt14, 4  , 9   , 8  , 9 , 23 , (uint16_t)DoNone}        , /*9  val2   */

  {txt0 , 10 , 15  , 10  , 11 , 0  , (uint16_t)DoNone}      , /*10  Back*/
  {txt7 , 10 , 15  , 10  , 12 , 255, (uint16_t)DoExe31BBB}  , /*11  2.1.BBB*/
  {txt8 , 10 , 15  , 11  , 13 , 255, (uint16_t)DoExe32BBB}  , /*12  2.2.BBB*/
  {txt9 , 10 , 15  , 12  , 14 , 255, (uint16_t)DoExe33BBB}  , /*13  2.3.BBB*/
  {txt15, 10 , 15  , 13  , 15 , 24 , (uint16_t)DoNone}      , /*14  val3   */
  {txt16, 10 , 15  , 14  , 15 , 25 , (uint16_t)DoNone}      , /*15  val4   */

  {txt0 , 16 , 21   , 16  , 17 , 0  , (uint16_t)DoNone}     , /*16  Back*/
  {txt10, 16 , 21   , 16  , 18 , 255, (uint16_t)DoExe31CCC} , /*17  3.1.CCC*/
  {txt11, 16 , 21   , 17  , 19 , 255, (uint16_t)DoExe32CCC} , /*18  3.2.CCC*/
  {txt12, 16 , 21   , 18  , 20 , 255, (uint16_t)DoExe33CCC} , /*19  3.3.CCC*/
  {txt17, 16 , 21   , 19  , 21 , 26 , (uint16_t)DoNone}     , /*20  val5   */
  {txt18, 16 , 21   , 20  , 21 , 27 , (uint16_t)DoNone}       /*21  val6   */
};
/*блок переменных*/
int val1, val2, val3, val4, val5, val6; /*настроечные переменные !!!!!*/
int val, maxVa1, minVa1;
typedef struct {           /*структура строки меню-переменная*/
  const char        *Name; /*текст строки*/
  const int      *pointer; /*указатель на переменную*/
  const byte         addr; /*адресс в EEPROM*/
  const int        maxVa1; /*максимальное значение переменной*/
  const int        minVa1; /*минимальной значение переменной*/
  const uint8_t      GoPg; /*перейти на строку*/
} strMenuVal;
const strMenuVal PROGMEM MenuVal[6]  = {/*блок для переменных*/
  /*Name,pointer,addr,maxVal,minVal,GoPg*/
  {txt13, &val1 , 0   , 100  , 10 , 8}    , /*22  val1   */
  {txt14, &val2 , 2   , 100  , 10 , 9}    , /*23  val2   */
  {txt15, &val3 , 4   , 100  , 10 , 14}   , /*24  val3   */
  {txt16, &val4 , 8   , 100  , 10 , 15}   , /*25  val4   */
  {txt17, &val5 , 10  , 100  , 10 , 20}   , /*26  val5   */
  {txt18, &val6 , 12  , 100  , 10 , 21}     /*27  val6   */
};
/*прочитать значение переменной из указателя*/
void readVal() {
  val = *(int*)pgm_read_word(&MenuVal[line - valLine].pointer) ;
  maxVa1 = pgm_read_word(&MenuVal[line - valLine].maxVa1) ;
  minVa1 = pgm_read_word(&MenuVal[line - valLine].minVa1) ;
}
/*записать значение переменной по указателю */
void saveVal(byte line) {
  *(int*)pgm_read_word(&MenuVal[line - valLine].pointer) = val ;
}
/*+1 к переменной */
void plusVal() {
  ++val;
  if (val > maxVa1) val = maxVa1;
}
/*-1 к переменной*/
void minusVal() {
  --val;
  if (val < minVa1) val = minVa1;
}
#include <EEPROM.h>

void saveValEEPROM(byte line) {  /*записать значение переменной в EEPROM */
  EEPROM.put(pgm_read_byte_near(&MenuVal[line - valLine].addr), val);
}
void readValEEPROM() {          /*прочитать значение переменной из EEPROM*/
  EEPROM.get(pgm_read_byte_near(&MenuVal[line - valLine].addr), val);
}
void readAllEEPROM() {           /*прочитать все значения переменнох из EEPROM*/
  for (byte i = 0; i < maxLine - valLine; ++i) {
    EEPROM.get(pgm_read_byte_near(&MenuVal[i].addr), val);
    *(int*)pgm_read_word(&MenuVal[i].pointer) = val ;
  }
}
/* ОБРАБОТЧИКИ МЕНЮ */
void printLn( byte line) { /*вывод строки на дисплей*/
  char buffer[15];
  strcpy_P(buffer, (char*)pgm_read_word(&MenuNote[line].Name));
  Serial.print(buffer);
}
void DoVievMenu()  {    /*Выполнить обработчик Menu viev*/
  static byte oldLine = 0;
  Serial.println();
  if (line < valLine) {
    if (oldLine >=  valLine) { /*переход из блока переменных на блок записей*/
      saveVal(oldLine);
      saveValEEPROM(oldLine);
    }
    byte ToLn   = pgm_read_byte_near(&MenuNote[line].ToLn);/*начало*/
    byte FrmLn = pgm_read_byte_near(&MenuNote[line].FrmLn);/*конец*/
    for (byte i = FrmLn; i <= ToLn; ++i) {
      Serial.println();
      if (i == line) Serial.print(">");
      else Serial.print(" ");
      printLn(i);
    }
  }
  else {
    if (oldLine < valLine)  /*переход из блока записей на блок переменных*/
      readVal();
    char buffer[15];
    strcpy_P(buffer, (char*)pgm_read_word(&MenuVal[line - valLine].Name));
    Serial.print(buffer);
    Serial.print("=");
    Serial.print(val);
  }
  oldLine = line;
}
void DoMinusMenu() {    /*Выполнить на обработчик Menu -1*/
  if (line < valLine)
    line = pgm_read_byte_near(&MenuNote[line].DnLn);
  else minusVal();
  Ctrl.refresh();
}
void DoPlusMenu()  {    /*Выполнить на обработчик Menu +1*/
  if (line < valLine)
    line = pgm_read_byte_near(&MenuNote[line].UpLn);
  else plusVal();
  Ctrl.refresh();
}
void DoExeMenu()   {    /*Выполнить обработчик Menu Exe*/
  if (line < valLine) {
    uint8_t GoPg = pgm_read_byte_near(&MenuNote[line].GoPg);
    if (GoPg == 255) {/*если команда перейти на новую строку*/
      pDo Do = (pDo)pgm_read_word_near(&MenuNote[line].DoExe);
      Do();
    }
    else { /*иначе сделать обработчик строки*/
      line = GoPg;
      Ctrl.refresh();
    }
  }
  else {
    line =  pgm_read_byte_near(&MenuVal[line - valLine].GoPg);
    Ctrl.refresh();
  }
}
//---main-----------------------------
void setup() {
  Serial.begin(9600);
  readAllEEPROM();
  Ctrl.init();
}

void loop() {
  mill = millis();
  Ctrl.run();
}
/*Скетч использует 4232 байт (13%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 382 байт (18%) динамической памяти, оставляя 1666 байт для локальных переменных. Максимум: 2048 байт.
*/

ПС: Вроде заготовка под меню у меня получилась 

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

Вам интересно профессиональное мнение или ну его нафиг?

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

ЕвгенийП пишет:

Вам интересно профессиональное мнение или ну его нафиг?

мне интересно пидагогическое мнение.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

А смысл. Мне что ВАШЕ профессиональное мнение ума добавит. Как могу так и пишу. Это пока потолок. Может попозже получше выйдет.  

Так как ковыряться в большом и запутаном коде сложно выложу некоторые моменты

/**/
struct sMenu {
  const uint8_t FromLn     ; /*верхняя линия*/
  const uint8_t ToLn       ; /*нижняя линия*/
  const uint8_t formatItem ; /*формат компонента*/
  const char   *addrItem   ; /*адресс компонента*/
};
//------Компоновка-----------------------------
const char txt0[]  PROGMEM = "Return";
const char txt1[]  PROGMEM = "1.AAA";
const char txt2[]  PROGMEM = "2.BBB";
const char txt3[]  PROGMEM = "3.CCC";
const sMenu Menu[] PROGMEM = {
  /*FromLn,ToLn,formatItem,addrItem*/
  {0, 3, 0, txt0}, /*#0 Return*/
  {0, 3, 0, txt1}, /*#1 1.AAA*/
  {0, 3, 0, txt2}, /*#2 2.ВВВ*/
  {0, 3, 0, txt3}, /*#3 3.ССС*/
};
void VievUnit(uint8_t line) { /*вывести юнит блока связаный с линией*/
  char buffer[15];
  strcpy_P(buffer, (char*)pgm_read_word(&Menu[line].addrItem));
  Serial.print(buffer);
}
void VievBlock(uint8_t line) {/*вывести блок связаный с линией*/
  uint8_t FromLn = pgm_read_byte(&Menu[line].FromLn);
  uint8_t ToLn   = pgm_read_byte(&Menu[line].ToLn);
  Serial.println();
  for (int i = FromLn; i <= ToLn; ++i) {
    Serial.println();
    if (line == i)Serial.print(">");
    else Serial.print(" ");
    VievUnit(i);
  }
}
//-------main()-----------------------------
void setup() {
  Serial.begin(9600);
  for (int i = 0; i <= 3; ++i)VievBlock(i);
}
void loop() {
}
/*Скетч использует 1672 байт (5%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 190 байт (9%) динамической памяти, оставляя 1858 байт для локальных переменных. Максимум: 2048 байт.
*/

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Это так промежуточный скетч на тему Меню и PROGMEM, и некоего поиска оптимизации написания и работы кода. Писать сразу код с PROGMEM это куча убитого времени. Так что лучше писать без, а потом переписывать с. Код без ...

/**/
struct sMenu {
  byte fromLn;
  byte toLn;
  byte format;
  void *Item;
};
struct sItem1 {
  char *Name;
  int *pVal;
};
//--------------------------------
int val1 = 11, val2 = 22, val3 = 33;
char txt1[] = "val 1";
char txt2[] = "val 2";
char txt3[] = "val 3";
char txt4[] = "Return";
sItem1 Item1[] = {
  {txt1, &val1},/*#0 val1*/
  {txt2, &val2},/*#1 val2*/
  {txt3, &val3} /*#2 val3*/
};
sMenu Menu[] = {
  {0, 4, 0, txt4},      /*#0  Return */
  {0, 4, 1, &Item1[0]}, /*#1  val 1*/
  {0, 4, 1, &Item1[1]}, /*#2  val 2*/
  {0, 4, 1, &Item1[2]}  /*#3  val 3*/
};
void vievItem(byte line) {
  byte format = Menu[line].format;
  void * pItem = Menu[line].Item;
  if (format == 0) {
    Serial.print((char*)pItem);
    return;
  }
  if (format == 1) {
    sItem1 * Item = (sItem1 *)pItem;
    char * Name = Item->Name;
    int * pVal  = Item->pVal;
    Serial.print(Name);
    Serial.print("=");
    Serial.print(*pVal);
    return;
  }
}

void vievBlock(byte line) {
  byte fromLn = Menu[line].fromLn;
  byte toLn = Menu[line].toLn;
  Serial.println();
  for (int i = fromLn ; i < toLn; ++i) {
    Serial.println();
    if (i == line) Serial.print(">");
    else Serial.print(" ");
    vievItem(i);
  }
}
//-------------------------
void setup() {
  Serial.begin(9600);
  for (int i = 0; i <= 3; ++i) {
    vievBlock(i);
  }
}

void loop() {

}
/*Скетч использует 1922 байт (6%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 254 байт (12%) динамической памяти, оставляя 1794 байт для локальных переменных. Максимум: 2048 байт.
*/

И код уже на PROGMEM.

/**/
struct sMenu {
  const byte fromLn;
  const byte toLn;
  const byte format;
  const void *Item;
};
struct sItem1 {
  const char *Name;
  const int *pVal;
};
//--------------------------------
int val1 = 11, val2 = 22, val3 = 33;
const char txt1[] PROGMEM = "val 1";
const char txt2[] PROGMEM = "val 2";
const char txt3[] PROGMEM = "val 3";
const char txt4[] PROGMEM = "Return";
const sItem1 Item1[] PROGMEM = {
  {txt1, &val1},/*#0 val1*/
  {txt2, &val2},/*#1 val2*/
  {txt3, &val3} /*#2 val3*/
};
const sMenu Menu[] PROGMEM = {
  {0, 4, 0, txt4},      /*#0  Return */
  {0, 4, 1, &Item1[0]}, /*#1  val 1*/
  {0, 4, 1, &Item1[1]}, /*#2  val 2*/
  {0, 4, 1, &Item1[2]}  /*#3  val 3*/
};
void vievItem(byte line) {
  byte format  = pgm_read_byte(&Menu[line].format);
  void * pItem = pgm_read_word(&Menu[line].Item);
  if (format == 0) {
    char buffer[15]; 
    strcpy_P(buffer, (char*)pItem);
    Serial.print(buffer);
    return;
  }
  if (format == 1) {
    sItem1 * Item = (sItem1 *)pItem;
    char* Name = pgm_read_word(&Item->Name);
    int * pVal = pgm_read_word(&Item->pVal);
    char buffer[15];
    strcpy_P(buffer, Name);
    Serial.print(buffer);
    Serial.print("=");
    Serial.print(*pVal);
    return;
  }
}

void vievBlock(byte line) {
  byte fromLn = pgm_read_byte(&Menu[line].fromLn);
  byte toLn   = pgm_read_byte(&Menu[line].toLn  );
  Serial.println();
  for (int i = fromLn ; i < toLn; ++i) {
    Serial.println();
    if (i == line) Serial.print(">");
    else Serial.print(" ");
    vievItem(i);
  }
}
//-------------------------
void setup() {
  Serial.begin(9600);
  for (int i = 0; i <= 3; ++i) {
    vievBlock(i);
  }
}

void loop() {

}
/*Скетч использует 2000 байт (6%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 198 байт (9%) динамической памяти, оставляя 1850 байт для локальных переменных. Максимум: 2048 байт.
*/

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

lean_74 пишет:

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

Ну если тебе так уж интересно мнение, то у меня есть его: код пуха для работы с меню - слабенький, не хватает декомпозиции. Вот делает он это меню, и мешает всё в кучу, например, вид представления данных со способом его отображения. Как итог - код получается нереентерабельный. Вернее сказать так: код пуха - это наброски меню, с непонятным пока профитом от их использования.

Касаемо меню: мухи отдельно - котлеты отдельно: меню не должно содержать в себе никаких методов вывода его содержимого куда-либо, от слова "совсем". Методы навигации по внутренней структуре меню - да пожалуйста; удаление/добавление пунктов - велкам. Сохранение/загрузка - тоже допустимо, через внешний интерфейс сериализатора, а не напрямую работать с EEPROM или SD (сериализаторы -  на случай, когда нужно на лету переключить загрузку/сохранение структуры с EEPROM на SD, например). Отображение - меню не должно ничего знать о способах его отображения - для этого должны быть отдельные сущности, наследуемые от общего интерфейса.

Как-то так, если совсем вкратце. Если интересуют детали - спрашивай, можно сделать набросок.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Вся эта пляска связана с малым размером ОЗУ, кривым PROGMEMом и моим маловатым уровнем знаний.  Для нано это еще способ вытащить еще ресурсы. А помощнее уже переходить более програссивным способам написания программы.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

qwone пишет:

кривым PROGMEMом

В каком месте он кривой?

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

В сложности совмещения в одном классе данных из ОЗУ и PROGMEM . По крайней мере я не видел таких скетчей. Или мне надо опять открыть "Америку".

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

qwone пишет:

В сложности совмещения в одном классе данных из ОЗУ и PROGMEM . 

Просто не надо мешать мух с котлетами, и тогда надуманных проблем станет на порядок меньше ;)

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

Так в том коде из #232 вобще нет класса меню на экран )) То что какбы оно - Cl_Display на самом деле класс отложеного вызова некоего обработчика. Его можна инитить где угодно (причем старый обработчик теряется без следа и предупреждения), можна вызвать запуск обработчика, класс запомнит это но не вызовет сразу, а вызовит позже, предполагаемо из лопа. Класс условно пригоден например для запуска действия из прерывания и т.д. но к меню и экрану вобще не относится.

В более позднем творчестве обнаружен дивный Cl_Control, который вобщем обернул три кнопки вместе с их обработчиками и функционал из Cl_Display. При этом все перечисленное хоть и обединено в класс, но логически не связано в нем. Хотя по назначению вроде должно, а по факту и связывается, но почемуто внешними сущностями. Очень странная штуковина. Годится для многого, например можна управление машинкой на ней сделать (лево, право, вперед и обработчик для внесения изменений в ШИМы и сервоприводы - както так). Эта универсальность - следствие полного отсутствия смысла внутри класса. Он безсмысленый, потому его можна использовать почти везде, он ниче правда не даст но и не помешает. Впринципе я бы его, после допилинга, как базовый для чегонить может и заюзал бы. Но не знаю зачем )))

ПМ. Тоже Cl_Control явно не меню.

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016
/**/
//-----------------ItemInt-----------------------------------
struct sItemInt {
  int *pointer;
  uint8_t addr;
  int maxInt;
  int minInt;
  int initSet;/*первичная настройка*/
};
const uint8_t numItemInt = 3;
int a0, a1, a2;
const sItemInt ItemInt[numItemInt] PROGMEM = {
  /*pnt,addr,max,min,initSet*/
  {&a0, 0, 100, 10, 25},/*a0*/
  {&a1, 2, 100, 10, 25},/*a1*/
  {&a2, 4, 100, 10, 25} /*a2*/
};
int ItemIntVal;/*аккумулятор A[the]*/
void writeItemInt(uint8_t the) { /*VAL <- A[the] */
  int *val = pgm_read_word (& ItemInt[the].pointer);
  *val = ItemIntVal;
}
void readItemInt(uint8_t the) {  /*A <- VAL[the] */
  ItemIntVal = pgm_read_word (& ItemInt[the].pointer);
}
#include <EEPROM.h>   /*EEPROM[the] <- A */
void writeItemIntEEPROM(uint8_t the) {
  uint8_t addr = pgm_read_byte(& ItemInt[the].addr);
  EEPROM.put(addr, ItemIntVal);
}
void readItemIntEEPROM(uint8_t the) {  /*A <- EEPROM[the] */
  uint8_t addr = pgm_read_byte(&ItemInt[the].addr);
  EEPROM.get(addr, ItemIntVal);
}
void readAllItemIntEEPROM() { /*OЗУ <- EEPROM[the] */
  for (int i = 0; i < numItemInt; ++i) {
    readItemIntEEPROM(i);
    writeItemIntEEPROM(i);
  }
}
void initAllItemIntEEPROM() { /*EEPROM[the] <-PROGMEM[the]*/
  for (int i = 0; i < numItemInt; ++i) {
    ItemIntVal = pgm_read_byte(& ItemInt[i].initSet);
    writeItemIntEEPROM(i);
  }
}
void plusItemInt(uint8_t the) {  /* if(А< Max[the]) ++A*/
  int maxInt = pgm_read_word (& ItemInt[the].maxInt);
  if (ItemIntVal < maxInt) ++ItemIntVal;
}
void minusItemInt(uint8_t the) { /* if(А> Min[the]) --A*/
  int minInt = pgm_read_word (& ItemInt[the].minInt);
  if (ItemIntVal  > minInt) --ItemIntVal;
}
//---------------END----------------------------------
void setup() {
  initAllItemIntEEPROM();
  readAllItemIntEEPROM();
}
void loop() {

}
/*Скетч использует 708 байт (2%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 17 байт (0%) динамической памяти, оставляя 2031 байт для локальных переменных. Максимум: 2048 байт.
*/

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Так еще один вариант Меню , идея от сюда https://geektimes.ru/post/255020/

/*
#0 ROOT[0] 
#1 var 1  CONFGINT[0]
#2 var 2  CONFGINT[1]
#3 var 3  CONFGINT[2]
*/

Выложу частями и потом целиком рабочую

Модуль Меню где формируется главная структура

//-------sItemMenu------------------
enum {
  ROOT,
  CONFIGINT
};
struct sItemMenu {
  byte type;
  byte num;
};
const sItemMenu dbItemMenu[] PROGMEM = {
  /*type,num*/
  {ROOT, 0}, /*#0 ROOT[0]*/
  {CONFIGINT, 0}, /*#1 var 1  CONFGINT[0]*/
  {CONFIGINT, 1}, /*#2 var 2  CONFGINT[1]*/
  {CONFIGINT, 2}  /*#3 var 3  CONFGINT[2]*/
};

Модуль Root

//-------sItemRoot------------------
struct sItemRoot {
  const byte top;
  const byte bottom;
  const pDo Do;
};
const sItemRoot dbItemRoot[] PROGMEM = {
  {1, 3, ExitMenu} /*ROOT[0]*/
};
byte readTopItemRoot(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemRoot[the].top);
}
byte readBottomItemRoot(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemRoot[the].bottom);
}
void DoItemRoot(byte the) { /* Do[the] */
  pDo Do = pgm_read_word(&dbItemRoot[the].Do);
  Do();
}

Модуль задания переменных int

//-----sItemConfigInt------------------
struct sItemConfigInt {
  int * pointer;
  byte addr;
  int   maxValue;
  int   minValue;
  int   setValue;
  const char *name;
};
const byte numItemConfigInt = 3; /*кол-во элементов ConfigInt */
const char txtItemConfigInt0[] PROGMEM = "var 1=";
const char txtItemConfigInt1[] PROGMEM = "var 2=";
const char txtItemConfigInt2[] PROGMEM = "var 3=";

const sItemConfigInt dbItemConfigInt[] PROGMEM = {
  /*pointer,addr,max,min,set,name*/
  { &Value1, 0, 100, 10, 50, txtItemConfigInt0}, /*CONFGINT[0]*/
  { &Value2, 2, 100, 10, 50, txtItemConfigInt1}, /*CONFGINT[1]*/
  { &Value3, 4, 100, 10, 50, txtItemConfigInt2}  /*CONFGINT[2]*/
};
int readItemConfigInt(byte the) {    /*<-VAL[the]*  ++ */
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  return * pointer;
}
void readItemConfigIntfromPROGMEM(byte the) { /*VAL[the]<-PROGMEM[the] ++ */
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  *pointer      = pgm_read_word(&dbItemConfigInt[the].setValue);
}
void plusItemConfigInt(byte the) {/*if (VAL[the]<max[the]) ++VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte maxValue = pgm_read_byte(&dbItemConfigInt[the].maxValue);
  if (* pointer < maxValue) *pointer += 1;
}
void minusItemConfigInt(byte the) {/*if (VAL[the]>min[the]) --VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte minValue = pgm_read_byte(&dbItemConfigInt[the].minValue);
  if (* pointer > minValue ) *pointer -= 1;
}
char * readItemConfigIntName(byte the) { /* <- name[the] */
  return (char *)pgm_read_byte(&dbItemConfigInt[the].name);
}
#include <EEPROM.h>
void readItemConfigIntfromEEPROM(byte the) { /*VAL[the]<-EEPROM[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte addr = pgm_read_byte(&dbItemConfigInt[the].addr);
  EEPROM.get(addr, *pointer);
}
void writeItemConfigIntToEEPROM(byte the) { /*EEPROM[the]<-VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte addr = pgm_read_byte(&dbItemConfigInt[the].addr);
  EEPROM.put(addr, *pointer);
}
void readAllItemConfigIntfromEEPROM() { /*VAL[All]<-EEPROM[All]*/
  for (int i = 0; i < numItemConfigInt; ++i) {
    readItemConfigIntfromEEPROM(i);
  }
}
/*EEPROM[All]<-PROGMEM[the][All]*/
void AllItemConfigIntPROGMEMtoEEPROM() {
  for (int i = 0; i < numItemConfigInt; ++i) {
    int ConfigInt = pgm_read_word(&dbItemConfigInt[i].setValue);
    byte addr = pgm_read_byte(&dbItemConfigInt[i].addr);
    EEPROM.put(addr, ConfigInt);
  }
}

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

А вот весь скетч. Потом буду в него добавлять модули

/**/
unsigned long mill;
typedef void (*pDo)();
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
class Cl_Btn {                      /* класс кнопка*/
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_BtnR----------------------
class Cl_BtnR : public Cl_Btn {      /* класс кнопка с повтором при удерж кнопки*/
  protected:
  public:
    /*конструктор*/
    Cl_BtnR(byte p, pDo D): Cl_Btn(p, D) {}
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
      if (!newBtn && !btn && mill - past >= 150) {
        past = mill;
        Do();
      }
    }
};
//-----Компоненты Меню-------------------------
void ExitMenu() {};
int Value1 = 10, Value2 = 20, Value3 = 30;
byte screen = 0; byte line = 1; bool fEdit = 0;
//-------sItemMenu------------------
enum {
  ROOT,
  CONFIGINT
};
struct sItemMenu {
  byte type;
  byte num;
};
const sItemMenu dbItemMenu[] PROGMEM = {
  /*type,num*/
  {ROOT, 0}, /*#0 ROOT[0]*/
  {CONFIGINT, 0}, /*#1 var 1  CONFGINT[0]*/
  {CONFIGINT, 1}, /*#2 var 2  CONFGINT[1]*/
  {CONFIGINT, 2}  /*#3 var 3  CONFGINT[2]*/
};
void pgm_viev(char *name) {/*вывод из PROGMEM*/
  char buffer[15];
  strcpy_P(buffer, name);
  Serial.print(buffer);
}
//-------sItemRoot------------------
struct sItemRoot {
  const byte top;
  const byte bottom;
  const pDo Do;
};
const sItemRoot dbItemRoot[] PROGMEM = {
  {1, 3, ExitMenu} /*ROOT[0]*/
};
byte readTopItemRoot(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemRoot[the].top);
}
byte readBottomItemRoot(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemRoot[the].bottom);
}
void DoItemRoot(byte the) { /* Do[the] */
  pDo Do = pgm_read_word(&dbItemRoot[the].Do);
  Do();
}
//-----sItemConfigInt------------------
struct sItemConfigInt {
  int * pointer;
  byte addr;
  int   maxValue;
  int   minValue;
  int   setValue;
  const char *name;
};
const byte numItemConfigInt = 3; /*кол-во элементов ConfigInt */
const char txtItemConfigInt0[] PROGMEM = "var 1=";
const char txtItemConfigInt1[] PROGMEM = "var 2=";
const char txtItemConfigInt2[] PROGMEM = "var 3=";

const sItemConfigInt dbItemConfigInt[] PROGMEM = {
  /*pointer,addr,max,min,set,name*/
  { &Value1, 0, 100, 10, 50, txtItemConfigInt0}, /*CONFGINT[0]*/
  { &Value2, 2, 100, 10, 50, txtItemConfigInt1}, /*CONFGINT[1]*/
  { &Value3, 4, 100, 10, 50, txtItemConfigInt2}  /*CONFGINT[2]*/
};
int readItemConfigInt(byte the) {    /*<-VAL[the]*  ++ */
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  return * pointer;
}
void readItemConfigIntfromPROGMEM(byte the) { /*VAL[the]<-PROGMEM[the] ++ */
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  *pointer      = pgm_read_word(&dbItemConfigInt[the].setValue);
}
void plusItemConfigInt(byte the) {/*if (VAL[the]<max[the]) ++VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte maxValue = pgm_read_byte(&dbItemConfigInt[the].maxValue);
  if (* pointer < maxValue) *pointer += 1;
}
void minusItemConfigInt(byte the) {/*if (VAL[the]>min[the]) --VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte minValue = pgm_read_byte(&dbItemConfigInt[the].minValue);
  if (* pointer > minValue ) *pointer -= 1;
}
char * readItemConfigIntName(byte the) { /* <- name[the] */
  return (char *)pgm_read_byte(&dbItemConfigInt[the].name);
}
#include <EEPROM.h>
void readItemConfigIntfromEEPROM(byte the) { /*VAL[the]<-EEPROM[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte addr = pgm_read_byte(&dbItemConfigInt[the].addr);
  EEPROM.get(addr, *pointer);
}
void writeItemConfigIntToEEPROM(byte the) { /*EEPROM[the]<-VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte addr = pgm_read_byte(&dbItemConfigInt[the].addr);
  EEPROM.put(addr, *pointer);
}
void readAllItemConfigIntfromEEPROM() { /*VAL[All]<-EEPROM[All]*/
  for (int i = 0; i < numItemConfigInt; ++i) {
    readItemConfigIntfromEEPROM(i);
  }
}
/*EEPROM[All]<-PROGMEM[the][All]*/
void AllItemConfigIntPROGMEMtoEEPROM() {
  for (int i = 0; i < numItemConfigInt; ++i) {
    int ConfigInt = pgm_read_word(&dbItemConfigInt[i].setValue);
    byte addr = pgm_read_byte(&dbItemConfigInt[i].addr);
    EEPROM.put(addr, ConfigInt);
  }
}
//-------------------
void VievMenu(byte screen, byte line, byte fEdit) {
  byte type = pgm_read_byte(&dbItemMenu[screen].type);
  byte num  = pgm_read_byte(&dbItemMenu[screen].num);
  if (type == ROOT) {
    byte top    = pgm_read_byte(&dbItemRoot[num].top);
    byte bottom = pgm_read_byte(&dbItemRoot[num].bottom);
    Serial.println();
    for (int i = top; i <= bottom; ++i) {
      Serial.println();
      if (i == line) {
        if (fEdit)Serial.print("*");
        else Serial.print(">");
      }
      else Serial.print(" ");
      byte t = pgm_read_byte(&dbItemMenu[i].type);
      byte n = pgm_read_byte(&dbItemMenu[i].num);
      if (t == CONFIGINT) {
        char * name = readItemConfigIntName(n);
        pgm_viev(name);
        int ItemValue = readItemConfigInt(n);
        Serial.print(ItemValue);
      }
    }
  }
}
//-----Компоновка------------------------------
void DoViev() {
  VievMenu(screen, line, fEdit);
}
Cl_Display Display(/*обработчик*/DoViev);
void DoBtn1() {/*-*/
  Display.refresh();
  if (fEdit) { /*реж редактирования*/
    byte t = pgm_read_byte(&dbItemMenu[line].type);
    byte n = pgm_read_byte(&dbItemMenu[line].num);
    if (t == CONFIGINT) {
      minusItemConfigInt(n);
    }
  }
  else {/*реж передвиж*/
    byte type = pgm_read_byte(&dbItemMenu[screen].type);
    byte num = pgm_read_byte(&dbItemMenu[screen].num);
    if (type == ROOT) {
      byte top    = pgm_read_byte(&dbItemRoot[num].top);
      if (line > top ) --line;
    }
  }
}
void DoBtn2() {/*+*/
  Display.refresh();
  if (fEdit) { /*реж редактирования*/
    byte t = pgm_read_byte(&dbItemMenu[line].type);
    byte n = pgm_read_byte(&dbItemMenu[line].num);
    if (t == CONFIGINT) {
      plusItemConfigInt(n);
    }
  }
  else {/*реж передвиж*/
    byte type = pgm_read_byte(&dbItemMenu[screen].type);
    byte num = pgm_read_byte(&dbItemMenu[screen].num);
    if (type == ROOT) {
      byte bottom = pgm_read_byte(&dbItemRoot[num].bottom);
      if (line < bottom ) ++line;
    }
  }
}
void DoBtn3() {/*exe*/
  Display.refresh();
  byte t = pgm_read_byte(&dbItemMenu[line].type);
  byte n = pgm_read_byte(&dbItemMenu[line].num);
  if (fEdit) { /*реж редактирования*/
    if (t == CONFIGINT) {
      writeItemConfigIntToEEPROM(n);
      fEdit = 0;
    }
    return;
  }
  else {/*реж передвиж*/
    if (t == CONFIGINT) {
      fEdit = 1;
    }
  }
}
Cl_BtnR Btn1(/*пин*/2,/*обработчик*/DoBtn1);/*-*/
Cl_BtnR Btn2(/*пин*/3,/*обработчик*/DoBtn2);/*+*/
Cl_Btn  Btn3(/*пин*/4,/*обработчик*/DoBtn3);/*exe*/
//--main()-------------------
void setup() {
  Serial.begin(9600);
  //AllItemConfigIntPROGMEMtoEEPROM();/*первичная настройка EEPROM из PROGMEM*/
  readAllItemConfigIntfromEEPROM(); /*прочитать из памяти*/
  Display.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}

void loop() {
  mill = millis();
  Display.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}
/*Скетч использует 3556 байт (11%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 236 байт (11%) динамической памяти, оставляя 1812 байт для локальных переменных. Максимум: 2048 байт.
*/

 

qwone
qwone аватар
Онлайн
Зарегистрирован: 03.07.2016

Дальнейшее развитие.  Для входа во внутрь папки кнопка Exe выход вверх выше 1 строки. Эскиз

/*
#0 ROOT[0]      !            1-3
#1 R0 HOLDER 1  !HOLDER[0]   4-6
#2 R0 HOLDER 2  !HOLDER[1]   7-9  
#3 R0 var 1     !CONFGINT[0]

#4 H0 HOLDER 2  !HOLDER[2]  10-12  
#5 H0 var 2     !CONFGINT[1]
#6 H0 var 3     !CONFGINT[2]

#7 H1 var 4     !CONFGINT[3]
#8 H1 var 5     !CONFGINT[4]
#9 H1 var 6     !CONFGINT[5]

#10 H2 var 7    !CONFGINT[6]
#11 H2 var 8    !CONFGINT[7]
#12 H2 var 9    !CONFGINT[8]
*/
void setup() {
}
void loop() {
}
/**/

Блок Меню 

/*ItemMenu*/
//-----sItemMenu----------------
enum {
  ROOT,
  HOLDER,
  CONFIGINT
};
struct sItemMenu {
  byte type;
  byte num;
};
const sItemMenu dbItemMenu[] PROGMEM = {
  /*type,num*/
  {ROOT, 0},     /*#0 ROOT[0]    1-3*/
  {HOLDER, 0},   /*#1 HOLDER[0]  4-6*/
  {HOLDER, 1},   /*#2 HOLDER[1]  7-9 */
  {CONFIGINT, 0},/*#3 var 1  CONFGINT[0]*/

  {HOLDER, 2},     /* #4 HOLDER[2]  10-12   */
  {CONFIGINT, 1},  /* #5 var 2  CONFGINT[1] */
  {CONFIGINT, 2},  /* #6 var 3  CONFGINT[2] */

  {CONFIGINT, 3},  /* #7 var 4  CONFGINT[3] */
  {CONFIGINT, 4},  /* #8 var 5  CONFGINT[4] */
  {CONFIGINT, 5},  /* #9 var 6  CONFGINT[5] */

  {CONFIGINT, 6},  /* #10 var 7  CONFGINT[6]*/
  {CONFIGINT, 7},  /* #11 var 8  CONFGINT[7]*/
  {CONFIGINT, 8}  /* #12 var 9  CONFGINT[8]*/
};
void pgm_viev(char *name) {/*вывод из PROGMEM*/
  char buffer[15];
  strcpy_P(buffer, name);
  Serial.print(buffer);
}
//---------------------
void setup() {


}

void loop() {

}

Блок Рут

/*ItemRoot*/
typedef void(*pDo)();
void ExitMenu() {};
//------sItemRoot-------------------
struct sItemRoot {
  const byte top;
  const byte bottom;
  const pDo Do;
};
const sItemRoot dbItemRoot[] PROGMEM = {
  {1, 3, ExitMenu} /*ROOT[0]*/
};
byte readTopItemRoot(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemRoot[the].top);
}
byte readBottomItemRoot(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemRoot[the].bottom);
}
void DoItemRoot(byte the) {  /* Do[the] */
  pDo Do = pgm_read_word(&dbItemRoot[the].Do);
  Do();
}
//----main()-----------------------
void setup() {

}

void loop() {

}
/**/

Блок папка

/*ItemHolder*/
//------sItemHolder-------------------
struct sItemHolder {
  const byte father;
  const byte top;
  const byte bottom;
  const char *name;
};
const char txtItemHolder0[] PROGMEM = "R0 HOLDER 1";
const char txtItemHolder1[] PROGMEM = "R0 HOLDER 2";
const char txtItemHolder2[] PROGMEM = "H0 HOLDER 3";
const sItemHolder dbItemHolder[] PROGMEM = {
  /*father,top,bottom,name*/
  {0,  4,  6, txtItemHolder0}, /*#0 HOLDER[0]*/
  {0,  7,  9, txtItemHolder1}, /*#1 HOLDER[1]*/
  {1, 10, 12, txtItemHolder2}  /*#2 HOLDER[2]*/
};
byte readFatherItemHolder(byte the) { /* <- father[the] */
  return pgm_read_byte(&dbItemHolder[the].father);
}
byte readTopItemHolder(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemHolder[the].top);
}
byte readBottomItemHolder(byte the) { /* <- top[the] */
  return pgm_read_byte(&dbItemHolder[the].bottom);
}
char * readItemHolderName(byte the) { /* <- name[the] */
  return (char *)pgm_read_byte(&dbItemHolder[the].name);
}
//----main()-----------------------
void setup() {

}

void loop() {

}
/**/

Блок Переменные int

/*ItemConfig*/
int Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8, Value9;
//-------sItemConfigInt----------------
struct sItemConfigInt {
  int * pointer;
  byte addr;
  int   maxValue;
  int   minValue;
  int   setValue;
  const char *name;
};
const byte numItemConfigInt = 9; /*кол-во элементов ConfigInt */
const char txtItemConfigInt0[] PROGMEM = "R0 var 1=";
const char txtItemConfigInt1[] PROGMEM = "H0 var 2=";
const char txtItemConfigInt2[] PROGMEM = "H0 var 3=";
const char txtItemConfigInt3[] PROGMEM = "H1 var 4=";
const char txtItemConfigInt4[] PROGMEM = "H1 var 5=";
const char txtItemConfigInt5[] PROGMEM = "H1 var 6=";
const char txtItemConfigInt6[] PROGMEM = "H2 var 7=";
const char txtItemConfigInt7[] PROGMEM = "H2 var 8=";
const char txtItemConfigInt8[] PROGMEM = "H2 var 9=";
const sItemConfigInt dbItemConfigInt[] PROGMEM = {
  /*pointer,addr,max,min,set,name*/
  { &Value1,  0, 100, 10, 10, txtItemConfigInt0}, /*CONFGINT[0]*/
  { &Value2,  2, 100, 10, 20, txtItemConfigInt1}, /*CONFGINT[1]*/
  { &Value3,  4, 100, 10, 30, txtItemConfigInt2}, /*CONFGINT[2]*/
  { &Value4,  6, 100, 10, 40, txtItemConfigInt3}, /*CONFGINT[3]*/
  { &Value5,  8, 100, 10, 50, txtItemConfigInt4}, /*CONFGINT[4]*/
  { &Value6, 10, 100, 10, 60, txtItemConfigInt5}, /*CONFGINT[5]*/
  { &Value7, 12, 100, 10, 70, txtItemConfigInt6}, /*CONFGINT[6]*/
  { &Value8, 14, 100, 10, 80, txtItemConfigInt7}, /*CONFGINT[7]*/
  { &Value9, 16, 100, 10, 90, txtItemConfigInt8}  /*CONFGINT[8]*/
};
int readItemConfigInt(byte the) {    /*<-VAL[the]*  ++ */
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  return * pointer;
}
void readItemConfigIntfromPROGMEM(byte the) { /*VAL[the]<-PROGMEM[the] ++ */
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  *pointer      = pgm_read_word(&dbItemConfigInt[the].setValue);
}
void plusItemConfigInt(byte the) {/*if (VAL[the]<max[the]) ++VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte maxValue = pgm_read_byte(&dbItemConfigInt[the].maxValue);
  if (* pointer < maxValue) *pointer += 1;
}
void minusItemConfigInt(byte the) {/*if (VAL[the]>min[the]) --VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte minValue = pgm_read_byte(&dbItemConfigInt[the].minValue);
  if (* pointer > minValue ) *pointer -= 1;
}
char * readItemConfigIntName(byte the) { /* <- name[the] */
  return (char *)pgm_read_byte(&dbItemConfigInt[the].name);
}
#include <EEPROM.h>
void readItemConfigIntfromEEPROM(byte the) { /*VAL[the]<-EEPROM[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte addr = pgm_read_byte(&dbItemConfigInt[the].addr);
  EEPROM.get(addr, *pointer);
}
void writeItemConfigIntToEEPROM(byte the) { /*EEPROM[the]<-VAL[the]*/
  int * pointer = pgm_read_word(&dbItemConfigInt[the].pointer);
  byte addr = pgm_read_byte(&dbItemConfigInt[the].addr);
  EEPROM.put(addr, *pointer);
}
void readAllItemConfigIntfromEEPROM() { /*VAL[All]<-EEPROM[All]*/
  for (int i = 0; i < numItemConfigInt; ++i) {
    readItemConfigIntfromEEPROM(i);
  }
}
void AllItemConfigIntPROGMEMtoEEPROM() { /*EEPROM[All]<-PROGMEM[the][All]*/
  for (int i = 0; i < numItemConfigInt; ++i) {
    int ConfigInt = pgm_read_word(&dbItemConfigInt[i].setValue);
    byte addr = pgm_read_byte(&dbItemConfigInt[i].addr);
    EEPROM.put(addr, ConfigInt);
  }
}
//-----------------------------
//-------main()-----------------
void setup() {

}

void loop() {

}
/**/