Оптимизация кода Ардуино для начинающих программирование и ускорение работы

Оптимизация кода Ардуино для начинающих программирование и ускорение работы

Оптимизация кода Ардуино для начинающих. Программирование и ускорение работы. Изменение кода и доступ к регистрам и портам. Продвинутый язык программирования ардуино. Оптимизация кода Ардуино для начинающих. Программирование и ускорение работы

Приветствую всех моих подписчиков и гостей канала.
Сегодня мы продолжим говорить про оптимизацию кода в скетче. Не думал, что эта тема заинтересует такое большое количество Ардуинщиков моего канала. Поэтому продолжим изучение работы с параллельными портами ввода и вывода.
Сегодня рассмотрим порты и регистры микроконтроллера АТМЕГА 328. Именно на нём и собрана Ардуино УНО и Ардуино НАНО версии 3.0 и выше.

Сначала как обычно смотрим пример, а потом я расскажу ка это работает.
Для этого примера я собрал простенькую схему состоящую из 2 кнопок и двух реле.
Скетч я объяснять не буду, он совсем простой. При нажатии первой кнопки включается первое реле, при нажатии второй второе. При повторном нажатии реле выключаются. Так же в монитор порта выводится статус реле, включено оно сейчас или выключено.

И вот такой код занимает у нас 2284 байта flash памяти или 7%, и 304 байта памяти ОЗУ или 14 процентов. Это довольно много для такого простого примера. Потом мы это исправим.
Открываем монитор порта и смотрим. Сначала, так как реле выключены нам выводятся две строчки, что реле выключены. Теперь понажимаем кнопки. Я не стал ничего подключать к реле и мы будем ориентироваться по светодиодам на блоке реле. Если лампочка загорелась, значит реле включено. Так же вы увидите сообщение в мониторе порта.  Можно нажимать сразу 2 кнопки, а можно и по одной, всё на ваше усмотрение. Думаю, что это всё понятно и объяснять ничего не нужно. Так как мы уже переходим на следующий уровень и такие простые примеры нам уже не интересны.

Давайте посмотрим как можно оптимизировать этот код, чтобы он занимал меньше памяти и работал быстрее.
Для начала перенесём все текстовые сообщения которые мы получаем в мониторе порта во flash память, тем самым мы освободим динамическую память, так как у нас её всего 2 килобайта и она очень ценная для работы проекта. Если вам что-то не понятно, то обязательно посмотрите этот пример я там всё подробно объяснял.
Ну вот теперь прошиваем и смотрим сколько у нас теперь освободилось памяти. Мы освободили 112 байт динамической памяти или получили экономию в полтора раза, заняв всего 14 байт флэш памяти, там даже процент не изменился, остался как и был на семи процентах. Давайте попробуем ещё поджать память.
Как видите на выводе это ни как не сказалось.

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

Прошиваем Ардуино и смотрим на сколько мы ещё уменьшили объём занимаемой памяти. Теперь у нас задействовано всего 3% flash памяти и 0% динамической памяти.
Видите, как просто уменьшить объём занимаемой памяти. А что насчёт скорости. Как это влияет на скорость выполнения скетча микроконтроллером.
Для определения скорости воспользуемся примером из прошлого урока. Измерять будем в микросекундах. Сначала 1000 раз выполнится не оптимизированный пример, а затем пример с удалёнными текстовыми строками. И в конце чтобы узнать во сколько раз быстрее выполняется пример один от другого, я разделю полученное время.
Так как это очень маленький пример, особо большого различия в скорости мы не получили. Выиграли всего 1000 микросекунд, и скорость увеличилась всего в 1 раз, не то что в предыдущем видео, где скорость достигала 5 раз.

Теперь выполним тот же пример, но уже с прямым обращением к портам и регистрам микроконтроллера.
Будем работать с портом D. Этот порт удобен тем, что биты этого порта соответствуют выводам на микроконтроллере. Например, PD4 соответствует выходу D4 Ардуино, а PD7 выходу D7.

Рассмотрим все порты микроконтроллера АТМЕГА 328.

