FAT详解¶
三、NAND FLASH Memory Map¶ RA[17:6]表示Blocks的index,RA[5:0]表示Pages的index,CA[11:0]表示具体到每个page中字节的地址。
RA[17:6]有12位,最大为2^12=4096,说明Blocks只有4096个,index范围为[0:4095]。
RA[5:0]有6位,最大为2^6=64,表示每个block有64个page。
CA[11:0]有12位,但是实际只有2175Byte可用。
每次操作时最小单元就是page,则最小单元扇区为:page_size=4096Byte,实际只有2175Byte,可用的为2048Byte=2KByte。
每个block有64 page,故block size=page_num*page_size=64*4096Byte=256KByte,实际可用为128Kbyte。
sector:表示扇区,每个扇区有1page,sector size:2KByte。sector可等同于page。
cluster:表示簇,每个簇有64page或者64sector,cluster size:128Kbyte。cluster可等同于block。
总计有4096*64个page。
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
这里的block_num指的是扇区的个数,即page的个数,为64*4096;block_size指的是一个扇区大小,即page大小,为2048Byte,即2KByte。
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
传进来的参数是扇区的地址和扇区的个数(即page的index和page个数),在进行读的时候要转换成字节地址和字节大小,读写是按照page来操作的。但是写之前需要擦除block,写block中,剩余page时不需要重复擦写。
写函数先将1 block内容读出来,再擦除,然后写进去。page是否为脏污应该由fat表来表明,但首先应该先擦除全部空间。读写速度受到限制。
使用数组flag标记是否脏污,或者擦除过。但是这种,脏污后重新擦除时不可用,因为标记是说明是否有擦除。如果有写过则是脏page,再写时需要擦除。如果不擦除则无法写入。每次断电后参数复位后,可能会重新擦除。这时候是由FAT表和FATFS管理。断电后写重复位置会擦除该区域。
/**
* @brief 传进来的参数是扇区的地址和扇区的个数(即page的地址和page个数),在进行读的时候要转换成字节地址和字节大小
* 这里USB的最大数据包是2048,为1page,故blk_len最大就是1
* @param lun: .
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
uint32_t n;
uint32_t block_num = 0;
uint32_t page_num = 0;
if (blk_len > 1)
{
return USBD_FAIL;
}
//SEGGER_RTT_printf(0, "Read=> blk_addr=%d, blk_len=%d\r\n", blk_addr, blk_len);
block_num = blk_addr / 64;
page_num = blk_addr % 64;
flash_Read(block_num, page_num, 0, buf, STORAGE_BLK_SIZ);
//SEGGER_RTT_printf(0, "Read=> Block=%d,Page=%d\r\n", block_num, page_num);
return (USBD_OK);
/* USER CODE END 6 */
}
uint8_t erase_flag[512] = {0}; //block erase flag,0:not erase,1:erase
uint8_t erase_f = 0x00;
uint8_t data_buf_t[2048];
uint32_t temp_block_num = 5000;
/**
* @brief .
* @param lun: .
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 7 */
uint32_t n;
uint32_t block_num = 0;
uint32_t page_num = 0;
SEGGER_RTT_printf(0, "Write=> blk_addr=%d, blk_len=%d\r\n", blk_addr, blk_len);
block_num = blk_addr / 64;
page_num = blk_addr % 64;
if ((erase_flag[block_num / 8] & erase_f) == 0)
{
/*
flash_Erase(4095);
for (n = 0; n < 64; n++)
{
flash_Read(block_num, n, 0, data_buf_t, STORAGE_BLK_SIZ);
flash_Write(4095, n, 0, data_buf_t, STORAGE_BLK_SIZ);
}
*/
flash_Erase(block_num);
erase_f = (1 << (block_num % 8));
erase_flag[block_num / 8] |= erase_f;
SEGGER_RTT_printf(0, "Write=> Erase Block=%d\r\n", block_num);
}
flash_Write(block_num, page_num, 0, buf, STORAGE_BLK_SIZ);
/*
if (temp_block_num != block_num)
{
for (n = 0; n < page_num; n++)
{
flash_Read(4095, n, 0, data_buf_t, STORAGE_BLK_SIZ);
flash_Write(block_num, n, 0, data_buf_t, STORAGE_BLK_SIZ);
}
for (n = page_num; n < 64; n++)
{
flash_Read(4095, n, 0, data_buf_t, STORAGE_BLK_SIZ);
flash_Write(block_num, n, 0, data_buf_t, STORAGE_BLK_SIZ);
}
}
temp_block_num = block_num;
*/
return (USBD_OK);
/* USER CODE END 7 */
}
后续的读写如果考虑erase就擦除,可能导致写不进去。需要擦除后再写。
/*---------- -----------*/
#define USBD_MAX_NUM_INTERFACES 1U
/*---------- -----------*/
#define USBD_MAX_NUM_CONFIGURATION 1U
/*---------- -----------*/
#define USBD_MAX_STR_DESC_SIZ 2048U
/*---------- -----------*/
#define USBD_DEBUG_LEVEL 0U
/*---------- -----------*/
#define USBD_LPM_ENABLED 1U
/*---------- -----------*/
#define USBD_SELF_POWERED 1U
/*---------- -----------*/
#define MSC_MEDIA_PACKET 2048U
/* MSC Class Config */
#ifndef MSC_MEDIA_PACKET
#define MSC_MEDIA_PACKET 512U
#endif /* MSC_MEDIA_PACKET */
#define MSC_MAX_FS_PACKET 0x40U
#define MSC_MAX_HS_PACKET 0x200U
#define BOT_GET_MAX_LUN 0xFE
#define BOT_RESET 0xFF
#define USB_MSC_CONFIG_DESC_SIZ 32
#define MSC_EPIN_ADDR 0x81U
#define MSC_EPOUT_ADDR 0x01U
可见USB操作时最大是2048Byte,即2Kbyte。就是1page数据。
Fatfs和USB都是按照最小单元page(sector)进行读写。即每次都是读写1page数据。
挂载和open有问题,貌似没有FAT表。写是128K擦除,读是2K读,导致读写page时如果写的是同一个block的同一个page则需要擦除整个block,会导致其他数据丢失。
改成以下方式后,erase次数太多,但是调试OK。
uint8_t data_buf_t[2048];
uint32_t temp_block_num = 5000, temp_page_num = 65;
DRESULT USER_write(
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
/* USER CODE BEGIN WRITE */
/* USER CODE HERE */
uint32_t n = 0, k = 0;
uint32_t block_num = 0;
uint32_t page_num = 0;
uint8_t i = 0, j = 0, temp = 0;
SEGGER_RTT_printf(0, "Fatfs Write=> sector=%d, count=%d\r\n", sector, count);
for (n = 0; n < count; n++)
{
block_num = sector / 64;
page_num = sector % 64;
flash_Erase(BLOCK_RSV);
for (k = 0; k < 64; k++)
{
flash_Read(block_num, k, 0, data_buf_t, PAGE_SIZE);
flash_Write(BLOCK_RSV, k, 0, data_buf_t, PAGE_SIZE);
}
flash_Erase(block_num);
SEGGER_RTT_printf(0, "Fatfs Erase=> block=%d\r\n", block_num);
for (i = 0; i < 64; i++)
{
if (i == page_num)
{
flash_Write(block_num, i, 0, (uint8_t *)&buff[0 + n * PAGE_SIZE], PAGE_SIZE);
}
else
{
flash_Read(BLOCK_RSV, i, 0, data_buf_t, PAGE_SIZE);
flash_Write(block_num, i, 0, data_buf_t, PAGE_SIZE);
}
}
sector++;
//SEGGER_RTT_printf(0, "Write=> Block=%d,Page=%d\r\n", block_num, page_num);
}
return RES_OK;
/* USER CODE END WRITE */
}
写时如果不close,则无法同时read。