1

I have problems creating an MSC device. I made the following functions:

#define STORAGE_BLK_NBR      0x4000
#define STORAGE_BLK_SIZ        0x200

    int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr,
        uint16_t blk_len) {
    w25qReadPage(buf, blk_addr*2);
    w25qReadPage(buf+256, blk_addr*2 + 1);
    return (USBD_OK);
}

int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr,
        uint16_t blk_len) {
    w25qWritingByUSB(blk_addr, buf);
    return (USBD_OK);
}

Read, Erase and Write:

void w25qReadPage(uint8_t *page, uint16_t pagenum) {

    uint8_t com[5] = { W25Q_READ_PAGE, (uint8_t) (pagenum >> 8),
            (uint8_t) (pagenum & 0x00FF), 0x00, 0x00 };

    W25Q_CS_LO();

    HAL_SPI_Transmit(W25Q_SPI, com, 5, 100);

    HAL_SPI_Receive(W25Q_SPI, page, W25Q_PAGE_LEN, 100);
    W25Q_CS_HI();
}

void w25qWritePage(uint8_t *page, uint16_t pagenum) {

    uint8_t temp[4] = { W25Q_WRITE_ENABLE, 0x00, 0x00, 0x00 };

    w25qSetBlockProtect(0x00);

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 1, 100);
    W25Q_CS_HI();

    temp[0] = W25Q_PAGE_PROGRAM;
    temp[1] = (uint8_t) (pagenum >> 8);
    temp[2] = (uint8_t) (pagenum & 0x00FF);
    temp[3] = 0x00;

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 4, 100);
    HAL_SPI_Transmit(W25Q_SPI, page, W25Q_PAGE_LEN, 100);
    W25Q_CS_HI();

    w25qWaitForReady();

    temp[0] = W25Q_WRRITE_DISABLE;

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 1, 100);
    W25Q_CS_HI();

    w25qSetBlockProtect(0x0F);

}

void w25qEraseSector(uint16_t sector) {
    w25qSetBlockProtect(0x00);

    sector = (sector << 1);
    uint8_t com = W25Q_WRITE_ENABLE;
    uint8_t temp[4] = { W25Q_SECTOR_ERASE_4, (uint8_t) (sector >> 8),
            (uint8_t) (sector & 0x00FF), 0x00 };
    W25Q_CS_LO();

    HAL_SPI_Transmit(W25Q_SPI, &com, 1, 100);
    W25Q_CS_HI();

    W25Q_CS_LO();

    HAL_SPI_Transmit(W25Q_SPI, temp, 4, 100);
    W25Q_CS_HI();

    w25qWaitForReady();

    temp[0] = W25Q_WRRITE_DISABLE;

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 1, 100);
    W25Q_CS_HI();

    w25qSetBlockProtect(0x0F);
}

void w25qWritingByUSB(uint16_t dpagenum, uint8_t *bufByUSB) {

    uint8_t current_sector_buf[4096];
    uint16_t current_sector_addr = dpagenum / 8;
    uint16_t startPage = current_sector_addr * 16;
    int offset = 512 * (dpagenum % 8);

    for (int i = 0; i < 16; i++) {
        w25qReadPage(current_sector_buf + 256 * i, startPage);
        startPage++;
    }

    memcpy(current_sector_buf + offset, bufByUSB, 512);

    w25qEraseSector(current_sector_addr);

    startPage = current_sector_addr * 16;

    for (int i = 0; i < 16; i++) {
        uint8_t buf[256];
        memcpy(buf, current_sector_buf + i * 256, 256);
        w25qWritePage(buf, startPage);
        startPage++;
    }
}

MSC_MEDIA_PACKET 512 USBD_MAX_STR_DESC_SIZ 512

_Min_Heap_Size = 0x400
_Min_Stack_Size = 0x800

I have already despaired and don’t know what the problem is. The flash drive is formatted, but when reconnected, it erases all the data that was on it and again asks for formatting. Maybe I made a mistake in the functions somewhere and don’t see it? Help me please!

