7.0 KiB
Портирование 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
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. Приёмочные проверки
- На пустой шине получить
SPI_NOR_E_NOT_FOUNDи CS=1. - Прочитать ожидаемый JEDEC.
- Проверить чтение первого и последнего адреса.
- В тестовом секторе выполнить erase и полный verify
0xFF. - Записать данные через границу страницы и сравнить readback.
- Имитировать SPI error/timeout и убедиться, что CS поднят.
- Проверить отказ для диапазона за концом памяти и невыровненного erase.
- При 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-порт не нужен:
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.