124 lines
7.0 KiB
Markdown
124 lines
7.0 KiB
Markdown
# Портирование SPI NOR на другую платформу
|
||
|
||
## 1. Добавить переносимое ядро
|
||
|
||
В сборку новой цели включите `Src/spi_nor.c`, а `Inc` добавьте в include path.
|
||
Это единственные обязательные файлы. Они используют только C99-заголовки
|
||
`stddef.h`, `stdint.h` и `string.h`.
|
||
|
||
## 2. Реализовать platform callbacks
|
||
|
||
Создайте собственный контекст шины и заполните `spi_nor_io_t`:
|
||
|
||
- `transfer` — полный синхронный SPI-обмен при уже активном CS;
|
||
- `chip_select` — `active=1` опускает CS, `active=0` поднимает его;
|
||
- `tick_ms` — монотонный 32-битный счётчик миллисекунд;
|
||
- `delay_ms` — задержка выхода из power-down и пауза BUSY polling;
|
||
- `lock/unlock` — необязательная пара для RTOS, оба либо заданы, либо `NULL`.
|
||
|
||
`transfer` должен поддерживать следующие варианты:
|
||
|
||
| `tx` | `rx` | Действие |
|
||
|---|---|---|
|
||
| не `NULL` | `NULL` | передать command или payload |
|
||
| `NULL` | не `NULL` | передавать dummy и сохранить входящие байты |
|
||
| не `NULL` | не `NULL` | full-duplex обмен, допустим для порта |
|
||
|
||
Callback не должен самостоятельно переключать CS. Ядро может вызвать transfer
|
||
дважды под одним CS: сначала command, затем data. Ошибка должна возвращаться как
|
||
`SPI_NOR_IO_ERROR` или `SPI_NOR_IO_TIMEOUT`.
|
||
|
||
## 3. Настроить SPI
|
||
|
||
Для W25Q/SST25 используйте master, 8 бит, MSB first и режим, разрешённый
|
||
datasheet. Частота не должна превышать предел микросхемы и возможности разводки.
|
||
CS перед `spi_nor_init()` должен быть настроен output push-pull и находиться в 1.
|
||
Проверьте питание, общую землю и отсутствие конфликта MISO с другими CS.
|
||
|
||
## 4. Инициализировать и выполнить probe
|
||
|
||
```c
|
||
spi_nor_t device;
|
||
spi_nor_io_t io = MakeMyPlatformIo();
|
||
spi_nor_config_t config = spi_nor_default_config();
|
||
|
||
config.erase_timeout_ms = 10000U; /* Пример для медленной микросхемы. */
|
||
if (spi_nor_init(&device, &io, &config) != SPI_NOR_OK) {
|
||
HandleConfigurationError();
|
||
}
|
||
if (spi_nor_probe(&device) != SPI_NOR_OK) {
|
||
HandleAbsentOrUnsupportedFlash();
|
||
}
|
||
```
|
||
|
||
До успешного probe read/program/erase отклоняются. Для диагностики неизвестной
|
||
памяти вызовите `spi_nor_get_last_jedec()` после неуспешного probe.
|
||
|
||
## 5. Подключить FlashStorage при необходимости
|
||
|
||
Добавьте в сборку:
|
||
|
||
- `Libraries/FlashStorage/Src/flash_storage.c`;
|
||
- `Libraries/FlashStorage/Adapter/SPI_NOR/Src/flash_storage_spi_nor_adapter.c`;
|
||
- include paths `FlashStorage/Inc` и `FlashStorage/Adapter/SPI_NOR/Inc`.
|
||
|
||
После probe создайте `flash_storage_spi_nor_adapter_t`, вызовите
|
||
`flash_storage_spi_nor_adapter_init()` и передайте `flash_storage_spi_nor_ops`
|
||
в `flash_storage_init()` или `eeprom_store_init()`. Layout должен лежать внутри
|
||
фактической `capacity_bytes` и быть выровнен по 4096 байтам.
|
||
|
||
## 6. STM32 HAL
|
||
|
||
Для другого STM32 можно использовать существующий порт, если семейство имеет
|
||
совместимый HAL API. Подключите `spi_nor_stm32f4_hal.c`, замените HAL include на
|
||
заголовок нужного семейства и переименуйте порт. Имена конкретных `hspi`, GPIO
|
||
и pin передавайте из приложения; не добавляйте их как `extern` в библиотеку.
|
||
|
||
Если используется DMA или RTOS, простой STM32F4 порт следует заменить:
|
||
|
||
- transfer обязан дождаться завершения DMA до возврата;
|
||
- lock/unlock должны использовать mutex, не semaphore из ISR;
|
||
- mutex должен быть общим для всех устройств на одной SPI-шине;
|
||
- callback delay должен освобождать CPU через RTOS delay, если это допустимо.
|
||
|
||
## 7. Приёмочные проверки
|
||
|
||
1. На пустой шине получить `SPI_NOR_E_NOT_FOUND` и CS=1.
|
||
2. Прочитать ожидаемый JEDEC.
|
||
3. Проверить чтение первого и последнего адреса.
|
||
4. В тестовом секторе выполнить erase и полный verify `0xFF`.
|
||
5. Записать данные через границу страницы и сравнить readback.
|
||
6. Имитировать SPI error/timeout и убедиться, что CS поднят.
|
||
7. Проверить отказ для диапазона за концом памяти и невыровненного erase.
|
||
8. При RTOS параллельно запустить два клиента и проверить mutex-анализатором.
|
||
|
||
Для текущего репозитория host mock запускается через
|
||
`python -m unittest tests.test_spi_nor_host` и не требует STM32 HAL.
|
||
|
||
## 8. Перенос read-only command service
|
||
|
||
Если нужен сервисный просмотр команд, добавьте в сборку
|
||
`Diagnostics/Src/spi_nor_command_service.c` и include path `Diagnostics/Inc`.
|
||
Передавайте ему уже успешно probed `spi_nor_t`; отдельный HAL-порт не нужен:
|
||
|
||
```c
|
||
spi_nor_command_service_request_t request = {0};
|
||
spi_nor_command_service_response_t response;
|
||
|
||
request.sequence = 1U;
|
||
request.tx_length = 1U;
|
||
request.rx_length = 3U;
|
||
request.tx[0] = 0x9FU;
|
||
(void)spi_nor_command_service_process(&device, &request, &response);
|
||
```
|
||
|
||
Не расширяйте существующий allowlist ради записи. Если продукту когда-нибудь
|
||
потребуются изменяющие операции, они должны иметь отдельный API, threat model,
|
||
защиту диапазонов и аппаратные тесты. Текущий сервис не содержит compile-time
|
||
или runtime переключателя, снимающего блокировку.
|
||
|
||
Проверьте mock-тестом своей платформы: TX/RX порядок под одним CS, освобождение
|
||
CS при timeout каждого transfer, общий mutex SPI bus, строгие максимумы 4/64 и
|
||
отказ для `06`, `02`, всех erase, status/protection writes, power/reset и
|
||
неизвестных opcode.
|