From 286e454464560ca02be5621094de15d3595c820e Mon Sep 17 00:00:00 2001 From: Andrey Kruchinkin Date: Sat, 5 Sep 2026 03:16:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B2=D1=8B=D0=BF=D1=83=D1=81=D0=BA=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=88=D0=B8=D0=B2=D0=BE=D0=BA=20=D0=B8=D0=B7=20Keil=20?= =?UTF-8?q?=D0=B8=20CCS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 + tools/firmware-publish/PUBLISH_FIRMWARE.bat | 221 ++++++++++++++++++ tools/firmware-publish/README.md | 159 +++++++++++++ .../examples/ccs12-firmware-release.cmd | 15 ++ .../examples/keil-firmware-release.cmd | 13 ++ 5 files changed, 415 insertions(+) create mode 100644 tools/firmware-publish/PUBLISH_FIRMWARE.bat create mode 100644 tools/firmware-publish/README.md create mode 100644 tools/firmware-publish/examples/ccs12-firmware-release.cmd create mode 100644 tools/firmware-publish/examples/keil-firmware-release.cmd diff --git a/README.md b/README.md index c6268bd..55dbe63 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ templates/ c/ библиотеки на C99: заголовок, реализация, README, где есть — порт и тесты python/ модули на чистом Python 3.9+, только stdlib + tools/ общие инструменты сборки и выпуска прошивок ``` Пошаговая раскладка нового проекта и выбор портов для STM32F103, STM32G431 @@ -48,6 +49,12 @@ templates/ | [`python/protocan`](python/protocan) | разбор ProtoCAN, транспортный кадр моста, кадр SETGUI, кодеки каталога | stdlib, Python 3.9+ | | [`python/protocan/trends.py`](python/protocan/trends.py) | общие настройки графиков, ограниченная история, ctypes-декодер GAS/raw CAN | stdlib, опционально SETProtocol DLL/SO | +### Инструменты + +| Инструмент | Что делает | +|---|---| +| [`tools/firmware-publish`](tools/firmware-publish) | единый BAT и конфигурации для проверки и публикации `.hex` Keil / `.bin` CCS 12 в каталоге SETGUI | + Общие тренды для Android GUI и SETGUI: [формат JSON, C99-ядро и адаптеры](c/set-protocol/docs/GUI_TRENDS.md). Общие масштабирование и маркеры: [C99, JNI и Python/Qt](c/set-protocol/docs/GUI_PLOT.md). diff --git a/tools/firmware-publish/PUBLISH_FIRMWARE.bat b/tools/firmware-publish/PUBLISH_FIRMWARE.bat new file mode 100644 index 0000000..af08934 --- /dev/null +++ b/tools/firmware-publish/PUBLISH_FIRMWARE.bat @@ -0,0 +1,221 @@ +@echo off +setlocal EnableExtensions DisableDelayedExpansion + +set "FW_TOOL_DIR=%~dp0" +set "FW_MODE=--preflight" +set "FW_CONFIG=" +set "FW_ASSUME_YES=0" +set "FW_ALLOW_DIRTY=0" + +:parse_arguments +if "%~1"=="" goto arguments_done +if /i "%~1"=="--preflight" ( + set "FW_MODE=--preflight" + shift + goto parse_arguments +) +if /i "%~1"=="--publish" ( + set "FW_MODE=--publish" + shift + goto parse_arguments +) +if /i "%~1"=="--yes" ( + set "FW_ASSUME_YES=1" + shift + goto parse_arguments +) +if /i "%~1"=="--allow-dirty" ( + set "FW_ALLOW_DIRTY=1" + shift + goto parse_arguments +) +if /i "%~1"=="--config" ( + if "%~2"=="" goto missing_config_value + set "FW_CONFIG=%~2" + shift + shift + goto parse_arguments +) +if /i "%~1"=="--help" goto usage +if /i "%~1"=="-h" goto usage +echo ERROR: unknown argument: %~1 +goto usage_error + +:missing_config_value +echo ERROR: --config requires a path. +goto usage_error + +:arguments_done +if not defined FW_CONFIG ( + if exist "%CD%\firmware-release.cmd" ( + set "FW_CONFIG=%CD%\firmware-release.cmd" + ) else ( + set "FW_CONFIG=%FW_TOOL_DIR%firmware-release.cmd" + ) +) +for %%I in ("%FW_CONFIG%") do set "FW_CONFIG=%%~fI" +if not exist "%FW_CONFIG%" ( + echo ERROR: publication config not found: + echo %FW_CONFIG% + echo Copy one of the examples to the firmware project root as + echo firmware-release.cmd, then edit its values. + exit /b 2 +) + +for %%I in ("%FW_CONFIG%") do set "FW_CONFIG_DIR=%%~dpI" +call "%FW_CONFIG%" +if errorlevel 1 ( + echo ERROR: publication config returned an error. + exit /b 2 +) + +if not defined FW_PRODUCT goto missing_FW_PRODUCT +if not defined FW_VERSION goto missing_FW_VERSION +if not defined FW_VERSION_CODE goto missing_FW_VERSION_CODE +if not defined FW_TRANSPORTS goto missing_FW_TRANSPORTS +if not defined FW_IMAGE goto missing_FW_IMAGE +if not defined FW_PROJECT_ROOT set "FW_PROJECT_ROOT=%FW_CONFIG_DIR%" + +pushd "%FW_CONFIG_DIR%" >nul +if errorlevel 1 ( + echo ERROR: cannot enter config directory: %FW_CONFIG_DIR% + exit /b 2 +) +for %%I in ("%FW_PROJECT_ROOT%") do set "FW_PROJECT_ROOT_ABS=%%~fI" +for %%I in ("%FW_IMAGE%") do set "FW_IMAGE_ABS=%%~fI" +if defined FW_SETGUI_ROOT for %%I in ("%FW_SETGUI_ROOT%") do set "FW_SETGUI_ROOT_ABS=%%~fI" +popd + +if not defined FW_SETGUI_ROOT_ABS call :find_setgui +if not defined FW_SETGUI_ROOT_ABS ( + echo ERROR: SETGUI was not found. + echo Set FW_SETGUI_ROOT in %FW_CONFIG% + exit /b 2 +) +set "FW_SETGUI_PUBLISHER=%FW_SETGUI_ROOT_ABS%\PUBLISH_FIRMWARE.bat" +if not exist "%FW_SETGUI_PUBLISHER%" ( + echo ERROR: SETGUI publisher not found: + echo %FW_SETGUI_PUBLISHER% + exit /b 2 +) +if not exist "%FW_IMAGE_ABS%" ( + echo ERROR: firmware image not found: + echo %FW_IMAGE_ABS% + exit /b 2 +) + +for %%I in ("%FW_IMAGE_ABS%") do set "FW_IMAGE_EXT=%%~xI" +if /i not "%FW_IMAGE_EXT%"==".hex" if /i not "%FW_IMAGE_EXT%"==".bin" ( + echo ERROR: FW_IMAGE must point to a .hex or .bin file, not %FW_IMAGE_EXT%. + echo Keil must create a HEX file; CCS must run C2000 Hex Utility. + exit /b 2 +) + +echo. +echo Firmware publication +echo Mode: %FW_MODE% +echo Project: %FW_PROJECT_ROOT_ABS% +echo Image: %FW_IMAGE_ABS% +echo Product: %FW_PRODUCT% +echo Version: %FW_VERSION% ^(%FW_VERSION_CODE%^) +echo Transports: %FW_TRANSPORTS% +if defined FW_BASE_ADDRESS echo Base address: %FW_BASE_ADDRESS% +echo. + +if /i "%FW_MODE%"=="--publish" call :guard_worktree +if errorlevel 1 exit /b %errorlevel% + +if /i "%FW_MODE%"=="--publish" if "%FW_ASSUME_YES%"=="0" ( + echo This will upload the image and change the public firmware catalog. + choice /C YN /N /M "Continue [Y/N]? " + if errorlevel 2 ( + echo Publication cancelled. + exit /b 2 + ) +) + +for %%T in (%FW_TRANSPORTS:,= %) do ( + call :publish_transport "%%~T" + if errorlevel 1 goto publication_failed +) + +if /i "%FW_MODE%"=="--publish" ( + echo. + echo Publication completed and verified for all transports. +) else ( + echo. + echo Preflight passed. Run with --publish only when this build is a release. +) +exit /b 0 + +:publication_failed +echo. +echo ERROR: publication step failed; remaining transports were not processed. +exit /b 1 + +:publish_transport +set "FW_TRANSPORT=%~1" +if "%FW_TRANSPORT%"=="" exit /b 0 +echo ----- %FW_TRANSPORT% ----- +if defined FW_BASE_ADDRESS ( + call "%FW_SETGUI_PUBLISHER%" %FW_MODE% --file "%FW_IMAGE_ABS%" --product "%FW_PRODUCT%" --version "%FW_VERSION%" --version-code "%FW_VERSION_CODE%" --transport "%FW_TRANSPORT%" --base-address "%FW_BASE_ADDRESS%" --notes "%FW_NOTES%" +) else ( + call "%FW_SETGUI_PUBLISHER%" %FW_MODE% --file "%FW_IMAGE_ABS%" --product "%FW_PRODUCT%" --version "%FW_VERSION%" --version-code "%FW_VERSION_CODE%" --transport "%FW_TRANSPORT%" --notes "%FW_NOTES%" +) +exit /b %errorlevel% + +:guard_worktree +where git >nul 2>nul +if errorlevel 1 ( + echo WARNING: Git is unavailable; source cleanliness was not checked. + exit /b 0 +) +git -C "%FW_PROJECT_ROOT_ABS%" rev-parse --is-inside-work-tree >nul 2>nul +if errorlevel 1 ( + echo WARNING: project is not a Git worktree; source cleanliness was not checked. + exit /b 0 +) +git -C "%FW_PROJECT_ROOT_ABS%" diff --quiet HEAD -- >nul 2>nul +if not errorlevel 1 exit /b 0 +if "%FW_ALLOW_DIRTY%"=="1" ( + echo WARNING: publishing from a worktree with tracked changes. + exit /b 0 +) +echo ERROR: tracked project files differ from HEAD. +echo Commit the release sources or repeat with --allow-dirty deliberately. +exit /b 3 + +:find_setgui +for %%I in ("%FW_CONFIG_DIR%..\SETGUI" "%FW_CONFIG_DIR%..\..\SETGUI" "%FW_CONFIG_DIR%..\..\..\SETGUI") do ( + if exist "%%~fI\PUBLISH_FIRMWARE.bat" if not defined FW_SETGUI_ROOT_ABS set "FW_SETGUI_ROOT_ABS=%%~fI" +) +exit /b 0 + +:missing_FW_PRODUCT +echo ERROR: FW_PRODUCT is not set in %FW_CONFIG% +exit /b 2 +:missing_FW_VERSION +echo ERROR: FW_VERSION is not set in %FW_CONFIG% +exit /b 2 +:missing_FW_VERSION_CODE +echo ERROR: FW_VERSION_CODE is not set in %FW_CONFIG% +exit /b 2 +:missing_FW_TRANSPORTS +echo ERROR: FW_TRANSPORTS is not set in %FW_CONFIG% +exit /b 2 +:missing_FW_IMAGE +echo ERROR: FW_IMAGE is not set in %FW_CONFIG% +exit /b 2 + +:usage +echo Usage: +echo PUBLISH_FIRMWARE.bat [--config PATH] --preflight +echo PUBLISH_FIRMWARE.bat [--config PATH] --publish [--yes] [--allow-dirty] +echo. +echo Default mode is --preflight. Default config is firmware-release.cmd +echo in the current directory or next to this script. +exit /b 0 + +:usage_error +call :usage +exit /b 2 diff --git a/tools/firmware-publish/README.md b/tools/firmware-publish/README.md new file mode 100644 index 0000000..9c27223 --- /dev/null +++ b/tools/firmware-publish/README.md @@ -0,0 +1,159 @@ +# Публикация прошивки из Keil и CCS 12 + +Этот комплект связывает проект прошивки с каталогом SETGUI. IDE по-прежнему +собирает штатный загрузочный файл, а `PUBLISH_FIRMWARE.bat`: + +1. читает метаданные выпуска из `firmware-release.cmd`; +2. проверяет наличие и формат `.hex` или `.bin`; +3. запускает локальную проверку SETGUI (`--preflight`) либо публикацию + (`--publish`); +4. для каждого транспорта загружает образ в Gitea, скачивает его обратно, + сверяет SHA-256 и только после этого обновляет `update.json`. + +Пароль в проекте не хранится. Публикатор использует учётные данные Gitea, +которые сохранены в SETGUI через окно «Версия и обновление». + +## Один раз на рабочем компьютере + +1. Соберите или запустите SETGUI и сохраните в нём логин и пароль (либо токен + вместо пароля) Gitea. +2. Убедитесь, что у SETGUI создано Python-окружение `.venv`. +3. Подключите `templates` как сабмодуль проекта либо используйте уже общий + checkout. Не делайте отдельные исправленные копии скрипта в каждом проекте. +4. Скопируйте подходящий файл из [`examples`](examples) в корень проекта под + именем `firmware-release.cmd` и исправьте значения. + +`firmware-release.cmd` содержит только метаданные: + +```bat +set "FW_PROJECT_ROOT=%~dp0" +set "FW_SETGUI_ROOT=%~dp0..\SETGUI" +set "FW_PRODUCT=F103DS18" +set "FW_VERSION=1.1.0" +set "FW_VERSION_CODE=0x00010100" +set "FW_TRANSPORTS=can rs485" +set "FW_BASE_ADDRESS=0x08003000" +set "FW_IMAGE=mdk\build\ds18b20_f103.hex" +set "FW_NOTES=Краткое описание выпуска" +``` + +Пути считаются относительно каталога `firmware-release.cmd`. Если SETGUI не +лежит рядом с проектом, укажите абсолютный `FW_SETGUI_ROOT` или системную +переменную с тем же именем. + +### Обязательные поля + +| Поле | Значение | +|---|---| +| `FW_PRODUCT` | Стабильный идентификатор изделия. Не меняйте регистр/написание между версиями. | +| `FW_VERSION` | Читаемая версия SemVer, например `1.1.0`. | +| `FW_VERSION_CODE` | Число для сравнения версий. Рекомендуется `(major << 16) + (minor << 8) + patch`: `1.1.0` = `0x00010100`. | +| `FW_TRANSPORTS` | Один или несколько транспортов через пробел: `can`, `rs485`, `tms`. | +| `FW_IMAGE` | Готовый файл `.hex` или `.bin`. `.axf` и `.out` публиковать нельзя. | + +`FW_BASE_ADDRESS` нужен для обычного бинарного образа STM32. Для Intel HEX +адрес уже записан в файле, но поле каталога всё равно лучше заполнить адресом +приложения. Для загрузочной таблицы TMS SCI8 оставьте поле пустым. + +## Keil MDK / Arm Compiler 6 + +1. В **Options for Target → Output** включите **Create HEX File**. +2. Укажите в `FW_IMAGE` реальный выходной файл, например + `mdk\build\ds18b20_f103.hex`. +3. Запустите полную сборку и локальную проверку. В `KONOR_ds18b20`, где + сабмодуль `templates` подключён как `lib`, команда выглядит так: + +```bat +call "lib\tools\firmware-publish\PUBLISH_FIRMWARE.bat" --config "firmware-release.cmd" --preflight +``` + +В **Options for Target → User → After Build/Rebuild** можно добавить эту же +команду с `--preflight`. Если Keil запускает её из каталога `mdk`, передайте +явный путь к конфигурации, например `--config "..\firmware-release.cmd"`. + +Не ставьте `--publish` в post-build: иначе обычная сборка станет внешней +операцией и сможет перезаписать опубликованную версию. + +## Code Composer Studio 12 / C2000 + +SETGUI не преобразует `.out`. Для активной конфигурации **Debug и/или Release** +включите **C2000 Hex Utility** и сформируйте загрузочный `.bin`. Для SCI8 boot +TMS320F2812 используются параметры проекта: + +```text +--binary +--boot +--sci8 +``` + +Выход удобно складывать в `bin\${BuildArtifactFileBaseName}.bin`. Затем укажите +этот путь в `FW_IMAGE`, `tms` в `FW_TRANSPORTS`, а `FW_BASE_ADDRESS` оставьте +пустым. + +После Build выполните: + +```bat +call "..\newProject\templates\tools\firmware-publish\PUBLISH_FIRMWARE.bat" --config "firmware-release.cmd" --preflight +``` + +Команда выше соответствует текущей раскладке `SETGIT\BALZAM_ALL` и +`SETGIT\newProject\templates`. Если `templates` подключён в сам проект как +`lib\templates`, используйте `lib\templates\tools\firmware-publish\...`. + +При желании ту же команду можно добавить в **Project Properties → Build → +Steps → Post-build steps**. В конфигурации CCS, где Hex Utility не включён, +файл `.bin` не обновится — это особенно важно отдельно проверить для Release. + +## Выпуск + +Рабочая последовательность одинакова для обеих IDE: + +```bat +rem 1. Собрать Release в IDE. + +rem 2. Проверить метаданные, размер, имя, SHA-256 и запись каталога без сети. +call "lib\tools\firmware-publish\PUBLISH_FIRMWARE.bat" --config "firmware-release.cmd" --preflight + +rem 3. Закоммитить исходники выпуска и опубликовать. +call "lib\tools\firmware-publish\PUBLISH_FIRMWARE.bat" --config "firmware-release.cmd" --publish +``` + +Перед публикацией скрипт показывает все параметры, проверяет tracked-файлы +Git и просит подтверждение. `--allow-dirty` осознанно разрешает публикацию из +изменённого worktree, а `--yes` отключает только интерактивное подтверждение +для доверенного CI: + +```bat +call PUBLISH_FIRMWARE.bat --publish --yes --allow-dirty +``` + +Повторная публикация той же комбинации `product + versionCode + transport` +заменяет запись каталога. Новый `versionCode` добавляет новую версию. Если при +нескольких транспортах сеть оборвалась посередине, исправьте причину и повторите +ту же команду: уже опубликованные записи будут безопасно заменены теми же +данными. + +## Контроль после публикации + +Успешное завершение означает, что образ: + +- загружен как asset выпуска; +- скачан обратно и совпал по SHA-256; +- записан в `firmware.releases` файла `update.json`; +- повторно прочитан и разобран тем же кодом, который использует SETGUI. + +После этого откройте в SETGUI **Версия и обновление → База прошивок → Обновить +каталог** и проверьте изделие, версию и транспорт. Для окончательной проверки +выполните загрузку на тестовое устройство именно тем транспортом, который +указан в записи. + +## Частые ошибки + +| Сообщение | Что проверить | +|---|---| +| `firmware image not found` | Сборка завершилась успешно, `FW_IMAGE` задан относительно конфигурации, нужная конфигурация IDE создаёт `.hex/.bin`. | +| `SETGUI was not found` | Исправьте `FW_SETGUI_ROOT`. | +| `Missing build environment: .venv` | Создайте окружение SETGUI и установите зависимости проекта. | +| Ошибка авторизации Gitea | Заново сохраните логин и токен в SETGUI; не записывайте токен в `.cmd`. | +| `tracked project files differ from HEAD` | Закоммитьте точные исходники выпуска либо осознанно добавьте `--allow-dirty`. | +| Версия не видна в SETGUI | Нажмите «Обновить каталог» и проверьте точное значение `FW_PRODUCT` и поддерживаемый выбранным устройством транспорт. | diff --git a/tools/firmware-publish/examples/ccs12-firmware-release.cmd b/tools/firmware-publish/examples/ccs12-firmware-release.cmd new file mode 100644 index 0000000..ef3ae21 --- /dev/null +++ b/tools/firmware-publish/examples/ccs12-firmware-release.cmd @@ -0,0 +1,15 @@ +@echo off +rem Copy this file to the firmware repository root as firmware-release.cmd. + +set "FW_PROJECT_ROOT=%~dp0" +rem This relative path matches SETGIT/BALZAM_ALL + SETGIT/newProject/SETGUI. +rem Change it if the repositories are stored differently. +set "FW_SETGUI_ROOT=%~dp0..\newProject\SETGUI" + +set "FW_PRODUCT=BALZAM-166" +set "FW_VERSION=1.0.0" +set "FW_VERSION_CODE=0x00010000" +set "FW_TRANSPORTS=tms" +set "FW_BASE_ADDRESS=" +set "FW_IMAGE=ccs12_8_proj\bin\balzam_166_ccs12_8.bin" +set "FW_NOTES=TMS320F2812 SCI8 boot image" diff --git a/tools/firmware-publish/examples/keil-firmware-release.cmd b/tools/firmware-publish/examples/keil-firmware-release.cmd new file mode 100644 index 0000000..40b7a95 --- /dev/null +++ b/tools/firmware-publish/examples/keil-firmware-release.cmd @@ -0,0 +1,13 @@ +@echo off +rem Copy this file to the firmware repository root as firmware-release.cmd. + +set "FW_PROJECT_ROOT=%~dp0" +set "FW_SETGUI_ROOT=%~dp0..\SETGUI" + +set "FW_PRODUCT=F103DS18" +set "FW_VERSION=1.1.0" +set "FW_VERSION_CODE=0x00010100" +set "FW_TRANSPORTS=can rs485" +set "FW_BASE_ADDRESS=0x08003000" +set "FW_IMAGE=mdk\build\ds18b20_f103.hex" +set "FW_NOTES=DS18B20 firmware release"