| .. | ||
| include/bsp | ||
| src | ||
| CMakeLists.txt | ||
| README.md | ||
bsp_qspi_flash — QSPI Flash W25Q64/128/256/512
Драйвер QSPI Flash на FlexSPI1 с поддержкой четырёх чипов Winbond. XIP-безопасен: все функции, трогающие FlexSPI IP-регистры, размещены в ITCM и выполняются под IRQ lock.
Аппаратура
| Параметр | Значение |
|---|---|
| Интерфейс MCU | FlexSPI1, порт A1 |
Поддерживаемые чипы:
| Чип | JEDEC mfr | JEDEC cap | Размер | Адресация |
|---|---|---|---|---|
| W25Q64 | 0xEF |
0x17 |
8 MB | 3-byte |
| W25Q128 | 0xEF |
0x18 |
16 MB | 3-byte |
| W25Q256 | 0xEF |
0x19 |
32 MB | 4-byte |
| W25Q512 | 0xEF |
0x20 |
64 MB | 4-byte |
Архитектура
XIP-безопасность
Прошивка исполняется XIP из Flash по AHB. Любая IP-команда FlexSPI блокирует AHB-путь — если в этот момент CPU фетчит инструкцию из Flash, происходит HardFault.
Решение — два уровня защиты:
flowchart LR
A["публичная функция\nbsp_qspi_*(...)"] --> B["IRQ lock\n__get_PRIMASK + DSB + ISB"]
B --> C["AHBCR.PREFETCHEN = 0"]
C --> D["IP-команда FlexSPI\nиз ITCM\n(AT_QUICKACCESS_SECTION_CODE)"]
D --> E["AHBCR.PREFETCHEN = 1"]
E --> F["IRQ unlock\nвосстановить PRIMASK"]
ITCM (0x00000000) подключён к CPU по выделенной шине — фетч инструкций
не конкурирует с AHB. IRQ lock гарантирует, что прерывание не застанет
FlexSPI в середине IP-транзакции; PRIMASK восстанавливается, не сбрасывается
безусловно — вызов из уже заблокированного контекста корректен.
Обязательный дефайн в CMakeLists потребителя:
target_compile_definitions(firmware_test PRIVATE
__STARTUP_INITIALIZE_RAMFUNCTION # ← без этого ITCM содержит нули → HardFault
__STARTUP_CLEAR_BSS
)
Адресация W25Q256/512
Вместо Enter 4-Byte Mode (0xB7) используются dedicated 4-byte opcodes — XIP-слот 0
(24-bit адресация) не изменяется:
| Операция | W25Q64/128 | W25Q256/512 |
|---|---|---|
| Sector Erase 4KB | 0x20 |
0x21 |
| Block Erase 32KB | 0x52 |
0x5C |
| Block Erase 64KB | 0xD8 |
0xDC |
| Quad Page Prog | 0x32 |
0x34 |
| IP Quad Out Read | 0x6B |
0x6C |
API
/* Инициализация — вызвать до bsp_tick_init() */
bsp_status_t bsp_qspi_init(void);
/* Идентификация */
bsp_status_t bsp_qspi_read_jedec_id(bsp_qspi_jedec_t *p_jedec);
uint32_t bsp_qspi_flash_size(void); /* доступно после init() */
/* Стирание */
bsp_status_t bsp_qspi_erase_sector(uint32_t addr); /* 4 KB, ~45 мс */
bsp_status_t bsp_qspi_erase_block_32k(uint32_t addr); /* 32 KB, ~120 мс */
bsp_status_t bsp_qspi_erase_block_64k(uint32_t addr); /* 64 KB, ~150 мс */
/* Запись одной страницы (256 байт, адрес выровнен на BSP_QSPI_PAGE_SIZE) */
bsp_status_t bsp_qspi_write_page(uint32_t addr, const uint8_t *p_data); /* ~3 мс */
/* Чтение через IP-команду (не AHB/XIP) */
bsp_status_t bsp_qspi_read(uint32_t addr, uint8_t *p_buf, size_t size);
Все функции возвращают BSP_OK или BSP_ERR_HW.
Выбор операции стирания:
| Объём | Рекомендация | Время |
|---|---|---|
| < 32 KB | erase_sector 4KB |
~45 мс × N |
| 32 KB – 1 MB | erase_block_32k |
~120 мс / 32KB |
| > 1 MB | erase_block_64k |
~150 мс / 64KB |
Пример: 4 MB через 64KB = 64 × 150 мс ≈ 9.6 с против 46 с через 4KB.
Быстрый старт
#include "bsp/qspi_flash.h"
/* main.c — порядок инициализации: */
board_hw_init();
bsp_qspi_init(); /* ← до bsp_tick_init() */
bsp_tick_init();
/* Идентификация чипа */
bsp_qspi_jedec_t jedec;
bsp_qspi_read_jedec_id(&jedec);
/* Стереть сектор и записать страницу */
bsp_qspi_erase_sector(0x00010000);
uint8_t page[256] = { /* ... */ };
bsp_qspi_write_page(0x00010000, page);
/* Прочитать обратно */
uint8_t buf[256];
bsp_qspi_read(0x00010000, buf, sizeof(buf));
CMake
target_link_libraries(firmware_test PRIVATE bsp_qspi_flash)
target_compile_definitions(firmware_test PRIVATE
__STARTUP_INITIALIZE_RAMFUNCTION
__STARTUP_CLEAR_BSS
)
Зависимости модуля:
| Зависимость | Тип | Описание |
|---|---|---|
bsp_status |
PUBLIC | bsp_status_t в публичном API |
bsp_board |
PRIVATE | Транзитивно: clock, SDK headers |
sdk_flexspi |
PRIVATE | fsl_flexspi.h — FlexSPI IP-команды |