Как стать автором
Обновить

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

Время на прочтение 16 мин
Количество просмотров 19K

Введение


В предыдущей статье был описан процесс превращения 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 <Arduino.h>

// 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, реализующим базовый функционал. Вот краткое описание заголовков 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    <Arduino.h>

#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

led-blink2.pro
TEMPLATE = subdirs

SUBDIRS += ./core
SUBDIRS += ./blink


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

Лог прошивки blink.hex
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "/mnt/data/Arduino/led-blink2/bin/blink.hex"
avrdude: writing flash (1040 bytes):

Writing | ################################################## | 100% 0.18s

avrdude: 1040 bytes of flash written
avrdude: verifying flash memory against /mnt/data/Arduino/led-blink2/bin/blink.hex:
avrdude: load data flash data from input file /mnt/data/Arduino/led-blink2/bin/blink.hex:
avrdude: input file /mnt/data/Arduino/led-blink2/bin/blink.hex contains 1040 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.15s

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

avrdude: safemode: Fuses OK (E:00, H:00, L:00)

avrdude done.  Thank you.


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

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.: Исходный код примера проекта из статьи можно взять здесь
Теги:
Хабы:
+21
Комментарии 28
Комментарии Комментарии 28

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн