跳转至

rocketpi_w25qxx_littlefs

效果展示

image-20251216013059881

功能说明

  • 同时将内部flash与外部nor flash 挂载为littlefs文件系统

硬件连接

image-20251216012520852

驱动以及测试代码

Core/Src/main.c
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdbool.h>

#define LFS_NOR_FLASH_TOTAL_SIZE (8UL * 1024UL * 1024UL) /* W25Q64 = 8 MiB */
#include "lfs_nor_flash_port.h"
#include "lfs_port.h"
#include "lfs_flash_port.h"
#include "debug_driver.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
static lfs_t g_lfs;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
static void LittleFS_Test(bool use_nor_flash);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

typedef struct {
  int (*format)(lfs_t *lfs);
  int (*mount)(lfs_t *lfs);
  int (*unmount)(lfs_t *lfs);
  const char *label;
} lfs_storage_ops_t;

static void LittleFS_Test(bool use_nor_flash)
{
  struct lfs_file file;
  char read_buffer[64] = {0};
  const char *path = use_nor_flash ? "nor_flash.txt" : "int_flash.txt";
  const char *payload = use_nor_flash
                          ? "LittleFS NOR Flash Demo\r\n"
                          : "LittleFS Internal Flash Demo\r\n";

  const lfs_storage_ops_t ops = use_nor_flash
                                  ? (lfs_storage_ops_t){lfs_nor_flash_port_format, lfs_nor_flash_port_mount, lfs_nor_flash_port_unmount, "NOR"}
                                  : (lfs_storage_ops_t){lfs_flash_port_format, lfs_flash_port_mount, lfs_flash_port_unmount, "INT"};

  int err = ops.mount(&g_lfs);
  if (err != LFS_ERR_OK) {
    uart_printf("[%s] mount failed (%d), formatting...\r\n", ops.label, err);
    err = ops.format(&g_lfs);
    if (err == LFS_ERR_OK) {
      uart_printf("[%s] format done\r\n", ops.label);
      err = ops.mount(&g_lfs);
    } else {
      uart_printf("[%s] format failed (%d)\r\n", ops.label, err);
    }
  }

  if (err != LFS_ERR_OK) {
    uart_printf("[%s] mount failed (%d)\r\n", ops.label, err);
    return;
  }

  err = lfs_file_open(&g_lfs, &file, path,
                      LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
  if (err < 0) {
    uart_printf("[%s] open for write failed (%d)\r\n", ops.label, err);
    ops.unmount(&g_lfs);
    return;
  }

  lfs_ssize_t written = lfs_file_write(&g_lfs, &file, payload, strlen(payload));
  lfs_file_close(&g_lfs, &file);

  if (written < 0) {
    uart_printf("[%s] write failed (%ld)\r\n", ops.label, (long)written);
    ops.unmount(&g_lfs);
    return;
  }

  err = lfs_file_open(&g_lfs, &file, path, LFS_O_RDONLY);
  if (err < 0) {
    uart_printf("[%s] open for read failed (%d)\r\n", ops.label, err);
    ops.unmount(&g_lfs);
    return;
  }

  lfs_ssize_t read = lfs_file_read(&g_lfs, &file, read_buffer,
                                   sizeof(read_buffer) - 1U);
  lfs_file_close(&g_lfs, &file);

  if (read < 0) {
    uart_printf("[%s] read failed (%ld)\r\n", ops.label, (long)read);
    ops.unmount(&g_lfs);
    return;
  }

  read_buffer[read] = '\0';
  uart_printf("[%s] LittleFS OK (%ld bytes): %s\r\n", ops.label, (long)read, read_buffer);

  ops.unmount(&g_lfs);
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
    HAL_Delay(500);
  LittleFS_Test(false); /* internal Flash */
  LittleFS_Test(true);  /* external NOR Flash*/
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 84;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
bsp/w25qxx/driver_w25qxx_interface.c
/**
 * Copyright (c) 2015 - present LibDriver All rights reserved
 * 
 * The MIT License (MIT)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 *
 * @file      driver_w25qxx_interface_template.c
 * @brief     driver w25qxx interface template source file
 * @version   1.0.0
 * @author    Shifeng Li
 * @date      2021-07-15
 *
 * <h3>history</h3>
 * <table>
 * <tr><th>Date        <th>Version  <th>Author      <th>Description
 * <tr><td>2021/07/15  <td>1.0      <td>Shifeng Li  <td>first upload
 * </table>
 */

#include "driver_w25qxx_interface.h"

#include "main.h"
#include "spi.h"

#include <stdarg.h>
#include <stdio.h>

#define W25QXX_CMD_PAGE_PROGRAM           0x02U
#define W25QXX_CMD_QUAD_PAGE_PROGRAM      0x32U
#define W25QXX_CMD_FAST_READ              0x0BU
#define W25QXX_CMD_FAST_READ_QUAD_OUTPUT  0x6BU
#define W25QXX_CMD_FAST_READ_QUAD_IO      0xEBU
#define W25QXX_CMD_DEVICE_ID_QUAD_IO      0x94U

/* SPI2 handle comes from STM32CubeMX generated code */
extern SPI_HandleTypeDef hspi2;

/* ensure DWT based delay is configured before first use */
static void w25qxx_interface_dwt_delay_prepare(void)
{
    /* enable tracing and the cycle counter if not already enabled */
    if ((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk) == 0U)
    {
        CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    }
    if ((DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk) == 0U)
    {
        DWT->CYCCNT = 0U;
        DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
    }
}

/* chunked transmit helper since HAL uses uint16_t length */
static HAL_StatusTypeDef w25qxx_interface_spi_transmit(const uint8_t *data, uint32_t len)
{
    while (len > 0U)
    {
        uint16_t chunk = (len > 0xFFFFU) ? 0xFFFFU : (uint16_t)len;
        if (HAL_SPI_Transmit(&hspi2, (uint8_t *)data, chunk, HAL_MAX_DELAY) != HAL_OK)
        {
            return HAL_ERROR;
        }
        data += chunk;
        len  -= chunk;
    }

    return HAL_OK;
}

/* chunked receive helper since HAL uses uint16_t length */
static HAL_StatusTypeDef w25qxx_interface_spi_receive(uint8_t *data, uint32_t len)
{
    while (len > 0U)
    {
        uint16_t chunk = (len > 0xFFFFU) ? 0xFFFFU : (uint16_t)len;
        if (HAL_SPI_Receive(&hspi2, data, chunk, HAL_MAX_DELAY) != HAL_OK)
        {
            return HAL_ERROR;
        }
        data += chunk;
        len  -= chunk;
    }

    return HAL_OK;
}

/**
 * @brief  interface spi qspi bus init
 * @return status code
 *         - 0 success
 *         - 1 spi qspi init failed
 * @note   none
 */
uint8_t w25qxx_interface_spi_qspi_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* ensure chip-select GPIO is configured and idles high */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin   = W25QXX_CS_Pin;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(W25QXX_CS_GPIO_Port, &GPIO_InitStruct);
    HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET);

    /* initialise SPI2 if it hasn't been configured yet */
    if (HAL_SPI_GetState(&hspi2) == HAL_SPI_STATE_RESET)
    {
        MX_SPI2_Init();
    }

    if (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY)
    {
        return 1;
    }

    return 0;
}

