cmake_minimum_required(VERSION 3.10) # Set the toolchain file BEFORE the project() call set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/.cmake/avr-gcc.cmake) project(AVRDxBlink C) # Configurable variables set(MCU "avr128db28" CACHE STRING "Target Microcontroller") set(F_CPU "20000000" CACHE STRING "Clock frequency in Hz") set(DEVICE_PACK_DIR "/opt/toolchains/microchip/device-packs/AVRDx" CACHE STRING "Path to Device Packs") set(PROGRAMMER "pickit4_updi" CACHE STRING "Programmer for avrdude") # often pickit4_updi is the right name for UPDI # Include Device Pack # -B instructs gcc to look for device-specific binaries/specs in this folder # -I adds the device-specific headers set(AVR_PACK_FLAGS "-B${DEVICE_PACK_DIR}/gcc/dev/${MCU} -I${DEVICE_PACK_DIR}/include") # Clangd (and therefore eglot/lsp) often does not know about the AVR Device Pack. # We define the corresponding macro manually so works. string(TOUPPER "${MCU}" MCU_UPPER) set(AVR_MACRO "-D__AVR_${MCU_UPPER}__ -D__AVR_DEVICE_NAME__=${MCU}") # If the MCU is a modern AVR (Dx/Ex) or XMEGA, avr-gcc defines __AVR_XMEGA__ internally. # Clangd needs this so uses NVM_FUSES_t instead of the old __fuse_t. if(MCU MATCHES "^avr[0-9]+" OR MCU MATCHES "^atxmega") set(AVR_MACRO "${AVR_MACRO} -D__AVR_XMEGA__") endif() # Set Build Type (Default: Release) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type: Debug or Release" FORCE) endif() # General Compiler Flags (for all build types) # -ffunction-sections and -fdata-sections allow the linker to remove unused code set(COMMON_FLAGS "-mmcu=${MCU} ${AVR_MACRO} -DF_CPU=${F_CPU}UL -Wall -Wstrict-prototypes -std=gnu99 ${AVR_PACK_FLAGS} -ffunction-sections -fdata-sections") set(CMAKE_C_FLAGS "${COMMON_FLAGS}") # Specific flags for Debug and Release set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG") # Linker Flags: -Wl,--gc-sections removes everything that is not needed set(CMAKE_EXE_LINKER_FLAGS "-mmcu=${MCU} ${AVR_PACK_FLAGS} -Wl,--gc-sections") # Generate compile_commands.json for Emacs/clangd (code completion) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Source and Header files file(GLOB_RECURSE SRC_FILES src/*.c) list(FILTER SRC_FILES EXCLUDE REGEX "/[.]#") # Ignore Emacs lock files like .#main.c include_directories(inc) # Generate Executable (.elf) add_executable(${PROJECT_NAME}.elf ${SRC_FILES}) # Display size after build (Hex file is obsolete thanks to ELF support) add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD COMMAND avr-size --format=avr --mcu=${MCU} ${PROJECT_NAME}.elf ) # --- Avrdude Programming Targets --- # F6: Program Flash only add_custom_target(flash COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U flash:w:${PROJECT_NAME}.elf:e DEPENDS ${PROJECT_NAME}.elf COMMENT "Flashing program code (Flash)..." ) # C-F6: Program Flash and EEPROM add_custom_target(flash_eeprom COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U flash:w:${PROJECT_NAME}.elf:e -U eeprom:w:${PROJECT_NAME}.elf:e DEPENDS ${PROJECT_NAME}.elf COMMENT "Flashing program code and EEPROM..." ) # F7: Read Fuses add_custom_target(read_fuses COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U fuses:r:-:h COMMENT "Reading fuses..." ) # C-F7: Program Fuses only add_custom_target(flash_fuses COMMAND avrdude -c ${PROGRAMMER} -p ${MCU} -U fuses:w:${PROJECT_NAME}.elf:e DEPENDS ${PROJECT_NAME}.elf COMMENT "Programming fuses from ELF file..." )