Официальный сайт компании Arduino по адресу arduino.cc
PROGMEM и массив указателей на функции
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Сб, 05/09/2020 - 12:22
Добрый день, камрады
Опять проклятый PROGMEM не дает мне покоя:
Есть такой скетч:
#define TYPE_0 0 #define TYPE_1 1 #define TYPE_2 2 struct menuStruct { const byte type; const byte param1; const byte param2; const byte param3; const byte param4; }; const menuStruct menu[] PROGMEM = { {TYPE_0, 0, 0, 0, 0 }, {TYPE_1, 0, 0, 0, 0 }, {TYPE_2, 0, 0, 0, 0 }, {TYPE_0, 0, 0, 1, 0 }, {TYPE_1, 0, 0, 0, 0 }, {TYPE_2, 0, 0, 0, 0 }, }; void testFunc1(); void testFunc2(); void testFunc3(); typedef void (*ptrFunc)(); const ptrFunc funcArray[] PROGMEM = {&testFunc1, &testFunc2, testFunc3}; void setup() { Serial.begin(57600); Serial.println("Started"); delay(1000); } void loop() { } void testFunc1() { Serial.println("testFunc 1"); } void testFunc2() { Serial.println("testFunc 2"); } void testFunc3() { Serial.println("testFunc 3"); }
Черт, ни удалить, ни отредактировать не могу после сохранения. Продолжение в этом письме:
Если в loop ставим такой код, то все работает нормально:
а если такой, то ардуина уходит в глухую перезагрузку:
При этом если считать pgm_read_byte(&menu[0].type) в переменную и вывести в сериал - видим корректные значения. Вопрос - что я делаю не так?
Не надо в progmem хранить указатели на функции. В этом нет никакого смысла.
Не надо в progmem хранить указатели на функции. В этом нет никакого смысла.
Понятно, что указатель это не текстовая строка и особой экономии памяти не получим. Но сам факт - почему не работает?
Нет. Вообще никакой экономии не будет, и даже наоборот.
Не работает потому что ты совсем не понимаешь что делаешь. Указатель на функцию это не индекс в массиве.
Не работает потому что ты совсем не понимаешь что делаешь. Указатель на функцию это не индекс в массиве.
Не отрицаю что я жестко туплю, вот и пытаюсь разобраться как это работает. Как правильно обратиться к функции через указатель, лежащий в массиве?
array[1]()
в работающем варианте так и сделано. Но мне нужно индекс взять из поля type структуры menu, тоже лежащей в progmem (что и пытаюсь проделать в неработающем примере).
То есть у меня array[1]() работает.
Но в то же время
array[pgm_read_byte(&menu[0].type)](); - не работает
byte a = pgm_read_byte(&menu[0].type); (проверяю, значение а равно единице)
array[a](); - не работает.
Потому что у тебя не массив указателей на функции, а массив указателей на флеш-память, в которой лежат указатели на функции.
а как должна быть правильно реализована подобная конструкция?
Без progmem. А еще лучше без указателей на функцию, если это просто меню.
Без progmem. А еще лучше без указателей на функцию, если это просто меню.
Про без progmem - это я услышал. а как обойтись без указателей на функцию? Из меню требуется запускать функции, сейчас использую указатели (как вы уже поняли по первому вопросу). как уйти от этого (и почему неправильно использовать для этого указатели)? Через флаги? Прибить гвоздями в обработчике нажатия кнопки ок (если номер пункта такой, то, то запускаем эту функцию, а если такой, то другую) - я думал что этот подход неверный?
P.S. Опять меня изучение ардуины в какие-то дебри завело?
И кто вам использовать лямда-функцию запретил? #298
Видимо отсутствие жопыта. Смотрю в ваш пост(который 298), вроде и все буквы знакомые, а смысл уловить не могу. Можете тезисно раскрыть идею?
Keys.Do_Right это указатель на функцию
[] {goPage(page0);} это сама лямда-функция.
Ну а меню строится на принципах цифрового автомата. Любой переход на следующий пункт меню это переход в состояние где надо напечатать новую страницу и поменять обработчики кнопок. Ведь естественно действие кнопок в другом состоянии другое,чем в предыдущем состоянии.
ПС:#222 этот пост и немного ниже покажет как писать меню через цифровой автомат
Почитал, пока это выше моего уровня понимания (я на весьма начальном этапе освоения), возможно осмыслю в будущем.... По меню у меня несколько другая идеология вышла: нажатия кнопок (кроме навигации по меню), в зависимости от активного пункта вызывают функции по указателю (и передают в них аргументы, которые прописаны в структуре меню), которые уже обрабатываются в функции. функция с разными аргументами может быть вызвана разными пунктами меню. После выполнении функции запускается (или не запускается, если не нужно) обновление экрана.
почему это естественно? кнопка + увеличивает значение переменной, - уменьшает..., select подтверждает выбор. То есть событие одно передается в функцию, а как она уже там будет его разруливать - определим в функции. Или я опять не правильно вас понял?
Разумеется.Во-первых надо определится сколько кнопок у вас есть. Потом разработать переходы в меню.Если у вас 3 кнопки(+ - sel), то надо делать переключение режимов бег по пунктам меню/ изменение меню. И много много еще, что очень надо для нормальной работы меню,но при этом очень записывает код. Так что даже если вам к примеру дать рабочий код пусть структуру и прогмем вы все равно в нем запутаетесь.
ПС:#236 смотрите мне этого не жалко или https://habr.com/ru/post/257607/
вот по мотивам этой статьи и делал, но с поправкой на свой невысокий уровень :). Все работает, но вот указатели и прогмемы мне покоя не дают )))
Ну а через лямду прячется в PROGMEM/ а точнее во флеш вся функция ,а не только указатель. Это очень сильно упрощает код для тех кто понимает.
Если в loop ставим такой код, то все работает нормально:
а если такой, то ардуина уходит в глухую перезагрузку:
При этом если считать pgm_read_byte(&menu[0].type) в переменную и вывести в сериал - видим корректные значения. Вопрос - что я делаю не так?
В первом случае - конcтанта потому она просто заменяется нужным адресом на этапе компиляции. Именно потому и кажется, что всё работает. Во втором случае Вы не читаете из PROGMEM!
Чтобы разобраться попробуйте переписать первый случай но не с конcтантой, а с переменной (нетривиальной, чтобы на этапе компиляции не вычислялась) - тоже не будет работать. Чтобы работать с переменной, нужно адрес функции явно из прогмем читать, а не просто писать funcArray[i].
Ну а через лямду прячется в PROGMEM/ а точнее во флеш вся функция
А что без лямбд функция живёт не во флеше? А где?
Евгений, спасибо за объяснение причины. Вопрос, почему тогда:
возвращает правильные значения? Тоже компилятор вычисляет? Или все-таки чтение значения из menu[i].type проходит верно?
Но стоит добавить в цикл
получаем в порт полную фигню. Уже не перезагрузку платы, как ранее, но тоже понятно что куда то не туда обращаюсь. То есть получается я как то не так пытаюсь обратиться к указателю функции (или как то неправильно я функции и(или) указатели сложил в прогмем?
Так progmem же используется для констант?
Перекурил несколько сайтов, везде такой пример чтения данных из progmem через указатель описан. Или я не улавливаю Вашу мысль? Или я где то глобально свернул с верного пути в своих рассуждениях?
Перекурил несколько сайтов, везде такой пример чтения данных из progmem через указатель описан. Или я не улавливаю Вашу мысль? Или я где то глобально свернул с верного пути в своих рассуждениях?
Dinosaur. Вот Вы можете отличить атом от молекулы. В некоторых случаях это одно и тоже, а с другой стороны это разные вещи. По все же подумайте как отличить атом от молекулы.Это очень фундаментально.
Это я к тому, что есть ОЗУ и есть Флеш-память. Для Ардуины это совершенно разные памяти. Но когда рисовался язык Си это было одно и тоже.То есть находилось в одном адресном пространстве, и ОЗУ и ПЗУ. По факту Си Ардуины работает с ОЗУ и только с ней. А с данными с PROGMEM только через PROGMEM и pgm_read_XXX размещение и извлечение. Не буду говорить про организацию ссылочных классов, которые могут реально подменять работу с флеш или EEROM как с ОЗУ c точки зрения Си.
Вот такие "различия атомов и молекул" у Ардуино.
Так progmem же используется для констант?
Я миел в виду индекс массива.
А по остальному, знаете, Вы путаетесь что у Вас где. Посмотрите вот этот пост (ну и тему вверх=вниз). Там я пытался пояснить разницу между хранением в прогмем указателя, сами данных и "и того, и другого".
Не надо в progmem хранить указатели на функции. В этом нет никакого смысла.
С этим утверждением можно поспорить. Например при реализации табличного перехода, вполне себе указатели могут быть в массивах, а массив в прогмем.
при реализации табличного перехода, вполне себе указатели могут быть в массивах, а массив в прогмем.
Читать их потом накладно оттудова. :)