Arduino в Linux: копаемся в кишках проекта

Введение

В предыдущей статье был описан процесс превращения Qt Creator в полноценную IDE для проектов на платформе Arduino. Шаги были описаны подробно, но без описания смысла происходящего, поэтому эффект от такой статьи небольшой. В каждом конкретном случае могут возникать и возникают разнообразные нюансы и разобраться в них без понимания того как устроен проект сложно.

Поэтому в данной заметке мы разберемся в структуре и настройках проекта.

1. Arduino Core и функция main()

Как известно, исполнение любой программы на C/C++ начинается с функции main(), в том числе это касается и прошивок микроконтроллеров. В той или иной форме эта функция присутствует в любом проекте. Про создании проекта в Arduino IDE нам предлагается сразу файл скетча (на ещё и с дурацки расширением *.ino), скрывая от разработчика расположение точки входа.

В арче исходники Arduino Core расположены по пути /usr/share/arduino/hardware/archlinux-arduino/avr/cores/arduino и содержат следующий

список файлов Arduino Core

$ ls -l итого 320 -rw-r--r-- 1 root root  1222 мар  3  2017 abi.cpp -rw-r--r-- 1 root root  7483 мар  3  2017 Arduino.h -rw-r--r-- 1 root root 11214 мар  3  2017 binary.h -rw-r--r-- 1 root root  8078 мар  9  2017 CDC.cpp -rw-r--r-- 1 root root  1529 мар  3  2017 Client.h -rw-r--r-- 1 root root  2605 мар  3  2017 HardwareSerial0.cpp -rw-r--r-- 1 root root  2315 мар  3  2017 HardwareSerial1.cpp -rw-r--r-- 1 root root  1975 мар  3  2017 HardwareSerial2.cpp -rw-r--r-- 1 root root  1975 мар  3  2017 HardwareSerial3.cpp -rw-r--r-- 1 root root  7743 мар  3  2017 HardwareSerial.cpp -rw-r--r-- 1 root root  5262 авг  3 16:57 HardwareSerial.h -rw-r--r-- 1 root root  4469 мар  3  2017 HardwareSerial_private.h -rw-r--r-- 1 root root  1142 мар  3  2017 hooks.c -rw-r--r-- 1 root root  2851 мар  3  2017 IPAddress.cpp -rw-r--r-- 1 root root  2861 мар  3  2017 IPAddress.h -rw-r--r-- 1 root root  1372 мар  3  2017 main.cpp -rw-r--r-- 1 root root  1027 мар  3  2017 new.cpp -rw-r--r-- 1 root root   979 мар  3  2017 new.h -rw-r--r-- 1 root root  2725 мар  3  2017 PluggableUSB.cpp -rw-r--r-- 1 root root  2063 мар  3  2017 PluggableUSB.h -rw-r--r-- 1 root root  1335 мар  3  2017 Printable.h -rw-r--r-- 1 root root  5442 мар  3  2017 Print.cpp -rw-r--r-- 1 root root  2963 авг  3 16:57 Print.h -rw-r--r-- 1 root root   963 мар  3  2017 Server.h -rw-r--r-- 1 root root  8804 авг  3 17:23 Stream.cpp -rw-r--r-- 1 root root  6060 авг  3 17:23 Stream.h -rw-r--r-- 1 root root 15022 мар  3  2017 Tone.cpp -rw-r--r-- 1 root root  4363 июл 18 16:52 Udp.h -rw-r--r-- 1 root root  6261 авг  3 16:57 USBAPI.h -rw-r--r-- 1 root root 20086 июл 18 16:52 USBCore.cpp -rw-r--r-- 1 root root  8435 мар  3  2017 USBCore.h -rw-r--r-- 1 root root  1519 мар  3  2017 USBDesc.h -rw-r--r-- 1 root root  4576 мар  3  2017 WCharacter.h -rw-r--r-- 1 root root  9409 мар  3  2017 WInterrupts.c -rw-r--r-- 1 root root  7850 мар  3  2017 wiring_analog.c -rw-r--r-- 1 root root 12024 мар  3  2017 wiring.c -rw-r--r-- 1 root root  4978 мар  3  2017 wiring_digital.c -rw-r--r-- 1 root root  2255 мар  3  2017 wiring_private.h -rw-r--r-- 1 root root  3435 мар  3  2017 wiring_pulse.c -rw-r--r-- 1 root root  6022 мар  3  2017 wiring_pulse.S -rw-r--r-- 1 root root  1550 мар  3  2017 wiring_shift.c -rw-r--r-- 1 root root  1641 мар  3  2017 WMath.cpp -rw-r--r-- 1 root root 16989 мар  3  2017 WString.cpp -rw-r--r-- 1 root root  9910 мар  3  2017 WString.h 

