
" Благодаря GNU Make Вы можете собирать прошивку для любого микроконтроллера любым компилятором. "
Собрать прошивку компилятором IAR с помощью GNU Make — это не просто возможно, это стандартный подход для автоматизации сборки, например, на CI/CD серверах, где использование IDE неудобно. IAR поставляется с набором консольных утилит, которые делают этот процесс вполне прямолинейным.
В чем проблема?
Разработчики IDE-IAR все настройки компилятора, компоновщика и binutils засунули в GUI. Это удобно для разовой, единовременной, единичной разработки и учебных проектов в ВУЗах, но абсолютно непригодно для промышленного программирования, когда надо малым коллективом поддерживать на плаву широкий ассортимент электронной продукции.
У меня был случай, когда была одна Legacy IAR-IDE сборка 55-ю конфигурациями для разных клиентов. Надо было для всех конфигураций добавить ещё один программный модуль: пару *.c файлов. Дак вот... Сам код я написал за 2-3 часа. Однако у меня ушло две недели тупо на то, чтобы просто в каждую IAR конфигурацию мышкой в IDE-IAR добавить папку, прописать пути к этой папке и добавить макросы в GUI IDE. Всё это потом как-то попадало в .ewp файл. Вот так, господа… В GNU Make же такое делается изменением буквально одной строки, если не одного символа.
Главный вопрос:
Как собрать прошивку для микроконтроллера stm32f407ve компилятором IAR из самостоятельно написанных GNU Make скриптов? То есть вообще без использования пресловутого XML-образного .ewp файла. Как должен выглядеть Makefile для IAR и ключи к компилятору и опции компоновщику?
Необходимость этой задачи обусловлена прежде всего потребностями пере использования конфигов для электронных плат, микроконтроллеров и процессорных ядер при создании новых похожих сборок с приблизительно одинаковым функционалом. При сборке из любых скриптов обычно очень легко добавить новую сборку. Достаточно только написать ��онфиг и 3 строчки в отдельном Makefile и вот у вас новая специфическая сборка. Происходит наследование конфигов. Подробнее про это можно почитать в отдельном тексте .
Если коротко, то самостоятельно написанные make скрипты открывают вам двери в новые возможности разработки и повышения производительности труда. Начиная с автоматического выравнивая отступов, заканчивая автоматической отправкой артефактов потребителям по электронной почте. С make скриптами вы не будете привязаны к какой-то конкретной IDE и сможете писать код в любом текстовом редакторе, который вам импонирует. В MakeFile очень просто менять компиляторы. Это, буквально, заменить одну строчку в скриптах. С GCC на Clang или c GHS на IAR. Сборка двумя компиляторами поможет вам найти больше ошибок в проекте. При сборке из makefile вам вообще всё равно для какой целевой платформы собирать код прошивки. Вы можете минимальными изменениями в makefile собрать прошивку и крутить её даже на x86.
Вот у меня есть работающий проект прошивки, которая мигает светодиодом для STM32 собранный компилятором GCC. Я хочу пересобрать код прошивки компилятором IAR. IAR хорош за счет того, что делает проверку MISRA, поддерживает горы разных MCU, про которые большинство даже не слышали и имеет развитую систему оптимизации, которая прощает разработчику ряд ошибок программирования.
Вашим основным документом для этого является IAR C/C++ Development Guide Compiling and Linking for Arm Limited’s Arm Cores. Там буквально 736 страниц. В документе расписано, что значит каждая опция компилятора IAR. Обычно в репозитории для IAR лежит проект, состоящий из файлов расширений
Исходные файлы | Пояснение |
*.icf | Настройки для компоновщика |
*.s | Исходный ассемблер код |
.c | Исходный C код |
.h | Заголовочные файлы |
К слову, ассемблер IAR отличается от ассемблера GCC. Для начала проанализируем лог сборки обыкновенной GUI-IDE. Я сгенерировал в STMCubeMX IAR проект, запустил сборку из-под IDE, дождался окончания и стал внимательно анализировать лог. И вот, что я там увидел.
Что же вызывает IAR-GUI система сборки?
Утилита | Файл на входе | Файл на выходе | Ключи |
iasmarm.exe | *.s | *.o | -s+ -M<> -w+ -r --cpu Cortex-M4 --fpu VFPv4_sp |
iccarm.exe | *.c | *.o | --no_cse --no_unroll --no_inline --no_code_motion --no_tbaa --no_clustering --no_scheduling --debug --endian=little --cpu=Cortex-M4 -e --fpu=VFPv4_sp |
ilinkarm.exe | *.o .icf | .map .out | --semihosting --entry __iar_program_start --vfe --text_out locale |
ielftool.exe | .out | .hex | --ihex --verbose |
Выглядит так, что IAR ToolChain на самом деле простой и примитивный. Состоит всего из 4х утилит. Все они после установки попадают в папку C:\Program Files (x86)\IAR Systems\Embedded Workbench 8.1\arm\bin. К слову, этот путь надо сразу прописать в переменную PATH.
Программа | Утилита |
Ассемблер | iasmarm.exe |
Компилятор | iccarm.exe |
Компоновщик | ilinkarm.exe |
Бинарный парсер (binutils) | ielftool.exe |
Схематически процесс сборки c IAR можно изобразить так

