ToolChain: Настройка Сборки Прошивок для Микроконтроллеров Artety из MakeFile
#make #ARM #Artery #CLI #GDB #GCC #Eclipse #C #Windows10 #OpenOCD
Настало время освоить очередное семейство микроконтроллеров. От компании Artery Technology.
Это уже 11тое по счету семейство микроконтроллеров с которыми мне пришлось разбираться. За спиной уже отгруженные прошивки на основе AVR (ATtiny, ATmega), ARM Cortex-M3(MDR32), ARM Cortex-M4(STM32, CC2650), ARM Cortex-M33(nRF5340), PowerPC (SPC58), Tensilica Xtensa (ESP32), 16bit (MSP430), 8bit (STM8), ARM7(LPC21xx), МicroBlaze. Не сталкивался ещё только с PIC микроконтроллерами.
Теперь вот надо запрограммировать MCU от Artery (а конкретно AT32F435ZMT7). Компания Artery Technology существует с 2016 года и уже возвела полноценную экосистему для своего продукта. Есть фирменные отладочные платы, программаторы. Есть документация, исходные коды MCAL, примеры кода проектов и даже кодогенераторы!
Вот так выглядит микроконтроллер AT32F435ZMT7 в натуре
Запрограммировать микроконтроллер из-под IDE- это очень просто. Под силу даже школьнику. Сложно запрограммировать микроконтроллер из самостоятельно написанных скриптов сборки. В идеале из makefile(ов). Зачем это надо? Если отвечать коротко, то сборка проектов из make скриптов (или CMake) ликвидирует дублирование конфигов и, как следствие, способствует упрощению масштабирования и пере использования кодовой базы.
Постановка задачи:
Научиться программировать микроконтроллер AT32F435ZMT7 компании Artery Technology.
Научиться собирать детерминированные прошивки, написанные на языке программирования Си компилятором GCC, собирать проекты из написанных вручную Makefile (лов).
Собрать минималистическую прошивку с частотой процессора 100MHz со cрабатывающим раз в 1ms системным таймером SysTick, мигающим раз в секунду heart-beat LED (ом) и интерфейсом командной строки CLI поверх UART на битовой скорости 460800 Bit/s.
То есть, подготовить то самое базовое программно-аппаратное окружение на основе которого происходит дальнейшая полноценная разработка и отладка какого бы то ни было приложения на микроконтроллере Artery.
Говоря метафорично, надо создать коробочку для микроконтроллерного приложения.
То есть, довести проект до ортодоксально канонической формы (Makefile, GCC, GDB, NoRTOS, HeartBeat-LED, UART-CLI, NVRAM).
Что надо из документации?
№ | Название документа | Ver | количество страниц |
1 | Eclipse with GCC | 2.0.0 | 23 |
2 | AT32F435/437 Series Datasheet | 2.11 | 120 |
3 | AT32F435/437 Series Reference Manual | 2.05 | 714 |
4 | AT-Link User Manual | 2.1.2 | 38 |
5 | ICP Programmer Manual | 2.07 | 50 |
6 | AT-Link Console User Manual | 2.08 | 15 |
Что надо из оборудования?
№ | Оборудование | Назначение |
1 | Электронная плата с Artery MCU | То что мы программирует |
2 | Программатора AT-Link+ | Переходник с USB на SWD |
3 | Кабель USB-USB-C | Соединить NetTop и программатор |
4 | Перемычки гнездо-гнездо | Соединить программатор и электронную плату |
5 | NetTop c Windows10 | Для запуска ToolChain (а) cross компиляции |
Фаза 1: Уcтановка Текстового Редактора Eclipse
Я не настаиваю на Eclipse. Когда вы собираете из make то вам всё равно какой там текстовый редактор. Лично мне импонирует его подсветка синтаксиса в Eclipse и горячие клавиши. Также перед установкой Eclipse надо установить виртуальную машину Java для вашего PC. Сам Eclipse IDE for C/C++ Developers можно скачать отсюда https://www.eclipse.org/downloads/packages/release/2023–12/r/eclipse-ide-cc-developers
Мне Eclipse нужен только как печатающая машинка. Я даже не планирую использовать плагины Eclipse.
После запуска Eclipse желательно сразу зайти в Window→Preferences→General→Editors→Text Editors и настроить предпочтения по текстовому редактору.
Сразу хочу отметить, что в этом тексте я буду использовать Eclipse без плагинов для ARM GCC, который умеет генерировать makefile (ы)
Ещё Eclipse мне понадобится для пошаговой отладки исходного кода, так как Eclipse способен ассоциироваться с GDB клиентом. Подробнее про это можно почитать тут https://habr.com/ru/articles/682498/
Фаза 2: Установить компилятор GCC
Компилятор GCC это консольная утилита, которая преобразует код на Си в бинарный нативный код для конкретного микропроцессора. Можно скачать компилятор 2016 года тут https://launchpad.net/gcc-arm-embedded/+download
Или лучше взять более новый компилятор с официального сайта ARM
https://developer.arm.com/downloads/-/gnu-rm#:~: text=The GNU Arm Embedded Toolchain, Arm Cortex-R processor families.
Однако там вам придется зарегистрироваться на сайте, придумать логин и пароль.
Вероятно, что у Вас на LapTop (е) уже давным-давно установлен GCC компилятор для ARM процессоров. Вероятно, что Вы уже и раньше программировали микропроцессоры ARM Cortex-M от таких производителей как STM32, Nordic Semiconductor, ПКК Миландр, АО НПЦ ЭЛВИС, НИИЭТ или TI. И у Вас уже на жестком диске остался компилятор arm-none-eabi-gcc. Чтобы это проверить надо открыть cmd и набрать:
arm-none-eabi-gcc –v
C:\Users\user>arm-none-eabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-none-eabi-gcc
COLLECT_LTO_WRAPPER=c:/program\ files\ (x86)/gnu\ arm\ embedded\ toolchain/10\ 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/lto-wrapper.exe
Target: arm-none-eabi
Configured with: /mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/src/gcc/configure --build=x86_64-linux-gnu --host=i686-w64-mingw32 --target=arm-none-eabi --prefix=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/install-mingw --libexecdir=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/install-mingw/lib --infodir=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/install-mingw/share/doc/gcc-arm-none-eabi/info --mandir=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/install-mingw/share/doc/gcc-arm-none-eabi/man --htmldir=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/install-mingw/share/doc/gcc-arm-none-eabi/html --pdfdir=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/install-mingw/share/doc/gcc-arm-none-eabi/pdf --enable-languages=c,c++ --enable-mingw-wildcard --disable-decimal-float --disable-libffi --disable-libgomp --disable-libmudflap --disable-libquadmath --disable-libssp --disable-libstdcxx-pch --disable-nls --disable-shared --disable-threads --disable-tls --with-gnu-as --with-gnu-ld --with-headers=yes --with-newlib --with-python-dir=share/gcc-arm-none-eabi --with-sysroot=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/install-mingw/arm-none-eabi --with-libiconv-prefix=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/build-mingw/host-libs/usr --with-gmp=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/build-mingw/host-libs/usr --with-mpfr=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/build-mingw/host-libs/usr --with-mpc=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/build-mingw/host-libs/usr --with-isl=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/build-mingw/host-libs/usr --with-libelf=/mnt/workspace/workspace/GCC-10-pipeline/jenkins-GCC-10-pipeline-338_20211018_1634516203/build-mingw/host-libs/usr --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --with-pkgversion='GNU Arm Embedded Toolchain 10.3-2021.10' --with-multilib-list=rmprofile,aprofile
Thread model: single
Supported LTO compression algorithms: zlib
gcc version 10.3.1 20210824 (release) (GNU Arm Embedded Toolchain 10.3-2021.10)
Насколько мне известно, самый свежий компилятор ARM-GCC это от 2021 года. В состав компилятора входит вот этот набор утилит
№ | Название утилиты | Назначение утилиты |
1 | addr2line | преобразует адрес из flash в строчку кода в файле |
2 | cpp | Утилита вставки и замены текста |
3 | gcc | компилятор языка Си |
4 | ar | архиватор |
5 | as | ассемблер |
6 | elfedit | парсер elf файлов |
7 | gdb | отладчик |
8 | ld | компоновщик |
9 | nm | показывает список символов в |
10 | objcopy | преобразователь elf файлов в bin hex |
11 | objdump | показывает информацию про обьектные файлы (*.o) |
12 | size | показывает размер секций в бинарном файле |
13 | strings | показывает печатаемые строки из бинарного файла |
14 | strips | удаляет символы и секции из файла |
Фаза 3: Установить Утилиты Сборки (Build Tools)
Для сборки программ нужны всяческие прикладные утилиты командной строки. Вот минимальный их перечень:
№ | Название утилиты | Назначение |
1 | make | Запустить конвейер утилит согласно скрипту в Makefile |
2 | rm | удалить файл или папку |
3 | cd | перейти в другую папку |
4 | mkdir | Создать папку |
5 | tee | разветвление лога консольной утилиты в файл и stdout |
Все они называются одним термином: Build Tools. Их можно установить разом вот по этой ссылке https://sourceforge.net/projects/gnuarmeclipse/files/Build Tools/.
Путь к Build Tools следует прописать в переменную PATH. Вообще пути ко всем *.exe надо прописать в переменную PATH. Тогда операционная система сможет находить их без явного указания полного пути на жестком диске.
Фаза 5: Сборка Прошивки
Каждый производитель микроконтроллеров поставляет значительно большую часть кода для абстракции от подсистем на микроконтроллере. Такой код называется MCAL/HAL/BSP и т. п.
Artery не исключение. Скачать фирменный MCAL можно с сайта https://www.arterytek.com/en/product/AT32F435.jsp
Надо скачать архив с названием AT32F435_437_Firmware_Library_EN_V2.1.5.zip
Вот тут лежит MCAL для GPIO, ADC, CAN, DMA, DAC, FLASH, I2C, SPI, UART, USB и т.п.
AT32F435_437_Firmware_Library_EN_V2.1.5\libraries\drivers\src
Там же есть Third-Paty код для CMSIS, FreeRTOS, LwIP, FatFS, Light and Versatile Graphics Library. Присутствуют примеры проектов.
Папка AT32F435_437_Firmware_Library_EN_V2.1.5 достаточно большая и чтобы не устать искать там что-то конкретное можно использовать культовую утилиту grep. Например, вот тут я одной строчкой
grep -rn at32_board_init | grep "\.c" | grep -v main
нашел то самое место, где определена функция at32_board_init ()
Есть очень важный файл core_cm4.h. В файле core_cm4.h, не много не мало, аж реализована поддержка микропроцессорного ядра ARM Cortex-M4! Вот полный путь:
AT32F435_437_Firmware_Library_EN_V2.1.5\libraries\cmsis\cm4\core_support\core_cm4.h
Внутри файла core_cm4.h можно обнаружить битовые поля для регистров процессора ARM Cortex-M4, код управления системным таймером SysTick, код для FPU, код для управления подсистемой прерываний NVIC: включить выключить конкретное прерывание, и прочее.
В корень папки с проектом следует положить файл at32f435_437_conf.h. Это конфиг для MCAL от Artery. Там при помощи макроопределений прописывается какие подсистемы микроконтроллера (CRM, GPIO, FLASH, UART и т.п.) должны участвовать в сборке артефактов прошивки. Тут есть два варианта. Первый, Вы прописываете эти макросы в *.h файле. Второй, более предпочтительный, Вы передаете макросы через опции компилятора при сборке проекта. Прямо в скриптах сборки. Вот так это выглядит в Make.
ifeq ($(CLOCK),Y)
OPT += -DCRM_MODULE_ENABLED
endif
В случае второго варианта, Вам не придется вручную потом в коде везде, где надо вставлять at32f435_437.h. Так как переданные компилятору макросы через опцию -D они глобальны для всех *.c и *.h файлов.
У каждого микроконтроллера есть прерывания. Обработчики прерываний расположены вот в этих файлах: at32f435_437_int.h, at32f435_437_int.c. Полный список прерываний, которые поддерживает данный процессор можно увидеть в файле startup_at32f435_437.S в массиве g_pfnVectors. Этот массив обычно называют «таблица векторов прерываний», хотя по факту в памяти это прописывается как массив в начале бинарного файла прошивки. Этот перечень у всех моделей прерываний разный. Если сработает прерывание для которого у вас нет обработчика в файле at32f435_437_int.c (например SPI1_IRQHandler), то прошивка зависнет.
Когда мы покупаем микроконтроллер то там уже записана заводская прошивка. Так называемый Boot code. Именно эта прошивка выходит на сцену когда мы подаем питание на микроконтроллер.
Когда на MCU подают и делают сброс, то процессор выполняет следующие шаги
1-- читает первые 2 qword памяти. То есть 8 байт.
2-- из адреса 0×00000000 получает значение верхушки указателя стека. По мере роста адрес стека будет уменьшаться.
3-- из адреса 0×00000004 получает начальное значение для программного счетчика PC.
Дело в том, что по нулевому адресу прописан заводской загрузчик. Его роль всегда в том чтобы глядя на Boot пины решить откуда грузиться дальше. Тут есть три варианта.
BOOT1 | BOOT0 | Boot source | Start Address |
0 | 0 | CODE starts from the main Flash memory | 0×08000000 |
1 | 0 | CODE starts from the main Flash memory | 0×08000000 |
0 | 1 | CODE starts from Boot code | 0×00000100 |
1 | 1 | CODE starts from SRAM | 0×20000000 |
Настройка тактирования ядра.
Программирование любого MCU начинается с выбора частоты тактирования его ядра. Это очень важно. Если вы не знаете на какой частоте у вас работает ядро, то Вы не сможете гарантировать никакие тайминги, никакой точности и вообще Ваша прошивка будет жить как-то сама по себе.
Но тут нам опять повезло! Artery Technology выкалили утилиту кодогенератор для синтеза настроек тактирования. Называется утилита AT32_New_Clock_Configuration.exe и лежит в архиве AT32_New_Clock_Configuration_Win32-x86_64_V3.0.11.zip вместе с инструкцией. Тут можно в GUI подобрать параметры и утилита сама предупредит и исправит ошибки.
Я решил поработать на частоте 100 MHz. Вот такой код мне предлагают вставить утилита в проект.
#include "at32f435_437_clock.h"
void system_clock_config(void){
crm_reset();
crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
pwc_ldo_output_voltage_set(PWC_LDO_OUTPUT_1V0);
flash_clock_divider_set(FLASH_CLOCK_DIV_2);
crm_clock_source_enable(CRM_CLOCK_SOURCE_HEXT, TRUE);
while(crm_hext_stable_wait() == ERROR) {
}
crm_pll_config(CRM_PLL_SOURCE_HEXT, 400, 4, CRM_PLL_FR_8);
crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);
while(crm_flag_get(CRM_PLL_STABLE_FLAG) != SET) {
}
crm_ahb_div_set(CRM_AHB_DIV_1);
crm_apb2_div_set(CRM_APB2_DIV_2);
crm_apb1_div_set(CRM_APB1_DIV_2);
crm_sysclk_switch(CRM_SCLK_PLL);
while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL) {
}
system_core_clock_update();
}
Небольшое резюме по файлам
Буквально насколько слов про файлы, которые будут в любом проекте с микроконтроллером семейства at32f435:
# | Название файла | Назначение |
0 | core_cm4.h | Управление процессорным ядром ARM Cortex-M4 |
1 | at32f435_437.h | настройки MCAL для микроконтроллера |
2 | at32f435_437_conf.h | настройки MCAL |
3 | startup_at32f435_437.S | Ассемблерный код для самой первой функции: Reset_Handler |
4 | system_at32f435_437.c | Си код функции SystemInit |
5 | system_at32f435_437.h | прототип функции SystemInit |
6 | AT32F435xM_FLASH.ld | настройки компоновщика |
7 | at32f435_437_int.c | Корневые функции обработчиков прерываний |
8 | at32f435_437_int.h | заголовочные функции для обработчиков прерываний |
9 | Makefile | Корневой файл с инструкцией как собирать проект |
Примеры всех этих файлов кроме Makefile есть в примерах от вендора.
Сборка проекта из Makefile
Для того, чтобы собрать Си программу надо как-то объяснить компилятору gcc, что надо собрать и из каких файлов. Если вы программировали на Eclipse c плагинами, то там вы вручную настраивали xml, а плагины генерировали из xml make файлы. Способ сборки через плагины Eclipse приводит потере значительной части контроля над проектом.
Более предпочтительный вариант это всё самому писать make файлы. В этом нет ничего сложного. Это позволит в полной степени контролировать процесс сборки и наследовать конфиги.
Далее представлен небольшой экскурс в организацию make скриптов сборки.
Это корневой Makefile. У каждого проекта он будет одинаковый.
MK_PATH:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
#@echo $(error MK_PATH=$(MK_PATH))
WORKSPACE_LOC:=$(MK_PATH)../../
INCDIR += -I$(MK_PATH)
INCDIR += -I$(WORKSPACE_LOC)
TARGET=board_name_config_name_m
#@echo $(error SOURCES_C=$(SOURCES_C))
include $(MK_PATH)config.mk
ifeq ($(CLI),Y)
include $(MK_PATH)cli_config.mk
endif
ifeq ($(DIAG),Y)
include $(MK_PATH)diag_config.mk
endif
ifeq ($(UNIT_TEST),Y)
include $(MK_PATH)test_config.mk
endif
include $(WORKSPACE_LOC)code_base.mk
include $(WORKSPACE_LOC)rules.mk
У каждой сборки есть свой набор конфигов. Конфиги для сборки передаются через переменные окружения в файле include $(MK_PATH)config.mk
ARRAY=Y
ASICS=Y
AT32=Y
AT32F435ZM=Y
AT32F435_437_FIRMWARE_LIBRARY_EN_V2_1_5=Y
CMSIS=Y
CORTEX_M4=Y
CRC=Y
DEBUG=Y
GPIO=Y
LED=Y
LIMITER=Y
LOG=Y
MCAL_AT32=Y
NORTOS=Y
NVS=Y
SUPER_CYCLE=Y
SYSTICK=Y
TASK=Y
TERMINAL=Y
UART=Y
Вся кодовая база индексируется в файле code_base.mk
ifneq ($(CODE_BASE_MK),Y)
CODE_BASE_MK=Y
include $(WORKSPACE_LOC)/code_base_preconfig.mk
INCDIR += -I$(WORKSPACE_LOC)
ifeq ($(MICROCONTROLLER),Y)
FIRMWARE=Y
include $(WORKSPACE_LOC)/mcu/mcu.mk
endif
ifeq ($(BOARD),Y)
include $(WORKSPACE_LOC)/boards/boards.mk
endif
ifeq ($(PROTOTYPE),Y)
include $(WORKSPACE_LOC)/prototypes/prototypes.mk
endif
ifeq ($(THIRD_PARTY),Y)
include $(WORKSPACE_LOC)/third_party/third_party.mk
endif
ifeq ($(CORE),Y)
include $(WORKSPACE_LOC)/core/core.mk
endif
ifeq ($(APPLICATIONS),Y)
include $(WORKSPACE_LOC)/applications/applications.mk
endif
ifeq ($(AT32F435_437_FIRMWARE_LIBRARY_EN_V2_1_5),Y)
include $(WORKSPACE_LOC)/AT32F435_437_Firmware_Library_EN_V2.1.5/AT32F435_437_Firmware_Library_EN_V2_1_5.mk
endif
ifeq ($(MCAL),Y)
include $(WORKSPACE_LOC)/mcal/mcal.mk
endif
ifeq ($(CONNECTIVITY),Y)
include $(WORKSPACE_LOC)/connectivity/connectivity.mk
endif
ifeq ($(CONTROL),Y)
include $(WORKSPACE_LOC)/control/control.mk
endif
ifeq ($(COMPUTING),Y)
include $(WORKSPACE_LOC)/computing/computing.mk
endif
ifeq ($(SENSITIVITY),Y)
include $(WORKSPACE_LOC)/sensitivity/sensitivity.mk
endif
ifeq ($(STORAGE),Y)
include $(WORKSPACE_LOC)/storage/storage.mk
endif
ifeq ($(SECURITY),Y)
include $(WORKSPACE_LOC)/security/security.mk
endif
ifeq ($(ASICS),Y)
include $(WORKSPACE_LOC)/asics/asics.mk
endif
ifeq ($(UNIT_TEST),Y)
include $(WORKSPACE_LOC)/unit_tests/unit_test.mk
endif
SOURCES_C += $(WORKSPACE_LOC)main.c
endif
Язык make позволяет делать вставку *.mk файла в другой файл *.mk прямь как препроцессор cpp.exe делает вставку файла ключевым словом #include.
Для каждой папки с исходниками надо написать *.mk файл. Вот, например, make скрипт для программного компонента GPIO source\mcal\mcal_at32f4\gpio\gpio.mk
$(info GPIO_DRV_MK_INC=$(GPIO_DRV_MK_INC))
ifneq ($(GPIO_DRV_MK_INC),Y)
GPIO_DRV_MK_INC=Y
GPIO_DIR = $(MCAL_AT32F4_DIR)/gpio
#@echo $(error GPIO_DIR=$(GPIO_DIR))
OPT += -DHAS_GPIO
OPT += -DHAS_GPIO_CUSTOM
INCDIR += -I$(GPIO_DIR)
SOURCES_C += $(GPIO_DIR)/gpio_drv.c
SOURCES_C += $(GPIO_DIR)/gpio_isr.c
ifeq ($(CLI),Y)
ifeq ($(GPIO_COMMANDS),Y)
OPT += -DHAS_GPIO_COMMANDS
SOURCES_C += $(GPIO_DIR)/gpio_custom_commands.c
endif
endif
ifeq ($(DIAG),Y)
ifeq ($(GPIO_DIAG),Y)
OPT += -DHAS_GPIO_DIAG
#@echo $(error GPIO_DIAG=$(GPIO_DIAG))
SOURCES_C += $(GPIO_DIR)/gpio_custom_diag.c
endif
endif
endif
Остальное делается по аналогии. Самое главное в make — это правила сборки. Обычно их формируют в файле rules.mk
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
$(info Build $(mkfile_path) )
BUILD_DIR=build
EXTRA_TARGETS=
INCDIR := $(subst /cygdrive/c/,C:/, $(INCDIR))
SOURCES_C += $(SOURCES_THIRD_PARTY_C)
SOURCES_C := $(subst /cygdrive/c/,C:/, $(SOURCES_C))
#@echo $(error SOURCES_C=$(SOURCES_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))
include $(WORKSPACE_LOC)/toolchain.mk
WORKSPACE_LOC := $(subst /cygdrive/c/,C:/, $(WORKSPACE_LOC))
FLOAT-ABI = -mfloat-abi=hard
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)
CSTANDARD = -std=gnu99
AS_DEFS =
AS_INCLUDES =
ifeq ($(DEBUG), Y)
CFLAGS += -g3 -ggdb -gdwarf-2
OPT += -O0
else
OPT += -Os
endif
OPT += -fmessage-length=0
OPT += -fsigned-char
OPT += -fno-common
OPT += -fstack-usage
OPT += -fzero-initialized-in-bss
OPT += -finline-small-functions
OPT += -Werror=switch
OPT += -Werror=implicit-function-declaration
OPT += -Werror=unused-but-set-variable
OPT += -Werror=unused-variable
OPT += -Werror=unused-function
OPT += -Werror=incompatible-pointer-types
OPT += -Werror=return-type
OPT += -Werror=enum-compare
OPT += -Werror=shift-count-overflow
OPT += -Wno-format-truncation
OPT += -Wno-restrict
OPT += -Wno-format
#Perform dead code elimination
OPT += -fdce
#Perform dead store elimination
OPT += -fdse
# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS += $(CSTANDARD)
CFLAGS += -Wall
#CFLAGS += -Wformat-overflow=1
CFLAGS += $(MCU) $(OPT) -fdata-sections -ffunction-sections $(INCDIR)
# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
CPP_FLAGS += $(CSTANDARD) $(INCDIR) $(OPT)
# LDFLAGS
# libraries
LINKER_FLAGS += -Xlinker --gc-sections
ifeq ($(MBR), Y)
LIBS += -lnosys
LDFLAGS += -specs=nano.specs
else
LINKER_FLAGS += -u _scanf_float
LINKER_FLAGS += -u _printf_float
endif
ifeq ($(LIBC), Y)
LIBS += -lc
endif
ifeq ($(MATH_LIB), Y)
LIBS += -lm
endif
LIBDIR =
LDFLAGS += $(MCU) -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections $(LINKER_FLAGS)
ARTIFACTS += $(BUILD_DIR)/$(TARGET).bin
ARTIFACTS += $(BUILD_DIR)/$(TARGET).hex
ARTIFACTS += $(BUILD_DIR)/$(TARGET).elf
# default action: build all
all: $(EXTRA_TARGETS) $(ARTIFACTS)
generate_definitions:
cpp $(CPP_FLAGS) $(WORKSPACE_LOC)empty_sourse.c -dM -E> c_defines_generated.h
# build the application
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_C:.c=.o)))
vpath %.c $(sort $(dir $(SOURCES_C)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_ASM:.S=.o)))
vpath %.S $(sort $(dir $(SOURCES_ASM)))
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
$(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR)
$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
$(SZ) $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(BIN) $< $@
$(BUILD_DIR):
mkdir -p $@
clean:
-rm -fR $(BUILD_DIR)
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
Для того, чтобы собрать сорцы достаточно открыть папку, где лежит корневой для данного проекта Makefile и исполнить вот скрипт (build_from_make.bat)
echo off
cls
make clean 2>&1 | tee clean_log.txt
make -j8 | tee build_log.txt
После этого в папке build будут лежать полный комплект артефактов (*.bin, *.hex, *.map, *.elf и прочее) для данной сборки.
Возможная конфигурация установленного софта
№ | Абсолютный путь | Назначение |
1 | C:\eclipse | Текстовый редактор |
2 | C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10 | Toolchain |
3 | C:\xpack-windows-build-tools-4.3.0–1 | build-tools |
5 | C:\СodeBaseWorkspace\CodeBase\src\AT32F435_437_Firmware_Library_EN_V2.1.5 | MCAL от вендора |
6 | C:\OpenOCD\bin | Отладочный Сервер |
7 | C:\Artery_ATLINK_Console_Win32-x86_64_V3.0.08\ATLink_Console.exe | Загрузчик прошивки в чип |
Фаза 6: Запись прошивки по SWD
Для того чтобы переместить вашу первую прошивку из жесткого диска в память микроконтроллера вам нужен программатор. Например AT-Link+
AT-Link+
Это в сущности переходник с USB на SWD. Внутри программатора AT-Link+ присутствует также переходник с USB на UART. Также есть кнопка, LEDы, звуко-излучатель.
Чтобы загрузить прошивку нужна утилита ATLink_Console.exe. Её тоже можно скачать с сайта Artery. Вот скрипт flash.bat для запуска процесса пере прошивки.
echo off
cls
set project_name=board_name_config_name_gcc_m
set project_dir=%cd%
echo project_dir=%project_dir%
set artefact_bin=%project_dir%\build\%project_name%.bin
echo artefact_bin=%artefact_bin%
set flash_tool=C:\Artery_ATLINK_Console_Win32-x86_64_V3.0.08\ATLink_Console.exe
set options=-device AT32F435ZMT7 -connect -p --dfap --depp -d --a 08000000 --fn %artefact_bin% --v -r
%flash_tool% %options%
pause
Это лог отчета об успешной загрузке прошивки.
Однако только загрузки прошивки вам будет не достаточно. В какой-то момент разработки прошивка зависнет. Перестанет мигать heart-Beat LED. Чтобы понять причину придется выполнять пошаговую отладку. А для этого нужен тандем: отладочный сервер (например OpenOCD)+ клиент отладки (GDB).
Фаза 7: Запуск Отладочного Сервера OpenOCD
Чтобы получить утилиту OpenOCD и конфиги для разных микропроцессоров (at32f435xM.cfg) специально адаптированные под MCU Artery Вам следует установить AT32_IDE для Windows. Вам не обязательно ей пользоваться. Просто установим, чтобы извлечь папку C:\AT32IDE\OpenOCD. AT32_IDE после установки ставит компилятор, Eclipse, плагины, отладочный сервер, HAL и Build Tools.
Надо составить .bat файл для запуска отладочного сервера OpenOCD.
echo off
cls
set openocd_path=C:\OpenOCD_Artery
set GDBServerPath=%openocd_path%\bin\openocd.exe
set options=-s %openocd_path%\scripts
-f %openocd_path%\scripts\interface\atlink.cfg
-f %openocd_path%\scripts\target\at32f435xM.cfg
call %GDBServerPath% %options%
Отладочный сервер позволит представить внешнее устройство на шине SWD (микроконтроллер) как процесс в операционной системе Windows.
Когда работает отладочный сервер, то на программаторе светится LED1.
Фаза 8: Запуск Отладочного Клиента
Чтобы была возможность делать пошаговую отладку надо чтобы прошивка была собрана с отладочными символами. Это опции компилятора -g3 -ggdb -gdwarf-2 -O0
Отладочный клиент это консольная утилита arm-none-eabi-gdb.exe
C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe
Именно она и делает пошаговую отладку. Эта утилита связана работает в тандеме с отладочным сервером openocd.exe. Данными они обмениваются через сокет с номером порта равным 3333.
Чтобы настроить пошаговую отладку надо открыть окно Debug Configuration. Щелкнуть на GDB OpenOCD Debugging. Кнопкой Search Project найти относительный путь к elf файлу
На вкладке Debugger прописать путь к утилите отладочному клиенту: C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe. Обратите внимание что я не ставлю опцию Start OpenOCD locally. Дело в том, что OpenOCD запускается отдельным *.bat скриптом. Важно чтобы был правильно указан порт: 3333
На вкладке Startup всё без имзенений
На вкладке Common ставим галочки как показано на скриншоте
Теперь можно запускать отладочный сервер и сразу после него отладочный клиент. Теперь прошивку можно отлаживать по шагам. Можно ставить точки останова, можно проникать внутрь функций, останавливать программу.
В принципе с GDB можно найти причину любого зависания прошивки. Если прошивка зависает значит надо запускать GDB отладку.
На самом деле Вам даже не нужен Eclipse для того чтобы осуществлять пошаговую отладку. Её можно делать прямо в консоли. Про это у меня есть отдельный текст https://habr.com/ru/articles/694708/.
Схема ToolChain (а)
Всё, что я написал выше можно представить вот этой схемой на одном мониторе. Это весь путь, который проходят исходники с момента написания до попадания во Flash память микроконтроллера.
Фаза 9: Отладка Прошивки
Работать вслепую, записывая прошивки и сидеть со скрещенными пальцами — это не наш метод. Надо как-то наблюдать за поведением прошивки. Одного мигающего LEDа, разумеется, не достаточно. Надо способ общаться с прошивкой подобно тому как мы привыкли в текстовых messenger (ах) общаться со своими приятелями.
Поэтому для разработки прошивок нужен текстовый интерфейс командной строки поверх UART. Подробнее про это у меня есть отдельный текст https://habr.com/ru/articles/694408/. CLI нужен хотя бы для того, чтобы убедиться, что инициализация прошивки произошла без ошибок.
CLI показывает, что прошивка стартовала! Можно сказать, что микроконтроллер Artery освоен. Далее уже дело техники.
Достоинства работы с микроконтроллерами Artery
1++ Это дёшево: 4к RUR за отладку и 4к RUR за программатор. Изучать Artery MCU вполне может себе позволить любой студент и даже школьник.
2++ Artery Technology — это санкционно-стойкий производитель микроконтроллеров! Artery не «перекрывает воздух» для своих клиентов, как это делают TI, Analog Devices и прочие.
3++ Сектора FLASH одного размера: 4kByte. Это лучше чем у STM32F4, где все сектора имели разный размер.
Что можно улучшить?
1--Пришлось самому прописывать makefile скрипты для папки AT32F435_437_Firmware_Library_EN_V2.1.5
ifneq ($(AT32F435_437_FIRMWARE_LIBRARY_EN_V2_1_5_MK_LOC),Y)
AT32F435_437_FIRMWARE_LIBRARY_EN_V2_1_5_MK_LOC=Y
HAL_AT32_DIR += $(WORKSPACE_LOC)/AT32F435_437_Firmware_Library_EN_V2.1.5
#@echo $(error HAL_AT32_DIR=$(HAL_AT32_DIR))
INCDIR += -I$(HAL_AT32_DIR)
INCDIR += -I$(HAL_AT32_DIR)/libraries
INCDIR += -I$(HAL_AT32_DIR)/libraries/cmsis
INCDIR += -I$(HAL_AT32_DIR)/libraries/cmsis/cm4
INCDIR += -I$(HAL_AT32_DIR)/libraries/cmsis/cm4/core_support
INCDIR += -I$(HAL_AT32_DIR)/libraries/drivers
INCDIR += -I$(HAL_AT32_DIR)/libraries/drivers/inc
ifeq ($(CLOCK),Y)
OPT += -DCRM_MODULE_ENABLED
SOURCES_C += $(HAL_AT32_DIR)/libraries/drivers/src/at32f435_437_crm.c
endif
...
...
endif
Хотелось бы, чтобы вендор предоставил библиотеку с уже прописанной индексацией файлов для сборки в виде *.mk файлов или даже с *.cmake файлами.
Итоги
Удалось научиться собирать, прошивать и отлаживать Cи-программы для микроконтроллера Artery.
Мы сами вручную аккуратно поставили всё, что нужно для cross компиляции Си программ микроконтроллеров. Вы конечно могли бы установить AT32 IDE как компьютерную игру и не задумываться ни о чём. Однако тогда бы вы не смогли контролировать ситуацию. Про это у меня есть отдельный текст https://habr.com/ru/articles/723054/
Как видите, если собирать Си программы из самостоятельно написанных MakeFile (ов), то Вам в общем-то, всё равно для какого там микроконтроллера собирается код (СС2640, STM32, nRF5304, Atrety, PowerPC или AVR). Система сборки из скриптов едина!
Это, пожалуй, главное преимущество сборки из make. Универсальность! У Вас одна кодовая база и просто отдельная папка для процессорного ядра, отдельная папка для конкретного микроконтроллера, отдельная папка для конкретной электронной платы и, собственно, папка самой сборки.
Всё разложено по полочкам. Всё остальное пере используется между всеми сборками по-максимому.
Сборка из make позволит Вам полностью ликвидировать дублирование конфигов, когда у вас 150 примерно одинаковых программных сборок для 35ти примерно одинаковых электронных плат с 7 мю примерно одинаковыми микроконтроллерами внутри. Конфиги просто наследуются из make скриптов, и всё. Если бы у Вас не было скриптов сборки, то вы замучились бы прощёлкивать мышкой одни и те же xml конфиги в IDE для разных сборок и плат.
При сборке прошивок из собственных Makefile (ов) один программист может поддерживать на плаву 100–200–300 проектов для 10–20–30 плат на основе 3–5–7 разных микроконтроллеров. С makefile (лами) многозадачность — это не проблема. Производительность бешеная!
Надеюсь этот текст поможет другим программистам микроконтроллеров беcшовно мигрировать на разработку прошивок для устройств с китайскими микроконтроллером AT32 внутри.
Если есть вопросы то пишите в комментариях.
Словарь
Акроним | Расшифровка |
ПО | Программное обеспечение |
SWD | serial wire debug interface |
AT | ARTERY |
ICP | in-circuit programmer |
MCAL | Microcontroller Abstraction Layer |
IDE | development |
CMSIS | Common Microcontroller Software Interface Standard |
OpenOCD | On-Chip Debugger |
LED | Light-emitting diode |
RISC | reduced instruction set computer |
CLI | Command-line interface |
UART | Universal asynchronous receiver-transmitter |
UNIX | UNiplexed Information Computing System |
FPU | Floating-point unit |
GCC | GNU Compiler Collection |
GDB | GNU Debugger |
GNU | GNU’s Not UNIX |
ARM | Advanced RISC Machine |
Links/URLs
Настройка ToolChain (а) для Win10+GCC+С+Makefile+ARM Cortex-Mx+GDB https://habr.com/ru/articles/673522/
Почему Нам Нужен UART-Shell? (или Добавьте в Прошивку Гласность) https://habr.com/ru/articles/694408/
Почему Важно Собирать Код из Скриптов https://habr.com/ru/articles/723054/
Официальный сайт компании Artery Technology https://www.arterytek.com/en/
ARTERY Technology https://www.youtube.com/@arterychip/videos
Установка Build Tools https://sourceforge.net/projects/gnuarmeclipse/files/Build Tools/
GCC, the GNU Compiler Collection https://gcc.gnu.org/
OpenOCD: руководство пользователя, начало https://microsin.net/programming/ARM/openocd-manual-part1.html
Сборка firmware для CC2652 из Makefile https://habr.com/ru/articles/726352/
Пошаговая GDB отладка ARM процессора из консоли в Win10 https://habr.com/ru/articles/694708/