in_text
  • 9
  • 2
  • I don't think your implementation of `w25qEraseSector` is correct (or the way you're calling it from `w25qWritingByUSB`. The sector erase command to the flash expects an address with single-byte resolution (the same as if you were reading or writing), but the value you're passing into `w25qEraseSector` from `w25qWritingByUSB` appears to be a sector number (with 4k-byte resolution). So either pass the address from `w25qWritingByUSB`, or multiply the sector number value by 4k inside `w25qEraseSector` . – brhans Apr 21 '20 at 15:00
  • Corrected sector erasure: sector = (sector << 1)*4096; Now the flash drive does not ask for formatting with each reconnection, but if you transfer data and reconnect to it, the system will ask you to format the flash drive. I have a packet size that I get from a PC equal to 512 bytes - maybe this is the problem? If to do so: sector = (sector << 1)*512; The flash drive will not ask for formatting every restart, but the files that were transferred to it will be deleted. – in_text Apr 21 '20 at 15:42
  • Why did you include the `<< 1`? That's effectively multiplying it by 2 - so instead of `sector x 4096` or `sector x 512` you actually got `sector x 8192` or `sector x 1024`. Just do `sector = sector * 4096;` to turn the sector number into a byte address. – brhans Apr 21 '20 at 16:10
  • Did as you said: sector = sector * 4096; Data is still not saved on the USB flash drive. – in_text Apr 21 '20 at 16:14
  • 1
    This type of problem is typically impossible to solve until you break the overall task down into pieces and either test them independently or add some means of verifying correctness in between them. You'll need to either start with an off-the-shelf USB-MSC example backed by RAM or internal flash and verify that it works as advertised, then modify for the external chip, or write up some independent firmware to dump the flash contents, or hook up a logic analyzer to the SPI or do *something* to divide the task down until you find the problem. – Chris Stratton Apr 21 '20 at 17:00
  • 1
    Also do your testing with something like `dd` to the USB-MSC raw block device (ie, /dev/sdb or whatever it is) rather than a filesystem, and make sure you've configured the host operating system not to cache the block device. Even if your goal is windows you should probably do initial tests on something like linux where you have more fine grained control and visibility. – Chris Stratton Apr 21 '20 at 17:02
  • Unfortunately, I do not have an analyzer. I tried examples with internal memory - everything works. I could not find examples of using external flash memory, no matter how much I looked. It was several times when I was able to save data on a USB flash drive, but this data was saved incorrectly. Thanks for the help! – in_text Apr 21 '20 at 17:06
  • My ultimate goal is to create flash memory to which I have access from the Mac, and I can also read it through the FATFS library inside the microcontroller. – in_text Apr 21 '20 at 17:06

1 Answers1

-1

I did it! It works!

#define STORAGE_BLK_NBR      0x4000
#define STORAGE_BLK_SIZ        0x200

int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr,
        uint16_t blk_len) {
    w25qReadPage(buf, blk_addr * 2);
    w25qReadPage(buf + 256, blk_addr * 2 + 1);

    return (USBD_OK);
}

int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr,
        uint16_t blk_len) {
    w25qWritingByUSB(blk_addr, buf);
    return (USBD_OK);
}


void w25qReadPage(uint8_t *page, uint16_t pagenum) {

    uint8_t com[5] = { W25Q_READ_PAGE, (uint8_t) (pagenum >> 8),
            (uint8_t) (pagenum & 0x00FF), 0x00, 0x00 };

    W25Q_CS_LO();

    HAL_SPI_Transmit(W25Q_SPI, com, 5, 10);

    HAL_SPI_Receive(W25Q_SPI, page, W25Q_PAGE_LEN, 10);
    W25Q_CS_HI();
}

void w25qWritePage(uint8_t *page, uint16_t pagenum) {

    uint8_t temp[4] = { W25Q_WRITE_ENABLE, 0x00, 0x00, 0x00 };

    w25qSetBlockProtect(0x00);

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 1, 10);
    W25Q_CS_HI();

    temp[0] = W25Q_PAGE_PROGRAM;
    temp[1] = (uint8_t) (pagenum >> 8);
    temp[2] = (uint8_t) (pagenum & 0x00FF);
    temp[3] = 0x00;

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 4, 100);
    HAL_SPI_Transmit(W25Q_SPI, page, W25Q_PAGE_LEN, 10);
    W25Q_CS_HI();

    w25qWaitForReady();

    temp[0] = W25Q_WRRITE_DISABLE;

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 1, 10);
    W25Q_CS_HI();

    w25qSetBlockProtect(0x0F);

}

void w25qEraseSector(uint32_t sector) {
    w25qSetBlockProtect(0x00);

    sector = (sector << 4);

    uint8_t com = W25Q_WRITE_ENABLE;
    uint8_t temp[4] = { W25Q_SECTOR_ERASE_4, (uint8_t) (sector >> 8),
            (uint8_t) (sector & 0x00FF), 0x00 };

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, &com, 1, 10);
    W25Q_CS_HI();

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 4, 10);
    W25Q_CS_HI();

    w25qWaitForReady();

    temp[0] = W25Q_WRRITE_DISABLE;

    W25Q_CS_LO();
    HAL_SPI_Transmit(W25Q_SPI, temp, 1, 10);
    W25Q_CS_HI();

    w25qSetBlockProtect(0x0F);
}

void w25qWritingByUSB(uint16_t dpagenum, uint8_t *bufByUSB) {

    uint8_t current_sector_buf[4096];
    uint16_t current_sector_addr = dpagenum / 8;
    uint16_t startPage = current_sector_addr * 16;
    int offset = 512 * (dpagenum % 8);

    for (uint16_t i = 0; i < 16; i++) {
        w25qReadPage(current_sector_buf + 256 * i, startPage + i);
    }

    memcpy(current_sector_buf + offset, bufByUSB, 512);

    w25qEraseSector(current_sector_addr);

    startPage = current_sector_addr * 16;

    for (uint16_t i = 0; i < 16; i++) {
        uint8_t buf[256];
        memcpy(buf, current_sector_buf + i * 256, 256);
        w25qWritePage(buf, startPage + i);
    }
}[

MSC_MEDIA_PACKET 512
USBD_MAX_STR_DESC_SIZ 512
in_text
  • 9
  • 2
  • what are the "W25Q_READ_PAGE" & "W25Q_SPI" & W25Q_PAGE_PROGRAM W25Q_WRRITE_DISABLE W25Q_WRITE_ENABLE ... values ? – ayoub kouch Jun 09 '21 at 16:54