/**
 * @brief  interface spi qspi bus deinit
 * @return status code
 *         - 0 success
 *         - 1 spi qspi deinit failed
 * @note   none
 */
uint8_t w25qxx_interface_spi_qspi_deinit(void)
{
    if (HAL_SPI_DeInit(&hspi2) != HAL_OK)
    {
        return 1;
    }

    HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET);

    return 0;
}

/**
 * @brief      interface spi qspi bus write read
 * @param[in]  instruction sent instruction
 * @param[in]  instruction_line instruction phy lines
 * @param[in]  address register address
 * @param[in]  address_line address phy lines
 * @param[in]  address_len address length
 * @param[in]  alternate register address
 * @param[in]  alternate_line alternate phy lines
 * @param[in]  alternate_len alternate length
 * @param[in]  dummy dummy cycle
 * @param[in]  *in_buf pointer to a input buffer
 * @param[in]  in_len input length
 * @param[out] *out_buf pointer to a output buffer
 * @param[in]  out_len output length
 * @param[in]  data_line data phy lines
 * @return     status code
 *             - 0 success
 *             - 1 write read failed
 * @note       none
 */
uint8_t w25qxx_interface_spi_qspi_write_read(uint8_t instruction, uint8_t instruction_line,
                                             uint32_t address, uint8_t address_line, uint8_t address_len,
                                             uint32_t alternate, uint8_t alternate_line, uint8_t alternate_len,
                                             uint8_t dummy, uint8_t *in_buf, uint32_t in_len,
                                             uint8_t *out_buf, uint32_t out_len, uint8_t data_line)
{
    uint8_t header[16];
    uint32_t header_len = 0U;
    uint32_t dummy_bytes = dummy / 8U;
    HAL_StatusTypeDef status;
    uint8_t effective_instruction = instruction;
    uint8_t effective_instruction_line = instruction_line;
    uint8_t effective_address_line = address_line;
    uint8_t effective_alternate_line = alternate_line;
    uint8_t effective_data_line = data_line;

    if ((effective_data_line > 1U) && (in_len > 0U))
    {
        if (effective_instruction == W25QXX_CMD_QUAD_PAGE_PROGRAM)
        {
            effective_instruction = W25QXX_CMD_PAGE_PROGRAM;
        }
    }
    if ((effective_data_line > 1U) && (out_len > 0U))
    {
        if ((effective_instruction == W25QXX_CMD_FAST_READ_QUAD_OUTPUT) ||
            (effective_instruction == W25QXX_CMD_FAST_READ_QUAD_IO))
        {
            effective_instruction = W25QXX_CMD_FAST_READ;
        }
    }
    if ((effective_instruction_line > 1U) || (effective_address_line > 1U) ||
        (effective_alternate_line > 1U) || (effective_data_line > 1U))
    {
        effective_instruction_line = (effective_instruction_line > 0U) ? 1U : 0U;
        effective_address_line = (address_len > 0U) ? 1U : 0U;
        effective_alternate_line = (alternate_len > 0U) ? 1U : 0U;
        effective_data_line = (in_len > 0U || out_len > 0U) ? 1U : 0U;
    }
    if ((in_len > 0U && in_buf == NULL) || (out_len > 0U && out_buf == NULL))
    {
        return 1;
    }
    if ((address_len > sizeof(uint32_t)) || (alternate_len > sizeof(uint32_t)))
    {
        return 1;
    }
    if ((dummy % 8U) != 0U)
    {
        dummy_bytes += 1U;
    }
    if ((header_len < sizeof(header)) && (effective_instruction_line != 0U))
    {
        header[header_len++] = effective_instruction;
    }

    if (address_len != 0U)
    {
        if ((header_len + address_len) > sizeof(header))
        {
            return 1;
        }
        for (uint8_t i = 0U; i < address_len; ++i)
        {
            uint8_t shift = (uint8_t)((address_len - 1U - i) * 8U);
            header[header_len++] = (uint8_t)((address >> shift) & 0xFFU);
        }
    }

    if (alternate_len != 0U)
    {
        if ((header_len + alternate_len) > sizeof(header))
        {
            return 1;
        }
        for (uint8_t i = 0U; i < alternate_len; ++i)
        {
            uint8_t shift = (uint8_t)((alternate_len - 1U - i) * 8U);
            header[header_len++] = (uint8_t)((alternate >> shift) & 0xFFU);
        }
    }

    HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_RESET);

    if (header_len > 0U)
    {
        status = w25qxx_interface_spi_transmit(header, header_len);
        if (status != HAL_OK)
        {
            HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET);
            return 1;
        }
    }

    if (in_len > 0U)
    {
        status = w25qxx_interface_spi_transmit(in_buf, in_len);
        if (status != HAL_OK)
        {
            HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET);
            return 1;
        }
    }

    if (dummy_bytes > 0U)
    {
        uint8_t dummy_buf[8] = {0};
        uint32_t remaining = dummy_bytes;
        while (remaining > 0U)
        {
            uint32_t send = (remaining > sizeof(dummy_buf)) ? sizeof(dummy_buf) : remaining;
            status = w25qxx_interface_spi_transmit(dummy_buf, send);
            if (status != HAL_OK)
            {
                HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET);
                return 1;
            }
            remaining -= send;
        }
    }

    if (out_len > 0U)
    {
        status = w25qxx_interface_spi_receive(out_buf, out_len);
        if (status != HAL_OK)
        {
            HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET);
            return 1;
        }
    }

    HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET);

    return 0;
}

