Почему эскизы занимают так много места и памяти?

12

Когда я собираю этот эскиз для Юн:

int led = 7;

void setup() {                
  pinMode(led, OUTPUT);     
}

void loop() {
  digitalWrite(led, HIGH);
}

Я получил:

Sketch использует 5,098 байт (17%) памяти для хранения программ.

Максимум составляет 28 672 байта. Глобальные переменные используют 153 байта (5%) динамической памяти, оставляя 2,407 байтов для локальных переменных. Максимум - 2560 байт.

Даже когда я компилирую эскиз BareMinimum:

void setup() {                
  // setup    
}

void loop() {
  // loop
}

Я получил:

Sketch использует 4548 байт (15%) памяти для хранения программ.

Максимум составляет 28 672 байта. Глобальные переменные используют 151 байт (5%) динамической памяти, оставляя 2,409 байт для локальных переменных. Максимум - 2560 байт.

Почему простой минимальный эскиз занимает 15% выделенного пространства для хранения программы? И почему очень простой эскиз занимает 17% памяти программы? По данным сайта Arduino :

Это легко использовать, имея в своей программе множество строк. Например, объявление типа: char message[] = "I support the Cape Wind project.";помещает 33 байта в SRAM (каждый символ занимает байт плюс терминатор '\ 0').

Тем не менее, ни в одном из этих эскизов не объявлено ни одной строки.

Кажется, что они могут импортировать или использовать другие библиотеки / классы, которые я не определяю. Может быть, он импортирует системную библиотеку по умолчанию? Или что-то еще?

hichris123
источник

Ответы:

6

YUN - это комбо. Часть Arduino и Часть OpenWRT (Linux). Ваш вопрос относится к Arduino. Где это на самом деле ATmega32u4, похожий на Леонардо, а не UNO (ATmega328p). 32u4 (Leo) взаимодействует через виртуальные последовательные порты через USB (краткий ответ: это необходимо поддерживать) , где UNO имеет настоящий последовательный порт (он же UART). Ниже приведена статистика сборок различных типов плат для процессоров AVR.

Обратите внимание, что на UNO есть внешний чип, который преобразует USB в вывод DTR последовательного порта, который переключает вывод сброса ATmega328 при подключении, вызывая перезагрузку загрузчика. В отличие от Leo / Yun, USB to Serial реализован в прошивке 32u4. Следовательно, для удаленной перезагрузки чипа Leo или YUN 32u4 загружаемое микропрограммное обеспечение должно всегда поддерживать драйвер на стороне USB-клиента. Который потребляет примерно 4К.

Если USB НЕ был необходим и никакие другие библиотечные ресурсы не были вызваны, как в случае BareMinimum.ino в UNO, для основной библиотеки Arduino требуется только приблизительно 466 байт.

скомпилировать статистику BareMinimum.ino для UNO (ATmega328p)

Sketch uses 466 bytes (1%) of program storage space. Maximum is 32,256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.

составить статистику BareMinimum.ino на Леонардо (ATmega32u4)

Sketch uses 4,554 bytes (15%) of program storage space. Maximum is 28,672 bytes.
Global variables use 151 bytes (5%) of dynamic memory, leaving 2,409 bytes for local variables. Maximum is 2,560 bytes.

скомпилировать статистику BareMinimum.ino на Yun (ATmega32u4)

Sketch uses 4,548 bytes (15%) of program storage space. Maximum is 28,672 bytes.
Global variables use 151 bytes (5%) of dynamic memory, leaving 2,409 bytes for local variables. Maximum is 2,560 bytes.
mpflaga
источник
7

Arduino компилирует множество стандартных библиотек, прерываний и т. Д. Например, функции pinMode и digitalWrite используют таблицу поиска, чтобы определить во время выполнения, в какой GPIO регистрируется запись данных. Другим примером является то, что Arduino отслеживает время, по умолчанию определяет некоторые прерывания, и все эти функциональные возможности требуют некоторого пространства. Вы заметите, что если вы расширите программу, отпечаток ноги изменится незначительно.

Мне лично нравится программировать контроллеры с минимумом, без «раздувания», но вы быстро войдете в мир EE.SE и SO, потому что несколько простых в использовании функций больше не будут работать из коробки. Существует несколько альтернативных библиотек для pinMode и digitalWrite, которые компилируются в меньшую площадь, но имеют и другие недостатки, такие как, например, статические скомпилированные выводы (где ledне может быть переменной, но является константой).

