基于STM32F103的USB学习笔记36 - Mass Storage之Memory管理

72 篇文章 36 订阅

用内部Flash作为Memory作为示例。开一个32KB空间(STM32F103C8T6后面32K字节)作为U盘的总容量。对于一个实际应用,对应的API函数是

void massFormat(uint8_t lun)对应命令SCSI_FORMAT_UNIT

void massWriteMemory(uint8_t lun)对应命令SCSI_WRITE10

void massReadMemory(uint8_t lun)对应命令SCSI_READ10

1. massFormat

void massFormat(uint8_t lun)
{
    iflashErasePage(IFLASH_START_ADDR, IFLASH_SIZE / IFLASH_PAGE_SIZE);
}

iflashErasePage对应擦除内部Flash页操作。 

这个函数把整个32KB空间擦除,不过格式化的时候并没有进这个函数。

2.  massWriteMemory

void massWriteMemory(uint8_t lun)
{
    uint32_t count;
    uint8_t status = IFLASH_STATUS_OK;
    count = (gScsiInfo.length > gMscBotData.len) ? gMscBotData.len : gScsiInfo.length;
    if(((IFLASH_START_ADDR + gScsiInfo.offset) % IFLASH_PAGE_SIZE) == 0)
        iflashErasePage(IFLASH_START_ADDR + gScsiInfo.offset, 1);
    status = iflashWriteBuf(IFLASH_START_ADDR + gScsiInfo.offset, gMscBotData.dat, count);
    //if(status != IFLASH_STATUS_OK)
    //    Printf("Write IFlash Fail:%d\n", status);
    gScsiInfo.offset += count;
    gScsiInfo.length -= count;
    gMscCSW.dDataResidue -= count;
    //Printf("W Remain:%d\n", gScsiInfo.length);
    if(gScsiInfo.length == 0 || gMscBotState == MSC_BOT_CSW_Send)
    {
        mscBotSendCSW(MSC_BOT_CSW_CMD_PASSED);
    }
}

 因为massFormat并没有调用,而且系统格式化U盘时会调用这个函数,所以这里判断如果地址是PAGE起始地址,就把该PAGE擦除掉。即

if(((IFLASH_START_ADDR + gScsiInfo.offset) % IFLASH_PAGE_SIZE) == 0)
        iflashErasePage(IFLASH_START_ADDR + gScsiInfo.offset, 1);

由于U盘会回报BLOCK大小为1个PAGE的大小,所以这里写数据的大小一定会是BLOCK的整数倍,不会出现擦除多余数据的情况。

写数据每次写入数据不会超过64字节(count的值不会超过EP的大小)。

3. massReadMemory

void massReadMemory(uint8_t lun)
{
    uint32_t count;
    uint8_t *pBuf = 0;
    count = (gScsiInfo.length > BULK_MAX_PACKET_SIZE) ? BULK_MAX_PACKET_SIZE : gScsiInfo.length;
    pBuf = (uint8_t *)iflashGetAddr(IFLASH_START_ADDR + gScsiInfo.offset);
    usbWriteBuf(EP_MSC_IN, pBuf, count);
    gScsiInfo.offset += count;
    gScsiInfo.length -= count;
    gMscCSW.dDataResidue -= count;
    if(gScsiInfo.length == 0)
    {
        gMscBotState = MSC_BOT_DATA_IN_LAST;
    }
}

读数据和写数据类似,读到的数据发给USB EP即可。

U盘第一次插入需要格式化,格式化后就可以看到U盘的信息了。

以上方式的应用在实际使用中作用不大,更多的应用可能是将某个外设或者内部flash、RAM作为文件(类似Linux里面的概念,外部设备也可以作为一个文件来处理),其他部分不改变。

以内部RAM作为一个文件为例:

U盘采用FAT12格式(小容量似乎只能用FAT12,用FAT16没试出来,老是有问题),扇区大小为512字节。U盘的结构如下:

DBR(1个扇区)+ 保留区(8个扇区)+ FAT(1个扇区)+ 副FAT(1个扇区)+ 跟目录(1个扇区)+  数据区(4个扇区,用于放置文件内容,对应2K的RAM空间)

U盘只允许更新文件内容,所以数据区前面的部分(12个扇区)都是固定不变的,而且必须保证整个U盘的容量都被使用,这样才能保证文件内容被更新到想要的地方。

1. DBR(分区引导扇区)

