2025-07-22 20:39:30

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。

头条如何看收藏 详细步骤与常见问题解答
中国有多少个卫星星座? 近年来,随着我国航天技术的不断突破和商业模式的多样化,中国的卫星星座建设正迎来爆发式发展,多领域星座正不断补强我国“空天...