jippie
источник
Так что в основном он компилируется во все виды стандартных библиотек без вашего ведома? Ухоженная.
hichris123
Да, я обычно называю это «раздувать», но это действительно удобство использования. Arduino - это среда низкого начального уровня, которая просто работает без лишних раздумий. Если вам нужно больше, Arduino позволяет вам использовать альтернативные библиотеки или вы можете скомпилировать на голом железе. Последнее, вероятно, выходит за рамки Arduino.SE
jippie
Смотрите мой ответ @mpflaga. Там не так много раздувать. Или, по крайней мере, в базовой библиотеке для минимальной функциональности. На самом деле не так много стандартных библиотек, кроме как «скетч». Скорее 15% связано с поддержкой USB 32u4.
mpflaga
4

У вас уже есть несколько очень хороших ответов. Я публикую это только для того, чтобы поделиться статистикой, которую я однажды сделал, и я задал себе те же вопросы: что занимает так много места на минимальном наброске? Какой минимум необходим для достижения той же функциональности?

Ниже приведены три версии минимальной мигающей программы, которая переключает светодиод на контакт 13 каждую секунду. Все три версии были скомпилированы для Uno (без USB), используя avr-gcc 4.8.2, avr-libc 1.8.0 и arduino-core 1.0.5 (я не использую Arduino IDE).

Во-первых, стандартным способом Arduino:

const uint8_t ledPin = 13;

void setup() {
    pinMode(ledPin, OUTPUT);
}

void loop() {
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(ledPin, LOW);
    delay(1000);
}

Это составляет 1018 байт. Используя avr-nmи разборку , я разбил этот размер на отдельные функции. От самого большого до самого маленького:

 148 A ISR(TIMER0_OVF_vect)
 118 A init
 114 A pinMode
 108 A digitalWrite
 104 C vector table
  82 A turnOffPWM
  76 A delay
  70 A micros
  40 U loop
  26 A main
  20 A digital_pin_to_timer_PGM
  20 A digital_pin_to_port_PGM
  20 A digital_pin_to_bit_mask_PGM
  16 C __do_clear_bss
  12 C __init
  10 A port_to_output_PGM
  10 A port_to_mode_PGM
   8 U setup
   8 C .init9 (call main, jmp exit)
   4 C __bad_interrupt
   4 C _exit
-----------------------------------
1018   TOTAL

В приведенном выше списке первый столбец представляет собой размер в байтах, а второй столбец указывает, поступает ли код из базовой библиотеки Arduino (A, всего 822 байта), среды выполнения C (C, 148 байтов) или пользователя (U 48 байт).

Как видно из этого списка, самой большой функцией является подпрограмма, обслуживающая прерывание переполнения таймера 0. Эта процедура отвечает за отслеживание времени и необходима millis(), micros()и delay(). Вторая по величине функция init(), которая устанавливает аппаратные таймеры для ШИМ, включает прерывание TIMER0_OVF и отключает USART (который использовался загрузчиком). И эта, и предыдущая функция определены в <Arduino directory>/hardware/arduino/cores/arduino/wiring.c.

Следующая версия C + avr-libc:

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB |= _BV(PB5);     /* set pin PB5 as output */
    for (;;) {
        PINB = _BV(PB5);  /* toggle PB5 */
        _delay_ms(1000);
    }
}

Разбивка отдельных размеров:

104 C vector table
 26 U main
 12 C __init
  8 C .init9 (call main, jmp exit)
  4 C __bad_interrupt
  4 C _exit
----------------------------------
158   TOTAL

Это 132 байта для среды выполнения C и 26 байтов пользовательского кода, включая встроенную функцию _delay_ms().

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

#include <avr/io.h>
#define io(reg) _SFR_IO_ADDR(reg)

    sbi io(DDRB), 5  ; set PB5 as output
loop:
    sbi io(PINB), 5  ; toggle PB5
    ldi r26, 49      ; delay for 49 * 2^16 * 5 cycles
delay:
    sbiw r24, 1
    sbci r26, 0
    brne delay
    rjmp loop

