From 26d5df362b36ab201d27fb81e9bf45a3b3989d22 Mon Sep 17 00:00:00 2001 From: Ezra Maccabee Date: Tue, 17 Mar 2026 13:18:59 +0300 Subject: [PATCH] # 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - добавлен модуль bsp/uart_host + хост-тесты с моками --- CMakePresets.json | 6 +- README.md | 4 +- TODO-HIL.md | 149 +++++++++++++ bsp/CMakeLists.txt | 18 +- bsp/common/CMakeLists.txt | 15 ++ bsp/common/include/bsp/status.h | 46 ++++ bsp/uart_host/CMakeLists.txt | 17 ++ bsp/uart_host/README.md | 164 +++++++++++++++ bsp/uart_host/include/bsp/uart_host.h | 120 +++++++++++ bsp/uart_host/mocks/uart_host_mock.c | 41 ++++ bsp/uart_host/mocks/uart_host_mock.h | 55 +++++ bsp/uart_host/src/uart_host.c | 266 ++++++++++++++++++++++++ HOW_TO_FLASH.md => docs/HOW_TO_FLASH.md | 0 firmware/test/CMakeLists.txt | 3 + firmware/test/main.c | 2 +- tests/host/CMakeLists.txt | 20 ++ tests/host/uart_host/test_uart_host.c | 221 ++++++++++++++++++++ tests/target/CMakeLists.txt | 47 +++++ 18 files changed, 1183 insertions(+), 11 deletions(-) create mode 100644 TODO-HIL.md create mode 100644 bsp/common/CMakeLists.txt create mode 100644 bsp/common/include/bsp/status.h create mode 100644 bsp/uart_host/CMakeLists.txt create mode 100644 bsp/uart_host/README.md create mode 100644 bsp/uart_host/include/bsp/uart_host.h create mode 100644 bsp/uart_host/mocks/uart_host_mock.c create mode 100644 bsp/uart_host/mocks/uart_host_mock.h create mode 100644 bsp/uart_host/src/uart_host.c rename HOW_TO_FLASH.md => docs/HOW_TO_FLASH.md (100%) create mode 100644 tests/host/uart_host/test_uart_host.c diff --git a/CMakePresets.json b/CMakePresets.json index f50f8b4..b44272c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -130,7 +130,8 @@ "targets": [ "test_bsp_led", "test_ring_buffer", - "test_timeout_pattern" + "test_timeout_pattern", + "uart_host_mock_example" ] }, { @@ -140,7 +141,8 @@ "targets": [ "test_bsp_led", "test_ring_buffer", - "test_timeout_pattern" + "test_timeout_pattern", + "uart_host_mock_example" ] } ], diff --git a/README.md b/README.md index 2dde742..41ad873 100644 --- a/README.md +++ b/README.md @@ -162,8 +162,8 @@ just build::build-firmware-test-debug # сборка firmware для тарге just build::hab-firmware-test-debug # подготовка HAB-образа # На хосте (вне контейнера): -just flash # прошивка через USB ROM +just flash ... # прошивка через USB ROM ``` -> Подробнее о прошивке — [HOW_TO_FLASH.md](HOW_TO_FLASH.md) +> Подробнее о прошивке — [HOW_TO_FLASH.md](docs/HOW_TO_FLASH.md) > Подробнее об окружении разработки — [docs/DEV_ARCH.md](docs/DEV_ARCH.md) diff --git a/TODO-HIL.md b/TODO-HIL.md new file mode 100644 index 0000000..3108c9b --- /dev/null +++ b/TODO-HIL.md @@ -0,0 +1,149 @@ +# HIL‑архитектура и окружение: итоговые решения + +## 1. Общий контекст репозитория + +- Монорепозиторий для MIMXRT1052 уже содержит: + - три firmware‑проекта: `firmware/test`, `firmware/bootloader`, `firmware/tft_app`; + - BSP, vendored SDK, Unity + fff, SEGGER RTT; + - devcontainer, CMakePresets, just‑модули, двухуровневое тестирование (host + target). +- Цель: добавить HIL‑контур так, чтобы он органично встроился в существующую архитектуру и CI/CD, без резкого усложнения. + +--- + +## 2. Выбор инструментов для HIL‑тестов + +### 2.1. Стек тестирования + +Принята связка: + +- **Host**: Python + pytest + - оркестрация тестов и стенда; + - фикстуры для управления питанием, прошивкой, реле, CAN/UART; + - интеграция с CI, маркировка тестов, отчёты. + +- **Target (DUT)**: Unity + - тесты на C, запускаемые на RT1052; + - вывод результатов по UART (или через RTT при необходимости); + - тестовая прошивка может быть отдельным firmware‑проектом, как уже сделано с `firmware/test`. + +- **Промежуточный HIL‑контроллер**: M5Stamp PLC + - принимает команды от хоста; + - управляет CAN, UART, реле, опто‑входами и прочей обвязкой DUT; + - выполняет только примитивные операции (send frame, toggle relay, read input, reset DUT), вся логика сценариев — в pytest. + +### 2.2. Почему так + +- Unity уже используется в проекте — нет смысла вводить второй C‑фреймворк тестирования. +- Python + pytest хорошо подходит для оркестрации внешних устройств (HIL), даёт удобные фикстуры, параметризацию и отчётность. +- Разделение обязанностей: + - DUT отвечает за внутреннюю логику и инварианты; + - HIL‑контроллер — за физические воздействия/измерения; + - pytest — за сценарии и проверки. + +--- + +## 3. Выбор платформы и стека для HIL‑контроллера + +### 3.1. Аппаратная платформа + +- Выбран **M5Stamp PLC** на базе ESP32‑S3: + - CAN‑шина, RS485, реле, опто‑входы — всё, что нужно для стенда; + - промышленная обвязка (галваническая развязка на входах, реле на выходах). + +### 3.2. Варианты ПО для M5Stamp + +Рассматривались: + +1. **MicroPython** + - Плюсы: быстрый старт, код на Python, удобно писать CLI. + - Минусы: + - нет готового BSP именно под PLC‑вариант; + - нужно вручную разбираться с разводкой, CAN/RS485/реле; + - меньше детерминизма по таймингам. + +2. **UIFlow** + - Плюсы: визуальное программирование, быстрые демки. + - Минусы: + - ориентирован на учебные/IoT‑сценарии; + - завязан на Web‑IDE/облако; + - неудобен для строгого, текстового, версионируемого CLI под pytest/CI. + +3. **Arduino‑фреймворк** (через Arduino core для ESP32) + - Плюсы: + - официальная поддержка M5‑плат, библиотек и BSP; + - примеры по работе с реле, RS485, CAN; + - легко поднять простой текстовый CLI по Serial; + - хорошо работает в headless‑режиме через `arduino-cli`. + - Минусы: + - код на C++ (в отличие от MicroPython); + - отдельный маленький проект. + +### 3.3. Принятое решение + +- Для M5Stamp PLC используется **Arduino‑фреймворк** + `arduino-cli`. +- MicroPython и UIFlow откладываются: + - MicroPython — как возможный эксперимент позже; + - UIFlow — не подходит как основа HIL‑контроллера. + +--- + +## 4. Архитектура HIL‑контроллера (M5Stamp PLC) + +### 4.1. Роль M5Stamp + +- Максимально «тупой» исполнитель команд от хоста: + - НЕ содержит сценарной логики тестов; + - предоставляет набор атомарных операций: + - управление реле; + - отправка/приём кадра по CAN/UART; + - переключение питания/Reset DUT; + - чтение опто‑входов/состояния линий. + +- Все сложные сценарии, тайминги и проверки живут в pytest на хосте. + +### 4.2. Протокол обмена host ↔ M5Stamp + +- Простой текстовый CLI по UART/USB: + - команды в виде строк, например: + - `RELAY SET 1 ON` + - `RELAY SET 1 OFF` + - `GPIO READ IN3` + - `CAN SEND 1 123#11223344` + - `UART DUT SEND "AT+PING\r\n"` + - `DUT RESET` + - ответы в понятном формате: + - `OK` + - `ERR ` + - `VAL 0|1` + - `EVENT ...` (если понадобится). + +- Преимущества: + - легко писать и отлаживать; + - удобно тестировать руками (через serial‑терминал); + - протокол можно описать и версионировать как часть репозитория. + +--- + +## 5. Интеграция с существующим репозиторием + +### 5.1. Структура репозитория + +HIL‑контроллер хранится в том же монорепо: + +```text +/ +├── firmware/ +│ ├── test/ # тестовая прошивка DUT +│ ├── bootloader/ +│ ├── tft_app/ +│ └── hil_controller/ # Arduino-проект для M5Stamp PLC (HIL) +├── tools/ +│ └── host/ +│ ├── hil/ # Python-код, pytest HIL тесты, утилиты +│ ├── flash_usb.py +│ └── ... +├── just/ +│ ├── host.just # добавятся host::hil-* цели +│ ├── build.just +│ └── ci.just +└── ... diff --git a/bsp/CMakeLists.txt b/bsp/CMakeLists.txt index 56298a3..13411ab 100644 --- a/bsp/CMakeLists.txt +++ b/bsp/CMakeLists.txt @@ -11,8 +11,10 @@ add_library( generated/board.c generated/pin_mux.c generated/clock_config.c generated/startup/startup_MIMXRT1052.S) +add_subdirectory(common) add_subdirectory(led) add_subdirectory(tick) +add_subdirectory(uart_host) # Подавляем предупреждения при компиляции собственных .c файлов библиотеки target_compile_options(bsp_board PRIVATE -w) @@ -20,18 +22,22 @@ target_compile_options(bsp_board PRIVATE -w) # SYSTEM подавляет предупреждения для всех внешних потребителей target_include_directories(bsp_board SYSTEM PUBLIC generated) -# Дефайны PUBLIC — транзитивно подтянутся во все firmware таргеты TODO: для -# каждого приложения свои дефайны +# Дефайны PUBLIC — транзитивно подтянутся во все firmware таргеты target_compile_definitions( bsp_board PUBLIC CPU_MIMXRT1052CVJ5B SKIP_SYSCLK_INIT XIP_EXTERNAL_FLASH=1 XIP_BOOT_HEADER_ENABLE=1 XIP_BOOT_HEADER_DCD_ENABLE=1) # sdk_device PUBLIC — транзитивно подтянется во все firmware таргеты -target_link_libraries(bsp_board PUBLIC sdk_device sdk_clock sdk_common) +target_link_libraries(bsp_board PUBLIC sdk_device sdk_clock sdk_common + sdk_lpuart) -# # Сценарий A: XIP — код исполняется прямо из Flash add_library(bsp_boot_xip -# INTERFACE) target_compile_definitions( bsp_boot_xip INTERFACE -# XIP_EXTERNAL_FLASH=1 XIP_BOOT_HEADER_ENABLE=1 XIP_BOOT_HEADER_DCD_ENABLE=1) +# Выбор boot стратегии (определяется в CMakePresets в зависимости от собираемого +# target) + +# TODO: длякаждого приложения свои дефайны # Сценарий A: XIP — код исполняется +# прямо из Flash add_library(bsp_boot_xip INTERFACE) target_compile_definitions( +# bsp_boot_xip INTERFACE XIP_EXTERNAL_FLASH=1 XIP_BOOT_HEADER_ENABLE=1 +# XIP_BOOT_HEADER_DCD_ENABLE=1) # # Сценарий B: копирование в ITCM (для bootloader или критичного кода) # add_library(bsp_boot_itcm INTERFACE) target_compile_definitions( bsp_boot_itcm diff --git a/bsp/common/CMakeLists.txt b/bsp/common/CMakeLists.txt new file mode 100644 index 0000000..a3e45e5 --- /dev/null +++ b/bsp/common/CMakeLists.txt @@ -0,0 +1,15 @@ +# ============================================================================= +# bsp_status — общий тип возврата для BSP-модулей +# +# INTERFACE-библиотека: нет .c файлов, только пробрасывает include-путь. +# Работает на любой платформе (ARM target и host unit-тесты) без ifdef. +# +# Использование: target_link_libraries( PUBLIC bsp_status) +# ============================================================================= + +cmake_minimum_required(VERSION 3.20) + +add_library(bsp_status INTERFACE) + +target_include_directories(bsp_status + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/bsp/common/include/bsp/status.h b/bsp/common/include/bsp/status.h new file mode 100644 index 0000000..de7e457 --- /dev/null +++ b/bsp/common/include/bsp/status.h @@ -0,0 +1,46 @@ +/** + * @file status.h + * @brief Общий тип возврата для BSP-модулей. + * + * Используется модулями, операции которых могут завершиться ошибкой + * в рантайме: uart_host, uart_iso, spi, can, usb, wdog и др. + * + * Не используется там, где ошибка невозможна или означает только + * неправильную конфигурацию: tick, led, gpio (простое управление). + * + * PUBLIC — если bsp_status_t есть в публичном заголовке модуля + * PRIVATE — если bsp_status_t используется только внутри .c файлов + * target_link_libraries( PUBLIC|PRIVATE bsp_status) + * В коде: + * #include "bsp/status.h" + */ + +#ifndef BSP_STATUS_H +#define BSP_STATUS_H + +/* -------------------------------------------------------------------------- */ +/* Тип */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Коды возврата BSP-модулей. + * + * Соглашение: + * BSP_OK — операция завершена успешно. + * BSP_ERR_* — операция завершена с ошибкой; подробности в имени кода. + * + * Значения фиксированы и не меняются: могут сохраняться в NVM / передаваться + * по протоколу. + */ +typedef enum bsp_status_e +{ + BSP_OK = 0, /**< Успех. */ + BSP_ERR_INIT = 1, /**< Ошибка инициализации / модуль не готов. */ + BSP_ERR_TIMEOUT = 2, /**< Истёк таймаут ожидания. */ + BSP_ERR_BUSY = 3, /**< Ресурс занят. */ + BSP_ERR_PARAM = 4, /**< Невалидный аргумент */ + BSP_ERR_OVERFLOW = 5, /**< Переполнение буфера, данные потеряны. */ + BSP_ERR_NOT_SUPPORTED = 6, /**< Функция не поддерживается */ +} bsp_status_t; + +#endif /* BSP_STATUS_H */ \ No newline at end of file diff --git a/bsp/uart_host/CMakeLists.txt b/bsp/uart_host/CMakeLists.txt new file mode 100644 index 0000000..2a72c45 --- /dev/null +++ b/bsp/uart_host/CMakeLists.txt @@ -0,0 +1,17 @@ +if(BUILD_TESTS_HOST) + set(UART_HOST_SOURCES mocks/uart_host_mock.c) +else() + set(UART_HOST_SOURCES src/uart_host.c) +endif() + +add_library(bsp_uart_host STATIC ${UART_HOST_SOURCES}) + +target_include_directories( + bsp_uart_host + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include + $<$:${CMAKE_CURRENT_SOURCE_DIR}/mocks>) + +target_link_libraries( + bsp_uart_host + PUBLIC bsp_status + PRIVATE bsp_tick utils sdk_lpuart $<$:fff>) diff --git a/bsp/uart_host/README.md b/bsp/uart_host/README.md new file mode 100644 index 0000000..79e0811 --- /dev/null +++ b/bsp/uart_host/README.md @@ -0,0 +1,164 @@ +# bsp_uart_host + +Коммуникационный канал с хост-машиной через LPUART1 (разъём J2, MCU-Link VCOM). + +Применяется для HIL-тестов (pytest + pyserial), отладочного вывода и резервного +канала связи. + +--- + +## Архитектура + +```bash +[LPUART1 RX] → LPUART1_IRQHandler → ring_buffer_put() + ↓ + bsp_uart_host_read() ← polling + таймаут + bsp_uart_host_read_byte() + +[LPUART1 TX] ← bsp_uart_host_write() ← LPUART_WriteBlocking() +``` + +- **TX** — blocking polling (`LPUART_WriteBlocking`). Пакеты короткие, задержка 1–2 мс приемлема. +- **RX** — ISR пишет в ring buffer, задача/main читает с таймаутом. +- **ISR** — `LPUART1_IRQHandler` определён в модуле, модуль владеет прерыванием целиком. +- **Singleton** — один экземпляр, один физический UART. + +--- + +## Быстрый старт + +```c +#include "bsp/uart_host.h" + +// В main(), после board_hw_init(): +bsp_uart_host_init(115200); + +// TX +bsp_uart_host_write_str("hello\r\n"); + +// RX — ждать байт до 100 мс +int32_t byte = bsp_uart_host_read_byte(100); +if (byte < 0) { /* таймаут */ } + +// RX — прочитать пакет целиком +uint8_t buf[64]; +size_t n = bsp_uart_host_read(buf, sizeof(buf), 500); +``` + +--- + +## Конфигурация + +Задаётся в CMakeLists.txt **firmware-таргета**, не модуля: + +```cmake +target_compile_definitions(firmware_test PRIVATE + BSP_UART_HOST_RX_BUFFER_SIZE=256 # степень двойки, дефолт 256 + BSP_UART_HOST_SRC_CLOCK_HZ=24000000 + BSP_UART_HOST_IRQ_PRIORITY=5 +) +``` + +| Define | Дефолт | Описание | +|--------|--------|----------| +| `BSP_UART_HOST_RX_BUFFER_SIZE` | `256` | Размер RX ring buffer. **Должен быть степенью двойки.** | +| `BSP_UART_HOST_SRC_CLOCK_HZ` | `24000000` | Частота источника тактирования LPUART1. | +| `BSP_UART_HOST_IRQ_PRIORITY` | `5` | Приоритет `LPUART1_IRQn`. Должен быть ≥ `configMAX_SYSCALL_INTERRUPT_PRIORITY` при использовании FreeRTOS. | + +--- + +## Таймауты + +```c +// Без ожидания — вернёт только то, что уже есть в буфере +bsp_uart_host_read(buf, len, 0); + +// Ждать с таймаутом (межбайтовый: сбрасывается после каждого принятого байта) +bsp_uart_host_read(buf, len, 100); + +// Ждать вечно +bsp_uart_host_read(buf, len, BSP_UART_HOST_WAIT_FOREVER); +``` + +`bsp_uart_host_read()` возвращает `size_t` — частичное чтение при таймауте +не является ошибкой, caller сам решает что делать с полученным количеством байт. + +--- + +## FreeRTOS + +Модуль работает в FreeRTOS без отдельной реализации. При сборке с +`BSP_TICK_FREERTOS_MODE` в цикле ожидания добавляется `vTaskDelay(1)` — +задача отдаёт управление планировщику вместо busy-wait. + +`BSP_UART_HOST_IRQ_PRIORITY` должен быть установлен ниже +`configMAX_SYSCALL_INTERRUPT_PRIORITY` (числовое значение выше). + +--- + +## Подключение + +```cmake +# bsp/CMakeLists.txt +add_subdirectory(common) +add_subdirectory(uart_host) + +# firmware/test/CMakeLists.txt +target_link_libraries(firmware_test PRIVATE + bsp_board + bsp_tick + bsp_uart_host +) +``` + +--- + +## Тестирование + +Для host unit-тестов модуль предоставляет fff-заглушки через **Humble Object**: +в тестовой сборке вместо `uart_host.c` линкуется `mocks/uart_host_mock.c`. + +```cmake +# tests/host/CMakeLists.txt +add_host_test( + NAME + uart_host_mock_example + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/uart_host/test_uart_host.c + ${CMAKE_SOURCE_DIR}/bsp/uart_host/mocks/uart_host_mock.c + # Если тестируете "protocol.c" который использует uart_host: + # ${CMAKE_SOURCE_DIR}/bsp/protocol/src/protocol.c + INCLUDES + ${CMAKE_SOURCE_DIR}/bsp/uart_host/include # bsp/uart_host.h + ${CMAKE_SOURCE_DIR}/bsp/uart_host/mocks # uart_host_mock.h + ${CMAKE_SOURCE_DIR}/bsp/common/include # bsp/status.h + # ${CMAKE_SOURCE_DIR}/bsp/protocol/include # protocol.h + MOCKS + ${CMAKE_SOURCE_DIR}/bsp/uart_host/mocks/uart_host_mock.c) +``` + +```c +#include "fff.h" +DEFINE_FFF_GLOBALS; + +#include "bsp/uart_host_mock.h" + +void setUp(void) { UART_HOST_MOCK_RESET_ALL(); } + +void test_something(void) { + bsp_uart_host_write_fake.return_val = BSP_OK; + // ... вызываем тестируемый код ... + TEST_ASSERT_EQUAL(1, bsp_uart_host_write_fake.call_count); +} +``` + +--- + +## Зависимости + +| Зависимость | Тип | Описание | +|-------------|-----|----------| +| `bsp_status` | PUBLIC | `bsp_status_t` в публичном API | +| `bsp_tick` | PRIVATE | `bsp_tick_get_ms()` для таймаутов | +| `utils` (ring_buffer) | PRIVATE | RX ring buffer | +| `sdk_lpuart` | PRIVATE | `fsl_lpuart.h`, `fsl_clock.h` | \ No newline at end of file diff --git a/bsp/uart_host/include/bsp/uart_host.h b/bsp/uart_host/include/bsp/uart_host.h new file mode 100644 index 0000000..7ad62dd --- /dev/null +++ b/bsp/uart_host/include/bsp/uart_host.h @@ -0,0 +1,120 @@ +/** + * @file uart_host.h + * @brief BSP: коммуникационный канал с хост-машиной (LPUART1, J2). + * + * Назначение: + * - HIL-тесты (pytest + pyserial через MCU-Link VCOM) + * - Резервный канал связи / отладочный вывод + * + * Архитектура: + * TX — blocking polling (LPUART_WriteBlocking) + * RX — ISR → ring_buffer → polling read с таймаутом + * + * Паттерн singleton: один экземпляр на всё приложение. + * + * Размер RX-буфера задаётся через CMake define: + * target_compile_definitions(... PRIVATE BSP_UART_HOST_RX_BUFFER_SIZE=256) + * Значение должно быть степенью двойки. Дефолт: 256. + */ +#ifndef BSP_UART_HOST_ +#define BSP_UART_HOST_ + +#include "bsp/status.h" + +#include +#include + +/* -------------------------------------------------------------------------- */ +/* Константы */ +/* -------------------------------------------------s------------------------- */ + +/** Передать в timeout_ms чтобы ждать данные бесконечно. */ +#define BSP_UART_HOST_WAIT_FOREVER (UINT32_MAX) + +/* -------------------------------------------------------------------------- */ +/* Инициализация */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Инициализирует LPUART1: тактирование, пины, прерывание, ring buffer. + * + * Вызывать один раз из main() после board_hw_init(). + * Повторный вызов без предварительного deinit вернёт BSP_ERR_INIT. + * + * @param baud_rate Скорость в бод, например 115200. + * @return BSP_OK при успехе, BSP_ERR_INIT при ошибке. + */ +bsp_status_t bsp_uart_host_init(uint32_t baud_rate); + +/** + * @brief Деинициализирует LPUART1, отключает прерывание, сбрасывает буфер. + * + * После вызова модуль можно инициализировать повторно. + */ +void bsp_uart_host_deinit(void); + +/* -------------------------------------------------------------------------- */ +/* TX — blocking polling */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Отправляет массив байт. Блокирует до завершения передачи. + * + * @param p_data Указатель на буфер данных. + * @param len Количество байт для отправки. + * @return BSP_OK или BSP_ERR_INIT если модуль не инициализирован. + */ +bsp_status_t bsp_uart_host_write(const uint8_t *p_data, size_t len); + +/** + * @brief Отправляет C-строку (без нулевого терминатора). + * + * @param p_str Нуль-терминированная строка. + * @return BSP_OK или BSP_ERR_INIT если модуль не инициализирован. + */ +bsp_status_t bsp_uart_host_write_str(const char *p_str); + +/* -------------------------------------------------------------------------- */ +/* RX — ring buffer + polling с таймаутом */ +/* -------------------------------------------------------------------------- */ + +/** + * @brief Читает до @p len байт с таймаутом. + * + * Функция возвращает управление, как только: + * - прочитано @p len байт, ИЛИ + * - истёк @p timeout_ms с момента последнего поступившего байта, + * ИЛИ буфер пуст и @p timeout_ms == 0. + * + * Частичное чтение — не ошибка; caller сам проверяет возвращённое значение. + * + * @param p_buf Буфер для записи принятых данных. + * @param len Максимальное число байт для чтения. + * @param timeout_ms Таймаут ожидания в мс. 0 — без ожидания, + * BSP_UART_HOST_WAIT_FOREVER — ждать бесконечно. + * @return Число фактически прочитанных байт (0..len). + */ +size_t bsp_uart_host_read(uint8_t *p_buf, size_t len, uint32_t timeout_ms); + +/** + * @brief Читает один байт с таймаутом. + * + * @param timeout_ms Таймаут в мс. 0 — без ожидания, + * BSP_UART_HOST_WAIT_FOREVER — ждать бесконечно. + * @return Принятый байт (0..255) или -1 при таймауте / не инициализирован. + */ +int32_t bsp_uart_host_read_byte(uint32_t timeout_ms); + +/** + * @brief Возвращает число байт, доступных в RX-буфере прямо сейчас. + * + * @return Число байт (0 если буфер пуст или модуль не инициализирован). + */ +size_t bsp_uart_host_rx_available(void); + +/** + * @brief Сбрасывает содержимое RX-буфера. + */ +void bsp_uart_host_rx_flush(void); + +#endif //BSP_UART_HOST_ \ No newline at end of file diff --git a/bsp/uart_host/mocks/uart_host_mock.c b/bsp/uart_host/mocks/uart_host_mock.c new file mode 100644 index 0000000..f2083fc --- /dev/null +++ b/bsp/uart_host/mocks/uart_host_mock.c @@ -0,0 +1,41 @@ +/** + * @file uart_host_mock.c + * @brief fff-заглушки bsp_uart_host для host unit-тестов (Humble Object). + * + * Подключается вместо uart_host.c при BUILD_TESTS_HOST. + * Не требует SDK, LPUART, прерываний. + * + * Использование в тесте: + * + * #include "fff.h" + * DEFINE_FFF_GLOBALS; + * + * #include "bsp/uart_host.h" + * #include "uart_host_mock.h" // объявления FAKE_* + * + * void setUp(void) { UART_HOST_MOCK_RESET_ALL(); } + * void tearDown(void) {} + * + * void test_something(void) { + * bsp_uart_host_write_fake.return_val = BSP_OK; + * // ... вызываем код под тестом ... + * TEST_ASSERT_EQUAL(1, bsp_uart_host_write_fake.call_count); + * } + */ + +#include "uart_host_mock.h" + +/* -------------------------------------------------------------------------- */ +/* Определения fff-заглушек */ +/* -------------------------------------------------------------------------- */ + +DEFINE_FAKE_VALUE_FUNC(bsp_status_t, bsp_uart_host_init, uint32_t); +DEFINE_FAKE_VOID_FUNC(bsp_uart_host_deinit); + +DEFINE_FAKE_VALUE_FUNC(bsp_status_t, bsp_uart_host_write, const uint8_t *, size_t); +DEFINE_FAKE_VALUE_FUNC(bsp_status_t, bsp_uart_host_write_str, const char *); + +DEFINE_FAKE_VALUE_FUNC(size_t, bsp_uart_host_read, uint8_t *, size_t, uint32_t); +DEFINE_FAKE_VALUE_FUNC(int32_t, bsp_uart_host_read_byte, uint32_t); +DEFINE_FAKE_VALUE_FUNC(size_t, bsp_uart_host_rx_available); +DEFINE_FAKE_VOID_FUNC(bsp_uart_host_rx_flush); \ No newline at end of file diff --git a/bsp/uart_host/mocks/uart_host_mock.h b/bsp/uart_host/mocks/uart_host_mock.h new file mode 100644 index 0000000..8e493c7 --- /dev/null +++ b/bsp/uart_host/mocks/uart_host_mock.h @@ -0,0 +1,55 @@ +/** + * @file uart_host_mock.h + * @brief Объявления fff-заглушек bsp_uart_host. + * + * Включать только в test-файлах, не в продакшн-коде. + * + * Порядок include в тест-файле: + * 1. fff.h + * 2. DEFINE_FFF_GLOBALS; + * 3. bsp/uart_host.h ← публичный API (сигнатуры функций) + * 4. uart_host_mock.h ← FAKE_* структуры + */ + +#ifndef UART_HOST_MOCK_H +#define UART_HOST_MOCK_H + +#include "bsp/status.h" +#include "bsp/uart_host.h" +#include "fff.h" + +#include +#include + +/* -------------------------------------------------------------------------- */ +/* Объявления заглушек (DEFINE_ живут в uart_host_mock.c) */ +/* -------------------------------------------------------------------------- */ + +DECLARE_FAKE_VALUE_FUNC(bsp_status_t, bsp_uart_host_init, uint32_t); +DECLARE_FAKE_VOID_FUNC(bsp_uart_host_deinit); + +DECLARE_FAKE_VALUE_FUNC(bsp_status_t, bsp_uart_host_write, const uint8_t *, size_t); +DECLARE_FAKE_VALUE_FUNC(bsp_status_t, bsp_uart_host_write_str, const char *); + +DECLARE_FAKE_VALUE_FUNC(size_t, bsp_uart_host_read, uint8_t *, size_t, uint32_t); +DECLARE_FAKE_VALUE_FUNC(int32_t, bsp_uart_host_read_byte, uint32_t); +DECLARE_FAKE_VALUE_FUNC(size_t, bsp_uart_host_rx_available); +DECLARE_FAKE_VOID_FUNC(bsp_uart_host_rx_flush); + +/* -------------------------------------------------------------------------- */ +/* Удобный макрос: сброс всех заглушек в setUp() */ +/* -------------------------------------------------------------------------- */ + +/* clang-format off */ +#define UART_HOST_MOCK_RESET_ALL() \ + RESET_FAKE(bsp_uart_host_init); \ + RESET_FAKE(bsp_uart_host_deinit); \ + RESET_FAKE(bsp_uart_host_write); \ + RESET_FAKE(bsp_uart_host_write_str); \ + RESET_FAKE(bsp_uart_host_read); \ + RESET_FAKE(bsp_uart_host_read_byte); \ + RESET_FAKE(bsp_uart_host_rx_available); \ + RESET_FAKE(bsp_uart_host_rx_flush) +/* clang-format on */ + +#endif /* UART_HOST_MOCK_H */ \ No newline at end of file diff --git a/bsp/uart_host/src/uart_host.c b/bsp/uart_host/src/uart_host.c new file mode 100644 index 0000000..d10876f --- /dev/null +++ b/bsp/uart_host/src/uart_host.c @@ -0,0 +1,266 @@ +/** + * @file uart_host.c + * @brief Реализация bsp_uart_host (ARM target only). + * + * Скомпилируется только при сборке под ARM (не при BUILD_TESTS_HOST). + * Для host unit-тестов используется uart_host_mock.c. + */ + +#include "bsp/uart_host.h" + +#include +#include +#include + +/* SDK */ +#include "fsl_clock.h" +#include "fsl_lpuart.h" + +/* BSP */ +#include "bsp/tick.h" + +/* Utils */ +#include "ring_buffer/ring_buffer.h" + +#if defined(BSP_TICK_FREERTOS_MODE) +#include "FreeRTOS.h" +#include "task.h" +#endif + +/* -------------------------------------------------------------------------- */ +/* Конфигурация */ +/* -------------------------------------------------------------------------- */ + +#ifndef BSP_UART_HOST_RX_BUFFER_SIZE +#define BSP_UART_HOST_RX_BUFFER_SIZE (256U) +#endif + +/* Частота источника тактирования LPUART1. + * BOARD_BootClockRUN() настраивает OSC → 24 MHz на LPUART. + * Скорректируй если у вас другой clock source. */ +#ifndef BSP_UART_HOST_SRC_CLOCK_HZ +#define BSP_UART_HOST_SRC_CLOCK_HZ (24000000U) +#endif + +/* Приоритет прерывания LPUART1 (0 = наивысший на CM7). */ +#ifndef BSP_UART_HOST_IRQ_PRIORITY +#define BSP_UART_HOST_IRQ_PRIORITY (5U) +#endif + +/* -------------------------------------------------------------------------- */ +/* Статическое состояние модуля */ +/* -------------------------------------------------------------------------- */ + +static uint8_t g_s_rx_buf[BSP_UART_HOST_RX_BUFFER_SIZE]; +static ring_buffer_desc_t g_s_rx_ring; +static bool g_s_initialized = false; + +/* -------------------------------------------------------------------------- */ +/* ISR — владеет прерыванием LPUART1 */ +/* -------------------------------------------------------------------------- */ + +void LPUART1_IRQHandler(void) +{ + /* Читаем все байты, которые накопились в RX FIFO. */ + while (LPUART_GetStatusFlags(LPUART1) & kLPUART_RxDataRegFullFlag) + { + uint8_t byte = LPUART_ReadByte(LPUART1); + /* Переполнение кольцевого буфера: байт молча теряется. + * Caller должен читать достаточно быстро. */ + (void) ring_buffer_put(&g_s_rx_ring, byte); + } + + /* Сброс флага прерывания выполняется автоматически при чтении регистра. */ + SDK_ISR_EXIT_BARRIER; +} + +/* -------------------------------------------------------------------------- */ +/* Инициализация / деинициализация */ +/* -------------------------------------------------------------------------- */ + +bsp_status_t bsp_uart_host_init(uint32_t baud_rate) +{ + if (g_s_initialized) + { + return BSP_ERR_INIT; + } + + /* Инициализация кольцевого буфера. */ + if (!ring_buffer_init(&g_s_rx_ring, g_s_rx_buf, BSP_UART_HOST_RX_BUFFER_SIZE)) + { + /* Размер не степень двойки — ошибка конфигурации. */ + return BSP_ERR_INIT; + } + + /* Тактирование LPUART1. */ + CLOCK_EnableClock(kCLOCK_Lpuart1); + + /* Настройка периферии. */ + lpuart_config_t config; + LPUART_GetDefaultConfig(&config); + config.baudRate_Bps = baud_rate; + config.enableRx = true; + config.enableTx = true; + + status_t sdk_status = LPUART_Init(LPUART1, &config, BSP_UART_HOST_SRC_CLOCK_HZ); + if (sdk_status != kStatus_Success) + { + CLOCK_DisableClock(kCLOCK_Lpuart1); + return BSP_ERR_INIT; + } + + /* Включаем прерывание на приход байта. */ + LPUART_EnableInterrupts(LPUART1, kLPUART_RxDataRegFullInterruptEnable); + NVIC_SetPriority(LPUART1_IRQn, BSP_UART_HOST_IRQ_PRIORITY); + EnableIRQ(LPUART1_IRQn); + + g_s_initialized = true; + return BSP_OK; +} + +void bsp_uart_host_deinit(void) +{ + if (!g_s_initialized) + { + return; + } + + DisableIRQ(LPUART1_IRQn); + LPUART_DisableInterrupts(LPUART1, kLPUART_RxDataRegFullInterruptEnable); + LPUART_Deinit(LPUART1); + CLOCK_DisableClock(kCLOCK_Lpuart1); + ring_buffer_reset(&g_s_rx_ring); + + g_s_initialized = false; +} + +/* -------------------------------------------------------------------------- */ +/* TX */ +/* -------------------------------------------------------------------------- */ + +bsp_status_t bsp_uart_host_write(const uint8_t *p_data, size_t len) +{ + if (!g_s_initialized || p_data == NULL || len == 0U) + { + return BSP_ERR_INIT; + } + + LPUART_WriteBlocking(LPUART1, p_data, len); + return BSP_OK; +} + +bsp_status_t bsp_uart_host_write_str(const char *p_str) +{ + if (!g_s_initialized || p_str == NULL) + { + return BSP_ERR_INIT; + } + + size_t len = strlen(p_str); + if (len == 0U) + { + return BSP_OK; + } + + LPUART_WriteBlocking(LPUART1, (const uint8_t *) p_str, len); + return BSP_OK; +} + +/* -------------------------------------------------------------------------- */ +/* RX */ +/* -------------------------------------------------------------------------- */ + +size_t bsp_uart_host_read(uint8_t *p_buf, size_t len, uint32_t timeout_ms) +{ + if (!g_s_initialized || p_buf == NULL || len == 0U) + { + return 0U; + } + + size_t received = 0U; + uint32_t start = bsp_tick_get_ms(); + + while (received < len) + { + uint8_t byte; + if (ring_buffer_get(&g_s_rx_ring, &byte)) + { + p_buf[received++] = byte; + /* Сбрасываем таймер после каждого принятого байта: + * timeout_ms — межбайтовый таймаут, а не общий. */ + start = bsp_tick_get_ms(); + continue; + } + + /* Буфер пуст — проверяем таймаут. */ + if (timeout_ms == 0U) + { + break; + } + + if (timeout_ms != BSP_UART_HOST_WAIT_FOREVER) + { + if ((bsp_tick_get_ms() - start) >= timeout_ms) + { + break; + } + } +#if defined(BSP_TICK_FREERTOS_MODE) + vTaskDelay(1); // ← отдаём управление планировщику на 1 тик +#endif + } + + return received; +} + +int32_t bsp_uart_host_read_byte(uint32_t timeout_ms) +{ + if (!g_s_initialized) + { + return -1; + } + + uint32_t start = bsp_tick_get_ms(); + + while (true) + { + uint8_t byte; + if (ring_buffer_get(&g_s_rx_ring, &byte)) + { + return (int32_t) byte; + } + + if (timeout_ms == 0U) + { + break; + } + + if (timeout_ms != BSP_UART_HOST_WAIT_FOREVER) + { + if ((bsp_tick_get_ms() - start) >= timeout_ms) + { + break; + } + } + } + + return -1; +} + +size_t bsp_uart_host_rx_available(void) +{ + if (!g_s_initialized) + { + return 0U; + } + return ring_buffer_count(&g_s_rx_ring); +} + +void bsp_uart_host_rx_flush(void) +{ + if (!g_s_initialized) + { + return; + } + ring_buffer_reset(&g_s_rx_ring); +} \ No newline at end of file diff --git a/HOW_TO_FLASH.md b/docs/HOW_TO_FLASH.md similarity index 100% rename from HOW_TO_FLASH.md rename to docs/HOW_TO_FLASH.md diff --git a/firmware/test/CMakeLists.txt b/firmware/test/CMakeLists.txt index cba7799..d6d5ad5 100644 --- a/firmware/test/CMakeLists.txt +++ b/firmware/test/CMakeLists.txt @@ -6,6 +6,9 @@ add_executable(firmware_test) target_sources(firmware_test PRIVATE main.c ${PROJECT_SOURCE_DIR}/bsp/generated/syscalls.c) +target_compile_definitions(firmware_test + PRIVATE BSP_UART_HOST_RX_BUFFER_SIZE=512) +# # ----------------------------------------------------------------------------- # Зависимости — только то что нужно для входного контроля bsp_board транзитивно # даёт: sdk_device, sdk_clock, sdk_common, CPU_MIMXRT1052CVJ5B, XIP_* дефайны diff --git a/firmware/test/main.c b/firmware/test/main.c index 265b9a0..77aed37 100644 --- a/firmware/test/main.c +++ b/firmware/test/main.c @@ -8,7 +8,7 @@ int main(void) board_hw_init(); bsp_led_init(); bsp_tick_init(); - const uint16_t DELAY_MS = 1000; + const uint16_t DELAY_MS = 50; while (1) { bsp_led_on(LED_HEARTBEAT); diff --git a/tests/host/CMakeLists.txt b/tests/host/CMakeLists.txt index eada118..c833f39 100644 --- a/tests/host/CMakeLists.txt +++ b/tests/host/CMakeLists.txt @@ -78,3 +78,23 @@ add_host_test( NAME test_timeout_pattern SOURCES timeout/test_timeout_pattern.c # нет INCLUDES и MOCKS — чистая математика, нет зависимостей ) + +# ----------------------------------------------------------------------------- +# Тест кода, использующего bsp_uart_host через мок (замените SOURCES на свой +# реальный тест и код под тестом) +# ----------------------------------------------------------------------------- +add_host_test( + NAME + uart_host_mock_example + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/uart_host/test_uart_host.c + ${CMAKE_SOURCE_DIR}/bsp/uart_host/mocks/uart_host_mock.c + # Если тестируете "protocol.c" который использует uart_host: + # ${CMAKE_SOURCE_DIR}/bsp/protocol/src/protocol.c + INCLUDES + ${CMAKE_SOURCE_DIR}/bsp/uart_host/include # bsp/uart_host.h + ${CMAKE_SOURCE_DIR}/bsp/uart_host/mocks # uart_host_mock.h + ${CMAKE_SOURCE_DIR}/bsp/common/include # bsp/status.h + # ${CMAKE_SOURCE_DIR}/bsp/protocol/include # protocol.h + MOCKS + ${CMAKE_SOURCE_DIR}/bsp/uart_host/mocks/uart_host_mock.c) diff --git a/tests/host/uart_host/test_uart_host.c b/tests/host/uart_host/test_uart_host.c new file mode 100644 index 0000000..dda614f --- /dev/null +++ b/tests/host/uart_host/test_uart_host.c @@ -0,0 +1,221 @@ +/** + * @file test_uart_host.c + * @brief Unit-тесты для кода использующего bsp_uart_host через fff-мок. + * + * Сборка: host (x86), без SDK, без железа. + * Фреймворки: Unity (assertions) + fff (mocking). + * + * Что тестируем: + * Не саму реализацию uart_host.c (она тестируется HIL-тестами через pyserial), + * а то что код верхнего уровня правильно вызывает API uart_host: + * корректные аргументы, правильная обработка возвращаемых значений, + * поведение при ошибках (BSP_ERR_INIT, таймаут и т.д.). + */ + +/* ── 1. fff — до любых mock-заголовков ─────────────────────────────────── */ +#include "fff.h" +DEFINE_FFF_GLOBALS; + +/* ── 2. Мок uart_host (внутри подтягивает bsp/uart_host.h) ─────────────── */ +#include "uart_host_mock.h" + +/* ── 3. Unity ───────────────────────────────────────────────────────────── */ +#include "unity.h" + +/* ── 4. Тестируемый модуль — раскомментировать когда появится ──────────── */ +/* #include "protocol/protocol.h" */ + +/* -------------------------------------------------------------------------- */ + +void setUp(void) +{ + UART_HOST_MOCK_RESET_ALL(); +} + +void tearDown(void) +{ +} + +/* -------------------------------------------------------------------------- */ +/* init */ +/* -------------------------------------------------------------------------- */ + +void test_init_called_once_with_correct_baud_rate(void) +{ + bsp_uart_host_init_fake.return_val = BSP_OK; + + bsp_status_t status = bsp_uart_host_init(115200U); + + TEST_ASSERT_EQUAL(BSP_OK, status); + TEST_ASSERT_EQUAL(1, bsp_uart_host_init_fake.call_count); + TEST_ASSERT_EQUAL(115200U, bsp_uart_host_init_fake.arg0_val); +} + +void test_init_returns_err_on_failure(void) +{ + bsp_uart_host_init_fake.return_val = BSP_ERR_INIT; + + bsp_status_t status = bsp_uart_host_init(115200U); + + TEST_ASSERT_EQUAL(BSP_ERR_INIT, status); +} + +/* -------------------------------------------------------------------------- */ +/* write */ +/* -------------------------------------------------------------------------- */ + +void test_write_called_with_correct_args(void) +{ + const uint8_t payload[] = { 0xAA, 0xBB, 0xCC }; + bsp_uart_host_write_fake.return_val = BSP_OK; + + bsp_status_t status = bsp_uart_host_write(payload, sizeof(payload)); + + TEST_ASSERT_EQUAL(BSP_OK, status); + TEST_ASSERT_EQUAL(1, bsp_uart_host_write_fake.call_count); + TEST_ASSERT_EQUAL_PTR(payload, bsp_uart_host_write_fake.arg0_val); + TEST_ASSERT_EQUAL(sizeof(payload), bsp_uart_host_write_fake.arg1_val); +} + +void test_write_str_called_with_correct_string(void) +{ + bsp_uart_host_write_str_fake.return_val = BSP_OK; + + bsp_status_t status = bsp_uart_host_write_str("hello\r\n"); + + TEST_ASSERT_EQUAL(BSP_OK, status); + TEST_ASSERT_EQUAL_STRING("hello\r\n", bsp_uart_host_write_str_fake.arg0_val); +} + +void test_write_returns_err_when_not_initialized(void) +{ + bsp_uart_host_write_fake.return_val = BSP_ERR_INIT; + + bsp_status_t status = bsp_uart_host_write(NULL, 0U); + + TEST_ASSERT_EQUAL(BSP_ERR_INIT, status); +} + +/* -------------------------------------------------------------------------- */ +/* read_byte */ +/* -------------------------------------------------------------------------- */ + +void test_read_byte_returns_byte_on_success(void) +{ + bsp_uart_host_read_byte_fake.return_val = 0x42; + + int32_t byte = bsp_uart_host_read_byte(100U); + + TEST_ASSERT_EQUAL(0x42, byte); + TEST_ASSERT_EQUAL(100U, bsp_uart_host_read_byte_fake.arg0_val); +} + +void test_read_byte_returns_minus1_on_timeout(void) +{ + bsp_uart_host_read_byte_fake.return_val = -1; + + int32_t byte = bsp_uart_host_read_byte(100U); + + TEST_ASSERT_EQUAL(-1, byte); +} + +void test_read_byte_wait_forever_passes_correct_timeout(void) +{ + bsp_uart_host_read_byte_fake.return_val = 0x01; + + (void) bsp_uart_host_read_byte(BSP_UART_HOST_WAIT_FOREVER); + + TEST_ASSERT_EQUAL(UINT32_MAX, bsp_uart_host_read_byte_fake.arg0_val); +} + +/* -------------------------------------------------------------------------- */ +/* read */ +/* -------------------------------------------------------------------------- */ + +void test_read_returns_number_of_bytes_read(void) +{ + uint8_t buf[16]; + bsp_uart_host_read_fake.return_val = 5U; + + size_t n = bsp_uart_host_read(buf, sizeof(buf), 200U); + + TEST_ASSERT_EQUAL(5U, n); + TEST_ASSERT_EQUAL_PTR(buf, bsp_uart_host_read_fake.arg0_val); + TEST_ASSERT_EQUAL(sizeof(buf), bsp_uart_host_read_fake.arg1_val); + TEST_ASSERT_EQUAL(200U, bsp_uart_host_read_fake.arg2_val); +} + +void test_read_partial_is_not_an_error(void) +{ + uint8_t buf[64]; + /* Запросили 64, получили 3 — частичное чтение, не ошибка */ + bsp_uart_host_read_fake.return_val = 3U; + + size_t n = bsp_uart_host_read(buf, sizeof(buf), 500U); + + TEST_ASSERT_EQUAL(3U, n); +} + +/* -------------------------------------------------------------------------- */ +/* rx helpers */ +/* -------------------------------------------------------------------------- */ + +void test_rx_available_returns_count(void) +{ + bsp_uart_host_rx_available_fake.return_val = 42U; + + size_t n = bsp_uart_host_rx_available(); + + TEST_ASSERT_EQUAL(42U, n); +} + +void test_rx_flush_called_once(void) +{ + bsp_uart_host_rx_flush(); + + TEST_ASSERT_EQUAL(1, bsp_uart_host_rx_flush_fake.call_count); +} + +/* -------------------------------------------------------------------------- */ +/* setUp сбрасывает счётчики между тестами */ +/* -------------------------------------------------------------------------- */ + +void test_mock_reset_clears_state(void) +{ + bsp_uart_host_write_fake.return_val = BSP_OK; + (void) bsp_uart_host_write(NULL, 0U); + TEST_ASSERT_EQUAL(1, bsp_uart_host_write_fake.call_count); + + /* setUp() следующего теста вызовет UART_HOST_MOCK_RESET_ALL() */ + UART_HOST_MOCK_RESET_ALL(); + TEST_ASSERT_EQUAL(0, bsp_uart_host_write_fake.call_count); + TEST_ASSERT_EQUAL(0, bsp_uart_host_write_fake.return_val); +} + +/* -------------------------------------------------------------------------- */ + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(test_init_called_once_with_correct_baud_rate); + RUN_TEST(test_init_returns_err_on_failure); + + RUN_TEST(test_write_called_with_correct_args); + RUN_TEST(test_write_str_called_with_correct_string); + RUN_TEST(test_write_returns_err_when_not_initialized); + + RUN_TEST(test_read_byte_returns_byte_on_success); + RUN_TEST(test_read_byte_returns_minus1_on_timeout); + RUN_TEST(test_read_byte_wait_forever_passes_correct_timeout); + + RUN_TEST(test_read_returns_number_of_bytes_read); + RUN_TEST(test_read_partial_is_not_an_error); + + RUN_TEST(test_rx_available_returns_count); + RUN_TEST(test_rx_flush_called_once); + + RUN_TEST(test_mock_reset_clears_state); + + return UNITY_END(); +} \ No newline at end of file diff --git a/tests/target/CMakeLists.txt b/tests/target/CMakeLists.txt index e69de29..43460ca 100644 --- a/tests/target/CMakeLists.txt +++ b/tests/target/CMakeLists.txt @@ -0,0 +1,47 @@ +# firmware/test/CMakeLists.txt Тестовая прошивка — входной контроль платы на +# производстве + +add_executable(firmware_test) + +target_sources(firmware_test + PRIVATE main.c ${PROJECT_SOURCE_DIR}/bsp/generated/syscalls.c) + +target_compile_definitions(firmware_test + PRIVATE BSP_UART_HOST_RX_BUFFER_SIZE=512) +# ----------------------------------------------------------------------------- +# Зависимости — только то что нужно для входного контроля bsp_board транзитивно +# даёт: sdk_device, sdk_clock, sdk_common, CPU_MIMXRT1052CVJ5B, XIP_* дефайны +# ----------------------------------------------------------------------------- +target_link_libraries( + firmware_test + PRIVATE bsp_board bsp_led bsp_tick + # Опциональные библиотеки + lib_external # SEGGER RTT если включён через SEGGER_RTT_ENABLED +) + +# ----------------------------------------------------------------------------- +# Linker script +# ----------------------------------------------------------------------------- +# --gc-sections — удалять неиспользуемые секции (работает с +# -ffunction/data-sections) --print-memory-usage — выводить таблицу +# использования Flash/RAM после линковки -Map — генерировать +# map-файл для анализа размещения символов -T — линкерный +# скрипт с описанием карты памяти IMXRT1052 +target_link_options( + firmware_test PRIVATE -Wl,--gc-sections -Wl,--print-memory-usage + -Wl,-Map=${CMAKE_BINARY_DIR}/firmware_test.map + -T${PROJECT_SOURCE_DIR}/cmake/linker/MIMXRT1052xxxxx_flexspi_nor.ld) + +# TODO: дефайны BOOT +# ----------------------------------------------------------------------------- +# Post-build: генерация .bin для прошивки через blhost +# ----------------------------------------------------------------------------- +add_custom_command( + TARGET firmware_test + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} -O binary $ + ${CMAKE_BINARY_DIR}/firmware_test.bin + COMMAND ${CMAKE_OBJCOPY} -O ihex $ + ${CMAKE_BINARY_DIR}/firmware_test.hex + COMMAND ${CMAKE_SIZE} $ + COMMENT "Generating firmware_test.bin")