const uint8_t mscDBR[FAT_SECTOR_SIZE * 1] =
{
    0xeb, 0x3c, 0x90,                                   //jump code - 0x0000
    0x4d, 0x53, 0x44, 0x4f, 0x53, 0x35, 0x2e, 0x30,     //String: MSDOS5.0 - 0x0003
    FAT_SECTOR_SIZE & 0xff,                             //the size of sector : 512 - 0x000b 
    (FAT_SECTOR_SIZE >> 8) & 0xff,                      
    1,                                                  //the sector size of cluster - 0x000d
    (FAT_RESERVE_SIZE & 0xff),                              //the size of reserved sector - 0x000e
    ((FAT_RESERVE_SIZE >> 8) & 0xff),                       
    0x02,                                               //the number of FAT table - 0x0010
    ((ROOT_SIZE * FAT_SECTOR_SIZE) / 32) & 0xFF, 
    (((ROOT_SIZE * FAT_SECTOR_SIZE) / 32) >> 8) & 0xFF,             //the number of the dir items in root. - 0x0011
    MSC_TOTAL_SECTOR & 0xff,                            //the total number of sectors. - 0x0013
    (MSC_TOTAL_SECTOR >> 8) & 0xff,                     //Valid for FAT12/FAT16, invalid for FAT32.
    0xf8,                                               //the description of the medie. - 0x0015
    0x01, 0x00,                                         //the sector number of FAT table. Only for FAT12/FAT16. - 0x0016
    0x01,//MSC_TOTAL_SECTOR & 0xff,                     //the sector number of one track. - 0x0018
    0x00,//(MSC_TOTAL_SECTOR >> 8) & 0xff,                                         
    0x01, 0x00,                                         //the number of heads - 0x001a
    0x00, 0x00, 0x00, 0x00,                             //the length of hidden sector - 0x001c
    0x00,                            //the sector number of file system. - 0x0020
    0x00, 
    0x00, 
    0x00,
    0x80,                                               //The type of medie. - 0x0024
    0x00,                                               //reserved. - 0x0025
    0x29,                                               //Extended boot flag - 0x0026
    0xb3, 0x5b, 0xa3, 0x44,                             //Volume serial number - 0x0027
    0x52, 0x41, 0x4d, 0x20, 0x44, 0x49, 0x53, 0x4b, 
    0x20, 0x20, 0x20,                                   //Volume label (ASCII) - 0x002b
    0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20,     //the ascii of FAT12. - 0x0036
    //0x003e
    0x33, 0xc9, 
    0x8e, 0xd1, 0xbc, 0xf0, 0x7b, 0x8e, 0xd9, 0xb8, 0x00, 0x20, 0x8e, 0xc0, 0xfc, 0xbd, 0x00, 0x7c,
    0x38, 0x4e, 0x24, 0x7d, 0x24, 0x8b, 0xc1, 0x99, 0xe8, 0x3c, 0x01, 0x72, 0x1c, 0x83, 0xeb, 0x3a,
    0x66, 0xa1, 0x1c, 0x7c, 0x26, 0x66, 0x3b, 0x07, 0x26, 0x8a, 0x57, 0xfc, 0x75, 0x06, 0x80, 0xca,
    0x02, 0x88, 0x56, 0x02, 0x80, 0xc3, 0x10, 0x73, 0xeb, 0x33, 0xc9, 0x8a, 0x46, 0x10, 0x98, 0xf7,
    0x66, 0x16, 0x03, 0x46, 0x1c, 0x13, 0x56, 0x1e, 0x03, 0x46, 0x0e, 0x13, 0xd1, 0x8b, 0x76, 0x11,
    0x60, 0x89, 0x46, 0xfc, 0x89, 0x56, 0xfe, 0xb8, 0x20, 0x00, 0xf7, 0xe6, 0x8b, 0x5e, 0x0b, 0x03,
    0xc3, 0x48, 0xf7, 0xf3, 0x01, 0x46, 0xfc, 0x11, 0x4e, 0xfe, 0x61, 0xbf, 0x00, 0x00, 0xe8, 0xe6,
    0x00, 0x72, 0x39, 0x26, 0x38, 0x2d, 0x74, 0x17, 0x60, 0xb1, 0x0b, 0xbe, 0xa1, 0x7d, 0xf3, 0xa6, //- 0x0b0
    0x61, 0x74, 0x32, 0x4e, 0x74, 0x09, 0x83, 0xc7, 0x20, 0x3b, 0xfb, 0x72, 0xe6, 0xeb, 0xdc, 0xa0,
    0xfb, 0x7d, 0xb4, 0x7d, 0x8b, 0xf0, 0xac, 0x98, 0x40, 0x74, 0x0c, 0x48, 0x74, 0x13, 0xb4, 0x0e,
    0xbb, 0x07, 0x00, 0xcd, 0x10, 0xeb, 0xef, 0xa0, 0xfd, 0x7d, 0xeb, 0xe6, 0xa0, 0xfc, 0x7d, 0xeb,
    0xe1, 0xcd, 0x16, 0xcd, 0x19, 0x26, 0x8b, 0x55, 0x1a, 0x52, 0xb0, 0x01, 0xbb, 0x00, 0x00, 0xeb,
    0x3b, 0x00, 0x72, 0xe8, 0x5b, 0x8a, 0x56, 0x24, 0xbe, 0x0b, 0x7c, 0x8b, 0xfc, 0xc7, 0x46, 0xf0, //- 0x100
    0x3d, 0x7d, 0xc7, 0x46, 0xf4, 0x29, 0x7d, 0x8c, 0xd9, 0x89, 0x4e, 0xf2, 0x89, 0x4e, 0xf6, 0xc6,
    0x06, 0x96, 0x7d, 0xcb, 0xea, 0x03, 0x00, 0x00, 0x20, 0x0f, 0xb6, 0xc8, 0x66, 0x8b, 0x46, 0xf8,
    0x66, 0x03, 0x46, 0x1c, 0x66, 0x8b, 0xd0, 0x66, 0xc1, 0xea, 0x10, 0xeb, 0x53, 0x0f, 0xb6, 0xc8,
    0x4a, 0x4a, 0x8a, 0x46, 0x0d, 0x32, 0xe4, 0xf7, 0xe2, 0x03, 0x46, 0xfc, 0x13, 0x56, 0xfe, 0xeb,
    0x4a, 0x52, 0x50, 0x06, 0x53, 0x6a, 0x01, 0x6a, 0x10, 0x91, 0x8b, 0x46, 0x18, 0x96, 0x92, 0x33,
    0xd2, 0xf7, 0xf6, 0x91, 0xf7, 0xf6, 0x42, 0x87, 0xca, 0xf7, 0x76, 0x1a, 0x8a, 0xf2, 0x8a, 0xe8,
    0xc0, 0xcc, 0x02, 0x0a, 0xcc, 0xb8, 0x01, 0x02, 0x80, 0x7e, 0x02, 0x0e, 0x75, 0x04, 0xb4, 0x42,
    0x8b, 0xf4, 0x8a, 0x56, 0x24, 0xcd, 0x13, 0x61, 0x61, 0x72, 0x0b, 0x40, 0x75, 0x01, 0x42, 0x03, //- 0x180
    0x5e, 0x0b, 0x49, 0x75, 0x06, 0xf8, 0xc3, 0x41, 0xbb, 0x00, 0x00, 0x60, 0x66, 0x6a, 0x00, 0xeb,
    0xb0, 0x42, 0x4f, 0x4f, 0x54, 0x4d, 0x47, 0x52, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x52, 0x65,
    0x6d, 0x6f, 0x76, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x74,
    0x68, 0x65, 0x72, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x23, 0xff, 0x0d, 0x0a, 0x44, 0x69, 0x73,
    0x6b, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0xff, 0x0d, 0x0a, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20,
    0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61,
    0x72, 0x74, 0x0d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xcb, 0xd8,
    0x55, 0xaa,                                         //Signature mark
};
  • 0x0b - 0x0c Bytes per sector: 扇区的大小,这里设置为最小单位512字节,如果U盘的容量比较大,可以设置为更大的值。
  • 0x0d Sectors per cluster: 簇的大小,这里RAM比较小,设置为一个簇大小为一个扇区。
  • 0x0e - 0x0f Reserved sectors: 保留扇区,FAT1之前的扇区数,即FAT1的开始扇区号,虽然只需要用一个扇区用于保存DBR,但是这个数字不能太小,如果设置为2,则U盘读不到。这里设置最小为8时才能正常识别到U盘,为了让总量为1K的整数倍,取值9.
  • 0x11 - 0x12 Root entries: 根目录项数,一个项目32字节,这里只设置1个扇区给根目录,所以有512/32=16个根目录项。
  • 0x13 - 0x14 Sectors: 分区总扇区数,小于2^16 * 512 = 32MB的分区才使用,如果是大于32M则读0x20-0x23。这里只分配了8KB(16个扇区)的空间。
  • 0x16 - 0x17 Sectors per FAT, FAT的大小,仅对FAT12/16有效,这里设置为1.
  • 0x36 - 0x3d File system, 文件系统名,ASCII码编码。注意没有用到的地方要用0x20,不能用0x00。

2. 跟目录项

由于现在的Windows系统会自动创建一个文件名为System Volume Information的文件夹,这样会破坏我们设计好的FAT格式,所以需要在根目录内建立一个只读并隐藏的System Volume Information文件夹,另外需要建立一个TEXT.TXT文件指向内部RAM空间(这个文件名可以根据实际应用修改名字)

const uint8_t mscRootDir[ROOT_SIZE * FAT_SECTOR_SIZE] = 
{   
    //Folder "System Volume Information"
    0x42, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x0F, 0x00, 0x72, 0x72, 0x00, 
    0x6D, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 
    0x01, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x0F, 0x00, 0x72, 0x6D, 0x00, 
    0x20, 0x00, 0x56, 0x00, 0x6F, 0x00, 0x6C, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x65, 0x00,
    'S', 'Y', 'S', 'T', 'E', 'M', '~', '1', //8 bytes name. "SYSTEM~1"
    0x20, 0x20, 0x20,                       //3 bytes
    0x13,                                   //0x01:Read Only; 0x02:Hidden; 0x10:SubDir
    0x00,                                   //Reserved
    0x99,                                   //the ms time of the file when created. 0-199.
    0x32, 0x54,                             //the time of the file when created. bit15-11: hour 0 - 23; bit10-5: minute 0 - 59; bit4-0: second 0 - 29(*2s)
    0xB5, 0x52,                             //the date of the file when created. bit15-9: year 0 is 1980; bit8-5: month, 1 to 12; bit4-0: day, 1 - 31 
    0xB5, 0x52,                             //the date of the file when last visited.
    0x00, 0x00,                             //Reserved
    0x33, 0x54,                             //the time of the file when modified.
    0xB5, 0x52,                             //the date of the file when modified.
    0x08, 0x00,                             //the first cluster of the file.
    0x00, 0x00,
    0x00, 0x00,
    //File 1, 32 bytes
    0xE5, 0xB0, 0x65, 0xFA, 0x5E, 0x87, 0x65, 0x2C, 0x67, 0x87, 0x65, 0x0F, 0x00, 0xD2, 0x63, 0x68, 
    0x2E, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 
    0xE5, 0xC2, 0xBD, 0xA8, 0xCE, 0xC4, 0x7E, 0x31, 0x54, 0x58, 0x54, 0x20, 0x00, 0xA0, 0xF3, 0x66, 
    0xB5, 0x52, 0xB5, 0x52, 0x00, 0x00, 0xF4, 0x66, 0xB5, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x54, 0x45, 0x58, 0x54, 0x20, 0x20, 0x20, 0x20, //8 bytes file name. "TEXT"
    0x54, 0x58, 0x54, //3 bytes: "TXT"
    0x20, //0x01:Read Only; 0x02:Hidden;0x04:System;0x08:Volume Label;0x10:SubDir;0x20:Document;0x40:Device;0x80:Reserved.
    0x10, //Reserved
    0x00, //the ms time of the file when created. 0-199.
    0x00, 0x01, //the time of the file when created. bit15-11: hour 0 - 23; bit10-5: minute 0 - 59; bit4-0: second 0 - 29(*2s)
    0xe1, 0x50, //the date of the file when created. bit15-9: year 0 is 1980; bit8-5: month, 1 to 12; bit4-0: day, 1 - 31 
    0xe1, 0x50, //the date of the file when last visited.
    0x00, 0x00,
    0x00, 0x01, //the time of the file when modified.
    0xe1, 0x50, //the date of the file when modified.
    0x02, 0x00, //the first cluster of the file.
    FILE_SIZE & 0xFF, 
    (FILE_SIZE >> 8) & 0xFF, 
    (FILE_SIZE >> 16) & 0xFF, 
    (FILE_SIZE >> 24) & 0xFF, //the file size.
    //File 2, 32 bytes
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //8 bytes file name. 0x00 means no file.
    0x00, 0x00, 0x00, //3 bytes
    0x00, //0x01:Read Only; 0x02:Hidden;0x04:System;0x08:Volume Label;0x10:SubDir;0x20:Document;0x40:Device;0x80:Reserved.
    0x00, //Reserved
    0x00, //the ms time of the file when created. 0-199.
    0x00, 0x00, //the time of the file when created. bit15-11: hour 0 - 23; bit10-5: minute 0 - 59; bit4-0: second 0 - 29(*2s)
    0x00, 0x00, //the date of the file when created. bit15-9: year 0 is 1980; bit8-5: month, 1 to 12; bit4-0: day, 1 - 31 
    0x00, 0x00, //the date of the file when last visited.
    0x00, 0x00,
    0x00, 0x00, //the time of the file when modified.
    0x00, 0x00, //the date of the file when modified.
    0x00, 0x00, //the first cluster of the file.
    0x00, 0x00, 0x00, 0x00, //the file size.
}; 

为了实现长文件名的支持,System Volume Information文件夹占用3个目录项(3x32字节)。注意把文件属性设置为0x13,让它只读且隐藏。

3. FAT表

FAT12前面4个字节F8 FF FF 是固定的,后面每1.5个字节为一个Cluster标记。这里开了1个2K大小的buffer作为文件内容空间,所以需要用到4个扇区。

const uint8_t mscFat[FAT_SIZE * FAT_SECTOR_SIZE] =
{
    0xf8, 0xff, 0xff, 
    0x03, 0x40,
    0x00, 0x05, 
    0xf0, 0xff,
};

4. 空数据

定义一个空数组,在保留区等数据时回传给主机。

const uint8_t mscNull[FAT_SECTOR_SIZE] =
{
    0x00
};

5. massReadMemory

根据scsi的offset参数返回对应的数据。

if(gScsiInfo.offset >= (FAT_RESERVE_SIZE + FAT_SIZE * 2 + ROOT_SIZE) * FAT_SECTOR_SIZE)
{//RAM Area
    offset = gScsiInfo.offset - (FAT_RESERVE_SIZE + FAT_SIZE * 2 + ROOT_SIZE) * FAT_SECTOR_SIZE;
    pBuf = gUDiskRam;
}
else if(gScsiInfo.offset >= (FAT_RESERVE_SIZE + FAT_SIZE * 2) * FAT_SECTOR_SIZE)
{//Root Area
    offset = gScsiInfo.offset - (FAT_RESERVE_SIZE + FAT_SIZE * 2) * FAT_SECTOR_SIZE;
    pBuf = mscRootDir;

}
else if(gScsiInfo.offset >= (FAT_RESERVE_SIZE + FAT_SIZE) * FAT_SECTOR_SIZE)
{
    offset = gScsiInfo.offset - (FAT_RESERVE_SIZE + FAT_SIZE) * FAT_SECTOR_SIZE;
    pBuf = (uint8_t *)mscFat;
}
else if(gScsiInfo.offset >= (FAT_RESERVE_SIZE + 0) * FAT_SECTOR_SIZE)
{
    offset = gScsiInfo.offset - (FAT_RESERVE_SIZE + 0) * FAT_SECTOR_SIZE;
    pBuf = (uint8_t *)mscFat;
}
else if(gScsiInfo.offset >= 1 * FAT_SECTOR_SIZE)
{
    offset = gScsiInfo.offset % FAT_SECTOR_SIZE;
    pBuf = (uint8_t *)mscNull;
}
else
{
    offset = gScsiInfo.offset;
    pBuf = (uint8_t *)mscDBR;
}

