• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Esp32 + fatfs + external flash

in_text

New member
Привет всем кто забрёл поинтересоваться
:)

Задача на первый взгляд достаточно проста, мне нужно читать/создавать/удалять файлы которые находятся на внешней флеш памяти на которой стоит файловая система ФАТ. Сразу скажу что использование внутренней флеш памяти мне не подходит - у меня требование именно к использованию внешней памяти.
Искал и находил много библиотек, но ни одна из них не была заточена конкретно под ESP32, мне кажется что проблема с инициализацией SPI. Не важно какой именно использовать, железный или софтовый, главное чтобы флешка читалась и было видно файлы.
Может у кто-то сталкивался с подобной задачей и может подсказать?
P.S. флеш-память W25Q64

Всем спасибо кто откликнется
:)
 

ewogs30

New member
Значит для этого придётся писать свой драйвер флешки, но я делал подобное для STM32, но там общение с флешкой шло через spiflash_execute, которое и работало с SPI.
Вот код, который содержит функцию spiflash_execute, и фуннкции fatfs для доступа к накопителю

C:
void spiflash_execute(uint8_t cmd, uint8_t *idata, int idatalen, uint8_t *odata, int odatalen) {
    // flash_cs = low
    
    // spi_write (cmd)
    
    while (idatalen--) {
        // spi_write (*idata++)
    }
    
    while (odatalen--) {
        // *odata++ = spi_read()
    }
    
    // flash_cs = high
}

DRESULT disk_read(BYTE drv, BYTE *buff, LBA_t lba, UINT count) {
    switch (drv) {
        case 0:
        {
            BYTE adr[3];
            
            adr[0] = ((lba*4096)>>16)&255;
            adr[1] = ((lba*4096)>>8 )&255;
            adr[2] = ((lba*4096)>>0 )&255;
            
            spiflash_execute(0x03, adr, 3, buff, count * 4096);
        return RES_OK;}
    }
    return RES_PARERR;
}

DRESULT disk_write(BYTE drv, const BYTE *dat, LBA_t lba, UINT cnt) {
    switch (drv) {
        case 0:
        {
            uint8_t bpos[259];
            int i;
            // erase
            for (i = 0; i < cnt; i++){
                bpos[2] = ((lba+i)*4096);
                bpos[1] = ((lba+i)*4096)>>8;
                bpos[0] = ((lba+i)*4096)>>16;
                
                spiflash_execute(0x06, 0, 0, 0, 0); // writeenable
                spiflash_execute(0x20, &bpos, 3, 0, 0); // erase sector
                while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1);
            }
            
            for (i = 0; i < (cnt * 16); i++){
                bpos[2] = (lba*4096)+(i*256);
                bpos[1] = ((lba*4096)+(i*256))>>8;
                bpos[0] = ((lba*4096)+(i*256))>>16;
                
                memcpy(&bpos[3], &dat[i*256], 256);
                spiflash_execute(0x06, 0, 0, 0, 0); // writeenable
                spiflash_execute(0x02, (void*)&bpos, 259, 0, 0); // write data
                while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1);
            }
            
            return RES_OK;
        }
    }
    return RES_PARERR;
}
Если всё вынести в отдельные функции, то будет
C:
void spiflash_execute(uint8_t cmd, uint8_t *idata, int idatalen, uint8_t *odata, int odatalen) {

    // flash_cs = low  

    // spi_write (cmd)

    while (idatalen--) {

        // spi_write (*idata++)

    }

    while (odatalen--) {

        // *odata++ = spi_read()

    }

    // flash_cs = high

}

void spiflash_read(int lba, int cnt, uint8_t *data) {
  uint8_t adr[3];
  adr[0] = ((lba*4096)>>16)&255; // 23..16
  adr[1] = ((lba*4096)>>8 )&255; // 15..8
  adr[2] = ((lba*4096)>>0 )&255; //  8..0
            
  spiflash_execute(0x03, adr, 3, buff, count * 4096);
}

void spiflash_write(int lba, int cnt, uint8_t *data) {
  uint8_t bpos[259];
  int i;
  // стереть сектора
  for (i = 0; i < cnt; i++){
      bpos[0] = ((lba*4096)>>16)&255; // 23..16
      bpos[1] = ((lba*4096)>>8 )&255; // 15..8
      bpos[2] = ((lba*4096)>>0 )&255; //  8..0

      spiflash_execute(0x06, 0, 0, 0, 0); // разрешить запись
      spiflash_execute(0x20, &bpos, 3, 0, 0); // стереть сектор
      while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1); // ждать пока сектор не сотрётся
    }
    // записать сектора
    for (i = 0; i < (cnt * 16); i++){
        bpos[0] = ((lba*4096)>>16)&255; // 23..16
        bpos[1] = ((lba*4096)>>8 )&255; // 15..8
        bpos[2] = ((lba*4096)>>0 )&255; //  8..0

        memcpy(&bpos[3], &dat[i*256], 256);
        spiflash_execute(0x06, 0, 0, 0, 0); // разрешить запись
        spiflash_execute(0x02, (void*)&bpos, 259, 0, 0); // записать 256 байт
        while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1); // ждать пока не запишется
    }
}
и потом можно их использовать.
Правда мой код заточен под работу с секторами в 4096 байт, так как это минимальный размер, который можно стереть, а выдумывать что-то для работы с секторами в 512 байт было нецелесообразно.
 

