# test_display: finished
This commit is contained in:
parent
be36d56170
commit
8d85ffe877
13 changed files with 289 additions and 170 deletions
10
Justfile
10
Justfile
|
|
@ -32,7 +32,7 @@ CACHE_DIR := justfile_directory() / '.cache'
|
||||||
mod host 'just/host.just'
|
mod host 'just/host.just'
|
||||||
# build.just — сборка в DevContainer
|
# build.just — сборка в DevContainer
|
||||||
mod build 'just/build.just'
|
mod build 'just/build.just'
|
||||||
# ci.just — CI/CD сценарии (опционально)
|
# ci.just — CI/CD сценарии
|
||||||
mod ci 'just/ci.just'
|
mod ci 'just/ci.just'
|
||||||
|
|
||||||
# === Default рецепт ===
|
# === Default рецепт ===
|
||||||
|
|
@ -42,17 +42,11 @@ mod ci 'just/ci.just'
|
||||||
default:
|
default:
|
||||||
@just --list --list-submodules
|
@just --list --list-submodules
|
||||||
|
|
||||||
# === Популярные алиасы (для удобства команды) ===
|
# === Популярные алиасы ===
|
||||||
# Сокращают длинные вызовы модулей
|
|
||||||
|
|
||||||
[doc('Первичная настройка окружения после git clone (на хосте)')]
|
[doc('Первичная настройка окружения после git clone (на хосте)')]
|
||||||
init:
|
init:
|
||||||
@just host::bootstrap
|
@just host::bootstrap
|
||||||
|
|
||||||
[doc('Прошить firmware_test Debug во Flash через USB ROM')]
|
|
||||||
flash:
|
|
||||||
@just host::flash-test-debug
|
|
||||||
|
|
||||||
[doc('Собрать все HAB-образы в Release')]
|
[doc('Собрать все HAB-образы в Release')]
|
||||||
build-all:
|
build-all:
|
||||||
@just build::hab-all-release
|
@just build::hab-all-release
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
## Аппаратный контекст
|
## Аппаратный контекст
|
||||||
|
|
||||||
| Сигнал / параметр | Аппаратное назначение |
|
| Сигнал / параметр | Аппаратное назначение |
|
||||||
| ----------------- | ---------------------------------------------------------------- |
|
| ----------------- | ------------------------------------------------------------------------------------------------ |
|
||||||
| ELCDIF | NXP ELCDIF, RGB-режим, формат пикселя `kELCDIF_PixelFormatXRGB8888`, шина `kELCDIF_DataBus24Bit` |
|
| ELCDIF | NXP ELCDIF, RGB-режим, формат пикселя `kELCDIF_PixelFormatXRGB8888`, шина `kELCDIF_DataBus24Bit` |
|
||||||
| Подсветка | `GPIO1[20]` — active-high |
|
| Подсветка | `GPIO1[20]` — active-high |
|
||||||
| LR (горизонт.) | `GPIO1[28]` (`Lcdlr_value`) |
|
| LR (горизонт.) | `GPIO1[28]` (`Lcdlr_value`) |
|
||||||
|
|
@ -30,10 +30,10 @@
|
||||||
Делители пиксельного клока (исходный код, PLL2 = 528 МГц):
|
Делители пиксельного клока (исходный код, PLL2 = 528 МГц):
|
||||||
|
|
||||||
| Дисплей | clk_mux | pre_div | div | Эффективная частота |
|
| Дисплей | clk_mux | pre_div | div | Эффективная частота |
|
||||||
| ------- | ------------- | ------- | --- | ------------------- |
|
| ------- | --------- | -------- | --- | ------------------------------ |
|
||||||
| TFT7 | PLL2 | 2 | 4 | `528/3/5 = 35.2 МГц`|
|
| TFT7 | PLL2 | 2 | 4 | `528/3/5 = 35.2 МГц` |
|
||||||
| TFT8 | PLL2 | 2 | 3 | `528/3/4 = 44.0 МГц`|
|
| TFT8 | PLL2 | 2 | 3 | `528/3/4 = 44.0 МГц` |
|
||||||
| TFT4 | Video PLL | — (TODO)| — | требует `CLOCK_InitVideoPll` |
|
| TFT4 | Video PLL | — (TODO) | — | требует `CLOCK_InitVideoPll` |
|
||||||
| TFT10 | — | — | — | таблица не заполнена (`{ 0 }`) |
|
| TFT10 | — | — | — | таблица не заполнена (`{ 0 }`) |
|
||||||
|
|
||||||
Тайминги HSW/HFP/HBP/VSW/VFP/VBP заданы константами в `display.c`
|
Тайминги HSW/HFP/HBP/VSW/VFP/VBP заданы константами в `display.c`
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
|
|
||||||
## Состав модуля
|
## Состав модуля
|
||||||
|
|
||||||
```
|
```bash
|
||||||
bsp/display/
|
bsp/display/
|
||||||
├── include/bsp/display.h # публичный заголовок
|
├── include/bsp/display.h # публичный заголовок
|
||||||
├── src/display.c # реализация API + LCDIF_IRQHandler
|
├── src/display.c # реализация API + LCDIF_IRQHandler
|
||||||
|
|
@ -130,10 +130,10 @@ bsp_display_type_t bsp_display_get_type(void);
|
||||||
Коды возврата:
|
Коды возврата:
|
||||||
|
|
||||||
| Код | Когда |
|
| Код | Когда |
|
||||||
| ---------------------- | ----------------------------------------------------------- |
|
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||||
| `BSP_OK` | Дисплей инициализирован (или уже был инициализирован). |
|
| `BSP_OK` | Дисплей инициализирован (или уже был инициализирован). |
|
||||||
| `BSP_ERR_PARAM` | `type >= BSP_DISPLAY_COUNT`. |
|
| `BSP_ERR_PARAM` | `type >= BSP_DISPLAY_COUNT`. |
|
||||||
| `BSP_ERR_NOT_SUPPORTED`| `type` требует Video PLL (TFT4) — `init_pixelclock` возвращает ошибку до реализации `CLOCK_InitVideoPll`. |
|
| `BSP_ERR_NOT_SUPPORTED` | `type` требует Video PLL (TFT4) — `init_pixelclock` возвращает ошибку до реализации `CLOCK_InitVideoPll`. |
|
||||||
|
|
||||||
> Поведение для `BSP_DISPLAY_TFT10` целостно не описано в коде: запись в
|
> Поведение для `BSP_DISPLAY_TFT10` целостно не описано в коде: запись в
|
||||||
> `K_HW_CFG[BSP_DISPLAY_TFT10]` сделана как `{ 0 }`. Использовать TFT10 как
|
> `K_HW_CFG[BSP_DISPLAY_TFT10]` сделана как `{ 0 }`. Использовать TFT10 как
|
||||||
|
|
@ -157,11 +157,11 @@ bsp_display_type_t bsp_display_get_type(void);
|
||||||
Соответствие rotation → LR/UD (из `display.c`):
|
Соответствие rotation → LR/UD (из `display.c`):
|
||||||
|
|
||||||
| Rotation | LR | UD |
|
| Rotation | LR | UD |
|
||||||
| ---------------------- | -- | -- |
|
| ----------------------------- | --- | --- |
|
||||||
| `BSP_DISPLAY_ROTATE_0` | 1 | 0 |
|
| `BSP_DISPLAY_ROTATE_0` | 1 | 0 |
|
||||||
| `BSP_DISPLAY_ROTATE_90`| 1 | 1 |
|
| `BSP_DISPLAY_FLIP_VERTICAL` | 1 | 1 |
|
||||||
| `BSP_DISPLAY_ROTATE_180`| 0 | 1 |
|
| `BSP_DISPLAY_FLIP_BOTH` | 0 | 1 |
|
||||||
| `BSP_DISPLAY_ROTATE_270`| 0 | 0 |
|
| `BSP_DISPLAY_FLIP_HORIZONTAL` | 0 | 0 |
|
||||||
|
|
||||||
### `bsp_display_set_next_buffer`
|
### `bsp_display_set_next_buffer`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,10 @@ typedef enum bsp_display_type_e
|
||||||
|
|
||||||
typedef enum bsp_display_rotation_e
|
typedef enum bsp_display_rotation_e
|
||||||
{
|
{
|
||||||
BSP_DISPLAY_ROTATE_0 = 0U, /**< Горизонтальная (нормальная). LR=1 UD=0. */
|
BSP_DISPLAY_ROTATE_0 = 0U, /**< LR=1 UD=0. Нормальная ориентация. */
|
||||||
BSP_DISPLAY_ROTATE_90, /**< Вертикальная CW 90°. LR=1 UD=1. */
|
BSP_DISPLAY_FLIP_VERTICAL, /**< LR=1 UD=1. Вертикальный флип (UPDN). */
|
||||||
BSP_DISPLAY_ROTATE_180, /**< Горизонтальная перевёрнутая. LR=0 UD=1. */
|
BSP_DISPLAY_FLIP_BOTH, /**< LR=0 UD=1. Горизонтальный + вертикальный. */
|
||||||
BSP_DISPLAY_ROTATE_270, /**< Вертикальная CCW 90°. LR=0 UD=0. */
|
BSP_DISPLAY_FLIP_HORIZONTAL, /**< LR=0 UD=0. Горизонтальный флип (SHLR). */
|
||||||
} bsp_display_rotation_t;
|
} bsp_display_rotation_t;
|
||||||
|
|
||||||
/* ── Размер дисплея ──────────────────────────────────────────────────── */
|
/* ── Размер дисплея ──────────────────────────────────────────────────── */
|
||||||
|
|
|
||||||
|
|
@ -377,15 +377,15 @@ bsp_status_t bsp_display_set_rotation(bsp_display_rotation_t rotation)
|
||||||
lr_value = 1U;
|
lr_value = 1U;
|
||||||
ud_value = 0U;
|
ud_value = 0U;
|
||||||
break;
|
break;
|
||||||
case BSP_DISPLAY_ROTATE_90:
|
case BSP_DISPLAY_FLIP_VERTICAL:
|
||||||
lr_value = 1U;
|
lr_value = 1U;
|
||||||
ud_value = 1U;
|
ud_value = 1U;
|
||||||
break;
|
break;
|
||||||
case BSP_DISPLAY_ROTATE_180:
|
case BSP_DISPLAY_FLIP_BOTH:
|
||||||
lr_value = 0U;
|
lr_value = 0U;
|
||||||
ud_value = 1U;
|
ud_value = 1U;
|
||||||
break;
|
break;
|
||||||
case BSP_DISPLAY_ROTATE_270:
|
case BSP_DISPLAY_FLIP_HORIZONTAL:
|
||||||
lr_value = 0U;
|
lr_value = 0U;
|
||||||
ud_value = 0U;
|
ud_value = 0U;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ static const char K_FIELD_CMD[] = "\"cmd\"";
|
||||||
static const char K_FIELD_ID[] = "\"id\"";
|
static const char K_FIELD_ID[] = "\"id\"";
|
||||||
static const char K_FIELD_CONFIRMED[] = "\"confirmed\"";
|
static const char K_FIELD_CONFIRMED[] = "\"confirmed\"";
|
||||||
|
|
||||||
|
/** @brief Буфер непрочитанного остатка chunk после вызова process_line(). */
|
||||||
|
static uint8_t g_s_chunk_buf[CLI_LINE_BUF_SIZE];
|
||||||
|
static size_t g_s_chunk_len = 0U;
|
||||||
|
static size_t g_s_chunk_pos = 0U;
|
||||||
|
|
||||||
/* ── RX line buffer ────────────────────────────────────────────────────── */
|
/* ── RX line buffer ────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
static uint8_t g_s_line_buf[CLI_LINE_BUF_SIZE];
|
static uint8_t g_s_line_buf[CLI_LINE_BUF_SIZE];
|
||||||
|
|
@ -283,6 +288,8 @@ static void process_line(const char *p_line)
|
||||||
void cli_init(void)
|
void cli_init(void)
|
||||||
{
|
{
|
||||||
g_s_line_len = 0U;
|
g_s_line_len = 0U;
|
||||||
|
g_s_chunk_len = 0U;
|
||||||
|
g_s_chunk_pos = 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_send(const char *p_resp)
|
void cli_send(const char *p_resp)
|
||||||
|
|
@ -292,41 +299,49 @@ void cli_send(const char *p_resp)
|
||||||
|
|
||||||
void cli_process(void)
|
void cli_process(void)
|
||||||
{
|
{
|
||||||
uint8_t chunk[CLI_LINE_BUF_SIZE];
|
/* Если в g_s_chunk_buf есть остаток от предыдущего вызова —
|
||||||
size_t nbytes = bsp_usb_cdc_read(chunk, sizeof(chunk));
|
* продолжаем его обработку. Иначе читаем новый chunk.
|
||||||
|
*
|
||||||
for (size_t byte_idx = 0U; byte_idx < nbytes; byte_idx++)
|
* Это решает проблему потери confirm при блокирующем dispatch_test:
|
||||||
|
* confirm приходит в chunk вместе с командой run, но process_line()
|
||||||
|
* уходит в блокировку не дочитав chunk до конца. При следующем вызове
|
||||||
|
* cli_process() (из inner polling loop в test_runner_wait_confirm)
|
||||||
|
* мы продолжаем обработку остатка chunk, уже после arm_confirm().
|
||||||
|
*/
|
||||||
|
if (g_s_chunk_pos >= g_s_chunk_len)
|
||||||
{
|
{
|
||||||
uint8_t byte = chunk[byte_idx];
|
g_s_chunk_len = bsp_usb_cdc_read(g_s_chunk_buf, sizeof(g_s_chunk_buf));
|
||||||
|
g_s_chunk_pos = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (g_s_chunk_pos < g_s_chunk_len)
|
||||||
|
{
|
||||||
|
uint8_t byte = g_s_chunk_buf[g_s_chunk_pos];
|
||||||
|
g_s_chunk_pos++;
|
||||||
|
|
||||||
if (g_s_line_len >= (CLI_LINE_BUF_SIZE - 1U))
|
if (g_s_line_len >= (CLI_LINE_BUF_SIZE - 1U))
|
||||||
{
|
{
|
||||||
g_s_line_len = 0U;
|
g_s_line_len = 0U;
|
||||||
protocol_send_error("LINE_TOO_LONG");
|
protocol_send_error("LINE_TOO_LONG");
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte == (uint8_t) '\n')
|
if (byte == (uint8_t) '\n')
|
||||||
{
|
{
|
||||||
/* Отбросить '\r' если терминал шлёт CR+LF */
|
|
||||||
if (g_s_line_len > 0U && g_s_line_buf[g_s_line_len - 1U] == (uint8_t) '\r')
|
if (g_s_line_len > 0U && g_s_line_buf[g_s_line_len - 1U] == (uint8_t) '\r')
|
||||||
{
|
{
|
||||||
g_s_line_len--;
|
g_s_line_len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_s_line_buf[g_s_line_len] = '\0';
|
g_s_line_buf[g_s_line_len] = '\0';
|
||||||
|
size_t completed_len = g_s_line_len;
|
||||||
if (g_s_line_len > 0U)
|
g_s_line_len = 0U; /* ← сбросить ДО process_line */
|
||||||
|
if (completed_len > 0U)
|
||||||
{
|
{
|
||||||
process_line((const char *) g_s_line_buf);
|
process_line((const char *) g_s_line_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_s_line_len = 0U;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
g_s_line_buf[g_s_line_len] = byte;
|
g_s_line_buf[g_s_line_len] = byte;
|
||||||
g_s_line_len++;
|
g_s_line_len++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Содержание
|
## 1 Содержание
|
||||||
|
|
||||||
- [Как читать эту таблицу](#как-читать-эту-таблицу)
|
- [Как читать эту таблицу](#как-читать-эту-таблицу)
|
||||||
- [test_sdram — SDRAM 32 MB](#test_sdram--sdram-32-mb)
|
- [test_sdram — SDRAM 32 MB](#test_sdram--sdram-32-mb)
|
||||||
- [test_qspi — QSPI Flash W25Qxx](#test_qspi--qspi-flash-w25qxx)
|
- [test_qspi — QSPI Flash W25Qxx](#test_qspi--qspi-flash-w25qxx)
|
||||||
- [test_usd — uSD SDIO](#test_usd--usd-sdio)
|
- [test_usd — uSD SDIO](#test_usd--usd-sdio)
|
||||||
- [test_display — Display RGB888](#test_display--display-rgb888) *(запланирован)*
|
- [test_display — Display RGB888](#test_display--display-rgb888)
|
||||||
- [test_buttons — Test_But_1 / Test_But_2](#test_buttons--test_but_1--test_but_2) *(запланирован)*
|
- [test_buttons — Test_But_1 / Test_But_2](#test_buttons--test_but_1--test_but_2) *(запланирован)*
|
||||||
- [test_can — CAN loopback](#test_can--can-loopback) *(запланирован)*
|
- [test_can — CAN loopback](#test_can--can-loopback) *(запланирован)*
|
||||||
- [test_uart_ttl — UART TTL](#test_uart_ttl--uart-ttl) *(запланирован)*
|
- [test_uart_ttl — UART TTL](#test_uart_ttl--uart-ttl) *(запланирован)*
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Как читать эту таблицу
|
## 2 Как читать эту таблицу
|
||||||
|
|
||||||
**Critical** — при провале в `run_all` все последующие тесты получают `SKIP`.
|
**Critical** — при провале в `run_all` все последующие тесты получают `SKIP`.
|
||||||
Некритические тесты могут упасть без остановки прогона.
|
Некритические тесты могут упасть без остановки прогона.
|
||||||
|
|
@ -42,13 +42,13 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## test_sdram — SDRAM 32 MB
|
## 3 test_sdram — SDRAM 32 MB
|
||||||
|
|
||||||
**Файл:** `test_sdram.c`
|
**Файл:** `test_sdram.c`
|
||||||
**ID:** `sdram`
|
**ID:** `sdram`
|
||||||
**Critical:** ✅ | **HIL:** ❌ | **Confirm:** —
|
**Critical:** ✅ | **HIL:** ❌ | **Confirm:** —
|
||||||
|
|
||||||
### Аппаратный контекст
|
### 3.1 Аппаратный контекст
|
||||||
|
|
||||||
| Параметр | Значение |
|
| Параметр | Значение |
|
||||||
| --------------- | ------------------------------------------------- |
|
| --------------- | ------------------------------------------------- |
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
| Тестовый регион | 0x80200000 — 0x80A00000 |
|
| Тестовый регион | 0x80200000 — 0x80A00000 |
|
||||||
| MPU | Region 8: Normal WB cacheable (BOARD_MPU_SDRAM=1) |
|
| MPU | Region 8: Normal WB cacheable (BOARD_MPU_SDRAM=1) |
|
||||||
|
|
||||||
### Инварианты выполнения
|
### 3.2 Инварианты выполнения
|
||||||
|
|
||||||
- SEMC инициализирован DCD **до** `main()` — `bsp_sdram_init()` только верифицирует.
|
- SEMC инициализирован DCD **до** `main()` — `bsp_sdram_init()` только верифицирует.
|
||||||
- MPU Region 8 (`Normal WB`) активен — кэш включён для SDRAM.
|
- MPU Region 8 (`Normal WB`) активен — кэш включён для SDRAM.
|
||||||
|
|
@ -70,9 +70,9 @@
|
||||||
- Сброс кэша (`SCB_CleanDCache_by_Addr`) выполняется **после каждого** write-прохода —
|
- Сброс кэша (`SCB_CleanDCache_by_Addr`) выполняется **после каждого** write-прохода —
|
||||||
readback всегда из физической SDRAM, не из кэша.
|
readback всегда из физической SDRAM, не из кэша.
|
||||||
|
|
||||||
### Что тестирует — четыре фазы
|
### 3.3 Что тестирует — четыре фазы
|
||||||
|
|
||||||
#### Фаза 1: Address bus (~1 мс)
|
#### 3.3.1 Фаза 1: Address bus (~1 мс)
|
||||||
|
|
||||||
Записывает уникальный байт в 24 позиции на степенях двойки
|
Записывает уникальный байт в 24 позиции на степенях двойки
|
||||||
(2^0..2^23 от TEST_BASE), каждую с индивидуальным cache line flush.
|
(2^0..2^23 от TEST_BASE), каждую с индивидуальным cache line flush.
|
||||||
|
|
@ -90,7 +90,7 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Фаза 2: Data bus (~1 с)
|
#### 3.3.2 Фаза 2: Data bus (~1 с)
|
||||||
|
|
||||||
Walking ones (0x01, 0x02, ..., 0x80, 0x01, ...) и его инверсия
|
Walking ones (0x01, 0x02, ..., 0x80, 0x01, ...) и его инверсия
|
||||||
на регионе 64 KB.
|
на регионе 64 KB.
|
||||||
|
|
@ -108,7 +108,7 @@ Walking ones (0x01, 0x02, ..., 0x80, 0x01, ...) и его инверсия
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Фаза 3: Sequential integrity (~25 с)
|
#### 3.3.3 Фаза 3: Sequential integrity (~25 с)
|
||||||
|
|
||||||
Address pattern (`offset & 0xFF`) и его инверсия на регионе 2 MB,
|
Address pattern (`offset & 0xFF`) и его инверсия на регионе 2 MB,
|
||||||
два паттерна × два прохода (write → flush → verify).
|
два паттерна × два прохода (write → flush → verify).
|
||||||
|
|
@ -127,7 +127,7 @@ Address pattern (`offset & 0xFF`) и его инверсия на регионе
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Фаза 4: Retention (~2 с)
|
#### 3.3.4Фаза 4: Retention (~2 с)
|
||||||
|
|
||||||
Address pattern на 256 KB: запись → `flush_dcache` → ожидание 200 мс → верификация.
|
Address pattern на 256 KB: запись → `flush_dcache` → ожидание 200 мс → верификация.
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Гарантии теста при PASS
|
### 3.4Гарантии теста при PASS
|
||||||
|
|
||||||
- Все 24 адресных бита работают независимо без aliasing.
|
- Все 24 адресных бита работают независимо без aliasing.
|
||||||
- Все 8 бит шины данных переключаются корректно.
|
- Все 8 бит шины данных переключаются корректно.
|
||||||
|
|
@ -154,7 +154,7 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
- 256 KB удерживают данные минимум через 3 refresh-цикла.
|
- 256 KB удерживают данные минимум через 3 refresh-цикла.
|
||||||
- SEMC контроллер инициализирован и отвечает.
|
- SEMC контроллер инициализирован и отвечает.
|
||||||
|
|
||||||
### Интерпретация FAIL
|
### 3.5 Интерпретация FAIL
|
||||||
|
|
||||||
`detail` содержит: `addr=0xXXXXXXXX exp=0xXX got=0xXX`
|
`detail` содержит: `addr=0xXXXXXXXX exp=0xXX got=0xXX`
|
||||||
|
|
||||||
|
|
@ -167,14 +167,14 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
|
|
||||||
**Анализ `exp` XOR `got`:** биты где `(exp ^ got) != 0` — сбойные линии шины данных.
|
**Анализ `exp` XOR `got`:** биты где `(exp ^ got) != 0` — сбойные линии шины данных.
|
||||||
|
|
||||||
### Ограничения
|
### 3.6 Ограничения
|
||||||
|
|
||||||
- Не покрывает все 32 MB (только 2 MB для sequential).
|
- Не покрывает все 32 MB (только 2 MB для sequential).
|
||||||
- Не проверяет retention при нагреве или низком напряжении питания.
|
- Не проверяет retention при нагреве или низком напряжении питания.
|
||||||
- Не является заменой полного March C− алгоритма.
|
- Не является заменой полного March C− алгоритма.
|
||||||
- Во время теста (~28 с) USB CDC занят, `ping` не отвечает.
|
- Во время теста (~28 с) USB CDC занят, `ping` не отвечает.
|
||||||
|
|
||||||
### Типичное время выполнения
|
### 3.7 Типичное время выполнения
|
||||||
|
|
||||||
| Фаза | ~Время |
|
| Фаза | ~Время |
|
||||||
| ----------- | --------- |
|
| ----------- | --------- |
|
||||||
|
|
@ -186,13 +186,13 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## test_qspi — QSPI Flash W25Qxx
|
## 4 test_qspi — QSPI Flash W25Qxx
|
||||||
|
|
||||||
**Файл:** `test_qspi.c`
|
**Файл:** `test_qspi.c`
|
||||||
**ID:** `qspi`
|
**ID:** `qspi`
|
||||||
**Critical:** ✅ | **HIL:** ❌ | **Confirm:** —
|
**Critical:** ✅ | **HIL:** ❌ | **Confirm:** —
|
||||||
|
|
||||||
### Аппаратный контекст
|
### 4.1 Аппаратный контекст
|
||||||
|
|
||||||
| Параметр | Значение |
|
| Параметр | Значение |
|
||||||
| --------------- | ------------------------------------------- |
|
| --------------- | ------------------------------------------- |
|
||||||
|
|
@ -202,7 +202,7 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
| BSP | `bsp_qspi` |
|
| BSP | `bsp_qspi` |
|
||||||
| Тестовый сектор | `flash_size - 4KB` (последний, динамически) |
|
| Тестовый сектор | `flash_size - 4KB` (последний, динамически) |
|
||||||
|
|
||||||
### Инварианты выполнения
|
### 4.2Инварианты выполнения
|
||||||
|
|
||||||
- `bsp_qspi_init()` вызывается в `init()` — если чип не опознан, `run()` возвращает FAIL.
|
- `bsp_qspi_init()` вызывается в `init()` — если чип не опознан, `run()` возвращает FAIL.
|
||||||
- Слот 0 FlexSPI LUT (XIP read, задан FDCB) **никогда не изменяется** — прошивка
|
- Слот 0 FlexSPI LUT (XIP read, задан FDCB) **никогда не изменяется** — прошивка
|
||||||
|
|
@ -215,9 +215,9 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
- `bsp_usb_cdc_poll()` вызывается вокруг каждой блокирующей операции — USB CDC
|
- `bsp_usb_cdc_poll()` вызывается вокруг каждой блокирующей операции — USB CDC
|
||||||
остаётся отзывчивым во время теста.
|
остаётся отзывчивым во время теста.
|
||||||
|
|
||||||
### Что тестирует — четыре шага
|
### 4.3 Что тестирует — четыре шага
|
||||||
|
|
||||||
#### Шаг 1: JEDEC ID (~1 мс)
|
#### 4.3.1 Шаг 1: JEDEC ID (~1 мс)
|
||||||
|
|
||||||
Команда 0x9F. Читает manufacturer ID и device ID.
|
Команда 0x9F. Читает manufacturer ID и device ID.
|
||||||
|
|
||||||
|
|
@ -242,7 +242,7 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Шаг 2: Erase + Verify (~500 мс)
|
#### 4.3.2 Шаг 2: Erase + Verify (~500 мс)
|
||||||
|
|
||||||
Стирает тестовый сектор (4KB). Читает первые 256 байт сектора.
|
Стирает тестовый сектор (4KB). Читает первые 256 байт сектора.
|
||||||
Все байты должны быть `0xFF`.
|
Все байты должны быть `0xFF`.
|
||||||
|
|
@ -261,7 +261,7 @@ Address pattern на 256 KB: запись → `flush_dcache` → ожидани
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Шаг 3: Write + Read + Compare (~200 мс)
|
#### 4.3.3 Шаг 3: Write + Read + Compare (~200 мс)
|
||||||
|
|
||||||
Паттерн `byte[i] = i & 0xFF` (256 байт). Записывает первую страницу
|
Паттерн `byte[i] = i & 0xFF` (256 байт). Записывает первую страницу
|
||||||
тестового сектора командой Quad Page Program. Читает обратно командой
|
тестового сектора командой Quad Page Program. Читает обратно командой
|
||||||
|
|
@ -283,7 +283,7 @@ IP Quad Output Read (слот 11). Сравнивает побайтово.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Шаг 4: Address range — только W25Q256/512 (~100 мс)
|
#### 4.3.4 Шаг 4: Address range — только W25Q256/512 (~100 мс)
|
||||||
|
|
||||||
Для чипов с `flash_size > 16 MB`.
|
Для чипов с `flash_size > 16 MB`.
|
||||||
Для W25Q64/128 шаг пропускается (24-bit покрывает весь чип).
|
Для W25Q64/128 шаг пропускается (24-bit покрывает весь чип).
|
||||||
|
|
@ -293,7 +293,7 @@ IP Quad Output Read (слот 11). Сравнивает побайтово.
|
||||||
При 24-bit адресации адрес обрезается до 24 бит. Для W25Q256/512
|
При 24-bit адресации адрес обрезается до 24 бит. Для W25Q256/512
|
||||||
last-sector находится выше 16 MB:
|
last-sector находится выше 16 MB:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
W25Q256: last sector = 0x1FFF000, 0x1FFF000 & 0xFFFFFF = 0xFFF000
|
W25Q256: last sector = 0x1FFF000, 0x1FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
```
|
```
|
||||||
|
|
@ -322,7 +322,7 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Гарантии теста при PASS
|
### 4.4 Гарантии теста при PASS
|
||||||
|
|
||||||
- FlexSPI1 инициализирован и отвечает.
|
- FlexSPI1 инициализирован и отвечает.
|
||||||
- Подключён поддерживаемый Winbond Flash с ожидаемым JEDEC ID.
|
- Подключён поддерживаемый Winbond Flash с ожидаемым JEDEC ID.
|
||||||
|
|
@ -330,7 +330,7 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
- Для W25Q256/512: dedicated 4-byte address opcodes функционируют и
|
- Для W25Q256/512: dedicated 4-byte address opcodes функционируют и
|
||||||
адресуют физически разные секторы выше и ниже 16 MB.
|
адресуют физически разные секторы выше и ниже 16 MB.
|
||||||
|
|
||||||
### Интерпретация FAIL
|
### 4.5 Интерпретация FAIL
|
||||||
|
|
||||||
`detail` содержит текстовое описание шага и причины:
|
`detail` содержит текстовое описание шага и причины:
|
||||||
|
|
||||||
|
|
@ -349,14 +349,14 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
**Анализ `rw mismatch`:** `exp XOR got` — биты, где `(exp ^ got) != 0`,
|
**Анализ `rw mismatch`:** `exp XOR got` — биты, где `(exp ^ got) != 0`,
|
||||||
соответствуют сбойным линиям Quad-шины.
|
соответствуют сбойным линиям Quad-шины.
|
||||||
|
|
||||||
### Ограничения
|
### 4.6 Ограничения
|
||||||
|
|
||||||
- Тестирует только **1 страницу** (256 байт) — не покрывает весь объём чипа.
|
- Тестирует только **1 страницу** (256 байт) — не покрывает весь объём чипа.
|
||||||
- Не проверяет endurance (многократные write/erase циклы).
|
- Не проверяет endurance (многократные write/erase циклы).
|
||||||
- Не является заменой полного тестирования Flash (march-алгоритмы).
|
- Не является заменой полного тестирования Flash (march-алгоритмы).
|
||||||
- Шаг 4 не проверяет адреса в диапазоне 16–32 MB (только крайние точки).
|
- Шаг 4 не проверяет адреса в диапазоне 16–32 MB (только крайние точки).
|
||||||
|
|
||||||
### Типичное время выполнения
|
### 4.7 Типичное время выполнения
|
||||||
|
|
||||||
| Шаг | Чип | ~Время |
|
| Шаг | Чип | ~Время |
|
||||||
| ----------------- | ----------- | ----------- |
|
| ----------------- | ----------- | ----------- |
|
||||||
|
|
@ -370,13 +370,13 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## test_usd — uSD SDIO
|
## 5 test_usd — uSD SDIO
|
||||||
|
|
||||||
**Файл:** `test_usd.c`
|
**Файл:** `test_usd.c`
|
||||||
**ID:** `usd`
|
**ID:** `usd`
|
||||||
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** pre_confirm
|
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** pre_confirm
|
||||||
|
|
||||||
### Аппаратный контекст
|
### 5.1 Аппаратный контекст
|
||||||
|
|
||||||
| Параметр | Значение |
|
| Параметр | Значение |
|
||||||
| ----------------- | --------------------------------------------------------------- |
|
| ----------------- | --------------------------------------------------------------- |
|
||||||
|
|
@ -387,7 +387,7 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
| Drive FatFS | `2:/` |
|
| Drive FatFS | `2:/` |
|
||||||
| BSP | `bsp_sd` (host init/deinit/card detect) + `firmware_test_fatfs` |
|
| BSP | `bsp_sd` (host init/deinit/card detect) + `firmware_test_fatfs` |
|
||||||
|
|
||||||
### Инварианты выполнения
|
### 5.2 Инварианты выполнения
|
||||||
|
|
||||||
- `bsp_sd_init()` вызывается в `init()`. При провале `run()` возвращает FAIL немедленно.
|
- `bsp_sd_init()` вызывается в `init()`. При провале `run()` возвращает FAIL немедленно.
|
||||||
- Все ресурсы (файл, mount, host) освобождаются в `deinit()` — вызывается test_runner
|
- Все ресурсы (файл, mount, host) освобождаются в `deinit()` — вызывается test_runner
|
||||||
|
|
@ -395,7 +395,7 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
- Тестовый файл `2:/FWTEST.TMP` удаляется при любом исходе.
|
- Тестовый файл `2:/FWTEST.TMP` удаляется при любом исходе.
|
||||||
- `bsp_usb_cdc_poll()` вызывается вокруг каждой блокирующей операции.
|
- `bsp_usb_cdc_poll()` вызывается вокруг каждой блокирующей операции.
|
||||||
|
|
||||||
### Что тестирует — четыре шага
|
### 5.3 Что тестирует — четыре шага
|
||||||
|
|
||||||
| Шаг | Действие | Progress event | ~Время |
|
| Шаг | Действие | Progress event | ~Время |
|
||||||
| ------------ | ------------------------------------------ | ------------------- | -------- |
|
| ------------ | ------------------------------------------ | ------------------- | -------- |
|
||||||
|
|
@ -406,14 +406,14 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
|
|
||||||
**Паттерн:** `byte[i] = i & 0xFF`, 4096 байт. Детектирует stuck-at-0/1 и partial write.
|
**Паттерн:** `byte[i] = i & 0xFF`, 4096 байт. Детектирует stuck-at-0/1 и partial write.
|
||||||
|
|
||||||
### Гарантии теста при PASS
|
### 5.4 Гарантии теста при PASS
|
||||||
|
|
||||||
- USDHC host инициализирован и карта подключена.
|
- USDHC host инициализирован и карта подключена.
|
||||||
- FatFS корректно монтирует раздел (FAT32 / exFAT).
|
- FatFS корректно монтирует раздел (FAT32 / exFAT).
|
||||||
- Запись и чтение 4 KB совпадают побайтово.
|
- Запись и чтение 4 KB совпадают побайтово.
|
||||||
- Тестовый файл удалён с карты.
|
- Тестовый файл удалён с карты.
|
||||||
|
|
||||||
### Интерпретация FAIL
|
### 5.5 Интерпретация FAIL
|
||||||
|
|
||||||
| Паттерн `detail` | Причина |
|
| Паттерн `detail` | Причина |
|
||||||
| ---------------------------------------------- | ----------------------------------------------------- |
|
| ---------------------------------------------- | ----------------------------------------------------- |
|
||||||
|
|
@ -425,13 +425,13 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
| `read failed: N` | Ошибка чтения |
|
| `read failed: N` | Ошибка чтения |
|
||||||
| `compare failed at offset N` | Данные не совпадают — битый сектор карты |
|
| `compare failed at offset N` | Данные не совпадают — битый сектор карты |
|
||||||
|
|
||||||
### Поведение без карты / отказ оператора
|
### 5.6 Поведение без карты / отказ оператора
|
||||||
|
|
||||||
- Оператор не ответил за 30 с → `TEST_STATUS_SKIP`, `detail: "confirm timeout"`.
|
- Оператор не ответил за 30 с → `TEST_STATUS_SKIP`, `detail: "confirm timeout"`.
|
||||||
- Оператор нажал "отказ" → `TEST_STATUS_SKIP`, `detail: "operator declined"`.
|
- Оператор нажал "отказ" → `TEST_STATUS_SKIP`, `detail: "operator declined"`.
|
||||||
- Тест не critical → `run_all` продолжает выполнение остальных тестов.
|
- Тест не critical → `run_all` продолжает выполнение остальных тестов.
|
||||||
|
|
||||||
### Типичное время выполнения
|
### 5.7 Типичное время выполнения
|
||||||
|
|
||||||
| Этап | ~Время |
|
| Этап | ~Время |
|
||||||
| ------------------- | ----------- |
|
| ------------------- | ----------- |
|
||||||
|
|
@ -443,44 +443,155 @@ W25Q512: last sector = 0x3FFF000, 0x3FFF000 & 0xFFFFFF = 0xFFF000
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## test_display — Display RGB888
|
## 6 test_display — TFT-дисплей RGB888
|
||||||
|
|
||||||
**Файл:** `test_display.c` *(не реализован — Этап 5)*
|
**Файл:** `test_display.c`
|
||||||
**ID:** `display`
|
**ID:** `display`
|
||||||
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** в run()
|
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** в run()
|
||||||
|
|
||||||
### Что будет тестировать
|
### 6.1 Аппаратный контекст
|
||||||
|
|
||||||
Четыре шага с визуальным подтверждением оператора:
|
| Параметр | Значение |
|
||||||
|
| -------------- | --------------------------------------------------- |
|
||||||
|
| Контроллер | ELCDIF (RGB888, DE mode) |
|
||||||
|
| Дисплейный IC | HX8264-D02 (TFT7/TFT8/TFT10) |
|
||||||
|
| Фреймбуфер | NonCacheable SDRAM, `AT_NONCACHEABLE_SECTION_ALIGN` |
|
||||||
|
| Подсветка | GPIO1[20] (LcdLed), active-high |
|
||||||
|
| Горизонт. скан | GPIO1[28] (LcdLR / SHLR контроллера) |
|
||||||
|
| Вертикал. скан | GPIO1[30] (LcdUd / UPDN контроллера) |
|
||||||
|
| MODE | GPIO1[29] — HIGH обязательно (DE mode для ELCDIF) |
|
||||||
|
| DITHB | GPIO1[31] — HIGH (dithering disable, IC default) |
|
||||||
|
| Тип дисплея | CMake-define `DISPLAY_TEST_TYPE` (сейчас TFT8) |
|
||||||
|
|
||||||
| Шаг | ID confirm | Промпт | Таймаут |
|
### 6.2 Инварианты выполнения
|
||||||
| ------- | --------------- | ---------------------- | ------- |
|
|
||||||
| Красный | `display_red` | "Экран залит красным?" | 15 с |
|
|
||||||
| Зелёный | `display_green` | "Экран залит зелёным?" | 15 с |
|
|
||||||
| Синий | `display_blue` | "Экран залит синим?" | 15 с |
|
|
||||||
| Белый | `display_white` | "Экран залит белым?" | 15 с |
|
|
||||||
|
|
||||||
Итог = AND всех четырёх подтверждений.
|
- `bsp_display_init()` вызывается в `init()`. При провале `run()` возвращает FAIL немедленно.
|
||||||
|
- Фреймбуфер `g_s_framebuf` статический, в NonCacheable SDRAM — ELCDIF DMA
|
||||||
|
всегда читает актуальные данные без cache flush.
|
||||||
|
- `bsp_usb_cdc_poll()` вызывается в циклах заливки буфера и ожидания FRAME_DONE —
|
||||||
|
USB CDC остаётся отзывчивым во время теста.
|
||||||
|
- После теста `bsp_display_deinit()` восстанавливает ROTATE_0 и выключает подсветку.
|
||||||
|
|
||||||
### Гарантии при PASS
|
### 6.3 Что тестирует — два этапа
|
||||||
|
|
||||||
- RGB888 интерфейс выводит каждый цвет независимо.
|
#### 6.3.1 Этап 1: Цвет (все типы дисплеев)
|
||||||
- Подсветка PWM активна (оператор видит экран).
|
|
||||||
|
|
||||||
### Интерпретация FAIL
|
Четыре шага: Red → Green → Blue → White.
|
||||||
|
|
||||||
`detail` содержит ID шага, который не был подтверждён: `"display_blue not confirmed"`.
|
На каждом шаге прошивка заливает фреймбуфер сплошным цветом XRGB8888,
|
||||||
Это указывает на конкретный цветовой канал с дефектом.
|
ждёт ISR FRAME_DONE, затем ждёт визуального подтверждения оператора
|
||||||
|
(таймаут 15 с).
|
||||||
|
|
||||||
|
**Ловит:**
|
||||||
|
|
||||||
|
- Неработающую подсветку.
|
||||||
|
- Обрыв или неправильное подключение отдельных RGB-каналов шины данных.
|
||||||
|
- Полное отсутствие изображения (ELCDIF / питание дисплея).
|
||||||
|
|
||||||
|
**Не ловит:**
|
||||||
|
|
||||||
|
- Деградацию отдельных пикселей (stuck pixel).
|
||||||
|
- Проблемы с яркостью и гамма-коррекцией.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## test_buttons — Test_But_1 / Test_But_2
|
#### 6.3.2 Этап 2: Проверка LR/UD пинов (TFT7/TFT8/TFT10)
|
||||||
|
|
||||||
|
Диагностирует непропаянные ножки `LcdLR` (GPIO1[28]) и `LcdUd` (GPIO1[30]).
|
||||||
|
|
||||||
|
**Паттерн:** левая половина экрана RED, правая BLUE.
|
||||||
|
|
||||||
|
**Шаг 1 — базовая ориентация (`ROTATE_0`):**
|
||||||
|
прошивка устанавливает `LR=1 UD=0`, заливает паттерн, оператор
|
||||||
|
подтверждает что левая зона красная, правая синяя.
|
||||||
|
|
||||||
|
**Шаг 2 — горизонтальный флип (`FLIP_HORIZONTAL`):**
|
||||||
|
прошивка устанавливает `LR=0 UD=0` — контроллер HX8264-D02 меняет
|
||||||
|
направление источника (SHLR). При исправном LR-пине цветовые зоны
|
||||||
|
меняются местами. Оператор подтверждает изменение.
|
||||||
|
|
||||||
|
После теста прошивка восстанавливает `ROTATE_0` независимо от результата.
|
||||||
|
|
||||||
|
> **Примечание по контроллеру HX8264-D02:**
|
||||||
|
> LR (SHLR) управляет **горизонтальным** направлением источников —
|
||||||
|
> меняет местами левую/правую части. UD (UPDN) управляет **вертикальным**
|
||||||
|
> направлением затвора. Для детектирования непропаянного LR-пина
|
||||||
|
> используется горизонтальный паттерн и `FLIP_HORIZONTAL`.
|
||||||
|
|
||||||
|
**Ловит:**
|
||||||
|
|
||||||
|
- Непропаянный `LcdLR` (GPIO1[28]) — горизонтальные зоны не меняются.
|
||||||
|
- Непропаянный `LcdUd` (GPIO1[30]) — тест не проверяет UD напрямую,
|
||||||
|
но бракованная плата с обоими непропаянными пинами также не пройдёт
|
||||||
|
шаг 1 (непредсказуемая ориентация при старте).
|
||||||
|
|
||||||
|
**Не ловит:**
|
||||||
|
|
||||||
|
- Изолированный непропай `LcdUd` при исправном `LcdLR`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6.4 Гарантии теста при PASS
|
||||||
|
|
||||||
|
- ELCDIF инициализирован, фреймбуфер DMA работает корректно.
|
||||||
|
- Все три RGB-канала шины данных функционируют.
|
||||||
|
- Подсветка включается и отключается.
|
||||||
|
- Пин `LcdLR` (GPIO1[28]) физически пропаян и реагирует на GPIO-запись.
|
||||||
|
|
||||||
|
### 6.5 Интерпретация FAIL
|
||||||
|
|
||||||
|
| `detail` | Вероятный диагноз |
|
||||||
|
| -------------------------------- | -------------------------------------------- |
|
||||||
|
| `display init failed` | Неверный `DISPLAY_TEST_TYPE` или нет питания |
|
||||||
|
| `display_red not confirmed` | Нет изображения / канал R / подсветка |
|
||||||
|
| `display_green not confirmed` | Канал G мёртв |
|
||||||
|
| `display_blue not confirmed` | Канал B мёртв |
|
||||||
|
| `display_white not confirmed` | Подсветка или несколько RGB-каналов |
|
||||||
|
| `display_rot_base not confirmed` | Нет изображения в паттерне (ELCDIF) |
|
||||||
|
| `display_rot_lr not confirmed` | **LcdLR (GPIO1[28]) не пропаян** |
|
||||||
|
|
||||||
|
### 6.6 Последовательность событий протокола
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test_begin
|
||||||
|
→ confirm_request(display_red) ← оператор видит сплошной красный
|
||||||
|
→ confirm_request(display_green) ← сплошной зелёный
|
||||||
|
→ confirm_request(display_blue) ← сплошной синий
|
||||||
|
→ confirm_request(display_white) ← сплошной белый
|
||||||
|
→ confirm_request(display_rot_base) ← левая RED / правая BLUE (ROTATE_0)
|
||||||
|
→ confirm_request(display_rot_lr) ← цветовые зоны поменялись (FLIP_HORIZONTAL)
|
||||||
|
test_result
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Важно:** `test_display` не использует `pre_confirm_prompt`.
|
||||||
|
> Первый confirm_request (`display_red`) отправляется изнутри `run()`
|
||||||
|
> после `test_begin`. Хост должен отвечать только на события типа
|
||||||
|
> `confirm_request`, не опережая их.
|
||||||
|
|
||||||
|
### 6.7 Зависимости
|
||||||
|
|
||||||
|
| Зависимость | Описание |
|
||||||
|
| ------------- | ------------------------------------------ |
|
||||||
|
| `bsp_display` | ELCDIF init/deinit, rotation, frame buffer |
|
||||||
|
| `bsp_usb_cdc` | `bsp_usb_cdc_poll()` в циклах ожидания |
|
||||||
|
|
||||||
|
### 6.8 Типичное время выполнения
|
||||||
|
|
||||||
|
| Этап | ~Время |
|
||||||
|
| -------------------- | -------------- |
|
||||||
|
| init (ELCDIF + GPIO) | < 10 мс |
|
||||||
|
| 4 × цветовой шаг | 4 × 15 с (max) |
|
||||||
|
| 2 × шаг ротации | 2 × 15 с (max) |
|
||||||
|
| **Итого (max)** | **~90 с** |
|
||||||
|
| **Итого (типичный)** | **~40–60 с** |
|
||||||
|
|
||||||
|
## 7 test_buttons — Test_But_1 / Test_But_2
|
||||||
|
|
||||||
**Файл:** `test_buttons.c` *(не реализован — Этап 5)*
|
**Файл:** `test_buttons.c` *(не реализован — Этап 5)*
|
||||||
**ID:** `buttons`
|
**ID:** `buttons`
|
||||||
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** prompt only
|
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** prompt only
|
||||||
|
|
||||||
### Аппаратный контекст
|
### 7.1 Аппаратный контекст
|
||||||
|
|
||||||
| Кнопка | Пин MCU | GPIO |
|
| Кнопка | Пин MCU | GPIO |
|
||||||
| ---------- | ---------- | --------- |
|
| ---------- | ---------- | --------- |
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,15 @@
|
||||||
#define DISPLAY_TEST_TYPE BSP_DISPLAY_TFT8
|
#define DISPLAY_TEST_TYPE BSP_DISPLAY_TFT8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* ── Вспомогательные функции ────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static test_result_t make_fail(const char *p_detail)
|
||||||
|
{
|
||||||
|
test_result_t result = { .status = TEST_STATUS_FAIL, .duration_ms = 0U };
|
||||||
|
(void) snprintf(result.detail, TEST_DETAIL_SIZE, "%s", p_detail);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Константы ───────────────────────────────────────────────────────── */
|
/* ── Константы ───────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
/** @brief XRGB8888 цвета для тестовых заливок. */
|
/** @brief XRGB8888 цвета для тестовых заливок. */
|
||||||
|
|
@ -216,8 +225,8 @@ static bool step_rotation(test_result_t *p_out)
|
||||||
"Screen: left RED, right BLUE?", p_out);
|
"Screen: left RED, right BLUE?", p_out);
|
||||||
if (ok)
|
if (ok)
|
||||||
{
|
{
|
||||||
ok = step_rot_apply_and_confirm(BSP_DISPLAY_ROTATE_90, "display_rot90",
|
ok = step_rot_apply_and_confirm(BSP_DISPLAY_FLIP_HORIZONTAL, "display_rot_base",
|
||||||
"Color zones changed orientation?", p_out);
|
"Left RED and right BLUE swapped sides?", p_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Восстановить ROTATE_0 в любом исходе */
|
/* Восстановить ROTATE_0 в любом исходе */
|
||||||
|
|
@ -246,12 +255,10 @@ static void display_test_init(void)
|
||||||
|
|
||||||
static test_result_t display_test_run(void)
|
static test_result_t display_test_run(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!g_s_ready)
|
if (!g_s_ready)
|
||||||
{
|
{
|
||||||
test_result_t result = { .status = TEST_STATUS_FAIL, .duration_ms = 0U };
|
return make_fail("display init failed");
|
||||||
(void) snprintf(result.detail, TEST_DETAIL_SIZE, "%s",
|
|
||||||
"display init failed — check DISPLAY_TEST_TYPE");
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test_result_t fail_result = { .status = TEST_STATUS_FAIL, .duration_ms = 0U, .detail = { 0 } };
|
test_result_t fail_result = { .status = TEST_STATUS_FAIL, .duration_ms = 0U, .detail = { 0 } };
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# scripts/build.just — сборка, тесты, HAB-образы
|
# scripts/build.just — сборка, тесты, HAB-образы
|
||||||
# Выполняется ТОЛЬКО внутри devcontainer (из терминала VSCode или tasks).
|
# Выполняется ТОЛЬКО внутри devcontainer (из терминала VSCode или tasks).
|
||||||
#
|
#
|
||||||
# Рабочая директория — корень репозитория (не scripts/).
|
# Рабочая директория — корень репозитория.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# === Импорт общих переменных ===
|
# === Импорт общих переменных ===
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ test:
|
||||||
lint:
|
lint:
|
||||||
@echo "TODO: enable just build::lint / just build::check after local stabilization"
|
@echo "TODO: enable just build::lint / just build::check after local stabilization"
|
||||||
|
|
||||||
[doc('Собрать HAB Release-артефакты для выкладки')]
|
[doc('Собрать HAB Release-артефакты для деплоя')]
|
||||||
[group('ci')]
|
[group('ci')]
|
||||||
release: build
|
release: build
|
||||||
just build::hab-all-release
|
just build::hab-all-release
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ setup-m5-udev:
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# ГРУППА: flash-sdp — USB Serial Downloader (SDP через ROM-загрузчик)
|
# ГРУППА: flash-sdp — USB Serial Downloader (SDP через ROM-загрузчик)
|
||||||
# Требует: плата в режиме Serial Downloader (BOOT_MODE = 01)
|
# Требование: плата в режиме Serial Downloader (BOOT_MODE = 01)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
[doc('Прошить образ через USB SDP: flash <firmware_test|bootloader|app> <debug|release>')]
|
[doc('Прошить образ через USB SDP: flash <firmware_test|bootloader|app> <debug|release>')]
|
||||||
|
|
@ -272,8 +272,6 @@ flash-production:
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# ГРУППА: flash-swd — прошивка через SWD (MCU-Link, без смены BOOT_MODE)
|
# ГРУППА: flash-swd — прошивка через SWD (MCU-Link, без смены BOOT_MODE)
|
||||||
# Требует: GDB-сервер НЕ запущен (pyOCD занимает пробник монопольно)
|
|
||||||
# После записи — обязательный power cycle платы
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
_flash_swd := TOOLS_DIR / "flash_swd.py"
|
_flash_swd := TOOLS_DIR / "flash_swd.py"
|
||||||
|
|
@ -379,7 +377,7 @@ hil-usb-cdc:
|
||||||
# just host::debug-server → VSCode: 🐛 Debug: <project> → F5
|
# just host::debug-server → VSCode: 🐛 Debug: <project> → F5
|
||||||
#
|
#
|
||||||
# Рабочий цикл Б — прошить и отладить без USB SDP:
|
# Рабочий цикл Б — прошить и отладить без USB SDP:
|
||||||
# just host::flash-swd-test-debug → power cycle →
|
# just host::flash-swd-test-debug
|
||||||
# just host::debug-server → VSCode: 🐛 Debug: firmware_test → F5
|
# just host::debug-server → VSCode: 🐛 Debug: firmware_test → F5
|
||||||
#
|
#
|
||||||
# Важно: debug-server и flash-swd используют MCU-Link монопольно.
|
# Важно: debug-server и flash-swd используют MCU-Link монопольно.
|
||||||
|
|
@ -506,12 +504,6 @@ sdp-status:
|
||||||
flashloader-status:
|
flashloader-status:
|
||||||
cd "{{ TOOLS_DIR }}" && UV_PROJECT_ENVIRONMENT={{ _venv }} uv run blhost -u 0x15A2,0x0073 -- get-property 1 0
|
cd "{{ TOOLS_DIR }}" && UV_PROJECT_ENVIRONMENT={{ _venv }} uv run blhost -u 0x15A2,0x0073 -- get-property 1 0
|
||||||
|
|
||||||
[doc('Обновить spsdk до указанной версии: upgrade-tools <version>')]
|
|
||||||
[group('util')]
|
|
||||||
upgrade-tools version:
|
|
||||||
cd "{{ TOOLS_DIR }}" && uv add "spsdk=={{ version }}" && uv sync
|
|
||||||
@echo " ✅ spsdk upgraded to {{ version }}"
|
|
||||||
|
|
||||||
[doc('Открыть UART монитор на MCU-Link VCOM (кроссплатформенный)')]
|
[doc('Открыть UART монитор на MCU-Link VCOM (кроссплатформенный)')]
|
||||||
[group('util')]
|
[group('util')]
|
||||||
uart-monitor:
|
uart-monitor:
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ USB CDC, Flash и т.д. Транспорт подключается через
|
||||||
callback-функции. Адаптеры живут в `port/log/`.
|
callback-функции. Адаптеры живут в `port/log/`.
|
||||||
|
|
||||||
| Параметр | Значение |
|
| Параметр | Значение |
|
||||||
|---|---|
|
| ------------------ | ------------------------------------------------- |
|
||||||
| Формат | `[ timestamp][L][TAG] сообщение\r\n` |
|
| Формат | `[ timestamp][L][TAG] сообщение\r\n` |
|
||||||
| Буфер строки | 256 байт (переопределяется через `LOG_BUF_SIZE`) |
|
| Буфер строки | 256 байт (переопределяется через `LOG_BUF_SIZE`) |
|
||||||
| Управление уровнем | `LOG_LEVEL` через CMake `-DLOG_LEVEL=N` |
|
| Управление уровнем | `LOG_LEVEL` через CMake `-DLOG_LEVEL=N` |
|
||||||
|
|
@ -17,7 +17,7 @@ callback-функции. Адаптеры живут в `port/log/`.
|
||||||
## Уровни
|
## Уровни
|
||||||
|
|
||||||
| N | Макрос | Имя |
|
| N | Макрос | Имя |
|
||||||
|---|--------|-----|
|
| --- | ------- | -------------------------------------------- |
|
||||||
| 0 | — | off — все `LOG_*` → `((void)0)`, нулевой ROM |
|
| 0 | — | off — все `LOG_*` → `((void)0)`, нулевой ROM |
|
||||||
| 1 | `LOG_E` | error |
|
| 1 | `LOG_E` | error |
|
||||||
| 2 | `LOG_W` | warn |
|
| 2 | `LOG_W` | warn |
|
||||||
|
|
@ -64,7 +64,7 @@ typedef void (*log_write_cb_t)(const char *p_buf, size_t len, void *p_ctx);
|
||||||
Готовые адаптеры в `port/log/`:
|
Готовые адаптеры в `port/log/`:
|
||||||
|
|
||||||
| Адаптер | Транспорт |
|
| Адаптер | Транспорт |
|
||||||
|---------|-----------|
|
| ---------- | ---------------------------------------- |
|
||||||
| `log_uart` | `bsp_uart_host` (LPUART1, MCU-Link VCOM) |
|
| `log_uart` | `bsp_uart_host` (LPUART1, MCU-Link VCOM) |
|
||||||
|
|
||||||
## Мьютекс и временна́я метка (FreeRTOS)
|
## Мьютекс и временна́я метка (FreeRTOS)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
элемент с более высоким приоритетом вытесняет наименее приоритетный.
|
элемент с более высоким приоритетом вытесняет наименее приоритетный.
|
||||||
|
|
||||||
| Параметр | Значение |
|
| Параметр | Значение |
|
||||||
|---|---|
|
| ------------- | ----------------------------------------------------------------------- |
|
||||||
| Элемент | любой тип, задаётся через `item_size` |
|
| Элемент | любой тип, задаётся через `item_size` |
|
||||||
| Ёмкость | любая, задаётся при `prio_queue_init`; фиксирована на всё время жизни |
|
| Ёмкость | любая, задаётся при `prio_queue_init`; фиксирована на всё время жизни |
|
||||||
| Порядок | определяется `cmp`-функцией пользователя (аналог `qsort`) |
|
| Порядок | определяется `cmp`-функцией пользователя (аналог `qsort`) |
|
||||||
|
|
@ -54,7 +54,7 @@ prio_queue_remove_at(&q, 0);
|
||||||
|
|
||||||
## Политика вытеснения при полном буфере
|
## Политика вытеснения при полном буфере
|
||||||
|
|
||||||
```
|
```bash
|
||||||
Очередь полна [A(1) B(2) C(3)], вставляем D(2):
|
Очередь полна [A(1) B(2) C(3)], вставляем D(2):
|
||||||
→ D приоритетнее C(3) → C вытесняется → [A(1) B(2) D(2)] PQ_EVICTED
|
→ D приоритетнее C(3) → C вытесняется → [A(1) B(2) D(2)] PQ_EVICTED
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ typedef int (*pq_cmp_fn)(const void *a, const void *b);
|
||||||
```
|
```
|
||||||
|
|
||||||
| Возврат | Смысл |
|
| Возврат | Смысл |
|
||||||
|---|---|
|
| ------- | ---------------------------------------- |
|
||||||
| `< 0` | `a` стоит перед `b` (a приоритетнее) |
|
| `< 0` | `a` стоит перед `b` (a приоритетнее) |
|
||||||
| `> 0` | `b` стоит перед `a` |
|
| `> 0` | `b` стоит перед `a` |
|
||||||
| `0` | равнозначны; порядок вставки сохраняется |
|
| `0` | равнозначны; порядок вставки сохраняется |
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
единственного consumer.
|
единственного consumer.
|
||||||
|
|
||||||
| Параметр | Значение |
|
| Параметр | Значение |
|
||||||
|---|---|
|
| ------------- | ------------------------------------------------------------------------------ |
|
||||||
| Элемент | 1 байт (`uint8_t`) |
|
| Элемент | 1 байт (`uint8_t`) |
|
||||||
| Ёмкость | любая степень двойки, задаётся при `ring_buffer_init` |
|
| Ёмкость | любая степень двойки, задаётся при `ring_buffer_init` |
|
||||||
| Thread-safety | SPSC без блокировок; multi-producer/consumer — только с внешней синхронизацией |
|
| Thread-safety | SPSC без блокировок; multi-producer/consumer — только с внешней синхронизацией |
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue