MCU & FPGA geral Sistemas de Arquivos em Microcontroladores: LittleFS, SPIFFS e SD Card no STM32N6

Sistemas de Arquivos em Microcontroladores: LittleFS, SPIFFS e SD Card no STM32N6

Exemplo conceitual usando SPIFFS em Flash externa

O SPIFFS, ou SPI Flash File System, é um sistema de arquivos criado para memórias SPI NOR Flash em sistemas embarcados pequenos. A documentação oficial o descreve como um sistema de arquivos voltado a dispositivos SPI NOR Flash, com foco em alvos embarcados, pouca RAM, apagamento por blocos e suporte a wear leveling. (GitHub)

A ideia geral é parecida com LittleFS: o firmware fornece uma camada de acesso à Flash, e o sistema de arquivos organiza essa memória em arquivos. A diferença está no desenho interno. O SPIFFS é mais antigo, simples e bastante usado em bases legadas, mas não possui diretórios reais e, para projetos novos, costuma ser menos interessante que LittleFS quando a robustez contra falha de energia é prioridade.

No STM32N6, o SPIFFS faria sentido se o projeto usasse uma Flash externa conectada por SPI, QSPI ou OctoSPI, e se houvesse algum motivo específico para escolhê-lo: compatibilidade com firmware antigo, reaproveitamento de ferramentas, migração de projeto vindo de ESP8266/ESP32 antigo ou uma biblioteca já validada em produção.

Assim como no LittleFS, o SPIFFS precisa de funções de leitura, escrita e apagamento. A diferença é que a interface de portabilidade do SPIFFS normalmente usa uma estrutura spiffs_config, na qual informamos o endereço físico da área reservada, o tamanho total do sistema de arquivos, o tamanho dos blocos, o tamanho das páginas e as funções de acesso à Flash.

Um exemplo conceitual de configuração seria:

#include "spiffs.h"
#include <stdio.h>
#include <string.h>

/*
 * Instância global do SPIFFS.
 */
static spiffs fs;

/*
 * Buffers usados pelo SPIFFS.
 * Os tamanhos devem ser ajustados conforme a configuração real.
 */
#define SPIFFS_WORK_BUFFER_SIZE       (4096U)
#define SPIFFS_FD_BUFFER_SIZE         (32U * 4U)
#define SPIFFS_CACHE_BUFFER_SIZE      (4096U)

static uint8_t spiffs_work_buffer[SPIFFS_WORK_BUFFER_SIZE];
static uint8_t spiffs_fd_buffer[SPIFFS_FD_BUFFER_SIZE];
static uint8_t spiffs_cache_buffer[SPIFFS_CACHE_BUFFER_SIZE];

/*
 * Região reservada na Flash externa.
 * Estes valores são didáticos.
 */
#define SPIFFS_FLASH_BASE_ADDR        0x00000000UL
#define SPIFFS_FLASH_TOTAL_SIZE       (4U * 1024U * 1024U)
#define SPIFFS_FLASH_BLOCK_SIZE       4096U
#define SPIFFS_FLASH_PAGE_SIZE        256U

No SPIFFS, é comum que o desenvolvedor configure uma região fixa da Flash para o sistema de arquivos. Em um produto real, essa região deve ser separada da área de bootloader, firmware principal, parâmetros críticos e qualquer outra partição usada pelo sistema.

Agora criamos as funções de acesso à Flash. O exemplo abaixo continua genérico, usando funções abstratas como ExternalFlash_Read, ExternalFlash_Program e ExternalFlash_EraseSector.

/*
 * Função de leitura usada pelo SPIFFS.
 */
static s32_t stm32_spiffs_read(
    u32_t address,
    u32_t size,
    u8_t *destination
)
{
    if (ExternalFlash_Read(address, destination, size) != 0)
    {
        return SPIFFS_ERR_INTERNAL;
    }

    return SPIFFS_OK;
}

/*
 * Função de escrita usada pelo SPIFFS.
 */
static s32_t stm32_spiffs_write(
    u32_t address,
    u32_t size,
    u8_t *source
)
{
    if (ExternalFlash_Program(address, source, size) != 0)
    {
        return SPIFFS_ERR_INTERNAL;
    }

    return SPIFFS_OK;
}

/*
 * Função de apagamento usada pelo SPIFFS.
 */
static s32_t stm32_spiffs_erase(
    u32_t address,
    u32_t size
)
{
    uint32_t currentAddress = address;
    uint32_t endAddress = address + size;

    while (currentAddress < endAddress)
    {
        if (ExternalFlash_EraseSector(currentAddress) != 0)
        {
            return SPIFFS_ERR_INTERNAL;
        }

        currentAddress += SPIFFS_FLASH_BLOCK_SIZE;
    }

    return SPIFFS_OK;
}

Aqui há um detalhe importante: em memórias Flash NOR, o apagamento acontece por setor ou bloco. Por isso, a função de erase percorre a região solicitada e apaga setor por setor. Em um driver real, também seria necessário aguardar o término das operações internas da memória e verificar erros de programação ou proteção de escrita.

A configuração e montagem do SPIFFS ficariam assim:

