1
\$\begingroup\$

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!

\$\endgroup\$
8
  • \$\begingroup\$ 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 . \$\endgroup\$
    – brhans
    Apr 21, 2020 at 15:00
  • \$\begingroup\$ 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. \$\endgroup\$
    – in_text
    Apr 21, 2020 at 15:42
  • \$\begingroup\$ 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. \$\endgroup\$
    – brhans
    Apr 21, 2020 at 16:10
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Apr 21, 2020 at 17:00
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Apr 21, 2020 at 17:02

1 Answer 1

-1
\$\begingroup\$

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
\$\endgroup\$
1
  • \$\begingroup\$ what are the "W25Q_READ_PAGE" & "W25Q_SPI" & W25Q_PAGE_PROGRAM W25Q_WRRITE_DISABLE W25Q_WRITE_ENABLE ... values ? \$\endgroup\$ Jun 9, 2021 at 16:54

Not the answer you're looking for? Browse other questions tagged or ask your own question.