ToolChain: Настройка Сборки Прошивок для Микроконтроллеров Artety из MakeFile

#make #ARM #Artery #CLI #GDB #GCC #Eclipse #C #Windows10 #OpenOCD

1cfb926bd561302e30c50facc2aa91d3.png

Настало время освоить очередное семейство микроконтроллеров. От компании 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 в натуре

Вот так выглядит микроконтроллер AT32F435ZMT7 в натуре

Запрограммировать микроконтроллер из-под IDE- это очень просто. Под силу даже школьнику. Сложно запрограммировать микроконтроллер из самостоятельно написанных скриптов сборки. В идеале из makefile(ов). Зачем это надо? Если отвечать коротко, то сборка проектов из make скриптов (или CMake) ликвидирует дублирование конфигов и, как следствие, способствует упрощению масштабирования и пере использования кодовой базы.

Постановка задачи:

Научиться программировать микроконтроллер AT32F435ZMT7 компании Artery Technology.

b9615ec69155171c64dc017aae2d7a5d.png

Научиться собирать детерминированные прошивки, написанные на языке программирования Си компилятором 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 и настроить предпочтения по текстовому редактору.

24aa07f727f564e4ee1eea12614ea807.png

Сразу хочу отметить, что в этом тексте я буду использовать 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 ()

266611f9dbd2d48fe4c8fbf668896bf4.png

Есть очень важный файл 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 подобрать параметры и утилита сама предупредит и исправит ошибки.

8f3f7873523aed8a8ebea390518844d5.png

Я решил поработать на частоте 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+

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

Это лог отчета об успешной загрузке прошивки.

ec3d416b862b297e9fe4633229c60bbb.png

Однако только загрузки прошивки вам будет не достаточно. В какой-то момент разработки прошивка зависнет. Перестанет мигать 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.

d72a8aa84fb6146ac50689190a41b4fe.png

Когда работает отладочный сервер, то на программаторе светится LED1.

020216d67ff90aaf3ebc7d4af92094ff.png

Фаза 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 файлу

063bc4d96bead341cc161202c0ff39c5.png

На вкладке Debugger прописать путь к утилите отладочному клиенту: C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin\arm-none-eabi-gdb.exe. Обратите внимание что я не ставлю опцию Start OpenOCD locally. Дело в том, что OpenOCD запускается отдельным *.bat скриптом. Важно чтобы был правильно указан порт: 3333

18772a272a82533c568d73b22f9a7bb6.png

На вкладке Startup всё без имзенений

3a74fc2afc7e4083e89ee853b66066cb.png

На вкладке Common ставим галочки как показано на скриншоте

54642ad7220b371ab59e25f360b50aba.png

Теперь можно запускать отладочный сервер и сразу после него отладочный клиент. Теперь прошивку можно отлаживать по шагам. Можно ставить точки останова, можно проникать внутрь функций, останавливать программу.

8590240ee924c265d9b73352b456265d.png

В принципе с GDB можно найти причину любого зависания прошивки. Если прошивка зависает значит надо запускать GDB отладку.

На самом деле Вам даже не нужен Eclipse для того чтобы осуществлять пошаговую отладку. Её можно делать прямо в консоли. Про это у меня есть отдельный текст https://habr.com/ru/articles/694708/.

Схема ToolChain (а)

Всё, что я написал выше можно представить вот этой схемой на одном мониторе. Это весь путь, который проходят исходники с момента написания до попадания во Flash память микроконтроллера.

77eda74a86aa41f8fd18bf3b70866de5.jpg

Фаза 9: Отладка Прошивки

Работать вслепую, записывая прошивки и сидеть со скрещенными пальцами — это не наш метод. Надо как-то наблюдать за поведением прошивки. Одного мигающего LEDа, разумеется, не достаточно. Надо способ общаться с прошивкой подобно тому как мы привыкли в текстовых messenger (ах) общаться со своими приятелями.

Поэтому для разработки прошивок нужен текстовый интерфейс командной строки поверх UART. Подробнее про это у меня есть отдельный текст https://habr.com/ru/articles/694408/. CLI нужен хотя бы для того, чтобы убедиться, что инициализация прошивки произошла без ошибок.

323cf58850eaa71784feed9a56284e2d.png

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/

© Habrahabr.ru