Функция main() расположена в файле main.cpp и выглядит так

#include   // Declared weak in Arduino.h to allow user redefinitions. int atexit(void (* /*func*/ )()) { return 0; }  // Weak empty variant initialization function. // May be redefined by variant files. void initVariant() __attribute__((weak)); void initVariant() { }  void setupUSB() __attribute__((weak)); void setupUSB() { }  int main(void) { 	init();  	initVariant();  #if defined(USBCON) 	USBDevice.attach(); #endif 	 	setup();      	for (;;) { 		loop(); 		if (serialEventRun) serialEventRun(); 	}          	return 0; } 

Видно, что в скетче нет ничего сверхъестественного: функции setup() и loop() вызываются непосредственно из main(). Файл led-blink.cpp, который мы создали ранее содержит определения этих функций. Если мы уберем данный файл из проекта

#Заголовки проекта #INCLUDEPATH += ./include #HEADERS += $$files(./include/*.h)  # Исходники проекта #SOURCES += $$files(./src/*.cpp) 

получим закономерную ошибку компоновщика

Arduino в Linux: копаемся в кишках проекта

Таким образом, все модули, которые мы добавим проекту будут скомпонованы с ядром Arduino, реализующим базовый функционал. Вот краткое описание заголовков Arduino Core:

  • Arduino.h — базовый заголовок, включающий заголовки стандартной библиотеки C, определения программного интерфейса к регистрам контроллеров AVR, основные макроопределения, используемые при программировании
  • binary.h — макроопределения для записи чисел от 0 до 255 в двоичной форме
  • Client.h — класс клиента сети Ethernet
  • HardwareSerial.h, HardwareSerial_private.h — библиотека для работы с аппаратным UART
  • IPAddress.h — работа с IP-адресами сетевых протоколов Ethernet
  • new.h — реализация операторов new и delete языка C++
  • PluggableUSB.h, USBAPI.h, USBCore.h, USBDesc.h — библиотека для реализации USB-устройств
  • Print.h, Printable.h, Stream.h — библиотеки для работы с символьными потоками данных, в том числе передаваемыми по UART
  • Server.h — класс, реализующий сервер Eternet
  • Udp.h — реализация протокола UDP
  • WCharacters.h, WString.h — классы для работы с символами и строками
  • wiring_private.h — библиотека платформы Wiring, на базе которой строится Arduino Core. Эта библиотека реализует относительно высокоуровневый интерфейс к системным ресурсам микроконтроллеров.

Таким образом, даже в простейшую программу мигания светодиодом включается масса ненужного кода. Такова плата за простоту разработки и низкий порог вхождения. Однако, говоря об этом, я лукавлю: пример, показанный в прошлой статье не соответствует тому, что получается после сборки в Arduino IDE.

2. Обрезаем жирок с прошивки

В Arduino IDE Core собирается в отдельную статическую библиотеку core.a, которая затем компонуется с объектными файлами скетча в готовый бинарник. Проделаем тоже самое в Qt Creator.

Создадим проект core со следующей структурой

Скрипт qmake этого проекта представлен ниже:

core.pro

