Отладка максимальной емкости

1. Пример

Проверьте приведенную ниже программу для Arduino Nano, Old Bootloader, для переменных значений N.

  • Для низких значений (N<=1857) программа выводит "[FFFFFFFF]",
  • Для более высоких значений (1858<=N<=1953) программа выводит непредсказуемые варианты этой строки,
  • Для более высоких значений (1954<=N<=1955) программа выдает ошибку заполнения символа "[",
  • Выше определенного значения (1956<=N) программы не выводят ни одного символа.

Я не проверял тщательно эти результаты, которые на самом деле могут сильно отличаться и полностью зависеть от платформы, но, думаю, воспроизводимы.

Обратите внимание, что динамическое размещение не используется.

Обратите внимание, что все инициализировано, первые значения 4 равны 0xFF, следующие значения N-3 равны 0x00 (Ссылка).

Компиляция не показывает никаких релевантных предупреждений, демонстрируя скромное использование емкости на 10 %.

Sketch uses 3206 bytes (10%) of program storage space. Maximum is 30720 bytes.
Global variables use 196 bytes (9%) of dynamic memory, leaving 1852 bytes for local variables. Maximum is 2048 bytes.

Из этого примера видно, как вычислить правильное значение N, действительно можно ожидать проблемы, связанной с емкостью (Ссылка).

Обратите внимание, что эти значения намного меньше "предположительного" значения N=32767 для 1-байтовых переменных (?) (Ref.).

2. Проблема

Теперь, со значительно огромной реальной программой, у меня та же проблема с этими неожиданными результатами, поэтому я почти уверен (несколько минут назад), что это является причиной.

При превышении некоторого "~максимального~" уровня программа начинает давать сбои, а при "~освобождении~" этой мощности программа начинает работать нормально (конечно, без удаленного функционала!).

3. Вопрос

Как мне отлаживать, измерять, измерять или предотвращать эту ситуацию, а также указывать, когда следует оставить этот процессор, а когда следует перейти на более крупный, когда явно компилятор не является разумным предположением?.

// ПРОВЕРКА ЕМКОСТИ
#include <SoftwareSerial.h>

// УСТАНОВИМ ПРАВИЛЬНОЕ ЗНАЧЕНИЕ N
// N<=1857: последний допустимый результат
// 1858<=N<=1953: Последний наблюдаемый вывод - один символ
// ПК=0x0444. [AVR MEMORY] Запись в ячейку памяти 0x0948 вне размера памяти 0x0900. [UP0]
// 1954<=N<=1955: Последний наблюдаемый результат — наводнение
// Неверный код операции 0xFFFF на ПК=0xA402
// ПК=0x0444. [AVR MEMORY] Запись в ячейку памяти 0x0948 вне размера памяти 0x0900. [UP0]
// 1956<=N: Нет вывода
// Неверный код операции 0xFFFF на ПК=0xA402
#define N 1857

void setup()
{
    Serial.begin(38400);while (!Serial){;}

    byte a[N+1]={0xFF,0xFF,0xFF,0xFF};

    char si[3];
    Serial.print("[");
    for (int i1=0;i1<=N-1&&a[i1]!=0;i1++){
        sprintf(si,"%02X",a[i1]);
        Serial.print(si);
    }
    Serial.print("]");
}

void loop(){}

, 👍1

Обсуждение

Ваши числа находятся в пределах предела SRAM для Arduino Nano (2 КБ). Существует различие между языковым ограничением AVR GCC и ограничением SRAM для данной платы/MCU., @Mikael Patel


1 ответ


Лучший ответ:

2

От нижней до верхней части оперативной памяти у вас есть:

  • раздел .data (статически выделенные инициализированные переменные)
  • раздел .bss (статически размещенные неинициализированные переменные)
  • куча (динамическое выделение с помощью malloc() и new)
  • свободное место
  • стек (обратные адреса и автоматическое выделение локальных переменных)

См. воспоминания Arduino для получения более подробной информации. обсуждение.

Размеры разделов .data и .bss известны во время сборки. Этот это использование памяти «Глобальные переменные», сообщаемое в конце компиляция. Остальная часть оперативной памяти доступна для кучи и куча. Это количество указано как «осталось 1852 байта для локального переменные», где «локальные переменные» — это небольшое упрощение.

Ваш пример программы не использует выделение кучи. Он использует некоторые статическое выделение, в основном для буферов Serial и много стека для:

  • адреса возврата из вызовов main() и setup() (4 байт)
  • локальные массивы a и si (N+4 байта)
  • память, необходимая для sprintf() или Serial.print() (неизвестно)

Даже игнорируя последний пункт, видно, что вся оперативная память будет потребляется, когда 4 + N + 4 = 1852, т. е. N = 1844. Когда вы сделаете N больше, чем это значение, стек упирается в раздел .bss, и вы начинаете повреждая ваши глобальные переменные. Описываемое вами поведение в соответствии с программой, повреждающей внутренние структуры данных объект Серийный.

Как мне отлаживать, измерять, измерять или предотвращать эту ситуацию

Не существует простого способа заранее определить объем кучи и стек, который будет потреблять ваша программа. Обычное решение состоит в том, чтобы измерить и сообщить об этом во время во время выполнения. См., например, библиотеку MemoryFree. (есть много подобных этому).

,