6. massWriteMemory

只更新RAM部分,其他地址不处理

if(gScsiInfo.offset >= (FAT_RESERVE_SIZE + ROOT_SIZE + FAT_SIZE * 2) * FAT_SECTOR_SIZE)
{//RAM Area
    uint32_t i;
    offset = gScsiInfo.offset - (FAT_RESERVE_SIZE + ROOT_SIZE + FAT_SIZE * 2) * FAT_SECTOR_SIZE;
    for(i = 0; i < count; i++)
        gUDiskRam[offset + i] = gMscBotData.dat[i];
}

注意事项:

1. FAT12支持的最大簇数是4085,即0xFFF - 8(0xFF8-0xFFF作为结束) - 2(簇号从2开始)。簇大小设置为512字节的情况下,最大支持2091520字节,如果需要支持更大的容量,修改DBR中的簇大小。更大的容量可能是要改为FAT32,不确定是否还支持FAT16.

2. 由于System Volume Information文件夹的特殊处理,导致插入U盘会弹出一个错误

并且点击这个隐藏文件夹时会提示错误

3. U盘内的文件TEXT.TXT占用了整个U盘,并且其ROOT项和FAT表都不会更改,所以当要写入时,文件大小必须是ROOT项中设置的大小,否则会写入失败。

4. 目前发现无法写入,可以打开编辑再保存。

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值