Как стать автором
Обновить

ToolChain: Настройка сборки прошивок для микроконтроллеров Artery из Makefile

Уровень сложностиПростой
Время на прочтение21 мин
Количество просмотров3.8K

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

Научиться собирать детерминированные прошивки, написанные на языке программирования Си компилятором GCC, собирать проекты из написанных вручную Makefile(лов).

Собрать минималистическую прошивку с частотой процессора 100MHz со cрабатывающим раз в 1ms системным таймером SysTick, мигающим раз в секунду heart-beat LED(ом) и интерфейсом командной строки CLI поверх UART на битовой скорости 460800 Bit/s.

То есть, подготовить то самое базовое программно-аппаратное окружение на основе которого происходит дальнейшая полноценная разработка и отладка какого бы то ни было приложения на микроконтроллере Artery.

Говоря метафорично, надо создать коробочку для микроконтроллерного приложения.

То есть, довести проект до ортодоксально канонической формы (Makefile, GCC, GDB, NoRTOS, HeartBeat-LED, UART-CLI, NVRAM).

Вот с этим предстоит работать.

Что надо из документации?

Для того чтобы запрограммировать микроконтроллер AT32F435ZMT7 Вам придется прошерстить вот эту стопку официальных документов.

Название документа

Ver

pages

Vendor

1

Eclipse with GCC

2.0.0

23

Artery

7

Cortex-M4 Technical Reference Manual

r0p1

121

ARM

2

AT32F435/437 Series Datasheet

2.11

120

Artery

3

AT32F435/437 Series Reference Manual

2.05

714

Artery

4

AT-Link User Manual

2.1.2

38

Artery

5

ICP Programmer Manual

2.07

50

Artery

6

AT-Link Console User Manual

2.08

15

Artery

Что надо из оборудования?

Оборудование

Назначение

1

Электронная плата (PCB) с 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 можно скачать отсюда.

Мне Eclipse нужен только как печатающая машинка. Я даже не планирую использовать плагины Eclipse.

После запуска Eclipse желательно сразу зайти в Window->Preferences->General->Editors->Text Editors и настроить предпочтения по текстовому редактору.

Сразу хочу отметить, что в этом тексте я буду использовать Eclipse без плагинов для ARM GCC, который умеет генерировать makefile(ы)

Ещё Eclipse мне понадобится для пошаговой отладки исходного кода, так как Eclipse способен ассоциироваться с GDB клиентом. Подробнее про это можно почитать тут.

Фаза 2: Установить компилятор GCC

Компилятор GCC это консольная утилита, которая преобразует код на Си в бинарный нативный код для конкретного микропроцессора. Можно скачать компилятор 2016 года тут.

Или лучше взять более новый компилятор с официального сайта ARM.

Однако там вам придется зарегистрироваться на сайте, придумать логин и пароль.

Вероятно, что у Вас на 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. Их можно установить разом вот по этой ссылке.

Путь к 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-- из адреса 0x00000000 получает значение верхушки указателя стека. По мере роста адрес стека будет уменьшаться.

3-- из адреса 0x00000004 получает начальное значение для программного счетчика PC.

Дело в том, что по нулевому адресу прописан заводской загрузчик. Его роль всегда в том чтобы глядя на Boot пины решить откуда грузиться дальше. Тут есть три варианта.

BOOT1

BOOT0

Boot source

Start Address

0

0

CODE starts from the main Flash memory

0x08000000

1

0

CODE starts from the main Flash memory

0x08000000

0

1

CODE starts from Boot code

0x00000100

1

1

CODE starts from SRAM

0x20000000

Настройка тактирования ядра

Программирование любого 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();
}

Активация FPU

Скорее всего в Вашей прошивке будет какое-то вычисление с плавающей точкой. Например расчет пред-делителя и периода для аппаратного таймера на основе частоты тактирования пред-делителя системной шины и желаемого периода вызова обработчиков прерываний по переполнению таймера.

Если ничего не предпринимать, то при возвращении из функции числа типа double прошивка свалится в HardFault. В связи с этим, чтобы прошивка не зависала, надо добавить активацию FPU. К счастью вендор предоставляет код, который это делает за вас. Называется эта функция SystemInit

