# 2
- Набросок структуру будущего bsp
This commit is contained in:
parent
806b41e0a5
commit
fdc7ce5870
3 changed files with 545 additions and 0 deletions
203
bsp/README.md
Normal file
203
bsp/README.md
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
# BSP — Board Support Package
|
||||||
|
|
||||||
|
> Целевая платформа: NXP IMXRT1052CVJ5B
|
||||||
|
> Используется в: `firmware/bootloader`, `firmware/test`, `firmware/tft_app`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Концепция
|
||||||
|
|
||||||
|
BSP — единственное место в монорепо где есть знание о конкретном железе. Все три прошивки работают с периферией только через BSP. Прямых вызовов NXP SDK (`fsl_*.h`) за пределами `bsp/` быть не должно.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
firmware/test firmware/bootloader firmware/tft_app
|
||||||
|
↓ ↓ ↓
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ BSP │
|
||||||
|
│ bsp_usb_cdc bsp_uart bsp_can bsp_sdram ... │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
↓ ↓ ↓
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ NXP SDK / middleware │
|
||||||
|
│ fsl_lpuart fsl_flexcan usb stack ... │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bsp/
|
||||||
|
├── CMakeLists.txt # корневой: add_subdirectory для всех компонентов
|
||||||
|
├── README.md # этот файл
|
||||||
|
│
|
||||||
|
├── generated/ # ← MCUXpresso Config Tools, не редактировать руками
|
||||||
|
│ ├── board.c / board.h
|
||||||
|
│ ├── clock_config.c / clock_config.h
|
||||||
|
│ ├── pin_mux.c / pin_mux.h
|
||||||
|
│ ├── peripherals.c / peripherals.h
|
||||||
|
│ └── startup/
|
||||||
|
│ └── startup_MIMXRT1052.S
|
||||||
|
│
|
||||||
|
├── usb_cdc/ # USB CDC ACM (Virtual COM Port)
|
||||||
|
├── uart/ # LPUART: TTL + изолированный RX +24V
|
||||||
|
├── can/ # FlexCAN
|
||||||
|
├── sdram/ # SEMC → SDRAM 32 MB (MT48LC16M16)
|
||||||
|
├── qspi/ # FlexSPI → W25Q128 (QSPI Flash)
|
||||||
|
├── sdio/ # uSDHC → uSD слот
|
||||||
|
├── display/ # eLCDIF → RGB888
|
||||||
|
├── gpio/ # кнопки, LED, гальванически развязанные входы
|
||||||
|
└── mqs/ # MQS → аналоговый аудио выход
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Компоненты CMake
|
||||||
|
|
||||||
|
Каждый компонент — отдельная статическая библиотека `bsp_<name>`.
|
||||||
|
|
||||||
|
### bsp_board — фундамент, от которого зависят все остальные
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
target_link_libraries(bsp_<любой_компонент> PUBLIC bsp_board)
|
||||||
|
```
|
||||||
|
|
||||||
|
`bsp_board` содержит: стартап, clock config, pin mux, board init. Формируется из `generated/` и не должен меняться руками — только через MCUXpresso Config Tools с последующей перегенерацией.
|
||||||
|
|
||||||
|
### Boot-сценарии — INTERFACE-библиотеки
|
||||||
|
|
||||||
|
Каждая прошивка выбирает один сценарий исполнения кода:
|
||||||
|
|
||||||
|
| Таргет CMake | Сценарий | Кто использует |
|
||||||
|
|---|---|---|
|
||||||
|
| `bsp_boot_xip` | XIP — код исполняется из Flash | `firmware/test`, `firmware/tft_app` |
|
||||||
|
| `bsp_boot_itcm` | копирование в ITCM | `firmware/bootloader` |
|
||||||
|
| `bsp_boot_sdram` | копирование в SDRAM | зарезервировано |
|
||||||
|
|
||||||
|
Подключается явно в каждом проекте:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
target_link_libraries(firmware_test PRIVATE bsp_board bsp_boot_xip ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Компоненты периферии
|
||||||
|
|
||||||
|
Каждый компонент подключается независимо — прошивка линкует только то что использует:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
# firmware/test — использует всё
|
||||||
|
target_link_libraries(firmware_test PRIVATE
|
||||||
|
bsp_board bsp_boot_xip
|
||||||
|
bsp_usb_cdc bsp_uart bsp_can
|
||||||
|
bsp_sdram bsp_qspi bsp_sdio
|
||||||
|
bsp_rtc bsp_display bsp_gpio bsp_ir
|
||||||
|
)
|
||||||
|
|
||||||
|
# firmware/bootloader — минимальный набор
|
||||||
|
target_link_libraries(bootloader PRIVATE
|
||||||
|
bsp_board bsp_boot_itcm
|
||||||
|
bsp_usb_cdc bsp_qspi bsp_uart
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Правила написания компонентов
|
||||||
|
|
||||||
|
### Граница изоляции
|
||||||
|
|
||||||
|
Публичные заголовки компонента (`include/bsp/*.h`) не должны содержать ни одного `#include` из NXP SDK. Снаружи BSP — только стандартные типы C (`stdint.h`, `stdbool.h`, `stddef.h`) и собственные типы проекта.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* ПРАВИЛЬНО — bsp/usb_cdc/include/bsp/usb_cdc.h */
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
typedef enum { USB_CDC_OK, USB_CDC_ERR_NOT_READY } usb_cdc_status_t;
|
||||||
|
usb_cdc_status_t usb_cdc_init(void);
|
||||||
|
|
||||||
|
/* НЕПРАВИЛЬНО */
|
||||||
|
#include "fsl_common.h" /* ← утечка NXP SDK наружу */
|
||||||
|
```
|
||||||
|
|
||||||
|
Платформенные хедеры (`fsl_*.h`, `usb_device_*.h`) живут только в `src/` — как PRIVATE зависимости.
|
||||||
|
|
||||||
|
### Структура одного компонента
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bsp/<name>/
|
||||||
|
├── CMakeLists.txt
|
||||||
|
├── include/
|
||||||
|
│ └── bsp/
|
||||||
|
│ └── <name>.h # публичный API — без NXP хедеров
|
||||||
|
└── src/
|
||||||
|
├── <name>.c # реализация
|
||||||
|
└── <конфиг>.h # приватные конфиги стека (напр. usb_device_config.h)
|
||||||
|
```
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
# bsp/<name>/CMakeLists.txt — шаблон
|
||||||
|
add_library(bsp_<name> STATIC src/<name>.c)
|
||||||
|
|
||||||
|
target_include_directories(bsp_<name>
|
||||||
|
PUBLIC include/ # bsp/<name>.h доступен снаружи
|
||||||
|
PRIVATE src/ # конфиги и NXP хедеры — только внутри
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(bsp_<name>
|
||||||
|
PUBLIC bsp_board # транзитивно во все потребители
|
||||||
|
PRIVATE sdk_<driver> # NXP SDK — не торчит наружу
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Защита от host-сборки
|
||||||
|
|
||||||
|
Каждый компонент должен быть безопасен при `BUILD_TESTS_HOST=ON`. Вариантов два:
|
||||||
|
|
||||||
|
**А — guard в CMakeLists (рекомендуется для большинства компонентов):**
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
if(BUILD_TESTS_HOST)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Б — stub-реализация для компонентов которые тестируются на хосте:**
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* src/usb_cdc.c */
|
||||||
|
#ifdef BSP_USB_CDC_VIRTUAL
|
||||||
|
/* заглушка — пишет в stdout, используется в host-тестах */
|
||||||
|
usb_cdc_status_t usb_cdc_write(const uint8_t *data, size_t len) {
|
||||||
|
fwrite(data, 1, len, stdout);
|
||||||
|
return USB_CDC_OK;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* реальная реализация через NXP USB stack */
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Связь с generated/
|
||||||
|
|
||||||
|
`generated/` — выхлоп MCUXpresso Config Tools. Содержит конфигурацию тактирования, пинов и периферии для конкретной платы.
|
||||||
|
|
||||||
|
**Что трогать можно:** файлы в `generated/` можно и нужно перегенерировать через Config Tools при изменении схемы.
|
||||||
|
|
||||||
|
**Что трогать нельзя:** редактировать `generated/` руками — изменения потеряются при следующей перегенерации.
|
||||||
|
|
||||||
|
**Как добавить новый пин или периферию:** открыть проект в MCUXpresso Config Tools → внести изменения → Update Code → закоммитить изменённые файлы из `generated/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Добавление нового компонента — чеклист
|
||||||
|
|
||||||
|
```bash
|
||||||
|
[ ] Создать bsp/<name>/ со структурой include/src/CMakeLists.txt
|
||||||
|
[ ] Публичный хедер include/bsp/<name>.h — без NXP хедеров
|
||||||
|
[ ] target_link_libraries: PUBLIC bsp_board, PRIVATE sdk_*
|
||||||
|
[ ] Guard BUILD_TESTS_HOST в CMakeLists или stub-реализация в .c
|
||||||
|
[ ] add_subdirectory(bsp/<name>) в bsp/CMakeLists.txt
|
||||||
|
[ ] Добавить target в нужные прошивки (firmware/*/CMakeLists.txt)
|
||||||
|
```
|
||||||
342
bsp/usb_cdc/README.md
Normal file
342
bsp/usb_cdc/README.md
Normal file
|
|
@ -0,0 +1,342 @@
|
||||||
|
# USB — теория для embedded-разработчика
|
||||||
|
|
||||||
|
> Памятка: фокус на CDC ACM (Virtual COM Port) для NXP IMXRT1052
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Основы архитектуры USB
|
||||||
|
|
||||||
|
USB — это **master-slave** шина. Хост всегда инициирует обмен, устройство только отвечает. Никакой "самодеятельности" от устройства быть не может — только реакция на запросы хоста.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
HOST (PC) DEVICE (MCU)
|
||||||
|
────────── ────────────
|
||||||
|
OS USB stack USB device stack
|
||||||
|
↕ ↕
|
||||||
|
Host controller (xHCI/EHCI) ←→ Device controller (EHCI на IMXRT)
|
||||||
|
D+ D− VBUS GND
|
||||||
|
```
|
||||||
|
|
||||||
|
### Физический уровень
|
||||||
|
|
||||||
|
| Параметр | USB Full Speed | USB High Speed |
|
||||||
|
|----------|---------------|----------------|
|
||||||
|
| Скорость | 12 Мбит/с | 480 Мбит/с |
|
||||||
|
| IMXRT1052 | ✅ | ✅ |
|
||||||
|
| Практическая пропускная способность BULK | ~1 МБ/с | ~40 МБ/с |
|
||||||
|
| Применение | CDC ACM, HID | MSD, Video |
|
||||||
|
|
||||||
|
IMXRT1052 имеет два USB контроллера: `USB1` (OTG, EHCI) и `USB2` (Host only). Для CDC ACM используем `USB1`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Ключевые понятия
|
||||||
|
|
||||||
|
### Дескрипторы
|
||||||
|
|
||||||
|
Дескрипторы — это набор структур, которые устройство возвращает хосту при подключении (в ответ на `GET_DESCRIPTOR`). Хост читает их и решает, какой драйвер загрузить.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Device Descriptor
|
||||||
|
└── Configuration Descriptor
|
||||||
|
├── Interface Descriptor #0 (CDC Control)
|
||||||
|
│ ├── CDC Header Functional Descriptor
|
||||||
|
│ ├── CDC Call Management Descriptor
|
||||||
|
│ ├── CDC ACM Functional Descriptor
|
||||||
|
│ ├── CDC Union Functional Descriptor
|
||||||
|
│ └── Endpoint Descriptor (INT IN)
|
||||||
|
└── Interface Descriptor #1 (CDC Data)
|
||||||
|
├── Endpoint Descriptor (BULK IN)
|
||||||
|
└── Endpoint Descriptor (BULK OUT)
|
||||||
|
```
|
||||||
|
|
||||||
|
Важные поля `Device Descriptor`:
|
||||||
|
|
||||||
|
| Поле | Значение | Смысл |
|
||||||
|
|------|----------|-------|
|
||||||
|
| `bDeviceClass` | 0xEF | Composite (классы на уровне интерфейсов) |
|
||||||
|
| `idVendor` | 0x1FC9 | VID NXP (или свой) |
|
||||||
|
| `idProduct` | произвольный | PID — идентификатор продукта |
|
||||||
|
| `bcdUSB` | 0x0200 | USB 2.0 |
|
||||||
|
|
||||||
|
### Endpoints (эндпоинты)
|
||||||
|
|
||||||
|
Эндпоинт — это буфер в устройстве с определённым направлением и типом передачи. EP0 — всегда управляющий (Control), остальные — настраиваются.
|
||||||
|
|
||||||
|
| Тип | Гарантия доставки | Применение |
|
||||||
|
|-----|------------------|------------|
|
||||||
|
| Control | да | конфигурация устройства, EP0 |
|
||||||
|
| Bulk | да (retry) | большие данные, CDC ACM данные |
|
||||||
|
| Interrupt | да (периодически) | HID, CDC ACM нотификации |
|
||||||
|
| Isochronous | нет | аудио, видео |
|
||||||
|
|
||||||
|
**Для CDC ACM нужны три эндпоинта:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
EP0 Control IN/OUT — управление (всегда есть, не конфигурируется)
|
||||||
|
EP1 INT IN — нотификации CDC (DTR, RTS — наследие модемов)
|
||||||
|
EP2 BULK IN — данные device → host (твои JSON-строки → PC)
|
||||||
|
EP3 BULK OUT — данные host → device (команды PC → плата)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enumeration — что происходит при подключении кабеля
|
||||||
|
|
||||||
|
```bash
|
||||||
|
1. Хост видит устройство (pull-up на D+)
|
||||||
|
2. USB Reset (SE0, 10 мс)
|
||||||
|
3. GET_DESCRIPTOR(Device) → хост узнаёт VID/PID, версию USB
|
||||||
|
4. SET_ADDRESS → устройство получает адрес на шине (1–127)
|
||||||
|
5. GET_DESCRIPTOR(Configuration) → хост видит интерфейсы
|
||||||
|
6. GET_DESCRIPTOR(String) × N → имена для Device Manager
|
||||||
|
7. SET_CONFIGURATION(1) → USB stack поднимает эндпоинты
|
||||||
|
→ на стороне устройства срабатывает callback kUSB_DeviceEventSetConfiguration
|
||||||
|
8. Хост загружает драйвер по (bDeviceClass, idVendor, idProduct)
|
||||||
|
→ CDC ACM: cdc_acm.ko (Linux) / usbser.sys (Windows)
|
||||||
|
9. Появляется /dev/ttyACM0 или COM3
|
||||||
|
10. Пользователь открывает порт → хост посылает SET_CONTROL_LINE_STATE с DTR=1
|
||||||
|
→ устройство видит "хост подключён"
|
||||||
|
```
|
||||||
|
|
||||||
|
Шаг 10 критически важен: **пока терминал не открыт — DTR = 0**. Слать данные до появления DTR бессмысленно — хост их не читает.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. CDC ACM — детали класса
|
||||||
|
|
||||||
|
CDC (Communications Device Class) — класс для коммуникационных устройств. ACM (Abstract Control Model) — подкласс, изначально для модемов, сейчас стандарт де-факто для Virtual COM Port.
|
||||||
|
|
||||||
|
### Почему CDC ACM а не другие классы
|
||||||
|
|
||||||
|
| Класс | Что видит OS | Проблема |
|
||||||
|
|-------|-------------|----------|
|
||||||
|
| **CDC ACM** | `/dev/ttyACM0`, `COM3` | — нет, это и нужно |
|
||||||
|
| Vendor | ничего | нужен свой драйвер под каждую ОС |
|
||||||
|
| HID | `/dev/hidraw0` | пакет максимум 64 байта, неудобно |
|
||||||
|
| MSC | блочное устройство | совсем не то |
|
||||||
|
|
||||||
|
Главное преимущество CDC ACM: **стандартный драйвер есть везде** — Linux, Windows 10+, macOS — без установки чего-либо.
|
||||||
|
|
||||||
|
### Ограничения которые надо знать
|
||||||
|
|
||||||
|
**USB CDC не гарантирует границы сообщений.** Данные идут потоком через BULK-эндпоинты. Если ты послал `{"type":"result"}\n{"type":"summary"}\n` — хост может получить это как один кусок, два куска, или три куска произвольного размера.
|
||||||
|
|
||||||
|
Поэтому **всегда нужен frame delimiter**. В нашем проекте — символ `\n` (JSON-lines). Приёмная сторона буферизирует до `\n` и только тогда парсит JSON.
|
||||||
|
|
||||||
|
**Скорость** не ограничена физическими 115200 бод как у UART. USB Full Speed BULK даёт практически ~1 МБ/с. Baudrate в настройках терминала для CDC ACM — декоративный, реально на скорость не влияет.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. NXP USB Stack на IMXRT1052
|
||||||
|
|
||||||
|
### Архитектура стека
|
||||||
|
|
||||||
|
```bash
|
||||||
|
твой код (bsp_usb_cdc)
|
||||||
|
↕ callbacks + API
|
||||||
|
usb_device_cdc_acm.c ← CDC ACM класс (middleware/usb/device/class/)
|
||||||
|
↕
|
||||||
|
usb_device_dci.c ← Device Controller Interface (middleware/usb/device/)
|
||||||
|
↕
|
||||||
|
usb_device_ehci.c ← EHCI контроллер (middleware/usb/device/)
|
||||||
|
↕
|
||||||
|
USB PHY (usb_phy.c) ← физический уровень (middleware/usb/phy/)
|
||||||
|
↕
|
||||||
|
EHCI hardware registers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Callback-архитектура
|
||||||
|
|
||||||
|
NXP USB stack работает через callbacks — ты не вызываешь функции стека для приёма данных, стек сам вызывает твои функции когда что-то происходит.
|
||||||
|
|
||||||
|
Два уровня callbacks:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* 1. Callback уровня устройства — системные события */
|
||||||
|
usb_status_t USB_DeviceCallback(usb_device_handle handle,
|
||||||
|
uint32_t event,
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case kUSB_DeviceEventBusReset:
|
||||||
|
/* сброс шины — переинициализировать эндпоинты */
|
||||||
|
break;
|
||||||
|
case kUSB_DeviceEventSetConfiguration:
|
||||||
|
/* хост завершил enumeration — можно начинать работать */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Callback уровня CDC ACM класса — данные и управление */
|
||||||
|
usb_status_t USB_DeviceCdcAcmCallback(class_handle_t handle,
|
||||||
|
uint32_t event,
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case kUSB_DeviceCdcEventSendResponse:
|
||||||
|
/* BULK IN передача завершена — буфер можно переиспользовать */
|
||||||
|
break;
|
||||||
|
case kUSB_DeviceCdcEventRecvResponse:
|
||||||
|
/* BULK OUT данные получены — param указывает на буфер */
|
||||||
|
break;
|
||||||
|
case kUSB_DeviceCdcEventSetControlLineState:
|
||||||
|
/* DTR/RTS изменились — проверяем подключение хоста */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### usb_device_config.h — конфигурационный файл
|
||||||
|
|
||||||
|
NXP USB stack требует конфигурационный хедер. Он **не входит в SDK** — его пишешь ты и кладёшь в `bsp/usb_cdc/src/`. Ключевые параметры:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* bsp/usb_cdc/src/usb_device_config.h */
|
||||||
|
|
||||||
|
/* Тип контроллера: EHCI для IMXRT1052 */
|
||||||
|
#define USB_DEVICE_CONFIG_EHCI 1
|
||||||
|
|
||||||
|
/* Включаем CDC ACM класс */
|
||||||
|
#define USB_DEVICE_CONFIG_CDC_ACM 1
|
||||||
|
|
||||||
|
/* Количество одновременных CDC инстансов */
|
||||||
|
#define USB_DEVICE_CONFIG_CDC_ACM_INSTANCE_COUNT 1
|
||||||
|
|
||||||
|
/* Количество эндпоинтов (EP0 + INT + BULK IN + BULK OUT = 4) */
|
||||||
|
#define USB_DEVICE_CONFIG_ENDPOINTS 4
|
||||||
|
|
||||||
|
/* Размер BULK буферов (степень двойки, FS max = 64 байта на транзакцию,
|
||||||
|
но можно использовать большие буферы для нескольких транзакций) */
|
||||||
|
#define USB_DEVICE_CONFIG_CDC_ACM_MAX_DATAPIPE_SIZE 512
|
||||||
|
|
||||||
|
/* Bare-metal (без RTOS) */
|
||||||
|
#define USB_DEVICE_CONFIG_USE_TASK 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### IRQ и polling
|
||||||
|
|
||||||
|
На IMXRT1052 USB работает через прерывания. Стек нужно "тикать" из ISR:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* в startup или IRQ handler регистрации */
|
||||||
|
void USB_OTG1_IRQHandler(void) {
|
||||||
|
USB_DeviceEhciIsrFunction(g_usb_device_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
В bare-metal также нужен периодический вызов `USB_DeviceTaskFunction()` из main loop — он обрабатывает отложенные события которые нельзя делать прямо в ISR.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Практические моменты для firmware_test
|
||||||
|
|
||||||
|
### Инициализация — правильный порядок
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* 1. Clock init — USB PLL должен быть поднят ДО USB init */
|
||||||
|
CLOCK_InitUsb1Pll(...); /* 480 MHz USB PLL */
|
||||||
|
CLOCK_InitUsb1Pfd(...);
|
||||||
|
|
||||||
|
/* 2. PHY init */
|
||||||
|
USB_EhciPhyInit(CONTROLLER_ID, CLK_USRPH_24MHZ, NULL);
|
||||||
|
|
||||||
|
/* 3. Device stack init */
|
||||||
|
USB_DeviceInit(CONTROLLER_ID, USB_DeviceCallback, &handle);
|
||||||
|
|
||||||
|
/* 4. Регистрация CDC ACM класса */
|
||||||
|
USB_DeviceCdcAcmInit(...);
|
||||||
|
|
||||||
|
/* 5. Старт */
|
||||||
|
USB_DeviceRun(handle);
|
||||||
|
```
|
||||||
|
|
||||||
|
Если clock не инициализирован до USB — enumeration не пройдёт, хост увидит "USB device not recognized".
|
||||||
|
|
||||||
|
### Определение факта подключения хоста
|
||||||
|
|
||||||
|
Не надо проверять "есть ли питание на VBUS". Правильный способ — смотреть на **DTR флаг** из `SET_CONTROL_LINE_STATE`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static volatile bool s_host_connected = false;
|
||||||
|
|
||||||
|
/* внутри USB_DeviceCdcAcmCallback */
|
||||||
|
case kUSB_DeviceCdcEventSetControlLineState: {
|
||||||
|
usb_device_cdc_acm_request_param_struct_t *p = param;
|
||||||
|
/* бит 0 = DTR, бит 1 = RTS */
|
||||||
|
s_host_connected = (p->setupValue & 0x01) != 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool usb_cdc_is_connected(void) {
|
||||||
|
return s_host_connected;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Буферизация
|
||||||
|
|
||||||
|
NXP USB stack не буферизует — это твоя ответственность. Минимальная схема:
|
||||||
|
|
||||||
|
```
|
||||||
|
TX: кольцевой буфер → usb_cdc_write() кладёт туда данные
|
||||||
|
→ USB task вычитывает и передаёт через USB_DeviceCdcAcmSend()
|
||||||
|
→ по kUSB_DeviceCdcEventSendResponse — можно слать следующий чанк
|
||||||
|
|
||||||
|
RX: USB_DeviceCdcAcmRecv() регистрирует буфер для приёма
|
||||||
|
→ по kUSB_DeviceCdcEventRecvResponse — данные в буфере
|
||||||
|
→ приложение вычитывает до '\n' и парсит JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
### Важно: двойная буферизация TX
|
||||||
|
|
||||||
|
`USB_DeviceCdcAcmSend()` принимает указатель на буфер и **не копирует данные**. Буфер должен жить до получения `kUSB_DeviceCdcEventSendResponse`. Типичная ошибка — передать указатель на локальную переменную.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* НЕПРАВИЛЬНО */
|
||||||
|
void send_something(void) {
|
||||||
|
char buf[64];
|
||||||
|
snprintf(buf, sizeof(buf), "{\"type\":\"result\"}\n");
|
||||||
|
USB_DeviceCdcAcmSend(handle, EP_BULK_IN, (uint8_t*)buf, strlen(buf));
|
||||||
|
/* buf уходит из стека — UB! */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ПРАВИЛЬНО — статический или глобальный буфер */
|
||||||
|
static uint8_t s_tx_buf[512];
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Схема эндпоинтов для дескрипторов
|
||||||
|
|
||||||
|
```
|
||||||
|
EP номер Направление Тип Размер пакета Назначение
|
||||||
|
──────── ─────────── ───────── ───────────── ──────────
|
||||||
|
EP0 IN + OUT Control 64 байта enumeration (автоматически)
|
||||||
|
EP1 IN Interrupt 16 байт CDC нотификации (DTR/RTS events)
|
||||||
|
EP2 IN Bulk 64 байта (FS) данные device → host
|
||||||
|
EP3 OUT Bulk 64 байта (FS) данные host → device
|
||||||
|
```
|
||||||
|
|
||||||
|
Номера EP назначаются в дескрипторах. NXP примеры используют именно эту схему для Full Speed CDC ACM.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Отладочные признаки проблем
|
||||||
|
|
||||||
|
| Симптом | Вероятная причина |
|
||||||
|
|---------|------------------|
|
||||||
|
| "USB device not recognized" на хосте | не инициализирован USB PLL / PHY |
|
||||||
|
| Устройство определяется, порт не появляется | ошибка в дескрипторах (класс, подкласс, протокол) |
|
||||||
|
| Порт появился, данные не идут | DTR не поднят (терминал не открыт) или ошибка TX буферизации |
|
||||||
|
| Данные обрываются / мусор | буфер TX освобождается до SendResponse |
|
||||||
|
| Работает раз через раз | нет re-submit RX буфера после RecvResponse |
|
||||||
|
| Зависает при переподключении | нет обработки kUSB_DeviceEventBusReset → не сбрасываются эндпоинты |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Ссылки
|
||||||
|
|
||||||
|
- `sdk/middleware/usb/` — исходники NXP USB stack
|
||||||
|
- `sdk/boards/evkbimxrt1050/usb_examples/usb_device_cdc_vcom/` — референсный пример
|
||||||
|
- `sdk/middleware/usb/device/class/usb_device_cdc_acm.c` — реализация класса
|
||||||
|
- `sdk/middleware/usb/include/usb_device_cdc_acm.h` — API класса
|
||||||
|
- USB 2.0 Specification — [usb.org](https://www.usb.org/document-library/usb-20-specification)
|
||||||
|
- USB CDC Specification (PSTN) — [usb.org](https://www.usb.org/document-library/class-definitions-communication-devices-12)
|
||||||
Loading…
Reference in a new issue