Перейти к содержанию

Можно ли объявить большой массив внутри функции на AVR?

Обновлено Редакция GAW

У ATmega328P 2048 байт ОЗУ (адреса 0x0100–0x08FF по рис. 8-3 даташита DS40002061B), и делят их три области. Документация avr-libc описывает стандартную раскладку: снизу — инициализированные (.data) и обнулённые (.bss) глобальные переменные, за ними — куча, а стек растёт от верхнего адреса вниз. Локальный массив — это место в стеке, которое функция занимает при вызове.

Сколько это в цифрах

Наш пример для ATmega328P: скетч, у которого глобальные переменные занимают 1200 байт.

ЧтоБайтОстаток для стека
всё ОЗУ2048—
.data и .bss1200848
локальный uint8_t buf[512]512336
вложенные вызовы, прерывания, printf?может не хватить

Если стек дорос до .bss, он молча перезаписывает глобальные переменные: аппаратной защиты областей у AVR нет. Сколько стека занимает вызов функции и как его оценить, разобрано в статье о стеке.

Как сделать безопасно

  1. Объявить массив static или глобальным. Он переедет в .bss, и его размер войдёт в итог сборки «глобальные переменные используют N байт» — переполнение станет видно сразу.
  2. Включить предупреждение. Ключ GCC -Wstack-usage=256 предупредит о функциях, чей стек может превысить 256 байт, а -fstack-usage запишет размеры кадров в файлы .su.
  3. Проверить запас на живой плате. Начиная с avr-libc 2.3 есть _get_ram_unused() из <util/ram-usage.h>: код запуска раскрашивает свободное ОЗУ, а функция считает нетронутые байты — это нижняя оценка использованной памяти. В тулчейне Arduino IDE для AVR сейчас avr-libc 2.0.0, и этой функции там нет: нужен отдельный тулчейн с avr-libc 2.3 или своя раскраска стека.

Методы оценки стека подробно описаны в статье «Переполнение стека на микроконтроллере», устройство кучи — в статье о куче, а как не тратить ОЗУ на константы — в статье «C для микроконтроллеров».

Похожие вопросы

Почему программа не падает сразу, а ведёт себя странно?

У AVR нет защиты памяти: стек, растущий вниз, просто перезаписывает лежащие ниже переменные и кучу. Ошибка проявится тогда, когда программа прочитает испорченное значение, — иногда через минуты и в совсем другом месте кода.

Сколько ОЗУ остаётся программе в Arduino?

Arduino IDE после сборки пишет, сколько занимают глобальные переменные, и сколько остаётся для локальных. Это сумма .data и .bss; стек и кучу IDE не считает, поэтому «осталось 500 байт» значит, что все вызовы функций, их локальные массивы и прерывания должны уместиться в эти 500 байт.

Поможет ли malloc вместо локального массива?

Нет: куча лежит в том же ОЗУ, а malloc avr-libc при нехватке памяти вернёт NULL, только если заметит близость стека с запасом __malloc_margin — по умолчанию 32 байта. Выделить буфер один раз при старте можно, но статический массив проще и виден при сборке.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet, DS40002061B, 2020 — 8.3, рис. 8-3 «Data Memory Map» (с. 29): 2048 байт внутреннего SRAM у ATmega328P по адресам 0x0100–0x08FF; указатель стека после сброса равен RAMEND
  2. AVR-LibC 2.3.2: doc/api/malloc.dox, include/util/ram-usage.h — стандартная раскладка ОЗУ: .data и .bss с начала, куча за .bss, стек от вершины вниз; аппаратной защиты областей нет; большие или многочисленные локальные переменные и рекурсия требуют много стека; __malloc_margin = 32; _get_ram_unused() — раскраска ОЗУ от __heap_start до RAMEND в .init3 и нижняя оценка использованной памяти, с avr-libc 2.3
  3. GCC 16.2 Manual: Developer Options (-fstack-usage), Warning Options (-Wstack-usage=) — -fstack-usage — размер стека каждой функции в файле .su; -Wstack-usage=N — предупреждение, если стек функции может превысить N байт, расчёт консервативный
  4. Arduino CLI 1.5.1. Arduino platform specification (docs/platform-specification.md) — после сборки выводится «Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.»; upload.maximum_data_size — память, доступная глобальным переменным
  5. Arduino. toolchain-avr, коммит 2c677fd: README и build.conf — сборка тулчейна Arduino для AVR: avr-gcc 7.3.0, avr-libc 2.0.0

Нашли ошибку?