Можно ли объявить большой массив внутри функции на AVR?
Обновлено Редакция GAW
У ATmega328P 2048 байт ОЗУ (адреса 0x0100–0x08FF по рис. 8-3 даташита DS40002061B), и делят их три области. Документация avr-libc описывает стандартную раскладку: снизу — инициализированные (.data) и обнулённые (.bss) глобальные переменные, за ними — куча, а стек растёт от верхнего адреса вниз. Локальный массив — это место в стеке, которое функция занимает при вызове.
Сколько это в цифрах
Наш пример для ATmega328P: скетч, у которого глобальные переменные занимают 1200 байт.
| Что | Байт | Остаток для стека |
|---|---|---|
| всё ОЗУ | 2048 | — |
| .data и .bss | 1200 | 848 |
локальный uint8_t buf[512] | 512 | 336 |
| вложенные вызовы, прерывания, printf | ? | может не хватить |
Если стек дорос до .bss, он молча перезаписывает глобальные переменные: аппаратной защиты областей у AVR нет. Сколько стека занимает вызов функции и как его оценить, разобрано в статье о стеке.
Как сделать безопасно
- Объявить массив static или глобальным. Он переедет в .bss, и его размер войдёт в итог сборки «глобальные переменные используют N байт» — переполнение станет видно сразу.
- Включить предупреждение. Ключ GCC
-Wstack-usage=256предупредит о функциях, чей стек может превысить 256 байт, а-fstack-usageзапишет размеры кадров в файлы .su. - Проверить запас на живой плате. Начиная с 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 байта. Выделить буфер один раз при старте можно, но статический массив проще и виден при сборке.
Источники
- 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
- 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
- GCC 16.2 Manual: Developer Options (-fstack-usage), Warning Options (-Wstack-usage=) — -fstack-usage — размер стека каждой функции в файле .su; -Wstack-usage=N — предупреждение, если стек функции может превысить N байт, расчёт консервативный
- 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 — память, доступная глобальным переменным
- Arduino. toolchain-avr, коммит 2c677fd: README и build.conf — сборка тулчейна Arduino для AVR: avr-gcc 7.3.0, avr-libc 2.0.0
Нашли ошибку?