# Целевой каталог и имя библиотеки DESTDIR = ../../lib TARGET = core  # Подключаем заголовочные файлы INCLUDEPATH += $$ARDUINO_DIR/cores/arduino INCLUDEPATH += $$ARDUINO_DIR/variants/standard INCLUDEPATH += $$ARDUINO_DIR/libraries INCLUDEPATH += /usr/avr/include  # Настройки компилятора C QMAKE_CC = /usr/bin/avr-gcc QMAKE_CFLAGS += -c -g -Os -w -ffunction-sections -fdata-sections QMAKE_CFLAGS += -MMD -mmcu=$$ARDUINO_MCU -DF_CPU=$$ARDUINO_FCPU QMAKE_CFLAGS += -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR  # Настройки компилятора C++ QMAKE_CXX = /usr/bin/avr-g++ QMAKE_CXXFLAGS += -c -g -Os -w  -ffunction-sections -fdata-sections QMAKE_CXXFLAGS += -fno-exceptions -fno-threadsafe-statics QMAKE_CXXFLAGS += -MMD -mmcu=$$ARDUINO_MCU -DF_CPU=$$ARDUINO_FCPU QMAKE_CXXFLAGS += -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR  # Заголовки Arduino Core HEADERS += $$files($$ARDUINO_DIR/cores/arduino/*.h) HEADERS += $$files($$ARDUINO_DIR/variants/standard/*.h)  # Исходники Arduino Core SOURCES += $$files($$ARDUINO_DIR/cores/arduino/*.c) SOURCES += $$files($$ARDUINO_DIR/cores/arduino/*.cpp) 

Проект содержит исключительно код Arduino Core. Его сборка дает на выходе библиотеку libcore.a

Теперь рядышком создаем проект прошивки, содержащий код скетча

blink.pro

# Определяем переменные окружения сборки  # Корневой каталог исходников Arduino Core ARDUINO_DIR=/usr/share/arduino/hardware/archlinux-arduino/avr/ # Выбираем целевой контроллер (Arduino Uno, Nano, Mini) ARDUINO_MCU=atmega328p # Частота тактирования контроллера ARDUINO_FCPU = 16000000L  # Ни гуи, ни ядра Qt нам не надо! QT -= gui core CONFIG -= qt  # Шаблон проекта - приложение, будет собираться исполняемый файл формата ELF TEMPLATE = app  # Целевой каталог и имя бинарника DESTDIR = ../../bin TARGET = blink  # Подключаем заголовочные файлы INCLUDEPATH += $$ARDUINO_DIR/cores/arduino INCLUDEPATH += $$ARDUINO_DIR/variants/standard INCLUDEPATH += $$ARDUINO_DIR/libraries INCLUDEPATH += /usr/avr/include  # Настройки компилятора C QMAKE_CC = /usr/bin/avr-gcc QMAKE_CFLAGS += -c -g -Os -w -ffunction-sections -fdata-sections QMAKE_CFLAGS += -MMD -mmcu=$$ARDUINO_MCU -DF_CPU=$$ARDUINO_FCPU QMAKE_CFLAGS += -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR  # Настройки компилятора C++ QMAKE_CXX = /usr/bin/avr-g++ QMAKE_CXXFLAGS += -c -g -Os -w  -ffunction-sections -fdata-sections QMAKE_CXXFLAGS += -fno-exceptions -fno-threadsafe-statics QMAKE_CXXFLAGS += -MMD -mmcu=$$ARDUINO_MCU -DF_CPU=$$ARDUINO_FCPU QMAKE_CXXFLAGS += -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR  # Настройки компоновщика QMAKE_LINK = /usr/bin/avr-gcc QMAKE_LFLAGS = -w -Os -Wl,--gc-sections -mmcu=$$ARDUINO_MCU QMAKE_LIBS = -lm  # Постобработка QMAKE_POST_LINK += /usr/bin/avr-objcopy -O ihex -j .text -j .data -S ${TARGET} ${TARGET}.hex  LIBS += -L../../lib -lcore  #Заголовки проекта INCLUDEPATH += ./include HEADERS += $$files(./include/*.h)  # Исходники проекта SOURCES += $$files(./src/*.cpp) 

