Přeskočit na hlavní obsah

Konfigurace sestavení

Konfigurace sestavení je popis toho, jak se má projekt přeložit. V angličtině se často používá výraz build configuration. U malého projektu to může být obyčejný Makefile. U většího projektu se často používá CMakeLists.txt.

Tahle konfigurace odpovídá na několik důležitých otázek:

  • Jaký překladač se má použít?
  • Pro jaký čip se překládá?
  • Jaká je frekvence procesoru?
  • Které .c soubory patří do programu?
  • Jak se z výsledku vytvoří .hex soubor?
  • Jakým příkazem se program nahraje do čipu?

Kde ji vzít

Soubor CMakeLists.txt není součástí avr-gcc. Je to popis vašeho projektu. Musíte ho tedy buď napsat, nebo vzít z existujícího projektu a upravit.

Nejjednodušší cesta je začít z malého funkčního příkladu. Například ze školního projektu, kde už funguje překlad pro konkrétní ATtiny. Potom obvykle měníte jen několik věcí:

  • název projektu v project(...),
  • cílový čip v MCU,
  • frekvenci procesoru ve F_CPU,
  • seznam souborů v add_executable(...),
  • port pro nahrávání přes UPDI.

To je běžná praxe. Nikdo soudný nepíše build systém pokaždé od nuly, stejně jako si pokaždé nevyrábí vlastní šroubovák. Jen je dobré rozumět tomu, co v něm ty důležité řádky dělají.

Šablona není zaklínadlo

Když kopírujete CMakeLists.txt z příkladu, nekopírujte ho naslepo. Zkontrolujte hlavně MCU, F_CPU, cesty ke zdrojovým souborům a způsob nahrávání. Projekt pro ATtiny1626 nemusí bez úprav sedět na jiný čip.

CMake

CMake není překladač. Je to nástroj, který připraví sestavení projektu. V našem případě mu řekneme, že má použít avr-gcc, pro jaký čip se překládá a jak z výsledného .elf souboru vyrobit .hex soubor pro nahrání do čipu.

Základní CMakeLists.txt pro blink projekt může vypadat takto:

CMakeLists.txt
cmake_minimum_required(VERSION 3.20)

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

find_program(AVR_GCC avr-gcc REQUIRED)
set(CMAKE_C_COMPILER ${AVR_GCC} CACHE FILEPATH "AVR C compiler" FORCE)

project(attiny_blink C)

set(MCU attiny1626 CACHE STRING "Target AVR MCU")
set(F_CPU 3333333UL CACHE STRING "CPU frequency")
set(AVR_INCLUDE_DIR /usr/lib/avr/include CACHE PATH "AVR libc include directory")

add_executable(${PROJECT_NAME}.elf
src/main.c
)

target_compile_definitions(${PROJECT_NAME}.elf PRIVATE
F_CPU=${F_CPU}
)

target_include_directories(${PROJECT_NAME}.elf SYSTEM PRIVATE
${AVR_INCLUDE_DIR}
)

target_compile_options(${PROJECT_NAME}.elf PRIVATE
-isystem
${AVR_INCLUDE_DIR}
-mmcu=${MCU}
-Os
-ffunction-sections
-fdata-sections
-Wall
-Wextra
-Wpedantic
)

target_link_options(${PROJECT_NAME}.elf PRIVATE
-mmcu=${MCU}
-Wl,--gc-sections
)

find_program(AVR_OBJCOPY avr-objcopy REQUIRED)
find_program(AVR_SIZE avr-size REQUIRED)

add_custom_command(
OUTPUT ${PROJECT_NAME}.hex
COMMAND ${AVR_OBJCOPY} -O ihex -R .eeprom
$<TARGET_FILE:${PROJECT_NAME}.elf>
${PROJECT_NAME}.hex
DEPENDS ${PROJECT_NAME}.elf
COMMENT "Generating ${PROJECT_NAME}.hex"
)

add_custom_target(hex ALL DEPENDS ${PROJECT_NAME}.hex)

add_custom_command(
TARGET ${PROJECT_NAME}.elf
POST_BUILD
COMMAND ${AVR_SIZE} $<TARGET_FILE:${PROJECT_NAME}.elf>
)

Nejdůležitější volba je:

-mmcu=${MCU}

Ta říká překladači, pro jaký konkrétní čip se program překládá. Bez ní by avr-gcc nevěděl, jaké registry, instrukce a paměť má použít.

Targety pro UPDI

Pokud používáte pymcuprog, můžete si do CMakeLists.txt přidat i target pro kontrolu spojení a target pro nahrání programu:

find_program(PYMCUPROG pymcuprog
PATHS "$ENV{HOME}/.local/bin"
REQUIRED
)

set(UPDI_PORT /dev/ttyUSB0 CACHE STRING "Serial UPDI port")
set(UPDI_BAUD 115k CACHE STRING "Serial UPDI baud rate")

add_custom_target(updi-ping
COMMAND ${PYMCUPROG} ping
--tool uart
--uart ${UPDI_PORT}
--clk ${UPDI_BAUD}
--device ${MCU}
COMMENT "Pinging ${MCU} via UPDI on ${UPDI_PORT}"
)

add_custom_target(flash
COMMAND ${PYMCUPROG} write
--tool uart
--uart ${UPDI_PORT}
--clk ${UPDI_BAUD}
--device ${MCU}
--filename ${PROJECT_NAME}.hex
--erase
--verify
DEPENDS hex
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Flashing ${PROJECT_NAME}.hex to ${MCU}"
)
--tool uart není UART vašeho programu

U pymcuprog znamená --tool uart, že se k UPDI adaptéru připojujeme přes sériový port počítače. Není to totéž jako USART nebo UART ve firmware. Program v čipu nemusí používat UART vůbec.

compile_commands.json

Některá vývojová prostředí umí číst soubor compile_commands.json. Ten obsahuje reálné příkazy, kterými se překládají jednotlivé soubory. Hodí se hlavně pro našeptávání, hledání chyb a správné nalezení hlaviček jako <avr/io.h>.

CMake ho vygeneruje takto:

cmake -S . -B build -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

Výsledek bude v:

build/compile_commands.json

Jednoduchý Makefile

CMake není povinný. Pro malý projekt může stačit i obyčejný Makefile. Například:

Makefile
MCU = attiny1626
F_CPU = 3333333UL
TARGET = attiny_blink

CC = avr-gcc
OBJCOPY = avr-objcopy
SIZE = avr-size

SRC = src/main.c
OBJ = $(SRC:.c=.o)

CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os
CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -Wall -Wextra -Wpedantic
CFLAGS += -isystem /usr/lib/avr/include

LDFLAGS = -mmcu=$(MCU) -Wl,--gc-sections

all: $(TARGET).hex

$(TARGET).elf: $(OBJ)
$(CC) $(LDFLAGS) -o $@ $^
$(SIZE) $@

%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<

$(TARGET).hex: $(TARGET).elf
$(OBJCOPY) -O ihex -R .eeprom $< $@

clean:
rm -f $(OBJ) $(TARGET).elf $(TARGET).hex

Překlad potom spustíte:

make

Makefile je kratší, ale u většího projektu se začne rychle rozrůstat. CMake se lépe hodí ve chvíli, kdy máte víc souborů, víc nastavení nebo chcete pohodlně přidat targety jako flash, updi-ping nebo size.