/**
 * @brief     interface delay ms
 * @param[in] ms time
 * @note      none
 */
void w25qxx_interface_delay_ms(uint32_t ms)
{
    HAL_Delay(ms);
}

/**
 * @brief     interface delay us
 * @param[in] us time
 * @note      none
 */
void w25qxx_interface_delay_us(uint32_t us)
{
    if (us == 0U)
    {
        return;
    }

    w25qxx_interface_dwt_delay_prepare();

    uint32_t cycles_per_us = HAL_RCC_GetHCLKFreq() / 1000000U;
    uint32_t total_cycles = cycles_per_us * us;
    uint32_t start = DWT->CYCCNT;

    while ((DWT->CYCCNT - start) < total_cycles)
    {
        __NOP();
    }
}

/**
 * @brief     interface print format data
 * @param[in] fmt format data
 * @note      none
 */
//void w25qxx_interface_debug_print(const char *const fmt, ...)
//{
//   va_list args;
//   va_start(args, fmt);
//   vprintf(fmt, args);
//   va_end(args);  
//}

void w25qxx_interface_debug_print(const char *const fmt, ...)
{
      char buf[256];
    va_list ap;
    va_start(ap, fmt);
    int n = vsnprintf(buf, sizeof buf, fmt, ap);
    va_end(ap);
    if (n < 0) return;
    if (n >= (int)sizeof buf) n = sizeof buf - 1;

    /* 输出时把 \n 规范为 \r\n;末尾无换行则补一行 */
    for (int i = 0; i < n; ++i) {
        if (buf[i] == '\n') fputc('\r', stdout);
        fputc((unsigned char)buf[i], stdout);
    }
    if (n == 0 || buf[n - 1] != '\n') { fputc('\r', stdout); fputc('\n', stdout); } 
}
component/littlefs-2.11.2/lfs_flash_port.c
#include "lfs_flash_port.h"

#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_flash_ex.h"

#include <stdbool.h>
#include <string.h>

#define LFS_FLASH_PORT_PROG_SIZE          (4U)      /* Word programming */
#define LFS_FLASH_PORT_READ_SIZE          (16U)     /* 128-bit reads */
#define LFS_FLASH_PORT_CACHE_SIZE         (256U)
#define LFS_FLASH_PORT_LOOKAHEAD_SIZE     (16U)

#define LFS_FLASH_PORT_FIRST_SECTOR       FLASH_SECTOR_5

#if defined(FLASH_FLAG_RDERR)
#define LFS_FLASH_PORT_ERROR_FLAGS (FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |     \
                                    FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR |    \
                                    FLASH_FLAG_PGSERR | FLASH_FLAG_RDERR)
#else
#define LFS_FLASH_PORT_ERROR_FLAGS (FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |     \
                                    FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR |    \
                                    FLASH_FLAG_PGSERR)
#endif

static int lfs_flash_port_read(const struct lfs_config *cfg, lfs_block_t block,
                               lfs_off_t off, void *buffer, lfs_size_t size);
static int lfs_flash_port_prog(const struct lfs_config *cfg, lfs_block_t block,
                               lfs_off_t off, const void *buffer, lfs_size_t size);
static int lfs_flash_port_erase(const struct lfs_config *cfg, lfs_block_t block);
static int lfs_flash_port_sync(const struct lfs_config *cfg);
static void lfs_flash_port_clear_flash_flags(void);

const struct lfs_config lfs_flash_port_cfg = {
    .context = NULL,
    .read = lfs_flash_port_read,
    .prog = lfs_flash_port_prog,
    .erase = lfs_flash_port_erase,
    .sync = lfs_flash_port_sync,
    .read_size = LFS_FLASH_PORT_READ_SIZE,
    .prog_size = LFS_FLASH_PORT_PROG_SIZE,
    .block_size = LFS_FLASH_PORT_BLOCK_SIZE,
    .block_count = LFS_FLASH_PORT_BLOCK_COUNT,
    .cache_size = LFS_FLASH_PORT_CACHE_SIZE,
    .lookahead_size = LFS_FLASH_PORT_LOOKAHEAD_SIZE,
    .block_cycles = 100,
};

static inline uint32_t lfs_flash_port_block_address(lfs_block_t block)
{
    return LFS_FLASH_PORT_START_ADDR + (block * LFS_FLASH_PORT_BLOCK_SIZE);
}

static bool lfs_flash_port_range_valid(lfs_block_t block, lfs_off_t off, lfs_size_t size)
{
    if (block >= LFS_FLASH_PORT_BLOCK_COUNT) {
        return false;
    }

    if (off + size > LFS_FLASH_PORT_BLOCK_SIZE) {
        return false;
    }

    const uint32_t start = lfs_flash_port_block_address(block) + off;
    const uint32_t end = start + size;

    return (start >= LFS_FLASH_PORT_START_ADDR) &&
           (end <= LFS_FLASH_PORT_END_ADDR);
}

static void lfs_flash_port_clear_flash_flags(void)
{
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | LFS_FLASH_PORT_ERROR_FLAGS);
}

static int lfs_flash_port_read(const struct lfs_config *cfg, lfs_block_t block,
                               lfs_off_t off, void *buffer, lfs_size_t size)
{
    (void)cfg;

    if (!lfs_flash_port_range_valid(block, off, size)) {
        return LFS_ERR_CORRUPT;
    }

    const void *src = (const void *)(lfs_flash_port_block_address(block) + off);
    memcpy(buffer, src, size);
    return LFS_ERR_OK;
}

static HAL_StatusTypeDef lfs_flash_port_program(uint32_t address,
                                                const uint8_t *data,
                                                size_t size)
{
    HAL_StatusTypeDef status = HAL_OK;

    while ((address % sizeof(uint32_t)) && size && (status == HAL_OK)) {
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, address, *data);
        address += 1U;
        data += 1U;
        size -= 1U;
    }

    while ((size >= sizeof(uint32_t)) && (status == HAL_OK)) {
        uint32_t word32;
        memcpy(&word32, data, sizeof(word32));
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, word32);
        address += sizeof(uint32_t);
        data += sizeof(uint32_t);
        size -= sizeof(uint32_t);
    }

    while ((size >= sizeof(uint16_t)) && (status == HAL_OK)) {
        uint16_t halfword;
        memcpy(&halfword, data, sizeof(halfword));
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, address, halfword);
        address += sizeof(uint16_t);
        data += sizeof(uint16_t);
        size -= sizeof(uint16_t);
    }

    while (size && (status == HAL_OK)) {
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, address, *data);
        address += 1U;
        data += 1U;
        size -= 1U;
    }

    return status;
}

static int lfs_flash_port_prog(const struct lfs_config *cfg, lfs_block_t block,
                               lfs_off_t off, const void *buffer, lfs_size_t size)
{
    (void)cfg;

    if (!lfs_flash_port_range_valid(block, off, size)) {
        return LFS_ERR_CORRUPT;
    }

    uint32_t address = lfs_flash_port_block_address(block) + off;

    HAL_FLASH_Unlock();
    lfs_flash_port_clear_flash_flags();
    HAL_StatusTypeDef status = lfs_flash_port_program(address,
                                                      (const uint8_t *)buffer,
                                                      size);
    HAL_FLASH_Lock();

    if (status != HAL_OK) {
        return LFS_ERR_IO;
    }

    return LFS_ERR_OK;
}

static int lfs_flash_port_erase(const struct lfs_config *cfg, lfs_block_t block)
{
    (void)cfg;

    if (block >= LFS_FLASH_PORT_BLOCK_COUNT) {
        return LFS_ERR_CORRUPT;
    }

    FLASH_EraseInitTypeDef erase = {
        .TypeErase = FLASH_TYPEERASE_SECTORS,
        .Banks = FLASH_BANK_1,
        .VoltageRange = FLASH_VOLTAGE_RANGE_3,
        .Sector = LFS_FLASH_PORT_FIRST_SECTOR + block,
        .NbSectors = 1,
    };

    uint32_t sector_error = 0U;

    HAL_FLASH_Unlock();
    lfs_flash_port_clear_flash_flags();
    HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase, &sector_error);
    HAL_FLASH_Lock();

    if ((status != HAL_OK) || (sector_error != 0xFFFFFFFFU)) {
        return LFS_ERR_IO;
    }

    return LFS_ERR_OK;
}

static int lfs_flash_port_sync(const struct lfs_config *cfg)
{
    (void)cfg;
    return LFS_ERR_OK;
}

int lfs_flash_port_format(lfs_t *lfs)
{
    return lfs_format(lfs, &lfs_flash_port_cfg);
}

int lfs_flash_port_mount(lfs_t *lfs)
{
    return lfs_mount(lfs, &lfs_flash_port_cfg);
}

int lfs_flash_port_unmount(lfs_t *lfs)
{
    return lfs_unmount(lfs);
}
component/littlefs-2.11.2/lfs_nor_flash_port.c
#include "lfs_nor_flash_port.h"

#include "driver_w25qxx_interface.h"

#include <stdbool.h>

#define LFS_NOR_FLASH_PAGE_SIZE          (256U)
#define LFS_NOR_FLASH_READ_SIZE          (16U)
#define LFS_NOR_FLASH_PROG_SIZE          (LFS_NOR_FLASH_PAGE_SIZE)
#define LFS_NOR_FLASH_CACHE_SIZE         (LFS_NOR_FLASH_PAGE_SIZE)
#define LFS_NOR_FLASH_LOOKAHEAD_SIZE     (16U)

static w25qxx_handle_t g_w25qxx_handle;
static bool g_w25qxx_ready = false;

static int lfs_nor_flash_read(const struct lfs_config *cfg, lfs_block_t block,
                              lfs_off_t off, void *buffer, lfs_size_t size);
static int lfs_nor_flash_prog(const struct lfs_config *cfg, lfs_block_t block,
                              lfs_off_t off, const void *buffer, lfs_size_t size);
static int lfs_nor_flash_erase(const struct lfs_config *cfg, lfs_block_t block);
static int lfs_nor_flash_sync(const struct lfs_config *cfg);

static bool lfs_nor_flash_range_valid(lfs_block_t block, lfs_off_t off, lfs_size_t size)
{
    if (block >= LFS_NOR_FLASH_BLOCK_COUNT) {
        return false;
    }

    if (off + size > LFS_NOR_FLASH_BLOCK_SIZE) {
        return false;
    }

    return true;
}

static int lfs_nor_flash_hw_init(void)
{
    if (g_w25qxx_ready) {
        return LFS_ERR_OK;
    }

    DRIVER_W25QXX_LINK_INIT(&g_w25qxx_handle, w25qxx_handle_t);
    DRIVER_W25QXX_LINK_SPI_QSPI_INIT(&g_w25qxx_handle, w25qxx_interface_spi_qspi_init);
    DRIVER_W25QXX_LINK_SPI_QSPI_DEINIT(&g_w25qxx_handle, w25qxx_interface_spi_qspi_deinit);
    DRIVER_W25QXX_LINK_SPI_QSPI_WRITE_READ(&g_w25qxx_handle, w25qxx_interface_spi_qspi_write_read);
    DRIVER_W25QXX_LINK_DELAY_MS(&g_w25qxx_handle, w25qxx_interface_delay_ms);
    DRIVER_W25QXX_LINK_DELAY_US(&g_w25qxx_handle, w25qxx_interface_delay_us);
    DRIVER_W25QXX_LINK_DEBUG_PRINT(&g_w25qxx_handle, w25qxx_interface_debug_print);

    uint8_t res = w25qxx_set_type(&g_w25qxx_handle, W25Q64);
    if (res != 0U) {
        return LFS_ERR_IO;
    }

    res = w25qxx_set_interface(&g_w25qxx_handle, W25QXX_INTERFACE_SPI);
    if (res != 0U) {
        return LFS_ERR_IO;
    }

    res = w25qxx_set_dual_quad_spi(&g_w25qxx_handle, W25QXX_BOOL_FALSE);
    if (res != 0U) {
        return LFS_ERR_IO;
    }

    res = w25qxx_init(&g_w25qxx_handle);
    if (res != 0U) {
        return LFS_ERR_IO;
    }

    g_w25qxx_ready = true;
    return LFS_ERR_OK;
}

static void lfs_nor_flash_hw_deinit(void)
{
    if (!g_w25qxx_ready) {
        return;
    }

    (void)w25qxx_deinit(&g_w25qxx_handle);
    g_w25qxx_ready = false;
}

