Чому ескізи займають стільки місця та пам’яті?


12

Коли я складаю цей ескіз для юна:

int led = 7;

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

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

Я отримав:

Ескіз використовує 5 098 байт (17%) місця для зберігання програми.

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

Навіть коли я складаю ескіз BareMinimum:

void setup() {                
  // setup    
}

void loop() {
  // loop
}

Я отримав:

Ескіз використовує 4,548 байт (15%) простору програмного місця для зберігання.

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

Чому просто мінімальний ескіз займає 15% виділеного місця для зберігання програми? І чому дуже простий ескіз займає 17% місця для зберігання програми? За даними веб-сайту Arduino :

Це легко використовувати все, маючи у своїй програмі багато рядків. Наприклад, декларація типу: char message[] = "I support the Cape Wind project.";додає 33 байти в SRAM (кожен символ займає байт, плюс термінатор '\ 0').

Однак в жодному з цих ескізів немає жодних рядків.

Схоже, вони можуть імпортувати або використовувати інші бібліотеки / класи, які я не вказую. Може бути, імпортує системну бібліотеку за замовчуванням? Або це щось інше?

Відповіді:


6

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

Зауважте, що в UNO є зовнішня мікросхема, яка перетворює USB на контактний DTR-контакт послідовного порту, який перемикає контактний контакт ATmega328 при підключенні, викликаючи перезавантаження завантажувача. На відміну від USB / серіалу Leo / Yun вбудований у прошивку 32u4. Отже, для віддаленого перезавантаження мікросхеми Leo або YUN 32u4 завантажена мікропрограмне забезпечення завжди має підтримувати драйвер USB-клієнта. Що споживає приблизно 4 к.

Якщо USB НЕ був потрібний, а інші бібліотечні ресурси не викликалися, як у випадку з BareMinimum.ino в UNO, для основної бібліотеки Arduino потрібно лише приблизно 466 байт.

компілювати статистику BareMinimum.ino в ООН (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.

7

Arduino збирає у багатьох стандартних бібліотеках, перериває, ... і т.д. Наприклад, функції pinMode та digitalWrite використовують таблицю пошуку, щоб визначити під час виконання, до якого GPIO реєструється запис даних. Іншим прикладом є те, що Arduino відслідковує час, він визначає деякі переривання за замовчуванням, і вся ця функціональність вимагає певного простору. Ви помітите, що якщо розширити програму, друк ніг лише незначно зміниться.

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


Так в основному він збирається у всілякі стандартні бібліотеки, не запитуючи вас? Акуратний.
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 (я не використовую ID 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в ньому) не був пов'язаний з. В результаті обробки для обробників переривань (для millis(), micros()був пропущений і т.д.). Це, мабуть, не особливо практично, якщо ви не витрачаєте часу на речі, але факт полягає в тому, що ескіз збільшується в розмірах залежно від того, що ви вкладаєте в нього. Наприклад, якщо ви використовуєте Serial, і пам'ять програми, і оперативна пам’ять вражають.
Нік Гаммон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.