blink.h

#ifndef LED_BLINK_H #define LED_BLINK_H  #include      #endif // LED_BLINK_H 

blink.cpp

#include    "blink.h"  #define LED_STAND_PIN 13  unsigned long time = 0; unsigned long DELAY = 1000000; bool on = false;  //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void setup() {     pinMode(LED_STAND_PIN, OUTPUT); }  //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void loop() {     if ( micros() >= time + DELAY )     {         time = micros();         on = !on;     }      uint8_t state = on ? HIGH : LOW;      digitalWrite(LED_STAND_PIN, state); } 

Оба проекта будем собирать совместно, используя тип проекта «сабдиректории» доступный в qmake

Лог прошивки blink.hex

TEMPLATE = subdirs  SUBDIRS += ./core SUBDIRS += ./blink 

Собираем проект, запускаем его на плате и смотрим лог прошивки

Заголовок спойлера

TEMPLATE = subdirs  SUBDIRS += ./core SUBDIRS += ./blink 

Здесь обращаем внимание на объем занятой памяти

avrdude: verifying ... avrdude: 1040 bytes of flash verified 

Ага, прошивка занимает уже 1040 байт против 2838 в проекте из прошлой статьи. Но всё же, аналогичный скетч в Arduino IDE занимает 882 байта. Внимательно изучив лог сборки среды ардуино, добавляем в проекты blink и core ключи компилятора C

QMAKE_CFLAGS += -flto -fno-fat-lto-objects 

и ключи компилятора C++

QMAKE_CXXFLAGS += -fpermissive -flto -fno-devirtualize -fno-use-cxa-atexit 

Пересобираем, шьем, запускаем и…

avrdude: verifying ... avrdude: 882 bytes of flash verified 

Ок, вожделенные 882 байта достигнуты. За счет чего так выходит?

Во-первых, посмотрим на ELF-файлы, получающиеся при сборке нынешнего и предыдущего проекта, а именно обратим внимание на символьную информацию, которая даст представление о том, что из функций и классов Arduino Core в итоге попадает в бинарник. Даем команду

$ avr-objdump -t led-blink 

Таблица символов led-blink