Он собран (с avr-gcc -nostdlib) только в 14 байтов, большинство из которых используются для задержки переключения так, чтобы мигание было видимым. Если вы удалите этот цикл задержки, вы получите 6-байтовую программу, которая мигает слишком быстро, чтобы ее можно было увидеть (с частотой 2 МГц):

    sbi io(DDRB), 5  ; set PB5 as output
loop:
    sbi io(PINB), 5  ; toggle PB5
    rjmp loop
Эдгар Бонет
источник
3

Я написал пост о том, почему для мигания одного светодиода требуется 1000 байт? ,

Краткий ответ: «Не нужно 2000 байтов, чтобы мигать два светодиода!»

Более длинный ответ заключается в том, что стандартные библиотеки Arduino (которые вам не нужны, если вы не хотите их использовать) имеют некоторые приятные функциональные возможности, упрощающие вашу жизнь. Например, вы можете адресовать контакты по номеру во время выполнения, где библиотека преобразует (скажем) контакт 8 в правильный порт и правильный битовый номер. Если вы жестко запрограммировали доступ к порту, вы можете сохранить эти издержки.

Даже если вы их не используете, стандартные библиотеки включают в себя код для подсчета «тиков», чтобы вы могли узнать текущее «время» (вызывая millis()). Для этого он должен добавить издержки некоторых подпрограмм обработки прерываний.

Если вы упростите (на Arduino Uno) этот эскиз, вы уменьшите использование памяти программы до 178 байт (в IDE 1.0.6):

int main ()
  {
  DDRB = bit (5);
  while (true)
    PINB = bit (5);
  }

Хорошо, 178 байтов не так много, и из этого первые 104 байта являются векторами аппаратного прерывания (4 байта каждый, для 26 векторов).

Возможно, для мигания светодиода требуется всего 74 байта. И из этих 74 байтов большинство - это код, сгенерированный компилятором для инициализации глобальной памяти. Если вы добавите достаточно кода, чтобы мигать два светодиода:

int main ()
  {
  DDRB = bit (5);  // pin 13
  DDRB |= bit (4);  // pin 12

  while (true)
    {
    PINB = bit (5); // pin 13
    PINB = bit (4); // pin 12
    }
  }

Затем размер кода увеличивается до 186 байт. Поэтому вы можете утверждать, что 186 - 178 = 8для мигания светодиода нужны только байты.

Итак, 8 байт для мигания светодиода. Звучит довольно эффективно для меня.


Если вы испытываете желание попробовать это дома, я должен отметить, что хотя приведенный выше код мигает двумя светодиодами, он действительно работает очень быстро. На самом деле они мигают с частотой 2 МГц - см. Скриншот. Канал 1 (желтый) - это контакт 12, канал 2 (голубой) - это контакт 13.

Быстрое мигание контактов 12 и 13

Как видите, выходные выводы имеют прямоугольную форму с частотой 2 МГц. Вывод 13 изменяет состояние 62,5 нс (один такт) перед выводом 12 из-за порядка переключения выводов в коде.

Поэтому, если у вас нет намного лучших глаз, чем у меня, вы на самом деле не увидите никакого эффекта моргания.


В качестве забавного дополнения вы можете на самом деле переключать два контакта в том же объеме программного пространства, что и переключение одного контакта.

int main ()
  {
  DDRB = bit (4) | bit (5);  // set pins 12 and 13 to output

  while (true)
    PINB =  bit (4) | bit (5); // toggle pins 12 and 13
  } // end of main

Это составляет 178 байтов.

Это дает вам более высокую частоту:

Очень быстрое мигание контактов 12 и 13

Теперь мы до 2,66 МГц.

Ник Гаммон
источник
Это имеет массу смысла. Так что же, стандартные библиотеки - это просто заголовки, которые автоматически включаются во время сборки? И как вы могли их не включать?
hichris123
2
Линкер агрессивно удаляет код, который не используется. Не вызывая init()(как это main()делает нормальный ), файл wiring.c (который initв нем есть) не был связан. В результате обработка для обработчиков прерываний (for millis()и micros()т. Д.) Была исключена. Вероятно, не особенно практично пропускать это, если вам никогда не нужно рассчитывать время, но факт в том, что эскиз увеличивается в размере в зависимости от того, что вы в него положили. Например, если вы используете Serial, то и программная память, и оперативная память получают удар.
Ник Гэммон