/*
 *   setup the microcontroller system initialize the flash interface.
 *   this function should be used only after reset.
 */
void SystemInit (void) {
#if defined (__FPU_USED) && (__FPU_USED == 1U)
  SCB->CPACR |= ((3U << 10U * 2U) |         /* set cp10 full access */
                 (3U << 11U * 2U)  );       /* set cp11 full access */
#endif
.....
.....

}

Небольшое резюме по файлам

Буквально насколько слов про файлы, которые будут в любом проекте с микроконтроллером семейства 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 есть в примерах от вендора.

Фаза 6: Сборка проекта из Makefile

Для того, чтобы собрать Си программу надо как-то объяснить компилятору gcc, что надо собрать и из каких файлов. Если вы программировали на Eclipse c плагинами, то там вы вручную настраивали xml, а плагины генерировали из xml make файлы. Способ сборки через плагины Eclipse приводит потере значительной части контроля над проектом.

Более предпочтительный вариант это всё самому писать make файлы. В этом нет ничего сложного. Это позволит в полной степени контролировать процесс сборки и наследовать конфиги. Makefile(лы) - это как остров дерева, который держит листья.

Далее представлен небольшой экскурс в организацию 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 скриптов строчные или заглавные буквы в названиях *.mk файлов не имеют значения. Это значит, что UART.mk и uart.mk будут восприняты как один и тот же файл! В связи с этим следует именовать все *.mk файлы уникальным именем. Это особенно важно, если Вы вдруг решите хранить все *.mk файлы в одной отдельной папке репозитория.

Самое главное в 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 и прочее) для данной сборки.

При сборке из Make *.c файл не будет собираться просто от того, что он лежит в папке. Чтобы *.c файл участвовал в сборке его следует добавить (проиндексировать) в скриптах Make.

Возможная конфигурация установленного софта

Абсолютный путь

Назначение

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

Загрузчик прошивки в чип

Фаза 7: Запись прошивки по SWD

Для того чтобы переместить вашу первую прошивку из жесткого диска в память микроконтроллера вам нужен программатор. Например AT-Link+

AT-Link+
AT-Link+

Это в сущности переходник с USB на SWD. Внутри программатора AT-Link+ присутствует также переходник с USB на UART. Также есть кнопка, LEDы, звуко-излучатель.

Чтобы загрузить прошивку нужна утилита ATLink_Console.exe. Её тоже можно скачать с сайта Artery.

Вот скрипт flash.bat для запуска процесса пере прошивки.

echo off
cls

call SetUpEnvVars.bat

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=ATLink_Console.exe
set options=-device AT32F435ZMT7 -connect -p --dfap --depp -d --a 08000000 --fn %artefact_bin% --v  -r
%flash_tool% %options%
pause

Чтобы Windows интерпретатор cmd обнаружил утилиту ATLink_Console.exe надо вручную добавить её в переменную PATH. Скрипт настройки окружения SetUpEnvVars.bat может быть таким

echo off

set PATH=C:\Artery_ATLINK_Console_Win32-x86_64_V3.0.08;%PATH%
echo %PATH%

После запуска скрипта flash.bat высыплется вот такой лог отчета об успешной загрузке прошивки в on-chip память микроконтроллера AT32F435ZMT7.

Однако только загрузки прошивки вам будет не достаточно. В какой-то момент разработки прошивка зависнет. Перестанет мигать heart-beat LED. Чтобы понять причину придется выполнять пошаговую отладку. А для этого нужен тандем: отладочный сервер (например OpenOCD)+ клиент отладки (GDB).

Фаза 8: Запуск Отладочного Сервера OpenOCD

Чтобы получить утилиту OpenOCD и конфиги для разных микропроцессоров (at32f435xM.cfg) специально адаптированные под MCU Artery Вам следует установить AT32_IDE для Windows. Вам не обязательно ей пользоваться. Просто установим, чтобы извлечь папку C:\AT32IDE\OpenOCD. AT32_IDE после установки ставит компилятор, Eclipse, плагины, отладочный сервер, HAL и Build Tools.

Для работы OpenOCP надо сделать так, чтобы были свободны порты с номерами: 6666, 4444 и 3333

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : Listening on port 3333 for gdb connections