led-blink:     формат файла elf32-avr  SYMBOL TABLE: 00800100 l    d  .data	00000000 .data 00000000 l    d  .text	00000000 .text 00800122 l    d  .bss	00000000 .bss 00000000 l    d  .stab	00000000 .stab 00000000 l    d  .stabstr	00000000 .stabstr 00000000 l    d  .comment	00000000 .comment 00000000 l    d  .note.gnu.avr.deviceinfo	00000000 .note.gnu.avr.deviceinfo 00000000 l    d  .debug_info	00000000 .debug_info 00000000 l    d  .debug_abbrev	00000000 .debug_abbrev 00000000 l    d  .debug_line	00000000 .debug_line 00000000 l    d  .debug_str	00000000 .debug_str 00000000 l    df *ABS*	00000000 WInterrupts.c 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 00000112 l     F .text	00000002 nothing 00800100 l     O .data	00000004 intFunc 00000000 l    df *ABS*	00000000 HardwareSerial.cpp 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 00000000 l    df *ABS*	00000000 IPAddress.cpp 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 0000078a l     F .text	00000016 _GLOBAL__sub_I_IPAddress.cpp 008001c8 l     O .bss	00000006 _ZL11INADDR_NONE 00000000 l    df *ABS*	00000000 Tone.cpp 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 0080011c l     O .data	00000001 _ZL9tone_pins 000000b8 l     O .text	00000001 _ZL21tone_pin_to_timer_PGM 00000000 l    df *ABS*	00000000 led-blink.cpp 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 00000000 l    df *ABS*	00000000 wiring_digital.c 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 00000304 l     F .text	00000052 turnOffPWM 00000000 l    df *ABS*	00000000 HardwareSerial0.cpp 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 00000694 l     F .text	0000005a _GLOBAL__sub_I_HardwareSerial0.cpp 00000000 l    df *ABS*	00000000 _clear_bss.o 000000ea l       .text	00000000 .do_clear_bss_start 000000e8 l       .text	00000000 .do_clear_bss_loop 00000000 l    df *ABS*	00000000 wiring.c 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 00800122 l     O .bss	00000001 timer0_fract 00000000 l    df *ABS*	00000000 main.cpp 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 00000000 l    df *ABS*	00000000 Print.cpp 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 0000081e l     F .text	0000001e _ZN5Print5writeEPKc.part.2 00000000 l    df *ABS*	00000000 _udivmodsi4.o 00000ac6 l       .text	00000000 __udivmodsi4_ep 00000aac l       .text	00000000 __udivmodsi4_loop 00000000 l    df *ABS*	00000000 _exit.o 00000af2 l       .text	00000000 __stop_program 00000000 l    df *ABS*	00000000 hooks.c 0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 0000010e  w      .text	00000000 __vector_22 00800127 g     O .bss	00000004 timer0_overflow_count 0000094a g     F .text	0000002a _Z6noToneh 00000772 g     F .text	00000018 _ZN9IPAddressC1Ehhhh 000000ae g     O .text	0000000a port_to_mode_PGM 00000114 g     F .text	0000004e __vector_1 0000ffa0 g       *ABS*	00000000 __DATA_REGION_LENGTH__ 00800123 g     O .bss	00000004 timer0_millis 00000442 g     F .text	0000001c _ZN14HardwareSerial4peekEv 0000084a g     F .text	00000098 _ZN5Print11printNumberEmh 000007c4 g     F .text	0000005a _ZN5Print5writeEPKhj 00000068 g       .text	00000000 __trampolines_start 008001cf g     O .bss	00000002 timer2_pin_port 00000af4 g       .text	00000000 _etext 0000042a g     F .text	00000018 _ZN14HardwareSerial9availableEv 0000010e  w      .text	00000000 __vector_24 00000a34 g     F .text	0000006c loop 000004c0 g     F .text	00000042 _ZN14HardwareSerial17_tx_udr_empty_irqEv 0000010e  w      .text	00000000 __vector_12 000007a0  w    F .text	00000002 initVariant 000006ee g     F .text	00000084 _ZNK9IPAddress7printToER5Print 00000542 g     F .text	0000008e _ZN14HardwareSerial5writeEh 0000010e g       .text	00000000 __bad_interrupt 00000b16 g       *ABS*	00000000 __data_load_end 0000010e  w      .text	00000000 __vector_6 008001d5 g     O .bss	00000001 on 00000068 g       .text	00000000 __trampolines_end 0000010e  w      .text	00000000 __vector_3 000003ce g     F .text	0000005c digitalWrite 00000356 g     F .text	00000078 pinMode 00000090 g     O .text	00000014 digital_pin_to_port_PGM 0000010e  w      .text	00000000 __vector_23 00000af4 g       *ABS*	00000000 __data_load_start 000000be g       .text	00000000 __dtors_end 008001da g       .bss	00000000 __bss_end 00000400 g       *ABS*	00000000 __LOCK_REGION_LENGTH__ 0000010e  w      .text	00000000 __vector_25 0000090a g     F .text	00000040 _Z12disableTimerh 0000010e  w      .text	00000000 __vector_11 00000486 g     F .text	0000001e _ZN14HardwareSerial17availableForWriteEv 000000be  w      .text	00000000 __init 000008fc g     F .text	0000000e _ZN5Print5printEhi 00000772 g     F .text	00000018 _ZN9IPAddressC2Ehhhh 000004a4  w    F .text	0000001c _Z14serialEventRunv 00000502 g     F .text	00000040 _ZN14HardwareSerial5flushEv 0000010e  w      .text	00000000 __vector_13 0000010e  w      .text	00000000 __vector_17 00000634 g     F .text	0000004c __vector_19 00000974 g     F .text	000000b8 __vector_7 0080012b g     O .bss	0000009d Serial 00800104  w    O .data	00000012 _ZTV14HardwareSerial 000000e0 g       .text	00000010 .hidden __do_clear_bss 0000083c g     F .text	0000000e _ZN5Print5printEc 00000680 g     F .text	00000014 _Z17Serial0_availablev 00810000 g       .stab	00000000 __eeprom_end 0000007c g     O .text	00000014 digital_pin_to_bit_mask_PGM 00800116  w    O .data	00000006 _ZTV9IPAddress 00000000 g       .text	00000000 __vectors 00800122 g       .data	00000000 __data_end 00000000  w      .text	00000000 __vector_default 0000010e  w      .text	00000000 __vector_5 00000400 g       *ABS*	00000000 __SIGNATURE_REGION_LENGTH__ 00000ae4 g       .text	0000000c .hidden __tablejump2__ 0000028e g     F .text	00000076 init 000000ba g       .text	00000000 __ctors_start 000000ca g       .text	00000016 .hidden __do_copy_data 0080011d g     O .data	00000004 DELAY 00800122 g       .bss	00000000 __bss_start 000007a2 g     F .text	00000022 main 0000010e  w      .text	00000000 __vector_4 008001d6 g     O .bss	00000004 time 00000244 g     F .text	0000004a micros 008001ce g     O .bss	00000001 timer2_pin_mask 00000000  w      *ABS*	00000000 __heap_end 0000010e  w      .text	00000000 __vector_9 00000162 g     F .text	0000004e __vector_2 00000400 g       *ABS*	00000000 __USER_SIGNATURE_REGION_LENGTH__ 0000010e  w      .text	00000000 __vector_21 0000010e  w      .text	00000000 __vector_15 000000a4 g     O .text	0000000a port_to_output_PGM 000008e2 g     F .text	0000001a _ZN5Print5printEmi 00000a2c g     F .text	00000008 setup 008001da g       .stab	00000000 __heap_start 000000be g       .text	00000000 __dtors_start 000000be g       .text	00000000 __ctors_end 000008ff  w      *ABS*	00000000 __stack 00800122 g       .data	00000000 _edata 008001da g       .stab	00000000 _end 0000010e  w      .text	00000000 __vector_8 00000068 g     O .text	00000014 digital_pin_to_timer_PGM 00000af0  w      .text	00000000 .hidden exit 0000045e g     F .text	00000028 _ZN14HardwareSerial4readEv 00000aa0 g       .text	00000044 .hidden __udivmodsi4 00010000 g       *ABS*	00000000 __EEPROM_REGION_LENGTH__ 00000af0 g       .text	00000000 .hidden _exit 0000010e  w      .text	00000000 __vector_14 0000010e  w      .text	00000000 __vector_10 008001d1 g     O .bss	00000004 timer2_toggle_count 000001b0 g     F .text	00000094 __vector_16 00800100 g       .data	00000000 __data_start 000005d0 g     F .text	00000064 __vector_18 00000400 g       *ABS*	00000000 __FUSE_REGION_LENGTH__ 00020000 g       *ABS*	00000000 __TEXT_REGION_LENGTH__ 0000010e  w      .text	00000000 __vector_20 000000f0 g       .text	00000016 .hidden __do_global_ctors 

