VSCode+CMake+openocd для программирования и отладки ch32
Вступление
На хабре было немало статей, посвященных настройке окружения для разработки под микроконтроллеры stm32 и другие платформы. Кто-то предпочитает использовать специальные среды разработки (CubeIDE, Keil), кто-то расширения для IDE «общего назначения» (PlatformIO для VSCode, VisualGDB для Microsoft Visual Studio), однако все это слабо переносимо между инструментами.
Для языков программирования C и С++ де-факто стандартом описания правил сборки проекта является CMake, который, пусть и не так быстро, как хотелось бы, набирает популярность и среди embedded-разработчиков. Например, для контроллеров STM32 существует замечательный проект ObKo/stm32-cmake, позволяющий одной командой загрузить библиотеки от ST (CMSIS, HAL), сгенерировать LD-скрипт и многое другое.
В настоящее время весьма активно развивается архитектура RISC-V, на рынке появляются и соответствующие микроконтроллеры. Например, свежие версии популярного SoC ESP32 имеют ядра RISC-V. В свою очередь, с привычными многим Stm32 конкурирует линейка ch32 китайского производителя Nanjing Qinheng Microelectronics (WCH). Про них мы сегодня и поговорим в разрезе подготовки рабочего места.
MounRiver Studio
Производитель предлагает собственную среду разработки на базе (а как же иначе?) Eclipse, скачать которую можно на официальном сайте. В целом, пакет очень похож на CubeIDE, имея в составе компилятор, утилиту для прошивки устройства, форк openocd и набор шаблонов проектов. В целом, с учетом особенностей Eclipse, вполне пригодный инструмент, в котором все настроено и работает из коробки.
Более того, установить MounRiver все равно, скорее всего, придется, поскольку openocd, startup-файлы и прочие необходимые для разработки файлы в другом месте не найти (по крайней мере на сайте wch и в их репозитории).
Установка необходимых пакетов
Для настройки окружения понадобится не так много: тулчейн, CMake, система сборки (Ninja) и набор подготовленных cmake-утилит. Для прошивки и отладки понадобится правильная версия openocd. Ну и, конечно, весь зоопарк средств удобно интегрировать в VSCode, что потребует установки нескольких расширений.
Процесс будет описан для ОС Windows, однако из-за того, что вручную потребуется установить, по сути, три утилиты, воспроизвести весь процесс на любой другой системе элементарно.
Установка GCC
Тулчейн можно было бы взять из того же MounRiver, но зачастую версия компилятора в подобных инструментах отстает от «оригинальной». Например, в последней на момент написания статьи версии 1.91 вообще два набора тулчейна: riscv-none-embed-gcc и пришедший ему на замену riscv-none-elf-gcc (версии GCC 8.2.0 и 12.2.0 соответственно). Хотя стоит признать, что в этом смысле WCH молодцы, и назвать GCC 12.2.0 старым все-таки нельзя.
Версия компилятора в MounRiver Studio
Из того, что удалось найти в интернете в процессе поиска, наиболее актуальным оказался репозиторий xpack-dev-tools, где последний из релизов датируется сентябрем 2023 года и основан на GCC 13.2.0. Надо признать, что для ARM нужные пакеты можно найти на официальном сайте.
Для Windows необходимо загрузить соответствующий пакет xpack-riscv-none-elf-gcc-13.2.0–2-win32-x64.zip и распаковать архив в удобное место. У меня это «C:/Program Files»
Далее следует прописать путь до директории bin в переменную PATH, чтобы при сборке нашёлся компилятор. Проверить, что всё сделано правильно, можно набрав команду вывода версии компилятора в командной строке и убедившись, что она корректно отработала:
Проверка версии GCC
C:\>riscv-none-elf-gcc --version
riscv-none-elf-gcc (xPack GNU RISC-V Embedded GCC x86_64) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Установка CMake
С CMake все просто, необходимо скачать с официального сайта и установить. В процессе установки выбрать опцию «добавить в переменную PATH». Как и на предыдущем шаге, для проверки корректности установки можно вызвать cmake с опцией --version:
Проверка версии CMake
C:\>cmake --version
cmake version 3.28.0-rc5
CMake suite maintained and supported by Kitware (kitware.com/cmake).
Установка Ninja
Здесь тоже всё крайне просто, достаточно скачать релиз с репозитория, распаковать в произвольную директорию и добавить ее в переменную PATH. Чтобы убедиться, что все в порядке, снова достаточно запросить версию:
Проверка версии Ninja
C:\>ninja --version
1.11.1
Установка openocd
Для прошивки устройства можно использовать WCH-LinkUtility (скачать можно на официальном сайте), которая по функционалу и даже внешнему виду похожа на STM32 ST-LINK Utility. Как и в случае с IDE, скачать её стоит хотя бы ради драйвера программатора WCHLink-E, который находится в директории Drv_Link (файл WCHLinkDrv_WHQL_S.exe).
Окно программы WCH-LinkUtility
Однако, разумеется, для полноценной разработки нужны функции внутрисхемной отладки, одним из наиболее популярным средством которой является OpenOCD). Оригинальная версия утилиты не подходит, поэтому одним из возможных решений является просто взять версию из MounRiver (небольшой тред по теме), выполнив простые действия:
Скопировать директорию
/toolchain/OpenOCD в произвольное место (можно и не копировать, но тогда придется не удалять MounRiver).Скопировать файл bin/wch-riscv.cfg в директорию share/openocd/scripts/interface (по умолчанию openocd ищет скрипты в директории share/openocd/scripts, почему разработчики поместили его рядом с бинарником — непонятно). Также в этой версии дистрибутива почему-то продублирована директория со скриптами, она также присутствует в корневой папке.
Для тех, кто не желает установить и попробовать MounRiver, залил архив с openocd на Я.Диск.
Проверить корректность можно вызовом openocd с передачей в качестве аргумента пути до соответствующего файла конфигурации программатора.
Вывод без подключенного программатора
C:\>openocd -f "interface/wch-riscv.cfg"
Open On-Chip Debugger 0.11.0+dev-02415-gfad123a16-dirty (2023-09-22-10:36)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'sdi'
Warn : Transport "sdi" was already selected
Ready for Remote Connections
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Error: WLink Open Error
Вывод с подключенным программатором и микроконтроллером
C:\>openocd -f "interface/wch-riscv.cfg"
Open On-Chip Debugger 0.11.0+dev-02415-gfad123a16-dirty (2023-09-22-10:36)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'sdi'
Warn : Transport "sdi" was already selected
Ready for Remote Connections
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : WCH-LinkE mode:RV version 2.11
Info : wlink_init ok
Info : clock speed 6000 kHz
Info : [wch_riscv.cpu.0] datacount=2 progbufsize=8
Info : [wch_riscv.cpu.0] Examined RISC-V core; found 1 harts
Info : [wch_riscv.cpu.0] XLEN=32, misa=0x40800014
[wch_riscv.cpu.0] Target successfully examined.
Info : starting gdb server for wch_riscv.cpu.0 on 3333
Info : Listening on port 3333 for gdb connections
Загрузка скриптов CMake
Имея опыт программирования на Stm32, в том числе используя CMake в связке с проектом stm32-cmake, я хотел обеспечить максимальную схожесть, в результате чего появились проект с cmake-утилитами wch_cmake и wch_libs — сборник исходных файлов, взятых из шаблонов проектов MounRiver и аккуратно сложенных вместе.
Использование wch_cmake аналогично использованию stm32-cmake, о котором весьма подробно написано в статье «Прошивка и отладка STM32 в VSCode под Windows».
Сборка проекта
Создадим простой демо-проект. В директорию проекта нужно скопировать папку cmake (в будущем, конечно, проще использовать git submodule), создать файл CMakeLists.txt и файл исходного кода, назовем его main.c и поместим в поддиректорию src.
Структура проекта
C:\dev\wch_demo>tree /f
Структура папок
Серийный номер тома: 088E-DBE8
C:.
│ CMakeLists.txt
│
├───cmake
│ │ FindCMSIS.cmake
│ │ FindSPL.cmake
│ │ wch_gcc.cmake
│ │
│ └───ch32
│ common.cmake
│ devices.cmake
│ linker_ld.cmake
│ utilities.cmake
│ v0.cmake
│ v2.cmake
│
└───src
main.c
В main.c напишем простую программу моргания диодом (код совпадает с кодом из примера examples/cmsis_blink/src/main.c):
Исходный программы (файл src/main.c)
#include "ch32v00x.h"
#define LED_PORT GPIOD
#define LED_PIN 2
#define LED_BUS RCC_IOPDEN
int main()
{
// Enable GPIOD
RCC->APB2PCENR |= LED_BUS;
// GPIO D2 Push-Pull
LED_PORT->CFGLR &= ~(0xf << (4 * LED_PIN));
LED_PORT->CFGLR |= GPIO_CFGLR_MODE0_0 << (4 * LED_PIN);
while (1)
{
LED_PORT->BSHR = (1 << LED_PIN); // Turn on PD2
for (int i = 0; i < 4000000; ++i)
__asm("nop");
LED_PORT->BSHR = (1 << (16 + LED_PIN)); // Turn off PD2
for (int i = 0; i < 4000000; ++i)
__asm("nop");
}
}
CMakeLists.txt аналогичен тому, что должно быть для связки stm32+stm32-cmake, поэтому переход должен быть безболезненным.
Правила сборки (файл CMakeLists.txt)
cmake_minimum_required(VERSION 3.16)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/wch_gcc.cmake)
project(wch_demo CXX C ASM)
# Загрузить библиотеки с github
ch32_fetch_cmsis(V0)
# Подготовить компонент CMSIS для серии CH32V0
find_package(CMSIS COMPONENTS CH32V0 REQUIRED)
set(PROJECT_SOURCES
src/main.c)
add_executable(wch_demo ${PROJECT_SOURCES})
# Слинковать прошивку с CMSIS для CH32V003F4,
# добавить опции -specs=nano.spec и -specs=nosys.specs
target_link_libraries(wch_demo CMSIS::CH32::V003F4 CH32::Nano CH32::NoSys)
# Отобразить данные о размерах
ch32_print_size_of_target(wch_demo)
# Сгенерировать hex
ch32_generate_hex_file(wch_demo)
Все готово к сборке проекта, команды всем известны:
Создание директории для файлов сборки
C:\dev\wch_demo>mkdir build && cd build
Запуск CMake
C:\dev\wch_demo\build>cmake -G "Ninja" ..
-- No CH32_TOOLCHAIN_PATH specified, using default: /usr
-- No CH32_TARGET_TRIPLET specified, using default: riscv-none-elf
-- The CXX compiler identification is GNU 13.2.0
-- The C compiler identification is GNU 13.2.0
-- The ASM compiler identification is GNU
-- Found assembler: C:/Program Files/xpack-riscv-none-elf-gcc-13.2.0-2/bin/riscv-none-elf-gcc.exe
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/xpack-riscv-none-elf-gcc-13.2.0-2/bin/riscv-none-elf-g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/xpack-riscv-none-elf-gcc-13.2.0-2/bin/riscv-none-elf-gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Search for CMSIS families: CH32V0
-- Found CMSIS: C:/dev/wch_demo/build/_deps/ch32-libs-src/CH32V00x/core;C:/dev/wch_demo/build/_deps/ch32-libs-src/CH32V00x/include found components: CH32V0
-- Configuring done (5.9s)
-- Generating done (0.0s)
-- Build files have been written to: C:/dev/wch_demo/build
Сборка проекта
C:\dev\wch_demo\build>cmake --build .
[6/6] Target Sizes:
text data bss dec hex filename
1040 0 512 1552 610 C:/dev/wch_demo/build/wch_demo.elf
В директории build можно найти файл wch_demo.hex, который готов к загрузке на микроконтроллер, сделаем это пока через WCH-LinkUtility (запускать openocd будем позже, для отладки).
Прошивка с WCH-LinkUtility
Успешная прошивка контроллера
Сборка и загрузка проекта в VSCode
В первую очередь в VSCode нужно установить следующие расширения:
Команда прошивки контроллера
Добавим возможность прошивать контроллер из VSCode, для этого понадобится кастомизировать задачи. Нужно нажать сочетание клавиш Ctrl+Shift+P
, выбрать пункт Tasks: Configure Task и cmake build. В директории .vscode будет создан файл tasks.json, в который нужно внести некоторые изменения.
Исходное содержимое tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "cmake",
"label": "CMake: build",
"command": "build",
"targets": [
"all"
],
"group": "build",
"problemMatcher": [],
"detail": "CMake template build task"
}
]
}
Дополняем файл командой прошивки контроллера. Также придется добавить input для замены слешей на прямые (в линуксе этого делать, конечно, не нужно).
Конечное содержимое tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "cmake",
"label": "CMake: build",
"command": "build",
"targets": [
"all"
],
"group": "build",
"problemMatcher": [],
"detail": "CMake template build task"
},
{
"type": "shell",
"label": "flash",
"group": "build",
"dependsOn": "CMake: build",
"command": "openocd -f interface/wch-riscv.cfg -c \"program ${input:workspaceFolderForwardSlash}/build/${workspaceFolderBasename}.hex verify reset exit\"",
}
],
"inputs": [
{
"id": "workspaceFolderForwardSlash",
"type": "command",
"command": "extension.commandvariable.transform",
"args": {
"text": "${workspaceFolder}",
"find": "\\\\",
"replace": "/",
"flags": "g"
}
}
]
}
Для внимательных, кто заметил отсутствие »-f target/XXX.cfg»
Знакомые с openocd могут заметить, что указан только скрипт interface и отсутствует нужный скрипт из target. Как я понял из чтения файла конфигурации wch-riscv.cfg, авторы зачем-то совместили два скрипта в один. В целом, не страшно, хотя когда я для эксперимента проделывал всё в Visual Studio с расширением VisualGDB, где нужно обязательно указать оба скрипта (тоже непонятно, зачем так), пришлось создать пустышку в target.
Теперь при нажатии сочетания Ctrl+Shift+B
появляется два варианта: build (сборка) и flash (прошивка).
Задачи сборки и прошивки
При выборе пункта flash микроконтроллер будет прошит, в терминале можно наблюдать лог от openocd:
Лог прошивки устройства
Executing task: openocd -f interface/wch-riscv.cfg -c "program c:/dev/wch_demo/build/wch_demo.hex verify reset exit"
Open On-Chip Debugger 0.11.0+dev-02415-gfad123a16-dirty (2023-09-22-10:36)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'sdi'
Warn : Transport "sdi" was already selected
Ready for Remote Connections
Info : WCH-LinkE mode:RV version 2.11
Info : wlink_init ok
Info : clock speed 6000 kHz
Info : [wch_riscv.cpu.0] datacount=2 progbufsize=8
Info : [wch_riscv.cpu.0] Examined RISC-V core; found 1 harts
Info : [wch_riscv.cpu.0] XLEN=32, misa=0x40800014
[wch_riscv.cpu.0] Target successfully examined.
Info : starting gdb server for wch_riscv.cpu.0 on 3333
Info : Listening on port 3333 for gdb connections
Info : [wch_riscv.cpu.0] Hart unexpectedly reset!
** Programming Started **
Info : device id = 0x2cd7abcd
** Programming Finished **
** Verify Started **
** Verified OK **
** Resetting Target **
shutdown command invoked
Отладка в VSCode
Не удалось найти какого-то общепринятого способа настройки отладки (для Stm32, например, почти во всех источниках используется расширение Cortex-Debug), поэтому по мотивам какой-то статьи, откуда узнал, что так можно, остановился на нём.
Для настройки отладки необходимо выполнить следующие действия (если лень, можно вручную создать файл .vscode/launch.json и просто скопировать туда содержимое):
Открыть в редакторе файл main.c (от того, какой файл сейчас открыт, зависит автогенерация конфигурации отладки).
Перейти во вкладку »Run and Debug» (сочетание
Ctrl+Shift+D
).Нажать ссылку »Show all automatic debug configurations».
В появившемся окне нажать »Add configuration».
В выпадающем списке выбрать »Cortex-Debug».
В директории .vscode появится новый файл launch.json со следующим содержимым:
Содержимое launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceFolder}",
"executable": "./bin/executable.elf",
"request": "launch",
"type": "cortex-debug",
"runToEntryPoint": "main",
"servertype": "jlink"
}
]
}
Отредактируем файл, добавив путь до gdb, target отладки и конфигурационный файл openocd. Итоговый файл под катом:
Конечное содержимое launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceFolder}",
"executable": "./build/${workspaceFolderBasename}.elf",
"request": "launch",
"type": "cortex-debug",
"runToEntryPoint": "main",
"servertype": "openocd",
"gdbPath": "riscv-none-elf-gdb",
"configFiles": [
"interface/wch-riscv.cfg"
]
}
]
}
Осталось сохранить файл, пересобрать проект, прошить его и можно нажимать F5. Выполнение остановилось на первой строке main()
!
Окно отладки
Просмотр периферии
Раз уж все довольно легко получилось, можно подтянуть еще и SVD-файл для просмотра регистров периферии. Для этого в launch.json необходимо добавить ключ »svdFile», значением которого является путь до svd-файла целевого микроконтроллера. Файл можно взять из того же репозитория wch_libs.
Содержимое lauch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceFolder}",
"executable": "./build/${workspaceFolderBasename}.elf",
"request": "launch",
"type": "cortex-debug",
"runToEntryPoint": "main",
"servertype": "openocd",
"gdbPath": "riscv-none-elf-gdb",
"configFiles": [
"interface/wch-riscv.cfg"
],
"svdFile": "./misc/CH32V003xx.svd"
}
]
}
После запуска отладки получаем удобный доступ к регистрам периферии.
Полноценная отладка
Готовый проект можно загрузить с репозитория wch_demo_vscode.
Заключение
Автор только начал своё знакомство с контроллерами семейства ch32, но в первом приближении они кажутся как минимум интересными.
Имея значительный опыт с stm32, в том числе в части настройки своего рабочего пространства, не мог не потратить время и силы, чтобы сделать всё так, к чему уже привык. Думаю, это получилось. Написанные cmake-скрипты, конечно, нуждаются в доработке (сейчас вообще поддерживаются только линейки V0 и V2), так что текущая версия не окончательная. Буду рад замечаниям и предложениям как по статье, так и по содержимому репозиториев!
Основными источниками вдохновения и информации при подготовке статьи, за что причастным большое спасибо, были: