ARDUINO Корректное программирование в Arduino

Shuster

★✩✩✩✩✩✩
4 Сен 2021
50
11
Добрый день!

Я знаю основы программирования, и пишу на C#.
Но в ардуино только с августа этого года.
Скетчи пишу в PlatformIO.

У меня несколько базовых вопросов, так как C++ не такой как C#, а ещё и работа с контроллером налаживает некоторые ограничения. Как минимум ограниченная память всего 30кб.


Во многих скетчах @AlexGyver (к примеру Куча эффектов для ws2812b) в служебном методе loop() находится очень много кода.

Я как обычно в программировании, выношу код в отдельные методы, в итоге у меня получается что-то вроде api в которых я поочерёдно выполняю методы опрос кнопки, опрос таймера, и изменение режима подсветки.

loop():
void loop()
{
  btn_survey();
  tmr_survey();
  mode_switch();
}
Код получается читаемым.

Но вижу количество созданных проектов на этом сайте, и на канале youtube, где в loop() очень много кода у меня закрадывается мысль, что в ардуино так делать как сделал я нельзя.
Или как минимум не желательно, типа какая-то там оптимизация в loop и т.д.
Информации об этом нигде не нашёл. Или так делать можно, или неправильными запросами кормил гугл.


Такой же вопрос по setup().
Можно ли код из setup вынести в отдельные методы?
Конечно Serial.begin(9600); останется там, а вот первоначальные настройки кнопки и таймера, я бы вынес в отдельные методы как и опросы в loop().


Наверное больше грузить не буду, а то и так простыня получилась )
 

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
@Shuster, да на здоровье. Пишите код как вам нравится. Только классами особо не увлекайтесь без надобности, ОЗУ мало, да и флеша не особо много, каждый байт на счету.
 
  • Лойс +1
Реакции: Shuster

Старик Похабыч

★★★★★★★
14 Авг 2019
4,272
1,303
Москва
Так делать правильно и красиво. Все верно. Но есть одно но. Каждый вызов функции (а в С++ функции в основном, и то что вы написали тоже функции. Методы они у объектов) увеличивает стек. При определенной вложенности и загрузке памяти стек наползает на кучу и случается "ой".
У меня лично так было. Долго не мог понять. Потом начал проверять свободное место и нашел проблему.
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Shuster, в приведённом примере нет вызова методов, а есть вызовы функций. @kostyamat прав: программируйте так, как Вам удобно, если результат помещается в память. Учтите также, что контроль памяти в микроконтроллерах весьма несовершенный, стек и динамическая память легко пересекаются, поэтому во многих случаях легче выделить переиспользуемые переменные с глобальной видимостью и обращаться к ним напрямую.
Что касается setup и loop - это ж просто инструмент, никто не заставляет Вас им пользоваться. Реализуемые программы в Ардуино не бог весть какие, потому сильно структурировать их без необходимости не требуется. К примеру, если говорить о Вашем примере: как передать команду из одного класса на выполнение в другой? В "больших" системах есть поддержка различного рода общих фреймворков, часть функций берёт на себя ОС или контейнер. Можно организовать очереди, асинхронную обработку... Здесь для этого далеко не всегда достаточно ресурсов. Поэтому всё обрабатывается в одном цикле, который и имитирует логику.
 
  • Лойс +1
Реакции: Shuster

Shuster

★✩✩✩✩✩✩
4 Сен 2021
50
11
@poty @Старик Похабыч, что прошу прощения за то что обозвал функции методами )
Как писал выше, я из C#, а там всё методы ))

Всем спасибо за ответы, особенно за стек!
 

rkit

★★★✩✩✩✩
5 Фев 2021
510
127
Простыня кода в одной функции будет жрать больше стека, чем множество мелких вызовов. Стек каждого вызова освобождается после выхода из вызова, простыня же удерживает все локальные переменные до конца.
 
  • Лойс +1