Теперь сравним со вторым проектом

Таблица символов blink

blink:     формат файла elf32-avr  SYMBOL TABLE: 00800100 l    d  .data	00000000 .data 00000000 l    d  .text	00000000 .text 00800100 l    d  .bss	00000000 .bss 00000000 l    d  .comment	00000000 .comment 00000000 l    d  .note.gnu.avr.deviceinfo	00000000 .note.gnu.avr.deviceinfo 00000000 l    d  .debug_info	00000000 .debug_info 00000000 l    d  .debug_abbrev	00000000 .debug_abbrev 00000000 l    d  .debug_line	00000000 .debug_line 00000000 l    d  .debug_str	00000000 .debug_str 00000000 l    df *ABS*	00000000  0000003e l       *ABS*	00000000 __SP_H__ 0000003d l       *ABS*	00000000 __SP_L__ 0000003f l       *ABS*	00000000 __SREG__ 00000000 l       *ABS*	00000000 __tmp_reg__ 00000001 l       *ABS*	00000000 __zero_reg__ 000000e0 l     F .text	00000038 pinMode.constprop.1 000000a4 l     O .text	00000014 digital_pin_to_bit_mask_PGM 00000090 l     O .text	00000014 digital_pin_to_port_PGM 00000086 l     O .text	0000000a port_to_mode_PGM 0000007c l     O .text	0000000a port_to_output_PGM 00000118 l     F .text	00000090 digitalWrite.constprop.0 00000068 l     O .text	00000014 digital_pin_to_timer_PGM 000001a8 l     F .text	00000076 init 0000021e l     F .text	0000004a micros 00800105 l     O .bss	00000004 timer0_overflow_count 0080010a l     O .bss	00000004 time 00800109 l     O .bss	00000001 on 00800101 l     O .bss	00000004 timer0_millis 00800100 l     O .bss	00000001 timer0_fract 00000000 l    df *ABS*	00000000 _clear_bss.o 000000ce l       .text	00000000 .do_clear_bss_start 000000cc l       .text	00000000 .do_clear_bss_loop 00000000 l    df *ABS*	00000000 _exit.o 00000370 l       .text	00000000 __stop_program 000000dc  w      .text	00000000 __vector_22 000000dc  w      .text	00000000 __vector_1 0000ffa0 g       *ABS*	00000000 __DATA_REGION_LENGTH__ 00000068 g       .text	00000000 __trampolines_start 00000372 g       .text	00000000 _etext 000000dc  w      .text	00000000 __vector_24 000000dc  w      .text	00000000 __vector_12 000000dc g       .text	00000000 __bad_interrupt 00000372 g       *ABS*	00000000 __data_load_end 000000dc  w      .text	00000000 __vector_6 00000068 g       .text	00000000 __trampolines_end 000000dc  w      .text	00000000 __vector_3 000000dc  w      .text	00000000 __vector_23 00000372 g       *ABS*	00000000 __data_load_start 000000b8 g       .text	00000000 __dtors_end 0080010e g       .bss	00000000 __bss_end 00000400 g       *ABS*	00000000 __LOCK_REGION_LENGTH__ 000000dc  w      .text	00000000 __vector_25 000000dc  w      .text	00000000 __vector_11 000000b8  w      .text	00000000 __init 000000dc  w      .text	00000000 __vector_13 000000dc  w      .text	00000000 __vector_17 000000dc  w      .text	00000000 __vector_19 000000dc  w      .text	00000000 __vector_7 000000c4 g       .text	00000010 .hidden __do_clear_bss 00810000 g       .comment	00000000 __eeprom_end 00000000 g       .text	00000000 __vectors 00000000  w      .text	00000000 __vector_default 000000dc  w      .text	00000000 __vector_5 00000400 g       *ABS*	00000000 __SIGNATURE_REGION_LENGTH__ 000000b8 g       .text	00000000 __ctors_start 00800100 g       .bss	00000000 __bss_start 000002fc g     F .text	00000072 main 000000dc  w      .text	00000000 __vector_4 00000000  w      *ABS*	00000000 __heap_end 000000dc  w      .text	00000000 __vector_9 000000dc  w      .text	00000000 __vector_2 00000400 g       *ABS*	00000000 __USER_SIGNATURE_REGION_LENGTH__ 000000dc  w      .text	00000000 __vector_21 000000dc  w      .text	00000000 __vector_15 000000b8 g       .text	00000000 __dtors_start 000000b8 g       .text	00000000 __ctors_end 000008ff  w      *ABS*	00000000 __stack 00800100 g       .data	00000000 _edata 0080010e g       .comment	00000000 _end 000000dc  w      .text	00000000 __vector_8 0000036e  w      .text	00000000 .hidden exit 00010000 g       *ABS*	00000000 __EEPROM_REGION_LENGTH__ 0000036e g       .text	00000000 .hidden _exit 000000dc  w      .text	00000000 __vector_14 000000dc  w      .text	00000000 __vector_10 00000268 g     F .text	00000094 __vector_16 000000dc  w      .text	00000000 __vector_18 00000400 g       *ABS*	00000000 __FUSE_REGION_LENGTH__ 00020000 g       *ABS*	00000000 __TEXT_REGION_LENGTH__ 000000dc  w      .text	00000000 __vector_20 