const struct lfs_config lfs_nor_flash_port_cfg = {
    .context = NULL,
    .read = lfs_nor_flash_read,
    .prog = lfs_nor_flash_prog,
    .erase = lfs_nor_flash_erase,
    .sync = lfs_nor_flash_sync,
    .read_size = LFS_NOR_FLASH_READ_SIZE,
    .prog_size = LFS_NOR_FLASH_PROG_SIZE,
    .block_size = LFS_NOR_FLASH_BLOCK_SIZE,
    .block_count = LFS_NOR_FLASH_BLOCK_COUNT,
    .cache_size = LFS_NOR_FLASH_CACHE_SIZE,
    .lookahead_size = LFS_NOR_FLASH_LOOKAHEAD_SIZE,
    .block_cycles = 500,
};

static int lfs_nor_flash_read(const struct lfs_config *cfg, lfs_block_t block,
                              lfs_off_t off, void *buffer, lfs_size_t size)
{
    (void)cfg;

    if (!lfs_nor_flash_range_valid(block, off, size)) {
        return LFS_ERR_CORRUPT;
    }

    if (!g_w25qxx_ready && lfs_nor_flash_hw_init() != LFS_ERR_OK) {
        return LFS_ERR_IO;
    }

    uint32_t address = (uint32_t)(block * LFS_NOR_FLASH_BLOCK_SIZE + off);

    if (w25qxx_read(&g_w25qxx_handle, address, buffer, size) != 0U) {
        return LFS_ERR_IO;
    }

    return LFS_ERR_OK;
}

static int lfs_nor_flash_prog(const struct lfs_config *cfg, lfs_block_t block,
                              lfs_off_t off, const void *buffer, lfs_size_t size)
{
    (void)cfg;

    if (!lfs_nor_flash_range_valid(block, off, size)) {
        return LFS_ERR_CORRUPT;
    }

    if (!g_w25qxx_ready && lfs_nor_flash_hw_init() != LFS_ERR_OK) {
        return LFS_ERR_IO;
    }

    uint32_t address = (uint32_t)(block * LFS_NOR_FLASH_BLOCK_SIZE + off);
    const uint8_t *data = (const uint8_t *)buffer;

    while (size > 0U) {
        uint32_t page_offset = address % LFS_NOR_FLASH_PAGE_SIZE;
        uint32_t chunk = LFS_NOR_FLASH_PAGE_SIZE - page_offset;
        if (chunk > size) {
            chunk = size;
        }

        if (w25qxx_page_program(&g_w25qxx_handle,
                                address,
                                (uint8_t *)data,
                                (uint16_t)chunk) != 0U) {
            return LFS_ERR_IO;
        }

        address += chunk;
        data += chunk;
        size -= chunk;
    }

    return LFS_ERR_OK;
}

static int lfs_nor_flash_erase(const struct lfs_config *cfg, lfs_block_t block)
{
    (void)cfg;

    if (block >= LFS_NOR_FLASH_BLOCK_COUNT) {
        return LFS_ERR_CORRUPT;
    }

    if (!g_w25qxx_ready && lfs_nor_flash_hw_init() != LFS_ERR_OK) {
        return LFS_ERR_IO;
    }

    uint32_t address = (uint32_t)(block * LFS_NOR_FLASH_BLOCK_SIZE);

    if (w25qxx_sector_erase_4k(&g_w25qxx_handle, address) != 0U) {
        return LFS_ERR_IO;
    }

    return LFS_ERR_OK;
}

static int lfs_nor_flash_sync(const struct lfs_config *cfg)
{
    (void)cfg;
    return LFS_ERR_OK;
}

int lfs_nor_flash_port_format(lfs_t *lfs)
{
    int err = lfs_nor_flash_hw_init();
    if (err != LFS_ERR_OK) {
        return err;
    }

    return lfs_format(lfs, &lfs_nor_flash_port_cfg);
}

int lfs_nor_flash_port_mount(lfs_t *lfs)
{
    int err = lfs_nor_flash_hw_init();
    if (err != LFS_ERR_OK) {
        return err;
    }

    err = lfs_mount(lfs, &lfs_nor_flash_port_cfg);
    if (err != LFS_ERR_OK) {
        lfs_nor_flash_hw_deinit();
    }

    return err;
}

int lfs_nor_flash_port_unmount(lfs_t *lfs)
{
    int err = lfs_unmount(lfs);
    lfs_nor_flash_hw_deinit();
    return err;
}