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