Разница очевидна. Видно, что при компоновке core в отдельную библиотеку, компилятор включает в прошивку только реально используемые в ней части core. В частности, ни в том ни в другом случае мы не используем UART, однако в первой прошивке присутствуют классы для работы с ним. Уменьшение объема прошивки ключами компилятора следует обсудить отдельно, как и сами ключи

3. Ключи компилятора и линковщика

Опции компилятора:

  • -flto — включает оптимизацию при компоновке. Функции в связанных объектных файлах линкуются так, как если бы они находились в пределах одной единицы трансляции
  • -fno-fat-lto-objects — не создавать «жирных» объектных файлов, содержащих промежуточный язык, кроме объектного кода. Действует совместно с предыдущим ключом.
  • -fpermissive — снижает уровень некоторых ошибок компилятора до уровня предупреждений. Может привести к генерации некорректного кода
  • -fuse-cxa-aexit — использовать в деструкторах объектов функцию __cxa-atexit() вместо atexit()
  • -ffunction-sections, -fdata-sections — помещать каждую функцию и данные в отдельные секции, для оптимизации при последующей компоновке. Позволяет компоновщику включать в итоговый файл только реально используемые функции
  • -fno-threadsafe-statics — не использовать потокобезопасные приемы работы со статическими членами классов. Имеет смысл, так как в контроллерах AVR единственный поток выполнения
  • -fno-exceptions — не использовать обработку исключений
  • -fno-devirtualize — не использовать «девиртуализацию». Если компилятор знает тип объекта, он может вызывать его виртуальные методы напрямую, не используя таблицу виртуальных функций. Данная опция выключает этот механизм
  • -MMD — генерация отдельных правил сборки для каждой единицы трансляции, с созданием списка зависимостей для неё в файле *.d (каждому файлу *.c/*.cpp соответствует файл с тем же именем и расширением *.d, содержащий пути к зависимостям)
  • -DARDUINO_AVR_UNO, -DARDUINO_ARCH_AVR — создают при предпроцессинге макроопределения ARDUINO_AVR_UNO и ARDUINO_ARCH_AVR, активирующие нужные направления условной компиляции исходников.

Опции линковщика:

  • -w — отключение всех предупреждений
  • -Os — оптимизация по размеру конечного файла
  • -Wl,—gc-sections — активирует выборочную компоновку функций. В конечный файл включаются только используемые функции
  • -mmcu — используемая модель контроллера

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

Выводы

Надеюсь, что данный текст расставляет все точки над «i». В платформе Arduino нет ничего сверхъестественного. Её архитектура направлена на сокрытие от начинающего разработчика всех механизмов, использование которых совершенно обычно для тех, кто использует для разработки ПО для AVR чистый C или ассемблер.

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

Надеюсь, что это информация оказалось полезной. В следующей статье, возможно, поговорим о возможностях отладки проектов AVR в Qt Creator

P.S.: Исходный код примера проекта из статьи можно взять здесь

 
Источник

Читайте также