Реакции: Nikanor и Shuster

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
Простыня кода в одной функции будет жрать больше стека, чем множество мелких вызовов. Стек каждого вызова освобождается после выхода из вызова, простыня же удерживает все локальные переменные до конца.
->>
Можно. Ардуинщики, в своей массе, просто плохие программисты.
Не удивляйтесь тому, что пишут в данной теме :), плохие музыканты играют как умеют. К тому же на ваше первое утверждение есть хорошее возражение - понатыкивать глобальных и статических переменных и затем их переиспользовать в разных местах... И затем эта хрень как-то еще работает, правда нихрена нечитаемо, неочевидно, неподдерживаемо, но экономит стек :) .
 
Изменено:
  • Лойс +1
Реакции: te238s и Shuster

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
Каждый вызов функции (а в С++ функции в основном, и то что вы написали тоже функции. Методы они у объектов) увеличивает стек.
Смотря как компилятор оптимизирует. Если вызов однократный как у ТС, то скорее всего сделает без вызова функции.
 
  • Лойс +1
Реакции: Shuster

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Геннадий П, ну, я бы не стал надеяться на оптимизатор, тем более, если это в реальности будут именно методы класса. С другой стороны, есть ли смысл разносить логику одного процесса по разным классам и методам? Почему бы не рассматривать loop, как метод создания логики?
 
  • Лойс +1
Реакции: Shuster

kDn

★★★★★✩✩
18 Ноя 2019
1,103
437
работа с контроллером налаживает некоторые ограничения
Накладывает. Налажать это несколько о другом.

По теме - можете пользоваться любыми конструкциями языка. Классы это хорошо и правильно, при нормальном написании кода. Беспокоиться на счет передачи параметров через стек или о пересечении стека/кучи можно лишь в случаях если есть выделения памяти под 80% от имеющейся, но даже в этом случае стоит не забывать что есть передача по значению, есть по ссылке/указателю. Всегда нужно прикидывать время жизни объекта и пусть он живет ровно столько, сколько нужно. Естественно если не использовать ООП, то об объектах речи особой не идет, а менеджмент памяти становится веселой задачей... Но вообще что касается видео Гайвера или кода на данном форуме, то в подавляющем большинстве случаев - код простой, примеры учебные, никто ни о чем не заботится - написано абыкак, наотстань. Если вам нужен более-менее адекватный код и используемые методы работы с ним - ищите библиотеки интенсивно работающие с памятью, рассчитанные на многие контроллеры и изучайте их код. В идеале чтобы они были при этом хорошо документированы. Как пример могу порекомендовать ArduinoJSON - если вдумчиво поймете как там все устроено и почему, то качество вашего кода значительно повысится.
 
  • Лойс +1
Реакции: Shuster

Геннадий П

★★★★★★✩
14 Апр 2021
1,975
634
45
тем более, если это в реальности будут именно методы класса
Если методы класса, то скорее всего будет вызов метода. Но у ТСа обычные функции, без классов, без всего, даже параметров нет.
С другой стороны, есть ли смысл разносить логику одного процесса по разным классам и методам?
Если логика сложная, то есть смысл, меньше запутаешься потом. Иногда лучше больше написать текста в коде, чем потом разбираться в простыне текста выискивая нужную логику.

Например, я люблю часто делать в файле с мейном скелет логики, примерно как у ТСа, и тут же делать эти же функции, но пустые с атрибутом weak. А уже потом в отдельных инклюдах переопределять эти функции с полной работоспособной логикой.
 
  • Лойс +1
Реакции: Shuster

SergejEU

★★✩✩✩✩✩
16 Сен 2020
120
73
Информации об этом нигде не нашёл. Или так делать можно, или неправильными запросами кормил гугл.
значит плохо искал. Рискну предложить вам серию видеоуроков от Alex Morozov на ютубе, человек он спеифический, если не сказать больше - социопат, но излагает очень грамотно, для новичка в си самое то:
Цикл уроков по программированию на C++ для Arduino. Часть 1.
 