У АТМЕГА 328 всего 3 порта. Все они обозначаются латинскими буквами  B C D. Так как контроллер у нас 8-ми битный, то у каждого полного порта есть  8 бит или по другому 8 выходов или входов на микроконтроллере.

Порты B и D цифровые и они могут работать как входы или выходы цифровых сигналов, то есть на них могут быть два состояния HIGH или LOW, а порт C может ещё работать и на приём аналоговых сигналов. Ну я думаю вам это и так известно. Это выходы аналоговые входы отмеченные на плате Ардуино буквой А. На Ардуино НАНО это выводы А0-А7.  

У каждого из портов есть 3 регистра состояния

  • DDRx – Это Регистр направления передачи. Может иметь 2 состояния. 0 – это INPUT, а 1 – это OUTPUT. Это равнозначно что мы всегда писали pinMode()
  • PORTx – Это Регистр ввода данных. Здесь мы устанавливаем значение на выводе контроллера.   0 - это LOW, а 1 – это HIGH. Это равнозначно тому, что мы всегда писали digitalWRITE
  • PINx – ЭТО Регистр информации. Он знает всё о любом вывода микроконтроллера, в каком он сейчас состоянии в HIGH или LOW. Это аналогично analogRead или digitalRead смотря какой порт вы исследуете.

Вот мы изучили все регистры АТМЕГА 328, И я забыл сказать, что вместо х надо писать букву порта с каким вы будете работать.
Теперь переходим к скетчу и перепишем его с управлением через порты микроконтроллера.
Слева старый скетч написанный как обычно по правилам Ардуино и уже оптимизированный по максимуму.
Видите как сократился скетч. То что слева занимает весь экран, справа уместилось всего в 4 строчки если убрать комментарии

В DDRD  мы указываем направления передачи данных. Биты 3 и 4 порта устанавливаем на выход, а остальные будут работать на вход. Слева нам для этого понадобилось 8 строчек, а здесь мы уложились в 1.

В регистре ввода вывода PORTD мы все биты выставляем в HIGH. Это нужно для того чтобы при включении не срабатывало реле, и на входах куда подключены кнопки был установлен режим INPUT_PULLUP.

PIND Для проверки состояния кнопки слева мы использовали digitalWrite, справа для этого мы использовали специальный регистр информации PIND, который всегда знает состояние любого бита, на любом порту.
Узнать состояние можно двумя способами. Вы можете выбрать какой вам удобнее.

Это условие проверки нажатия кнопки. Если кнопка нажата и флаг равен 0, то Включить реле и изменить флаг на 1, а если кнопка отпущена и флаг равен единице, то выключить реле и изменить флаг на 0. Для второго реле всё то же самое, поэтому я рассказывать дальше не буду.
Теперь проверим как изменилось состоянии памяти. Для этого сначала прошьём первый скетч, а затем второй и посмотрим результат.
Здесь даже и говорить нечего, цифры говорят сами за себя. Так что изучайте этот метод, он очень полезен и может пригодиться когда вам не хватает быстродействия, или программа занимает много памяти и возможны сбои.

Теперь сравним два скетча, что бы определить какой работает быстрее и во сколько раз. Тестировать будем оптимизированный скетч и тот что с прямым доступом к регистрам. В прошлом видео разница в скорости был больше чем в 5 раз. А сколько сейчас?

Прошиваем скетч и видим, что скорость скетча с прямым доступом так же как и раньше быстрее в 5 раз. Не знаю как вам, а мне нравится этот способ. Когда я садился писать это видео, то набросал себе кучку тем, о которых хотел рассказать, но получилось осветить меньше половины, гораздо меньше. Так что если будет много откликов то я продолжу показывать как ещё можно использовать этот метод. Все видео будут на живых примерах, так что это не просто теория, а вполне себе готовые скетчи, которые вы можете доработать под себя.

Спасибо тем кто досмотрел видео до конца. Скоро будут ещё видео, так что пишите, что бы вы хотели видеть в следующих уроках, по возможности постараюсь сделать.