![](https://habrastorage.org/getpro/habr/upload_files/1cf/b92/6bd/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 в натуре](https://habrastorage.org/getpro/habr/upload_files/1a9/3d8/f51/1a93d8f51d8259463470f87e30d999aa.png)
Запрограммировать микроконтроллер из-под IDE- это очень просто. Под силу даже школьнику. Сложно запрограммировать микроконтроллер из самостоятельно написанных скриптов сборки. В идеале из makefile(ов). Зачем это надо? Если отвечать коротко, то сборка проектов из make скриптов (или CMake) ликвидирует дублирование конфигов и, как следствие, способствует упрощению масштабирования и пере использования кодовой базы.
Постановка задачи
Научиться программировать микроконтроллер AT32F435ZMT7 компании Artery Technology.
![](https://habrastorage.org/getpro/habr/upload_files/b96/15e/c69/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).
Вот с этим предстоит работать.
![](https://habrastorage.org/getpro/habr/upload_files/c4a/ff2/e5c/c4aff2e5c1389916d4f9e3866ab4eb20.png)
Что надо из документации?
Для того чтобы запрограммировать микроконтроллер 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 и настроить предпочтения по текстовому редактору.
![](https://habrastorage.org/getpro/habr/upload_files/24a/a07/f72/24aa07f727f564e4ee1eea12614ea807.png)
Сразу хочу отметить, что в этом тексте я буду использовать 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()
![](https://habrastorage.org/getpro/habr/upload_files/266/611/f9d/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-- из адреса 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 подобрать параметры и утилита сама предупредит и исправит ошибки.
![](https://habrastorage.org/getpro/habr/upload_files/8f3/f78/735/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();
}
Активация 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+](https://habrastorage.org/getpro/habr/upload_files/c0e/2e1/53d/c0e2e153d9e43742cd115f4616706c6c.png)
Это в сущности переходник с USB на SWD. Внутри программатора AT-Link+ присутствует также переходник с USB на UART. Также есть кнопка, LEDы, звуко-излучатель.
Чтобы загрузить прошивку нужна утилита ATLink_Console.exe. Её тоже можно скачать с сайта Artery.
![](https://habrastorage.org/getpro/habr/upload_files/7de/0f5/01d/7de0f501dd254d6d35e192729ff60c73.png)
Вот скрипт 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.
![](https://habrastorage.org/getpro/habr/upload_files/ec3/d41/6b8/ec3d416b862b297e9fe4633229c60bbb.png)
Однако только загрузки прошивки вам будет не достаточно. В какой-то момент разработки прошивка зависнет. Перестанет мигать heart-beat LED. Чтобы понять причину придется выполнять пошаговую отладку. А для этого нужен тандем: отладочный сервер (например OpenOCD)+ клиент отладки (GDB).
Фаза 8: Запуск Отладочного Сервера OpenOCD
Перед запуском отладочного сервера очень важно, чтобы pin(ы) микроконтроллера (SWDIO SWCLK SWO JTDI) к которому подключена шина SWD или JTAG в самой прошивке никак не были проинициализированы. Иначе отладочный сервер просто не сможет подключиться к микроконтроллеру по SWD.
Чтобы получить утилиту OpenOCD и конфиги для разных микропроцессоров (at32f435xM.cfg) специально адаптированные под MCU Artery Вам следует установить AT32_IDE для Windows. Да, это так... При этом вам не обязательно пользоваться этой IDE. Просто установим, чтобы извлечь папку C:\AT32IDE\OpenOCD. AT32_IDE после установки ставит компилятор, Eclipse, плагины, отладочный сервер, HAL и Build Tools (make).
Для работы 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 того процесса который занял нужный отладочному серверу порт. Например PID-5800.
Определить соответствие между PID процесса и именем программы можно командой
tasklist
Очевидно что следует остановить процессы, которые заняли нужные порты. Для этого открывает cmd из-под администратора и выполняем команду
taskkill /F /T /PID 5800
![](https://habrastorage.org/getpro/habr/upload_files/16c/0be/b72/16c0beb72b57012fd8035c427ede51db.png)
Остановку также можно произвести в диспетчере устройств мышкой зная имя процесса.
Надо составить .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.
![](https://habrastorage.org/getpro/habr/upload_files/d72/a8a/a84/d72a8aa84fb6146ac50689190a41b4fe.png)
Когда работает отладочный сервер, то на программаторе светится LED1.
![](https://habrastorage.org/getpro/habr/upload_files/020/216/d67/020216d67ff90aaf3ebc7d4af92094ff.png)
Возможные неисправности с отладочным сервером
Если при запуске GDB сервера сборки появится ошибка
Error: Error connecting DP: cannot read IDR, то надо отчистить всю прошивку утилитой Artery ICP и подключиться на пустой чип снова. Ошибка исчезнет.
Фаза 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 файлу
![](https://habrastorage.org/getpro/habr/upload_files/063/bc4/d96/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
![](https://habrastorage.org/getpro/habr/upload_files/187/72a/272/18772a272a82533c568d73b22f9a7bb6.png)
На вкладке Startup всё без имзенений
![](https://habrastorage.org/getpro/habr/upload_files/3a7/4fc/2af/3a74fc2afc7e4083e89ee853b66066cb.png)
На вкладке Common ставим галочки как показано на скриншоте
![](https://habrastorage.org/getpro/habr/upload_files/546/42a/d72/54642ad7220b371ab59e25f360b50aba.png)
Теперь можно запускать отладочный сервер и сразу после него отладочный клиент. Теперь прошивку можно отлаживать по шагам. Можно ставить точки останова, можно проникать внутрь функций, останавливать программу.
![](https://habrastorage.org/getpro/habr/upload_files/859/024/0ee/8590240ee924c265d9b73352b456265d.png)
В принципе с GDB можно найти причину любого зависания прошивки. Если прошивка зависает значит надо запускать GDB отладку.
На самом деле Вам даже не нужен Eclipse для того чтобы осуществлять пошаговую отладку. Её можно делать прямо в консоли. Про это у меня есть отдельный текст.
Схема ToolChain(а)
Всё, что я написал выше можно представить вот этой схемой на одном мониторе. Это весь путь, который проходят исходники с момента написания до попадания во Flash память микроконтроллера.
![](https://habrastorage.org/getpro/habr/upload_files/77e/da7/4a8/77eda74a86aa41f8fd18bf3b70866de5.jpg)
Фаза 10: Отладка Прошивки
Работать вслепую, записывая прошивки и сидеть со скрещенными пальцами - это не наш метод. Надо как-то наблюдать за поведением прошивки. Одного мигающего LEDа, разумеется, не достаточно. Надо способ общаться с прошивкой подобно тому как мы привыкли в текстовых messenger(ах) общаться со своими приятелями.
Поэтому для разработки прошивок нужен текстовый интерфейс командной строки поверх UART. Подробнее про это у меня есть отдельный текст https://habr.com/ru/articles/694408/. CLI нужен хотя бы для того, чтобы убедиться, что инициализация прошивки произошла без ошибок.
![](https://habrastorage.org/getpro/habr/upload_files/323/cf5/885/323cf58850eaa71784feed9a56284e2d.png)
Как известно, любая техническая система ломается либо в самом начале, либо в самом конце. Поэтому лог инициализации крайне важен. У вас могут быть, например, неправильные конфиги. Это надо проверить через 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 для разных сборок и плат. Уж поверь мне, я знаю, что пишут. Поддерживал как-то 57 конфигураций в IAR. Это был сущий кошмар.
При сборке прошивок из собственных 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
GCC, the GNU Compiler Collection
OpenOCD: руководство пользователя, начало