in_text

New member
Значит для этого придётся писать свой драйвер флешки, но я делал подобное для STM32, но там общение с флешкой шло через spiflash_execute, которое и работало с SPI.
Вот код, который содержит функцию spiflash_execute, и фуннкции fatfs для доступа к накопителю

C:
void spiflash_execute(uint8_t cmd, uint8_t *idata, int idatalen, uint8_t *odata, int odatalen) {
    // flash_cs = low
   
    // spi_write (cmd)
   
    while (idatalen--) {
        // spi_write (*idata++)
    }
   
    while (odatalen--) {
        // *odata++ = spi_read()
    }
   
    // flash_cs = high
}

DRESULT disk_read(BYTE drv, BYTE *buff, LBA_t lba, UINT count) {
    switch (drv) {
        case 0:
        {
            BYTE adr[3];
           
            adr[0] = ((lba*4096)>>16)&255;
            adr[1] = ((lba*4096)>>8 )&255;
            adr[2] = ((lba*4096)>>0 )&255;
           
            spiflash_execute(0x03, adr, 3, buff, count * 4096);
        return RES_OK;}
    }
    return RES_PARERR;
}

DRESULT disk_write(BYTE drv, const BYTE *dat, LBA_t lba, UINT cnt) {
    switch (drv) {
        case 0:
        {
            uint8_t bpos[259];
            int i;
            // erase
            for (i = 0; i < cnt; i++){
                bpos[2] = ((lba+i)*4096);
                bpos[1] = ((lba+i)*4096)>>8;
                bpos[0] = ((lba+i)*4096)>>16;
               
                spiflash_execute(0x06, 0, 0, 0, 0); // writeenable
                spiflash_execute(0x20, &bpos, 3, 0, 0); // erase sector
                while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1);
            }
           
            for (i = 0; i < (cnt * 16); i++){
                bpos[2] = (lba*4096)+(i*256);
                bpos[1] = ((lba*4096)+(i*256))>>8;
                bpos[0] = ((lba*4096)+(i*256))>>16;
               
                memcpy(&bpos[3], &dat[i*256], 256);
                spiflash_execute(0x06, 0, 0, 0, 0); // writeenable
                spiflash_execute(0x02, (void*)&bpos, 259, 0, 0); // write data
                while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1);
            }
           
            return RES_OK;
        }
    }
    return RES_PARERR;
}
Если всё вынести в отдельные функции, то будет
C:
void spiflash_execute(uint8_t cmd, uint8_t *idata, int idatalen, uint8_t *odata, int odatalen) {

    // flash_cs = low 

    // spi_write (cmd)

    while (idatalen--) {

        // spi_write (*idata++)

    }

    while (odatalen--) {

        // *odata++ = spi_read()

    }

    // flash_cs = high

}

void spiflash_read(int lba, int cnt, uint8_t *data) {
  uint8_t adr[3];
  adr[0] = ((lba*4096)>>16)&255; // 23..16
  adr[1] = ((lba*4096)>>8 )&255; // 15..8
  adr[2] = ((lba*4096)>>0 )&255; //  8..0
           
  spiflash_execute(0x03, adr, 3, buff, count * 4096);
}

void spiflash_write(int lba, int cnt, uint8_t *data) {
  uint8_t bpos[259];
  int i;
  // стереть сектора
  for (i = 0; i < cnt; i++){
      bpos[0] = ((lba*4096)>>16)&255; // 23..16
      bpos[1] = ((lba*4096)>>8 )&255; // 15..8
      bpos[2] = ((lba*4096)>>0 )&255; //  8..0

      spiflash_execute(0x06, 0, 0, 0, 0); // разрешить запись
      spiflash_execute(0x20, &bpos, 3, 0, 0); // стереть сектор
      while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1); // ждать пока сектор не сотрётся
    }
    // записать сектора
    for (i = 0; i < (cnt * 16); i++){
        bpos[0] = ((lba*4096)>>16)&255; // 23..16
        bpos[1] = ((lba*4096)>>8 )&255; // 15..8
        bpos[2] = ((lba*4096)>>0 )&255; //  8..0

        memcpy(&bpos[3], &dat[i*256], 256);
        spiflash_execute(0x06, 0, 0, 0, 0); // разрешить запись
        spiflash_execute(0x02, (void*)&bpos, 259, 0, 0); // записать 256 байт
        while (spiflash_execute(0x05, 0, 0, &bpos, 1), bpos[0]&1); // ждать пока не запишется
    }
}
и потом можно их использовать.
Правда мой код заточен под работу с секторами в 4096 байт, так как это минимальный размер, который можно стереть, а выдумывать что-то для работы с секторами в 512 байт было нецелесообразно.
Спасибо, сектор в моей памяти тоже занимает 4КБ, а одна страница занимает 256 байт. Процесс записи выглядит немного страшно, прочитать весь сектор, обновить данные и записать всё это обратно, но такова структура памяти. За помощь спасибо!
 
Сверху Снизу