static int SPIFFS_Init(void)
{
    spiffs_config config;

    memset(&config, 0, sizeof(config));

    config.phys_addr = SPIFFS_FLASH_BASE_ADDR;
    config.phys_size = SPIFFS_FLASH_TOTAL_SIZE;
    config.phys_erase_block = SPIFFS_FLASH_BLOCK_SIZE;
    config.log_block_size = SPIFFS_FLASH_BLOCK_SIZE;
    config.log_page_size = SPIFFS_FLASH_PAGE_SIZE;

    config.hal_read_f = stm32_spiffs_read;
    config.hal_write_f = stm32_spiffs_write;
    config.hal_erase_f = stm32_spiffs_erase;

    s32_t result = SPIFFS_mount(
        &fs,
        &config,
        spiffs_work_buffer,
        spiffs_fd_buffer,
        sizeof(spiffs_fd_buffer),
        spiffs_cache_buffer,
        sizeof(spiffs_cache_buffer),
        0
    );

    if (result != SPIFFS_OK)
    {
        /*
         * Em desenvolvimento, pode-se formatar se a montagem falhar.
         * Em produto final, isso deve ser feito com muito cuidado.
         */
        SPIFFS_format(&fs);

        result = SPIFFS_mount(
            &fs,
            &config,
            spiffs_work_buffer,
            spiffs_fd_buffer,
            sizeof(spiffs_fd_buffer),
            spiffs_cache_buffer,
            sizeof(spiffs_cache_buffer),
            0
        );
    }

    return result;
}

A lógica é parecida com LittleFS: tenta montar; se falhar, pode formatar; depois tenta montar novamente. Porém, em firmware de produto, formatar automaticamente pode ser perigoso. O ideal é validar se o erro realmente indica sistema de arquivos inexistente ou corrompido, e não apenas uma falha temporária de leitura, alimentação ou inicialização da Flash.

Agora podemos criar um arquivo simples:

static int SPIFFS_WriteConfig(void)
{
    const char *content =
        "{\n"
        "  \"device\": \"STM32N6\",\n"
        "  \"storage\": \"SPIFFS\",\n"
        "  \"mode\": \"legacy_flash_fs\"\n"
        "}\n";

    spiffs_file file = SPIFFS_open(
        &fs,
        "config.json",
        SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR,
        0
    );

    if (file < 0)
    {
        return SPIFFS_errno(&fs);
    }

    s32_t written = SPIFFS_write(
        &fs,
        file,
        (void *)content,
        strlen(content)
    );

    if (written < 0)
    {
        int error = SPIFFS_errno(&fs);
        SPIFFS_close(&fs, file);
        return error;
    }

    SPIFFS_close(&fs, file);

    return SPIFFS_OK;
}

E a leitura:

static int SPIFFS_ReadConfig(void)
{
    char buffer[256];

    spiffs_file file = SPIFFS_open(
        &fs,
        "config.json",
        SPIFFS_RDONLY,
        0
    );

    if (file < 0)
    {
        return SPIFFS_errno(&fs);
    }

    s32_t bytesRead = SPIFFS_read(
        &fs,
        file,
        buffer,
        sizeof(buffer) - 1
    );

    if (bytesRead < 0)
    {
        int error = SPIFFS_errno(&fs);
        SPIFFS_close(&fs, file);
        return error;
    }

    buffer[bytesRead] = '\0';

    printf("Configuração lida do SPIFFS:\r\n%s\r\n", buffer);

    SPIFFS_close(&fs, file);

    return SPIFFS_OK;
}

O uso dentro de uma tarefa FreeRTOS seria direto:

#include "cmsis_os.h"

void SPIFFSTask(void *argument)
{
    if (SPIFFS_Init() == SPIFFS_OK)
    {
        SPIFFS_WriteConfig();
        SPIFFS_ReadConfig();
    }

    for (;;)
    {
        osDelay(1000);
    }
}

Este exemplo mostra que SPIFFS pode ser integrado ao STM32N6 sem depender de um sistema operacional específico. O FreeRTOS apenas executa a tarefa. O sistema de arquivos em si depende principalmente da camada de acesso à Flash.

A principal limitação prática do SPIFFS, quando comparado ao LittleFS, é que ele não trabalha com diretórios reais. Um nome como "logs/system.txt" pode ser tratado apenas como uma string de nome de arquivo, dependendo da configuração e da camada superior. Já o LittleFS oferece uma abstração mais próxima de um sistema de arquivos moderno, com diretórios de fato. Além disso, LittleFS é explicitamente documentado com foco em resiliência contra falhas de energia, wear leveling dinâmico e uso limitado de RAM/ROM. (GitHub)

Para um projeto novo com STM32N6 e Flash externa, minha recomendação técnica continua sendo LittleFS. Eu reservaria SPIFFS para cenários de legado, reaproveitamento de código ou compatibilidade com ferramentas já existentes. Para SD Card, a recomendação permanece sendo FATFS ou FreeRTOS+FAT, porque o cartão SD se comporta como dispositivo de blocos e precisa de compatibilidade com computadores; o FatFs, por exemplo, documenta funções clássicas como f_open, f_read, f_write e f_close para manipulação de arquivos em volumes FAT. (Elm Chan)

Em resumo, a escolha pode ser organizada assim:

STM32N6 + SD Card
    Use FATFS ou FreeRTOS+FAT.

STM32N6 + Flash externa em projeto novo
    Prefira LittleFS.

STM32N6 + Flash externa em projeto legado
    SPIFFS pode ser mantido, se já estiver validado.
0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

0
Adoraria saber sua opinião, comente.x