ссылка на указатель функции . Само сочетание слов уже сбивает с толка.
void func() { // это просто функция
Serial.println("qwqewqewq");
}
void (*pFun)() = &func;// это указатель на функцию
void (*&pA)() = pFun;// это ссылка на указатель функции
void setup() {
Serial.begin(9600);
pA();
}
void loop() {
}
/* Пример 1
*/
//---------------классы---------
// пример класса Cl_AAA
class Cl_AAA {
int data;// параметры класса
public:
// конструктор класса
Cl_AAA(int _data):data(_data) {}
Как раз читаю книгу по С++. И вроде конструкторы уже позади, но эта форма конструктора не понятна. Во всех примерах конструктор выглядит ну где-то так:
Cl_AAA(int k){data = k;}
Не сочтите за труд направить где почитать про такую форму?
Инициализируйте члены класса из аргументов конструктора, используя список инициализации членов. В этом методе применяется прямая инициализация, что более эффективно, чем использование операторов присваивания в теле конструктора.
ПС: Си(Си++) не стоит на месте, он развивается. А значит находятся новые приемы, чем описаны в более старых источниках.
#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-ух кнопок для выбора цикла(режима > скейтча)
Для тех кого раздражает глобальный писец 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)
Это суперсимметрия. Появление параметра вызвано наличием его обявления ;) Восстановить симетрию можна доработав void init(void) с аналогичным параметром. А вот передача параметра по ссылке может вызвать глобальное искажение времени. И даже его обратный ход.
А вот передача параметра по ссылке может вызвать глобальное искажение времени. И даже его обратный ход.
Если вы заметили ошибку, то укажите и метод решения. Или вы живете по принципу "чукча не читатель- чукча писатель, поэтому чукча не читает, что сам пишет"
ПС: Не нравится посто ссылка, замените на константную ссылку.
/*работа-вставить в loop()*/
void run(const unsigned long &mill) {
if (mill - past >= time) {
past = mill;
invert();
}
}
Событийное программирование для "бедных". Во компьютерах очень часто упоминается о собитиях и их обработчиках. Но как очень просто организовать такой подход на Ардуине. Разумеется можно использовать аппаратное прерывание. Но в некоторых случаях хватает и программного прерывания.
/**/
//---событие A---------------
bool eventA = 0; // 0-не произошло /1- произошло
/*обработчик события*/
void DoA() {}
//---main()------------
void setup() {
}
void loop() {
if (eventA) DoA();
}
Недостаток такого кода в том, что обработчик вызывается каждый раз, когда проходит loop . Так что есть такой вариант. Выпольнился обработчик и сбросился
Код у Вас какойто слишком философский. Нигде eventA в 1 не устанавливается. А раз нет такого - зачем вобще все остальное? ИМХО рассматривать единичное событие (а я бы сказал что это скорей флаг у Вас чем событие) - пустая трата времени. Запилите сразу очередь событий, да позабористей! Чтоб обработчик событий не просто так, а обрабатывая событие добавлял пару-тройку новых в очередь. Например, рассмотрим пример, на примере кнопки - с удержаниями, даблкликами и прочими наворотами.
Стремно это все. Скажите qwone, Вы мазохист или просто засрать флеш повторами кода хотите? Посмотрите на классы Cl_Btn и Cl_BtnR. Их отличает три строки 116, 117 и 118 . Они есть во втором классе и отсутствуют в первом. Сколько кода и сколько раз Вы готовы нагло продублировать, чтоб не разбиратся с наследованием классов?
Logik. Если бы смысл был в данном случае от наследовании, то да. а так крутить код ради этого не стоит. Это как экономить спички паля бумажные банкноты. Да и этот скеч заглатывает много переменных, но ест похоже мало памяти. Можно хоть на 100 переменных закатать.
// Если бы смысл был в данном случае от наследовании, то да. а так крутить код ради этого не стоит. Это как экономить спички паля бумажные банкноты.
Даже незнаю, плакать или смеятся над Вашим упрямством. Попробуем так:
Вариант1. Без наследования, как у Вас. В исходниках 2 класса, их код, с мизерным отличием записан 2 раза. Все доработки, правки и т.д необходимо не забывать дублировать. В флеше код также будет присутствовать в 2-х экземплярах занимая в два раз больше места чем возможно.
Вариант2. С статическим наследованием. В исходниках 2 класса. Первый содержит код, общий для 2-х классов. Второй указан как наследник первого, что означает что методы и данные которые явно не прописаны брать из первого класса. Явно прописаны только отличия, по сути те три строки о которых было выше. Соответственно доработки общего для классов функционала делаются только в коде первого класса. В флеше все аналогично - все методы из первого класса и метод реализующий отличие второго класса от первого, и места займет почти в 2 раза менше.
Какие спички, какие ассигнации?! Очевидно что вариант2 наголову превосходит вариант1. Тем более что все механизмы наследования скрыты от разработчика и автоматом реализуются компилятором. Вашему коду пока очень далеко до состояния когда "крутить код ради этого не стоит". Увы. Вы и на 3% возможности ООП не освоили и не раскрываете. Хотя и выгодно отличаетесь от общей массы форума тем что хоть пробуете. Но Вам пока не учить и примеры выкладывать, а учится и по сто раз переписывать впору. Не обижайтесь, но Ваше упрямство совершенно ирационально, больше всего оно вредит именно Вам.
Для любителей экономить строчки исходника переписал код #222 Ну может у них в экран вся программа не помещается. А а скролл крутить религия не дает . размер такой же.
Проводим эксперимент. Берем 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() и чудеса заканчиваются ))
И чем сложней - код тем больше наследование будет выигрывать.
Надеюсь все понятно? Не проверяйте на коротком коде, оптимизация и мелкие издержки исказят результат.
ПС. Не устраюйте срач отрицая очевидное. Дураку понятно что меньше повторов в исходнике - лучше. Меньше флеша занято - лучше. Вы взялись учить ООП - так слушаете тех кто его знает.
ПС: Код сыроват. Требует отладки не только на уровне кода, а так же структуры программы и удобства для пользователя. + - можно менять долго удерживая нужные кнопки. вывод на главный экран не организован.
Это так промежуточный скетч на тему Меню и PROGMEM, и некоего поиска оптимизации написания и работы кода. Писать сразу код с PROGMEM это куча убитого времени. Так что лучше писать без, а потом переписывать с. Код без ...
ну вот опять пересрались, а может некоторым интересно, мнение всех и гуру и ...
Ну если тебе так уж интересно мнение, то у меня есть его: код пуха для работы с меню - слабенький, не хватает декомпозиции. Вот делает он это меню, и мешает всё в кучу, например, вид представления данных со способом его отображения. Как итог - код получается нереентерабельный. Вернее сказать так: код пуха - это наброски меню, с непонятным пока профитом от их использования.
Касаемо меню: мухи отдельно - котлеты отдельно: меню не должно содержать в себе никаких методов вывода его содержимого куда-либо, от слова "совсем". Методы навигации по внутренней структуре меню - да пожалуйста; удаление/добавление пунктов - велкам. Сохранение/загрузка - тоже допустимо, через внешний интерфейс сериализатора, а не напрямую работать с EEPROM или SD (сериализаторы - на случай, когда нужно на лету переключить загрузку/сохранение структуры с EEPROM на SD, например). Отображение - меню не должно ничего знать о способах его отображения - для этого должны быть отдельные сущности, наследуемые от общего интерфейса.
Как-то так, если совсем вкратце. Если интересуют детали - спрашивай, можно сделать набросок.
Вся эта пляска связана с малым размером ОЗУ, кривым PROGMEMом и моим маловатым уровнем знаний. Для нано это еще способ вытащить еще ресурсы. А помощнее уже переходить более програссивным способам написания программы.
Так в том коде из #232 вобще нет класса меню на экран )) То что какбы оно - Cl_Display на самом деле класс отложеного вызова некоего обработчика. Его можна инитить где угодно (причем старый обработчик теряется без следа и предупреждения), можна вызвать запуск обработчика, класс запомнит это но не вызовет сразу, а вызовит позже, предполагаемо из лопа. Класс условно пригоден например для запуска действия из прерывания и т.д. но к меню и экрану вобще не относится.
В более позднем творчестве обнаружен дивный Cl_Control, который вобщем обернул три кнопки вместе с их обработчиками и функционал из Cl_Display. При этом все перечисленное хоть и обединено в класс, но логически не связано в нем. Хотя по назначению вроде должно, а по факту и связывается, но почемуто внешними сущностями. Очень странная штуковина. Годится для многого, например можна управление машинкой на ней сделать (лево, право, вперед и обработчик для внесения изменений в ШИМы и сервоприводы - както так). Эта универсальность - следствие полного отсутствия смысла внутри класса. Он безсмысленый, потому его можна использовать почти везде, он ниче правда не даст но и не помешает. Впринципе я бы его, после допилинга, как базовый для чегонить может и заюзал бы. Но не знаю зачем )))
ссылка на указатель функции . Само сочетание слов уже сбивает с толка.
void func() { // это просто функция Serial.println("qwqewqewq"); } void (*pFun)() = &func;// это указатель на функцию void (*&pA)() = pFun;// это ссылка на указатель функции void setup() { Serial.begin(9600); pA(); } void loop() { }Следующий этап Пример 1
/* Пример 1 */ //---------------классы--------- // пример класса Cl_AAA class Cl_AAA { int data;// параметры класса public: // конструктор класса Cl_AAA(int _data):data(_data) {}Как раз читаю книгу по С++. И вроде конструкторы уже позади, но эта форма конструктора не понятна. Во всех примерах конструктор выглядит ну где-то так:
Cl_AAA(int k){data = k;}Не сочтите за труд направить где почитать про такую форму?
5N62V !Цитата от сюда https://msdn.microsoft.com/ru-ru/library/s16xw1a8.aspx#member_lists
Инициализируйте члены класса из аргументов конструктора, используя список инициализации членов. В этом методе применяется прямая инициализация, что более эффективно, чем использование операторов присваивания в теле конструктора.
ПС: Си(Си++) не стоит на месте, он развивается. А значит находятся новые приемы, чем описаны в более старых источниках.
Вот еще код под RFID. Переделал свой старый код под существующую концепцию
а есть вариант что бы работало с Двумя ( для карт(MFRC522) и в виде брелков(металических))
пытаюсь с CLASS сделать но что то не выходит
> Дубликатор <
Заранее благоарю)
ЗЫ. не получается совместить всё и смена режима считывания на кнопку отдельную.
а второй кнопкой делать (запись ->считывание)
Ну нет у меня двух RFID приемников. Что в наборе было, тем и пользовался . Был брелок там и карточка. Но приемник там был один на все и работал.
у меня выходит что нужно совместить грубо говоря.
#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
вот для него еще не сделал(не нашел скейтч)
есть только считывание
записать только не получается с скопированного
http://forum.amperka.ru/threads/%D0%A0%D0%B5%D1%88%D0%B5%D0%BD%D0%BE-%D0...
у меня выходит что нужно совместить грубо говоря.
#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
мне нужно то бы смена режима была на кнопку.
ну по типу .
Включил (ожидание)
нажал одну из 2-ух кнопок для выбора цикла(режима > скейтча)
мне сама суть нужна смены (выбор).
по сути 2 кнопки всего
--------------------------------------------> :-)
выбора цикла по идее
Для тех кого раздражает глобальный
писец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)
В начальном ардуине скетче есть void setup(void) и симметричная ей void loop(void) .
ох, Пух, наерна я, на старости лет, чота не понимаю в симметрии...
Это суперсимметрия. Появление параметра вызвано наличием его обявления ;) Восстановить симетрию можна доработав void init(void) с аналогичным параметром. А вот передача параметра по ссылке может вызвать глобальное искажение времени. И даже его обратный ход.
ПС: Не нравится посто ссылка, замените на константную ссылку.
/*работа-вставить в loop()*/ void run(const unsigned long &mill) { if (mill - past >= time) { past = mill; invert(); } }Статья :: Обработка событий в С++ : Александр Клюев http://programming-lang.com/ru/comp_programming/klyuev/0/j0.html
Событийное программирование для "бедных". Во компьютерах очень часто упоминается о собитиях и их обработчиках. Но как очень просто организовать такой подход на Ардуине. Разумеется можно использовать аппаратное прерывание. Но в некоторых случаях хватает и программного прерывания.
/**/ //---событие 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(); } }ПС: Там есть защита от вызова, если функция не подключена
А как это выглядит "для богатых"? Вместо "
typedefvoid(*pDo)();" использовать using или как-то по-другому?Если в процессе надо менять функции обработчики на различные функции, то может пойти такой вариант
/**/ 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(); } }и где тут замена обработчиков на разные функци?
Код у Вас какойто слишком философский. Нигде eventA в 1 не устанавливается. А раз нет такого - зачем вобще все остальное? ИМХО рассматривать единичное событие (а я бы сказал что это скорей флаг у Вас чем событие) - пустая трата времени. Запилите сразу очередь событий, да позабористей! Чтоб обработчик событий не просто так, а обрабатывая событие добавлял пару-тройку новых в очередь. Например, рассмотрим пример, на примере кнопки - с удержаниями, даблкликами и прочими наворотами.
Скину сюда учебный скетч Меню , который вызывает у меня кучу вопросов в своей неокоченности
//**/ 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 байт. */Вот немного доработал код Меню. Так немного погонял. Но скорее там надо еще 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 байт. */Вот это уже можно применять в ваших поделках. Обновление не по времени, а по щелчку кнопки
/**/ 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 байт. */Стремно это все. Скажите qwone, Вы мазохист или просто засрать флеш повторами кода хотите? Посмотрите на классы Cl_Btn и Cl_BtnR. Их отличает три строки 116, 117 и 118 . Они есть во втором классе и отсутствуют в первом. Сколько кода и сколько раз Вы готовы нагло продублировать, чтоб не разбиратся с наследованием классов?
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 байт. */// Если бы смысл был в данном случае от наследовании, то да. а так крутить код ради этого не стоит. Это как экономить спички паля бумажные банкноты.
Даже незнаю, плакать или смеятся над Вашим упрямством. Попробуем так:
Вариант1. Без наследования, как у Вас. В исходниках 2 класса, их код, с мизерным отличием записан 2 раза. Все доработки, правки и т.д необходимо не забывать дублировать. В флеше код также будет присутствовать в 2-х экземплярах занимая в два раз больше места чем возможно.
Вариант2. С статическим наследованием. В исходниках 2 класса. Первый содержит код, общий для 2-х классов. Второй указан как наследник первого, что означает что методы и данные которые явно не прописаны брать из первого класса. Явно прописаны только отличия, по сути те три строки о которых было выше. Соответственно доработки общего для классов функционала делаются только в коде первого класса. В флеше все аналогично - все методы из первого класса и метод реализующий отличие второго класса от первого, и места займет почти в 2 раза менше.
Какие спички, какие ассигнации?! Очевидно что вариант2 наголову превосходит вариант1. Тем более что все механизмы наследования скрыты от разработчика и автоматом реализуются компилятором. Вашему коду пока очень далеко до состояния когда "крутить код ради этого не стоит". Увы. Вы и на 3% возможности ООП не освоили и не раскрываете. Хотя и выгодно отличаетесь от общей массы форума тем что хоть пробуете. Но Вам пока не учить и примеры выкладывать, а учится и по сто раз переписывать впору. Не обижайтесь, но Ваше упрямство совершенно ирационально, больше всего оно вредит именно Вам.
Тут я с Logic согласно, Пух.
Проводим эксперимент. Берем 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 байт. */Ура мы ЭКОНОМИМ СТРОЧКИ ИСХОДНИКА, но нафиг это мне сдалось, так как программа занимает один и тот же размер.
Для любителей экономить строчки исходника переписал код #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 байт. */Проводим эксперимент. Берем 2 класса с отличающими методами.
Ура мы ЭКОНОМИМ СТРОЧКИ ИСХОДНИКА, но нафиг это мне сдалось, так как программа занимает один и тот же размер.
Кривые руки дают кривой опыт. Собираю оба Ваших кода, имею одинаковое
И чем сложней - код тем больше наследование будет выигрывать.
Надеюсь все понятно? Не проверяйте на коротком коде, оптимизация и мелкие издержки исказят результат.
ПС. Не устраюйте срач отрицая очевидное. Дураку понятно что меньше повторов в исходнике - лучше. Меньше флеша занято - лучше. Вы взялись учить ООП - так слушаете тех кто его знает.
Еще вариант использования 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 байт. */Еще вариант использования PROGMEM . Так для пробы
и чЁ?! "ОйНуВсеПроехали"? :)) По бабски это както, qwone, не по мужски ;)
Пух, пей лучше простую вотку, без добавок.
Очередной вариант скетча Меню.
/**/ 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 байт. */Ну и собственно скетч.
/*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
как подписоваться без комментария не знаю, потому оставлю )
И собственно сам код
/*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 байт. */ПС: Код сыроват. Требует отладки не только на уровне кода, а так же структуры программы и удобства для пользователя. + - можно менять долго удерживая нужные кнопки. вывод на главный экран не организован.
Ну и сам скетч.
/*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(); } /**//*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 байт. */ПС: Вроде заготовка под меню у меня получилась
Вам интересно профессиональное мнение или ну его нафиг?
Вам интересно профессиональное мнение или ну его нафиг?
мне интересно пидагогическое мнение.
А смысл. Мне что ВАШЕ профессиональное мнение ума добавит. Как могу так и пишу. Это пока потолок. Может попозже получше выйдет.
Так как ковыряться в большом и запутаном коде сложно выложу некоторые моменты
/**/ 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 байт. */Это так промежуточный скетч на тему Меню и 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 байт. */ну вот опять пересрались, а может некоторым интересно, мнение всех и гуру и ...
Ну если тебе так уж интересно мнение, то у меня есть его: код пуха для работы с меню - слабенький, не хватает декомпозиции. Вот делает он это меню, и мешает всё в кучу, например, вид представления данных со способом его отображения. Как итог - код получается нереентерабельный. Вернее сказать так: код пуха - это наброски меню, с непонятным пока профитом от их использования.
Касаемо меню: мухи отдельно - котлеты отдельно: меню не должно содержать в себе никаких методов вывода его содержимого куда-либо, от слова "совсем". Методы навигации по внутренней структуре меню - да пожалуйста; удаление/добавление пунктов - велкам. Сохранение/загрузка - тоже допустимо, через внешний интерфейс сериализатора, а не напрямую работать с EEPROM или SD (сериализаторы - на случай, когда нужно на лету переключить загрузку/сохранение структуры с EEPROM на SD, например). Отображение - меню не должно ничего знать о способах его отображения - для этого должны быть отдельные сущности, наследуемые от общего интерфейса.
Как-то так, если совсем вкратце. Если интересуют детали - спрашивай, можно сделать набросок.
Вся эта пляска связана с малым размером ОЗУ, кривым PROGMEMом и моим маловатым уровнем знаний. Для нано это еще способ вытащить еще ресурсы. А помощнее уже переходить более програссивным способам написания программы.
кривым PROGMEMом
В каком месте он кривой?
В сложности совмещения в одном классе данных из ОЗУ и PROGMEM . По крайней мере я не видел таких скетчей. Или мне надо опять открыть "Америку".
В сложности совмещения в одном классе данных из ОЗУ и PROGMEM .
Просто не надо мешать мух с котлетами, и тогда надуманных проблем станет на порядок меньше ;)
Так в том коде из #232 вобще нет класса меню на экран )) То что какбы оно -
Cl_Displayна самом деле класс отложеного вызова некоего обработчика. Его можна инитить где угодно (причем старый обработчик теряется без следа и предупреждения), можна вызвать запуск обработчика, класс запомнит это но не вызовет сразу, а вызовит позже, предполагаемо из лопа. Класс условно пригоден например для запуска действия из прерывания и т.д. но к меню и экрану вобще не относится.В более позднем творчестве обнаружен дивный
Cl_Control, который вобщем обернул три кнопки вместе с их обработчиками и функционал изCl_Display. При этом все перечисленное хоть и обединено в класс, но логически не связано в нем. Хотя по назначению вроде должно, а по факту и связывается, но почемуто внешними сущностями. Очень странная штуковина. Годится для многого, например можна управление машинкой на ней сделать (лево, право, вперед и обработчик для внесения изменений в ШИМы и сервоприводы - както так). Эта универсальность - следствие полного отсутствия смысла внутри класса. Он безсмысленый, потому его можна использовать почти везде, он ниче правда не даст но и не помешает. Впринципе я бы его, после допилинга, как базовый для чегонить может и заюзал бы. Но не знаю зачем )))ПМ. Тоже
Cl_Controlявно не меню./**/ //-----------------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 байт. */Так еще один вариант Меню , идея от сюда https://geektimes.ru/post/255020/
Выложу частями и потом целиком рабочую
Модуль Меню где формируется главная структура
//-------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); } }А вот весь скетч. Потом буду в него добавлять модули
/**/ 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 байт. */Дальнейшее развитие. Для входа во внутрь папки кнопка 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() { } /**/