Найти в Дзене
За_тех_кто_в_коде();

PROGMEM в Ардуино

Вот что нам пишет ИИ Алиса в ответ на то что же такое PROGMEM в Ардуино. PROGMEM — ключевое слово в программировании Arduino, которое позволяет хранить константные данные в памяти программы (flash-памяти) вместо оперативной памяти (RAM). Это полезно, например, для работы с большими массивами константных данных: таблицами поиска, строками, графическими активами. techexplorations.comamperkot.ru Все что написано во многих других статьях, примерно одно и того же смысла с этим заявлением. Разумеется очень давно я читал об этом. Проблема использования PROGMEM мной была в том, что у меня ранее не возникало проблем с размещением константных массивов во flash-памяти. Данный массив имеет четко определенный размер, и его можно даже не указывать в [ квадратных скобках ]. Но, если в скобках указать значение на 1 ед. больше, чем реально занято, то массив расположится во flash памяти. Если не указывать размер, или указать в точности равный количеству занятых элементов, либо расположить массив

Вот что нам пишет ИИ Алиса в ответ на то что же такое PROGMEM в Ардуино.

PROGMEM — ключевое слово в программировании Arduino, которое позволяет хранить константные данные в памяти программы (flash-памяти) вместо оперативной памяти (RAM). Это полезно, например, для работы с большими массивами константных данных: таблицами поиска, строками, графическими активами. techexplorations.comamperkot.ru

Все что написано во многих других статьях, примерно одно и того же смысла с этим заявлением. Разумеется очень давно я читал об этом. Проблема использования PROGMEM мной была в том, что у меня ранее не возникало проблем с размещением константных массивов во flash-памяти. Данный массив имеет четко определенный размер, и его можно даже не указывать в [ квадратных скобках ]. Но, если в скобках указать значение на 1 ед. больше, чем реально занято, то массив расположится во flash памяти. Если не указывать размер, или указать в точности равный количеству занятых элементов, либо расположить массив в области глобальных переменных (даже с указанием размерности на 1ед выше), то массив будет размещен в ОЗУ. Это особенность компилятора AVR gcc, возможно работает не на всех микроконтроллерах. Ранее я часто упоминал это в примерах и описаниях. С чем это связано я не знаю. Это уже надо копать в работу компилятора.

1.Массивы во flash памяти.   2.Массивы в ОЗУ.
1.Массивы во flash памяти. 2.Массивы в ОЗУ.

И я давно заметил, что компилятору очень не нравится когда во flash памяти размещается большой объем константных значений. Во-первых они занимают конский объем, во-вторых когда их объем достигает некоторых критических значений, он начинает разного рода оптимизации и как правило все равно начинает размещать данные в ОЗУ. Может быть, это можно решить атрибутами функции, в которых расположены константные значения, но мой первый подход к ним не принес мне результата, хотя находил я вроде то что надо.

И тут я вспомнил про PROGMEM, может быть компилятору нужно более конкретно указать что делать, пусть даже это работает только с библиотекой Arduino.h

Но при ближайшем рассмотрении оказалось что это несколько иное, чем просто размещение констант во flash памяти. При размещении массива во flash, указанным выше мной способом, он начинает занимать приличный объем. На один байт константного значения приходится от 3х до 10 байт flash памяти. Причем наблюдается следующая тенденция, чем больше объем данных, тем выше «лишних» байт пишется во flash память. Я полагаю что это связано с работой компилятора, который не настроен на извлечение большого объема констант из мест где должен располагаться только код. Но как выясняется, так это работает не во всех компиляторах.

Перевод из описания в файле pgmspace.h ( заголовочник PROGMEM)

Эти функции являются попыткой обеспечить некоторую совместимость с заголовочными файлами, которые поставляются с IAR C, чтобы упростить перенос приложений между различными компиляторами. Однако, это не 100% совместимость (GCC пока не имеет полной поддержки нескольких адресных пространств).

Размещая массив через PROGMEM, он располагается так, как если бы он располагался в адресуемой памяти типа ОЗУ или EEPROM, с тем же расходом занимаемого места. Но основная фишка данного подхода тут в том, что далеко не всегда требуется вытаскивать в ОЗУ весь массив целиком. Имеется небольшой регистровый массив, который указывает где именно в большом массиве располагаются требуемые данные и какой у них размер. И через функцию pgm_read_byte, мы уже адресно обращаемся к конкретному байту. Так у меня работают функции написания текста. И при использовании PROGMEM прям серьезно увеличилась скорость. При простом отображении текста, прирост составил ~23%. А тестовый скетч с индикаторами который еле-еле влез в ATMega 328, теперь занимает 61% flash памяти. Можно сказать, что Ардуино нано у меня снова на коне!

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

Но объехать это достаточно легко и просто. Так же создается массив-буфер, только обращаемся уже к самописной функции которая хранит все созданные строки. А в самой функции заполняем переданный буфер, более функциональными pgm_read_byte. И тут уже размер прошивки начинается уверенно снижаться. Да и в целом такой подход выглядит достаточно приемлемо. Причем заметных просадок в скорости замечено не было, но какие-то они безусловно есть.

-2

Стоит отметить, пару моментов. Помимо функции pgm_read_byte(вариант _word, _dword) есть еще pgm_read_byte_far и pgm_read_byte_near. Которые как я понял более конкретно указывают, где в прошивке будут располагаться константы. В варианте near сразу после векторов прерывания. Так же присутствует вариант чтения данных с плавающей точкой pgm_read_float, который в статьях не упоминают, и который кстати говоря, писал какой-то Ivanov Anton.

Второй момент, нужно не забывать, что массив должен иметь указание const и располагаться в области глобальных переменных. Если же он внутри функции, то он должен быть еще и static, тоже собственно условно глобальным.

Мой итог. Ну так-то да… Конечно преимущества весьма впечатляющие, странно что я раньше в это не вникал. Ужать текст во flash можно прям очень конкретно, а в моем случае это еще и работает гораздо быстрее. Но с другой стороны, это несколько не стандартные решения, так сказать супротив воли компилятора, что меня несколько настораживает. И я пришел к выводу, что в пределах рабочих или библиотечных функций, подобные решения нужно заворачивать в условную компиляцию, чтобы иметь в распоряжении оба варианта. К тому же иначе не может быть реализована кроссплатформенность.