Особенности работы Arduino nano с памятью

Свободная память

В качестве хобби, разрабатываю бортовой компьютер для своего автомобиля УАЗ патриот. Ну как компьютер..громко звучит. Функций не много, но те которых не хватает в повседневном использовании.. Но сейчас не об этом, о БП выпущу потом отдельную статью.

А сейчас поговорим о самой прошивке. Использую в разработке дисплей на базе чипсета SSD1306. Сначала заметил, что в то время как разрастается код программы, на экране случайным образом начинают появляться различные артефакты, в виде «белого шума». Дальше — больше, заполняю массив в одном месте программы, и через какое то время он начинает затираться случайными данными. Одно время даже уже даже почти отчаялся, И уж никак не приходило в голову, что всё это следствие простой не хватки оперативной памяти Arduino.

Хотя по идее, это первое что должно было придти в голову. Но! во всем виноват Яндекс ;). Он подсовывал ссылки в которых рассказывалось о подобных проблемах, но ни в одной из них не было рекомендации подумать в сторону памяти. Всякий бред типа «подпаяйте резистор, нет контакта, плохая плата, не правильно передаёте массив, не так работаете со строками» и .д. и т.п. Однако, решил на всякий случай измерить остаток оперативной памяти в процессе выполнения программы. Код для измерения следующий:

Запустил… и оказалось что проблемы с экраном начинаются, когда свободно памяти становится меньше 168 байт. Проблемы с очисткой и замусоривание переменных — когда остается меньше 140 байт. А сейчас самое время вспомнить, что в arduino nano всего 2кб оперативной памяти и 32кб flash (ну на самом деле на разных чипах по разному, но конкретно на используемой мной — Atmega 327p именно столько). И этого мало! Любое объявление переменной тратит память. Выход? Arduino способен хранить данные и во Flash. Но храниться они будут несколько своеобразно: фактически в оперативной памяти будет храниться ссылка на ячейку памяти Flash где хранятся данные. Показать переменной что она будет храниться во Flash памяти призван модификатор PROGMEM. Например, для того чтобы объявить что данные для отображения графики будут храниться во Flash памяти можно так:

И в принципе всё. Данные, будут помещены во Flash память. PROGMEM может работать со всеми целочисленными типами (8, 16, 32, 64 бита), float и char.
Важное замечание — PROGMEM применяется только к глобальным переменным, которые расположены ВНЕ функций.

Чтение переменных

Если с записью всё просто, то с чтением всё гораздо интереснее: оно осуществляется при помощи специальных функций:

  • pgm_read_byte(data) – для 1-го байта (char, byte, int8_t, uint8_t)
  • pgm_read_word(data) – для 2-х байт (int, word, unsigned int, int16_t, int16_t)
  • pgm_read_dword(data) – для 4-х байт (long, unsigned long, int32_t, int32_t)
  • pgm_read_float(data) – для чисел с плавающей точкой

,где data это адрес (или указатель) блока данных

Вот как например можно прочитать массив из флэш памяти:

Кроме того, есть встроенная функция — помогалка F(), которая при использовании компилируется в строку — константу во флеш памяти, и подставляется в нужном месте сама. Например код вида:

Serial.println(F(«Я занимаю одинаковое количество оперативной памяти вне зависимости от своей длины»));

Будет соответственно занимать 2 байта при любой длине строки.

Собственно перелопатив весь код, с учётом выше озвученного, удалось избавиться от артефактов на дисплее и не адекватного поведения переменных