Скрипт сборки
В скрипте сборки make надо определить переменные окружения на утилиты, которые и делают всю работу по сборке программы.
# binaries #IAR_PATH="" $(info IAR_PATH=$(IAR_PATH)) # The IAR compiler bin path can be either defined in make command via IAR_PATH # variable (> make IAR_PATH=xxx) # either it can be added to the PATH environment variable. ifdef IAR_PATH $(info WithPath) PREPROCESSOR_TOOL =$(IAR_PATH)/$(PREFIX)iccarm.exe CC = $(IAR_PATH)/$(PREFIX)iccarm.exe AS = $(IAR_PATH)/$(PREFIX)iasmarm.exe ELF_TOOL = $(IAR_PATH)/$(PREFIX)ielftool.exe AR = $(IAR_PATH)/$(PREFIX)iarchive.exe LD = $(IAR_PATH)/$(PREFIX)ilinkarm.exe else $(info WithOutPath) PREPROCESSOR_TOOL = $(PREFIX)iccarm.exe CC = $(PREFIX)iccarm.exe AS = $(PREFIX)iasmarm.exe ELF_TOOL = $(PREFIX)ielftool.exe AR = $(PREFIX)iarchive.exe LD = $(PREFIX)ilinkarm.exe endif MAIN_TARGET_FILE=$(TARGET).out HEX = $(ELF_TOOL) --ihex --verbose BIN = $(ELF_TOOL) --bin --verbose
Это ключевые опции компилятора IAR
Ключ | Назначение |
-e | Активировать IARовские расширения языка С |
--cpu=Cortex-M4 | Выбор процессорного ядра |
--fpu=VFPv4_sp | Выбор FPU |
--endian=little | Выбор порядка байт |
--debug | Добавить отладочную информацию |
--no_inline | Запрет вставных функций |
в make скрипте это выглядит так
COMPILE_IAR_OPT += -DHAS_IAR #COMPILE_IAR_OPT += -D HAS_IAR COMPILE_IAR_OPT += -D__ICCARM__=1 #COMPILE_IAR_OPT += -D __ICCARM__ COMPILE_IAR_OPT +=--no_cse COMPILE_IAR_OPT +=--no_unroll COMPILE_IAR_OPT +=--no_inline COMPILE_IAR_OPT +=--no_code_motion COMPILE_IAR_OPT +=--no_tbaa COMPILE_IAR_OPT +=--no_clustering COMPILE_IAR_OPT +=--no_scheduling COMPILE_IAR_OPT +=--debug COMPILE_IAR_OPT +=--endian=little COMPILE_IAR_OPT +=--cpu=Cortex-M4 COMPILE_IAR_OPT +=-e COMPILE_IAR_OPT +=--fpu=VFPv4_sp COMPILE_IAR_OPT +=--diag_suppress=Pe029 COMPILE_IAR_OPT +=--diag_suppress=Pe1345 COMPILE_IAR_OPT +=--diag_suppress=Pe513 COMPILE_IAR_OPT +=--diag_suppress=Pe144 COMPILE_IAR_OPT +=--diag_suppress=Pe188 ASM_FLAGS += -s+ #ASM_FLAGS += -M<> ASM_FLAGS += -w+ ASM_FLAGS += -r ASM_FLAGS += --cpu Cortex-M4 ASM_FLAGS += --fpu VFPv4_sp LDFLAGS +=--semihosting LDFLAGS +=--entry __iar_program_start LDFLAGS +=--vfe LDFLAGS +=--text_out locale LDFLAGS +=--redirect _Printf=_PrintfFull LDFLAGS +=--redirect _Scanf=_ScanfFull LDFLAGS +=--no_out_extension LDFLAGS +=--map $(BUILD_DIR)/$(TARGET).map LDFLAGS += --config $(LDSCRIPT) CFLAGS += $(COMPILE_IAR_OPT)
Основной скрипт сборки
CSTANDARD = -std=c11 mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) $(info mkfile_path:$(mkfile_path) ) #$(info MAKE:$(MAKE)) #$(info MAKEFILE_LIST:$(MAKEFILE_LIST)) MK_PATH:=$(subst /cygdrive/c/,C:/,$(MK_PATH)) $(info MK_PATH=$(MK_PATH)) BUILD_DIR=build EXTRA_TARGETS= INCDIR := $(subst /cygdrive/c/,C:/, $(INCDIR)) #@echo $(error INCDIR=$(INCDIR)) #$(error SOURCES_DIAG_C=$(SOURCES_DIAG_C)) #$(error SOURCES_THIRD_PARTY_C=$(SOURCES_THIRD_PARTY_C)) SOURCES_TOTAL_C += $(SOURCES_C) SOURCES_TOTAL_C += $(SOURCES_DIAG_C) SOURCES_TOTAL_C += $(SOURCES_CONFIGURATION_C) SOURCES_TOTAL_C += $(SOURCES_THIRD_PARTY_C) SOURCES_TOTAL_C := $(subst /cygdrive/c/,C:/, $(SOURCES_TOTAL_C)) #@echo $(error SOURCES_TOTAL_C=$(SOURCES_TOTAL_C)) SOURCES_ASM := $(subst /cygdrive/c/,C:/, $(SOURCES_ASM)) #@echo $(error SOURCES_ASM=$(SOURCES_ASM)) LIBS := $(subst /cygdrive/c/,C:/, $(LIBS)) LDSCRIPT := $(subst /cygdrive/c/,C:/, $(LDSCRIPT)) #@echo $(error SOURCES_ASM=$(SOURCES_ASM)) WORKSPACE_LOC := $(realpath $(WORKSPACE_LOC)) WORKSPACE_LOC := $(subst /cygdrive/c/,C:/, $(WORKSPACE_LOC)) include $(WORKSPACE_LOC)/make_scripts/toolchain.mk # CFLAGS #https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html AS_DEFS = AS_INCLUDES = # OPT_C += -O0 include $(WORKSPACE_LOC)/make_scripts/compiler_options.mk include $(WORKSPACE_LOC)/make_scripts/compiler_errors.mk include $(WORKSPACE_LOC)/make_scripts/warning_options.mk include $(WORKSPACE_LOC)/make_scripts/linker_options.mk MCAL_OPT += $(OPT_C) CFLAGS += $(MCAL_OPT) ASFLAGS += $(MCU) ASFLAGS += $(AS_DEFS) ASFLAGS += $(AS_INCLUDES) ASFLAGS += $(MCAL_OPT) ASFLAGS += $(COMPILE_OPT) ASFLAGS += -Wall ASFLAGS +=-fdata-sections ASFLAGS += -ffunction-sections #@echo $(error LDSCRIPT=$(LDSCRIPT)) LIBDIR = ARTIFACTS += $(BUILD_DIR)/$(TARGET).bin ARTIFACTS += $(BUILD_DIR)/$(TARGET).hex ARTIFACTS += $(BUILD_DIR)/$(MAIN_TARGET_FILE) #@echo $(error ARTIFACTS=$(ARTIFACTS)) .PHONY: all # default action: build all all: $(EXTRA_TARGETS) $(ARTIFACTS) .PHONY: generate_definitions # build the application # list of objects OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_TOTAL_C:.c=.o))) vpath %.c $(sort $(dir $(SOURCES_TOTAL_C))) # list of ASM program objects #@echo $(error SOURCES_ASM=$(SOURCES_ASM)) ASM_OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_ASM:.s=.o))) #@echo $(error ASM_OBJECTS=$(ASM_OBJECTS)) ELF_OBJECTS += $(OBJECTS) #@echo $(error ASM_OBJECTS=$(ASM_OBJECTS)) ELF_OBJECTS += $(ASM_OBJECTS) #@echo $(error ELF_OBJECTS=$(ELF_OBJECTS)) ifeq ($(IAR),Y) vpath %.s $(sort $(dir $(SOURCES_ASM))) endif TOTAL_FILES := $(words $(OBJECTS)) $(info TOTAL_FILES:$(TOTAL_FILES) ) #@echo $(error COMPILE_GCC_OPT=$(COMPILE_GCC_OPT)) ifeq ($(IAR),Y) $(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR) @echo Compile Asm $@ #@ $(CC) -c -MD $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ $(AS) -c $(ASM_FLAGS) $< -o $@ endif $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) $(eval CURRENT_CNT=$(shell echo $$(($(CURRENT_CNT)+1)))) @echo Compiling $(CURRENT_CNT)/$(TOTAL_FILES) $@ #@ $(CC) -c -MD $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ $(CC) -c $(CFLAGS) $< -o $@ $(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR) $(AS) -c $(CFLAGS) $< -o $@ #@echo $(error MAIN_TARGET_FILE=$(MAIN_TARGET_FILE)) #@echo $(error LDFLAGS=$(LDFLAGS)) $(BUILD_DIR)/$(MAIN_TARGET_FILE): $(ELF_OBJECTS) Makefile $(BUILD_DIR) @echo GenerateMainArtifact $@ $(LD) $(ELF_OBJECTS) $(LDFLAGS) -o $@ $(SZ) $@ $(BUILD_DIR)/%.hex: $(BUILD_DIR)/$(MAIN_TARGET_FILE) | $(BUILD_DIR) @echo GenerateHex $@ $(HEX) $< $@ #@echo $(error MAIN_TARGET_FILE=$(MAIN_TARGET_FILE)) $(BUILD_DIR)/%.bin: $(BUILD_DIR)/$(MAIN_TARGET_FILE) $(BUILD_DIR) @echo GenerateBin $@ $(BIN) $< $@ $(BUILD_DIR): mkdir -p $@ # clean up .PHONY: clean clean: -rm -fR $(BUILD_DIR) # dependencies -include $(wildcard $(BUILD_DIR)/*.d) # *** EOF ***
Чтобы инициировать процесс построения прошивки надо запустить на исполнение файл build_from_make.bat
echo off cls call clean_temp.bat make -i clean 2>&1 | tee clean_log.txt make -i all | tee build_log.txt
Тут скрипт файл clean_temp.bat просто делает удаление временных файлов
@echo off del /S *.o del /S *.obj del /S *.d del /S *.map del /S *.hex del /S *.elf del /S *.lst del /S *.bin del /S *.su del /S *.pp del /S *.bak
Сравнение GCC и IAR
Между компиляторами GCC и IAR есть серия существенных отличий.
Параметр | GCC | IAR |
Файл на выходе | .elf | .out |
Компилятор (CC ) | gcc | iccarm.exe |
Ассемблер (AS) | gcc -x assembler-with-cpp | iasmarm.exe |
Бинарный парсер (CP) | objcopy | ielftool.exe |
Архиватор (AR ) | ar | iarchive.exe |
Компоновщик (LD ) | gcc | ilinkarm.exe |
Скрипт компоновщика | .ld | .icf |
Отладка прошивки
Загружать и отлаживать программу можно при помощи Segger Ozone. Выбираем микроконтролер STM32F407VET6, интерфейс JTAG, скорость 4MHz, загружаем *.out файл и можно пошагово отлаживаться.


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