Shuster

★✩✩✩✩✩✩
4 Сен 2021
50
11
Избыточное цитирование
значит плохо искал. Рискну предложить вам серию видеоуроков от Alex Morozov на ютубе, человек он спеифический, если не сказать больше - социопат, но излагает очень грамотно, для новичка в си самое то:
Цикл уроков по программированию на C++ для Arduino. Часть 1.
Полистал первый урок, ничего о использовании кода в loop() не услышал, но по ответам выше уже понятно почему я не находил этого ответа.
У меня вопрос был про подводные камни использования вне loop(), так как не работал ни с языком C++ ни с контроллерами, вот на всякий случай переспросил.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
Полистал первый урок, ничего о использовании кода в loop() не услышал
вообще изучать С++ по видео - это что-то. Все-таки не макраме.
Советую почитать любую книжку по с с++, не обязательно по ардуино, раздел процедуры и функции. В ардуино применяется стандартный gnu c++, все принципы обычного с++ применимы
 

SergejEU

★★✩✩✩✩✩
16 Сен 2020
120
73
@Shuster

Первый урок о совсем элементарных вещах - типах данных и т.д. О функциях там тоже есть, но в каком из уроков, я сейчас сказать не могу. Но не смотря на элементарное, все же рекомендую просмотреть уроки 2 и 3. Там речь идет конкретно применительно к программированию AVR микроконтроллеров.

Если ваш вопрос ограничивался только о функции loop(), и того как стиль кода может повлияет на оптимальность, то это скорее риторический вопрос. Как было сказано выше, изучайте доки по GCC. Конкретно вам никто не ответит, многое зависит от настроек самого тулчена avr-gcc / AVR-GCC Toolchain
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,272
1,303
Москва
@rkit,Стек буду жрать вложенные функции.
Т.е. так:
вызов функции ( взяли стек)
выход функции (отдали стек)

вызов функции ( взяли стек)
выход функции (отдали стек)

вызов функции ( взяли стек)
выход функции (отдали стек)
нормально будет

а так:
вызов функции ( взяли стек)
вызов функции ( взяли стек)
вызов функции ( взяли стек)

выход функции (отдали стек)
выход функции (отдали стек)
выход функции (отдали стек)
могут быть проблемы
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@Старик Похабыч, стек - это хотя бы прогнозируемо. Но вот динамическое выделение памяти без возможности репозиции и сборки мусора - это уже серьёзно. Когда память, вроде, освободил, но в свободный пул она не вернулась. Учесть это практически мало возможно.
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
@poty, @Старик Похабыч,
Если понимаешь что делаешь - проблем нет. Я в рамках чего-то вроде конкурса писал на атмеге328 довольно сложную программу с динамическим выделением массивов и рекурсивными вызовами функций - работало до 6 уровней вложенности . Примечание - речь о прямой рекурсии - то есть функция внутри своего кода вызывает сама себя.
 

poty

★★★★★★✩
19 Фев 2020
3,261
948
@bort707, динамика динамике рознь. Если динамика заключается в том, чтобы вначале выделить память определенной ёмкости под объект и потом не трогать или, например, как в стеке, только в обратном порядке, - lifo, то учитывать ничего не нужно, а если несколько классов в методах независимо управляют памятью, то...
 

bort707

★★★★★★✩
21 Сен 2020
3,067
915
Динамика в рекурсивной функции - каждый уровень вложенности запрашивает свое выделение памяти, причем не одно.
Те еще была интересная задачка о порядке выделения и очистки блоков памяти для сведения фрагментации к минимуму
 

xanstar6067

✩✩✩✩✩✩✩
3 Фев 2022
6
1
27
В ардуино программа по сути выглядит так:


main{
setup()
{
//code
}
while (1)
{
loop()
{
//code
}
}
}

это стандартный срр файл подчиняющийся программированию как обычно.