Просмотреть состояние портов можно в cmd консоли утилитой
netstat -aon

Определить соответствие между PID процесса и именем программы можно командой
tasklist

Следует остановить процессы, которые заняли нужные порты. Это можно произвести в диспетчере устройств зная имя процесса.

Надо составить .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.

Фаза 9: Запуск Отладочного Клиента

Чтобы была возможность делать пошаговую отладку надо чтобы прошивка была собрана с отладочными символами. Это опции компилятора -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 для того чтобы осуществлять пошаговую отладку. Её можно делать прямо в консоли. Про это у меня есть отдельный текст.

Схема ToolChain(а)

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

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

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

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

Как известно, любая техническая система ломается либо в самом начале, либо в самом конце. Поэтому лог инициализации крайне важен. У вас могут быть, например, неправильные конфиги. Это надо проверить через CLI.

CLI показывает, что прошивка стартовала! Можно сказать, что микроконтроллер Artery освоен. Далее уже дело техники.

Достоинства работы с микроконтроллерами Artery

1++ Это дёшево: 4к RUR за отладку и 4к RUR за программатор. Изучать Artery MCU вполне может себе позволить любой студент и даже школьник.

2++ Artery Technology - это санкционно-стойкий производитель микроконтроллеров! Artery не "перекрывает воздух" для своих клиентов, как это делают TI, Analog Devices и прочие.

3++ Сектора FLASH одного размера: 4kByte. Это лучше чем у STM32F4, где все сектора имели разный размер.

Что можно улучшить?

1--У Artery микроконтроллеров, как и у STM аппаратные подсистемы (I2S, UART, CAN и т п) выведены только на очень ограниченный набор пинов. Если схемотехники ошибутся в топологии, то вы не сможете пользоваться I2S UART или I2S. В этом плане Artery сильно проигрывают микроконтроллерам от Nordic или TI где, например, аппаратный I2S_BCLK можно назначить вообще на любой пин.

2--Было бы лучше если Artery поставляли примеры в виде сборок из скриптов (CMake Make Ningia). А то Eclipse с плагинами приходится настраивать в основном только мышкой, что неудобно и медленно. Пришлось самому прописывать 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, Artery, 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

FPU

Floating-point unit

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

Почему Нам Нужен UART-Shell? (или Добавьте в Прошивку Гласность)

Почему важно собирать код из скриптов

Официальный сайт компании Artery Technology

ARTERY Technology

Установка Build Tools

GCC, the GNU Compiler Collection

OpenOCD: руководство пользователя, начало

Сборка firmware для CC2652 из Makefile

Пошаговая GDB отладка ARM процессора из консоли в Win10

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы программировали микроконтроллеры Artery?
27.59% да8
72.41% нет21
Проголосовали 29 пользователей. Воздержавшихся нет.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Как вы собираете прошивки?
30% из самостоятельно написанных Make скриптов9
20% из самостоятельно написанных CMake скриптов6
63.33% мне IDE собирает проект19
0% из самостоятельно написанных Ninja скриптов0
Проголосовали 30 пользователей. Воздержавшихся нет.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Каким отладочным сервером Вы обычно пользуетесь?
20.83% OpenOCD5
41.67% SEGGER J-link10
37.5% ST-LINK_gdbserver9
29.17% другой7
Проголосовали 24 пользователя. Воздержались 3 пользователя.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
На каком языке программирования Вы больше программируете микроконтроллеры?
90.91% C30
15.15% C++5
3.03% Rust1
3.03% только Assembler1
0% другой0
Проголосовали 33 пользователя. Воздержавшихся нет.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы добавляете в прошивки для отладки интерфейс командной строки (CLI,Shell,TUI)?
44.83% да13
55.17% нет16
Проголосовали 29 пользователей. Воздержался 1 пользователь.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Как вы чаще организовываете программу?
30% RTOS9
70% NoRTOS21
Проголосовали 30 пользователей. Воздержался 1 пользователь.
Теги:
Хабы:
Всего голосов 13: ↑10 и ↓3+7
Комментарии11

Публикации

Истории

Работа

Программист С
35 вакансий
DevOps инженер
43 вакансии

Ближайшие события

One day offer от ВСК
Дата16 – 17 мая
Время09:00 – 18:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область