Итоги
Удалось собрать прошивку для STM32 при помощи компилятора IAR и системы сборки GNU Make. Это открывает дорогу для иcпользования мощных средств заложенных в компилятор IAR (например автоматическая проверка MISRA). Плюс позволяет легко и веерно мигрировать существующий функционал на другие платы и микроконтроллеры. Теперь вы можете забыть про *.ewp файл. Он вам больше не нужен. Всё, что вам надо это .c .h .mk .icf и .s файлы.

Сборка проекта двумя-тремя компиляторами (GCC+ IAR + MinGW) - это хорошая практика при разработке ответственных систем. Если один компилятор не обнаружил ни одной ошибки, то второй компилятор может найти какую-нибудь серьезную ошибку в программе.
Ссылки
Название | URL |
How to set up IAR to use GNU make (makefile)? | https://stackoverflow.com/questions/31155117/how-to-set-up-iar-to-use-gnu-make-makefile |
Конвеерум #30: Эволюция рабочего окружения для embedded разработки | |
CI/CD прошивок для микроконтроллеров в Wiren Board ( начало на 25:20) | |
Почему Сборка с Помощью GUI-IDE — это Тупиковый Путь | |
Эффективное использование GNU Make | |
Основы по GNU Make | |
Статический Анализ С-кода | |
Почему важно собирать код из скриптов | |
IAR Eclipse Setup Guide Part 3 — Makefile Project | https://www.jblopen.com/iar-eclipse-setup-guide-part-3-makefile-project/ |
Дымовая Завеса в Eclipse IDE | |
EWARM_DevelopmentGuide.ENU.pdf | https://library.tsilikin.ru/Техника/Программирование/Embedded system/IAR/EWARM_DevelopmentGuide.ENU.pdf |
Сборка firmware для CC2652 из Makefile | |
Техникум: Автоматическое Aрхивирование Aртефактов | |
Интеграция утилиты Artistic Style в скрипт сборки прошивки | |
Интеграция clang-format в Процесс Сборки | |
Progress Bar для Сборки Программы | |
Настройка ToolChain(а) для Win10+GCC+С+Makefile+ARM Cortex-Mx+GDB | |
Интеграция Стилистического Анализа в общий Make Скрипт Сборки Проекта | |
Обновление Прошивки из Make Скрипта | |
IAR Embedded Workbench & Makefiles |
Вопросы
--Зачем в make скриптах нужно ключевое слово VPATH? VPATH позволяет держать объектные файлы отдельно от исходников.
--Как отлаживать прошивку, если она заклинивает при старте и нет возможности воспользоваться пошаговым отладчиком? Использовать GPIO toggle и осциллограф.
