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é
.csoubory patří do programu? - Jak se z výsledku vytvoří
.hexsoubor? - 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í.
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:
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 programuU 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:
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.