STM32CbueMX之SPI_FLASH + FATFS + USB MSC + 虚拟扩容

2024-02-10 14:48

本文主要是介绍STM32CbueMX之SPI_FLASH + FATFS + USB MSC + 虚拟扩容,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:

做一个在spi flash 上挂一个文件系统,然后板子用USB线连接电脑能识别读出spi flash上的文件。

背景:

因为spi flash容量比较小,我使用的是32MB的,插上电脑,感觉不好看,显示容量太小了。

所以我打算虚拟扩容一下变成1GB,但是实际可用的还是32MB。把电脑的写权限取消,只读。

fatfs调用写函数

FRESULT f_write (
    FIL* fp,            /* Pointer to the file object */
    const void* buff,    /* Pointer to the data to be written */
    UINT btw,            /* Number of bytes to write */
    UINT* bw            /* Pointer to number of bytes written */
)

返回值正常,然后*bw = 0;表示无空间写入。

主要是我下面实现了在MCU的fatfs文件系统分配超过32MB的空间都会提示已满,不再写入数据。

毕竟真实物理空间是32MB.这样不会破坏文件系统的文件表,导致文件系统出错。

为什么取消电脑写权限呢?是因为电脑写入fatfs文件系统的数据超过32MB的时候会破坏文件系统的文件表。

或者可以再改进一下,可以自我尝试。

STM32CbueMX配置图

 USB pack 大小 =  4096;包越大速度越快。

heap大小:比我们设置的USB pack大512字节就够了。

代码

spi flash 驱动以前文章有写。

user_diskio.c

DSTATUS USER_initialize (BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{Stat = BSP_W25Q256_Init();return Stat;
}DSTATUS USER_status (BYTE pdrv       /* Physical drive number to identify the drive */
)
{return Stat;
}DRESULT USER_read (BYTE pdrv,      /* Physical drive nmuber to identify the drive */BYTE *buff,     /* Data buffer to store read data */DWORD sector,   /* Sector address in LBA */UINT count      /* Number of sectors to read */
)
{if(BSP_W25Q256_Read(buff, sector << 12, count << 12) != W25Q256_OK)return RES_ERROR;}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 */
)
{if(BSP_W25Q256_Erase_Sector(sector << 12) != W25Q256_OK)return RES_ERROR;if(BSP_W25Q256_Write((uint8_t*)buff, sector << 12, count << 12) != W25Q256_OK)return RES_ERROR;return RES_OK;
}DRESULT USER_ioctl (BYTE pdrv,      /* Physical drive nmuber (0..) */BYTE cmd,       /* Control code */void *buff      /* Buffer to send/receive control data */
)
{DRESULT res = RES_ERROR;if(pdrv != 0)   return RES_PARERR;switch(cmd){case CTRL_SYNC:res = RES_OK;break;case GET_SECTOR_COUNT:*(DWORD*)buff = (W25Q256FV_FLASH_SIZE / W25Q256FV_SECTOR_SIZE);res = RES_OK;break;case GET_SECTOR_SIZE:*(WORD*)buff = W25Q256FV_SECTOR_SIZE;res = RES_OK;break;case GET_BLOCK_SIZE:*(DWORD*)buff = 1;res = RES_OK;break;default:res = RES_PARERR;break;}return res;
}

usbd_storage_if.c

#define MYSTORAGE_LUN_NBR                  1
#define MYSTORAGE_BLK_NBR                  (W25Q256FV_FLASH_SIZE / W25Q256FV_SECTOR_SIZE);
#define MYSTORAGE_BLK_SIZ                  W25Q256FV_SECTOR_SIZEint8_t STORAGE_Init_FS(uint8_t lun)
{return (USBD_OK);
}int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{*block_num  = MYSTORAGE_BLK_NBR;*block_size = MYSTORAGE_BLK_SIZ;return (USBD_OK);
}int8_t STORAGE_IsReady_FS(uint8_t lun)
{return (USBD_OK);
}int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
{return (USBD_OK);
}int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{if(BSP_W25Q256_Read(buf, blk_addr << 12, blk_len << 12) != W25Q256_OK)return USBD_FAIL;return (USBD_OK);
}int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{if(BSP_W25Q256_Erase_Sector(blk_addr << 12) != W25Q256_OK)return USBD_FAIL;if(BSP_W25Q256_Write(buf, blk_addr << 12, blk_len << 12) != W25Q256_OK)return USBD_FAIL;return (USBD_OK);
}int8_t STORAGE_GetMaxLun_FS(void)
{return (MYSTORAGE_LUN_NBR - 1);
}

main.c

#include <stdio.h>
#include "w25q256.h"FIL fil;
FATFS spi_fs;
unsigned char work[4096] = {0};
unsigned char read_buf[50] = {0};
unsigned char write_buf[4096] = "hello sudaroot\r\n";static void USB_GPIO_Init(void);int main(void)
{/* USER CODE BEGIN 1 */unsigned int res = 0;unsigned int count = 0;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit */USB_GPIO_Init();/* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_USB_DEVICE_Init();MX_SPI5_Init();MX_FATFS_Init();/* USER CODE BEGIN 2 */printf("sudaroot usb and fatfs msc\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){printf("文件系统挂载失败,f_mount = %d\r\n", res);res = f_mkfs("0:", FM_ANY, 0, (void*)&work, 4096);if(res != FR_OK){printf("文件系统格式化失败,f_mkfs = %d\r\n", res);}else{printf("重新挂载文件系统\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){printf("文件系统挂载失败,f_mount = %d\r\n", res);}else{printf("文件系统挂载成功\r\n");}}}else printf("文件系统挂载成功\r\n");/*----------------------- 文件系统测试:写测试 -----------------------------*/printf("\r\n****** 即将进行文件写入测试... ******\r\n");res = f_open(&fil, "0:sudaroot.txt", FA_OPEN_APPEND | FA_WRITE);if(res == FR_OK){res = f_write(&fil, write_buf, 4096, &count);if(res != FR_OK){printf("f_write 发生错误,err = %d, count = %d\r\n", res, count);printf("关闭打开的sudaroot.txt文件\r\n");count = 0;f_close(&fil);}else{if(count == 0){printf("没有空间写入数据了\r\n");}count = 0;f_close(&fil);}}else{printf("打开/创建sudaroot.txt文件失败,err = %d\r\n", res);}printf("\r\n****** 结束进行文件写入测试... ******\r\n");/*------------------- 文件系统测试:读测试 ------------------------------------*/printf("****** 即将进行文件读取测试... ******\r\n");res = f_open(&fil, "0:sudaroot.txt", FA_OPEN_EXISTING | FA_READ);if(res == FR_OK){printf("打开sudaroot.txt文件成功\r\n");res = f_read(&fil, read_buf, sizeof(read_buf), &count);if(res != FR_OK){printf("f_read 发生错误,err = %d\r\n", res);printf("关闭打开的sudaroot.txt文件\r\n");f_close(&fil);}else{printf("文件读取成功,读取字节数据:%d\n", count);printf("向文件读取的数据为:\r\n%s\r\n", read_buf);printf("关闭打开的sudaroot.txt文件\r\n");f_close(&fil);}}else printf("打开sudaroot.txt文件失败,err = %d\r\n", res);/*------------------- 不再使用文件系统,取消挂载文件系统 ------------------------------------*/printf("不再使用文件系统,取消挂载文件系统\r\n");res = f_mount(NULL, "0:", 1);if(res == FR_OK) printf("取消挂载文件系统成功\r\n");else    printf("取消挂载文件系统失败,err = %d\r\n", res);printf("文件系统测试结束\r\n");printf("csize = 0x%X = %d\r\n", spi_fs.csize, spi_fs.csize);printf("ssize = 0x%X = %d\r\n", spi_fs.ssize, spi_fs.ssize);printf("n_fatent = 0x%lX = %lu\r\n", spi_fs.n_fatent, spi_fs.n_fatent);printf("database = 0x%lX = %lu\r\n", spi_fs.database, spi_fs.database);printf("fsize = 0x%lX = %lu\r\n", spi_fs.fsize, spi_fs.fsize);printf("last_clst = 0x%lX = %lu\r\n", spi_fs.last_clst, spi_fs.last_clst);printf("free_clst = 0x%lX = %lu\r\n", spi_fs.free_clst, spi_fs.free_clst);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);HAL_Delay(500);}/* USER CODE END 3 */
}int fputc(int ch, FILE *FILE)
{HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, HAL_MAX_DELAY);return ch;
}static void USB_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_RESET);/*Configure GPIO pin : W25Q256_CS_Pin */GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_PULLDOWN;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);HAL_Delay(2);
}

效果:

扩容:

先算一下1GB转换成4KB一个扇区的话一共有几个扇区。

1GB = 1024MB =1024 * 1024KB

总扇区数 = 1024 * 1024KB / 4 KB = 262144

修改 user_diskio.c

DRESULT USER_ioctl (BYTE pdrv,      /* Physical drive nmuber (0..) */BYTE cmd,       /* Control code */void *buff      /* Buffer to send/receive control data */
)
{
...case GET_SECTOR_COUNT:*(DWORD*)buff = 262144;res = RES_OK;break;
...
}

修改usbd_storage_if.c

#define MYSTORAGE_BLK_NBR                 262144int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
//    if(BSP_W25Q256_Erase_Sector(blk_addr << 12) != W25Q256_OK)
//        return USBD_FAIL;//    if(BSP_W25Q256_Write(buf, blk_addr << 12, blk_len << 12) != W25Q256_OK)
//        return USBD_FAIL;return (USBD_OK);
}

main.c

#include <stdio.h>
#include "w25q256.h"FIL fil;
FATFS spi_fs;
unsigned char work[4096] = {0};
unsigned char read_buf[50] = {0};
unsigned char write_buf[4096] = "hello sudaroot\r\n";static void USB_GPIO_Init(void);int main(void)
{/* USER CODE BEGIN 1 */unsigned int res = 0;unsigned int count = 0;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit */USB_GPIO_Init();/* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_USB_DEVICE_Init();MX_SPI5_Init();MX_FATFS_Init();/* USER CODE BEGIN 2 */printf("sudaroot usb and fatfs msc\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){printf("文件系统挂载失败,f_mount = %d\r\n", res);res = f_mkfs("0:", FM_ANY, 0, (void*)&work, 4096);if(res != FR_OK){printf("文件系统格式化失败,f_mkfs = %d\r\n", res);}else{printf("重新挂载文件系统\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){printf("文件系统挂载失败,f_mount = %d\r\n", res);}else{printf("文件系统挂载成功\r\n");}}}else printf("文件系统挂载成功\r\n");printf("csize = 0x%X = %d\r\n", spi_fs.csize, spi_fs.csize);printf("ssize = 0x%X = %d\r\n", spi_fs.ssize, spi_fs.ssize);printf("n_fatent = 0x%lX = %lu\r\n", spi_fs.n_fatent, spi_fs.n_fatent);printf("database = 0x%lX = %lu\r\n", spi_fs.database, spi_fs.database);printf("fsize = 0x%lX = %lu\r\n", spi_fs.fsize, spi_fs.fsize);printf("last_clst = 0x%lX = %lu\r\n", spi_fs.last_clst, spi_fs.last_clst);printf("free_clst = 0x%lX = %lu\r\n", spi_fs.free_clst, spi_fs.free_clst);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);HAL_Delay(500);}/* USER CODE END 3 */
}int fputc(int ch, FILE *FILE)
{HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, HAL_MAX_DELAY);return ch;
}static void USB_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_RESET);/*Configure GPIO pin : W25Q256_CS_Pin */GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_PULLDOWN;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);HAL_Delay(2);
}

编译烧录重启连接USB到PC

一切正常。

分析:

先分析一下FATFS结构体和容量相关的参数,然再改。

/* File system object structure (FATFS) */typedef struct {
...
WORD csize; / *簇大小[扇区] * /
WORD ssize; / *扇区大小(512,1024,2048或4096)* /
DWORD last_clst; / *最后分配的簇* /
DWORD free_clst; / *没使用簇数量* /
DWORD n_fatent; / * FAT条目数(簇数+ 2)* /
DWORD fsize; / * FAT的大小[扇区] * /
DWORD volbase; / *卷起始扇区* /
DWORD fatbase; / * FAT起始扇区* /
DWORD dirbase; / *根目录基本扇区* /
DWORD database; / *数据起始扇区* /
...
} FATFS;

簇是文件系统的最小单位,spi_flash的最小擦除单位是扇区。

变量csize表示一个簇由几个扇区组成,ssize表示一个扇区大小。n_fatent表示可用于存数据簇的总数。

还有就是一般小于4GB的文件系统都会格式化成FAT12或者FAT16,这种方式扩容不支持FAT32。

由上面串口打印的信息我们可以看出

簇大小 = csize *  ssize = 8 * 4KB = 32KB

那么1GB可以分成几个32KB的簇

簇总数 = 1GB / 32KB = 1024 * 1024KB  / 32KB = 32768 = 0X‭8000‬

文件表和保留扇区用几个簇 = 簇总数 - n_fatent = 32768 - 32759 = 9

本来是32MB的flash = 8192 * 4KB = 1024 * 32KB

所以我们使用更改n_fatent = 1024 - 9 = 1015

还要注意的是n_fatent会在实际可用簇的基础上+2。

但是直接更改n_fatent是不行的,因为n_fatent会写入分区表,这样子会破坏分区表。

所以换一种方法,看哪里用n_fatent分配空间,在那里把n_fatent修改成常量。

下面这个函数是分配新的簇函数

/*-----------------------------------------------------------------------*/
/* FAT handling - Stretch a chain or Create a new chain                  */
/*-----------------------------------------------------------------------*/
static
DWORD create_chain (	/* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */_FDID* obj,			/* Corresponding object */DWORD clst			/* Cluster# to stretch, 0:Create a new chain */
)
{DWORD cs, ncl, scl;FRESULT res;FATFS *fs = obj->fs;if (clst == 0) {	/* Create a new chain */scl = fs->last_clst;				/* Get suggested cluster to start from */if (scl == 0 || scl >= fs->n_fatent) scl = 1;}else {				/* Stretch current chain */cs = get_fat(obj, clst);			/* Check the cluster status */if (cs < 2) return 1;				/* Invalid FAT value */if (cs == 0xFFFFFFFF) return cs;	/* A disk error occurred */if (cs < fs->n_fatent) return cs;	/* It is already followed by next cluster */scl = clst;}#if _FS_EXFATif (fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */ncl = find_bitmap(fs, scl, 1);				/* Find a free cluster */if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl;	/* No free cluster or hard error? */res = change_bitmap(fs, ncl, 1, 1);			/* Mark the cluster 'in use' */if (res == FR_INT_ERR) return 1;if (res == FR_DISK_ERR) return 0xFFFFFFFF;if (clst == 0) {							/* Is it a new chain? */obj->stat = 2;							/* Set status 'contiguous' */} else {									/* It is a stretched chain */if (obj->stat == 2 && ncl != scl + 1) {	/* Is the chain got fragmented? */obj->n_cont = scl - obj->sclust;	/* Set size of the contiguous part */obj->stat = 3;						/* Change status 'just fragmented' */}}if (obj->stat != 2) {	/* Is the file non-contiguous? */if (ncl == clst + 1) {	/* Is the cluster next to previous one? */obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2;	/* Increment size of last framgent */} else {				/* New fragment */if (obj->n_frag == 0) obj->n_frag = 1;res = fill_last_frag(obj, clst, ncl);	/* Fill last fragment on the FAT and link it to new one */if (res == FR_OK) obj->n_frag = 1;}}} else
#endif{	/* On the FAT12/16/32 volume */ncl = scl;	/* Start cluster */for (;;) {ncl++;							/* Next cluster */if (ncl >= fs->n_fatent) {		/* Check wrap-around */ncl = 2;if (ncl > scl) return 0;	/* No free cluster */}cs = get_fat(obj, ncl);			/* Get the cluster status */if (cs == 0) break;				/* Found a free cluster */if (cs == 1 || cs == 0xFFFFFFFF) return cs;	/* An error occurred */if (ncl == scl) return 0;		/* No free cluster */}res = put_fat(fs, ncl, 0xFFFFFFFF);	/* Mark the new cluster 'EOC' */if (res == FR_OK && clst != 0) {res = put_fat(fs, clst, ncl);	/* Link it from the previous one if needed */}}if (res == FR_OK) {			/* Update FSINFO if function succeeded. */fs->last_clst = ncl;if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--;fs->fsi_flag |= 1;} else {ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;	/* Failed. Generate error status */}return ncl;		/* Return new cluster number or error status */
}#endif /* !_FS_READONLY */

修改一下

static
DWORD create_chain (	/* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */_FDID* obj,			/* Corresponding object */DWORD clst			/* Cluster# to stretch, 0:Create a new chain */
)
{...if (clst == 0) {	/* Create a new chain */scl = fs->last_clst;				/* Get suggested cluster to start from *///if (scl == 0 || scl >= fs->n_fatent) scl = 1;if (scl == 0 || scl >= 1015) scl = 1;}
...//if (ncl >= fs->n_fatent) {		/* Check wrap-around */if (ncl >= 1015) {		/* Check wrap-around */ncl = 2;if (ncl > scl) return 0;	/* No free cluster */}
....
}

把注释的两行的fs->n_fatent 换成我们算出来的 1015;

测试程序:

每次调用f_write()往缓存区写入4096个字节数据,统计能写多少次。

f_write(&fil, write_buf, 4096, &count); 函数返回值不等于FR_OK一般是配置出错,或者硬件出错。

而count写入字节统计为0的话才是没用多余的空间写入。

FIL fil;
FATFS spi_fs;
unsigned char read_buf[50] = {0};
unsigned char work[4096] = {0};
unsigned char write_buf[4096] = "\r\nhello sudaroot\r\n";/*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */unsigned int res = 0;unsigned int count = 0;unsigned int sum = 0;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit */USB_GPIO_Init();/* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_USB_DEVICE_Init();MX_SPI5_Init();MX_FATFS_Init();/* USER CODE BEGIN 2 */HAL_Delay(1000);BSP_W25Q256_Erase_Sector(0);HAL_Delay(100);printf("sudaroot usb and fatfs msc\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){printf("文件系统挂载失败,f_mount = %d\r\n", res);res = f_mkfs("0:", FM_ANY, 0, (void*)&work, 4096);if(res != FR_OK){printf("文件系统格式化失败,f_mkfs = %d\r\n", res);}else{printf("重新挂载文件系统\r\n");res = f_mount(&spi_fs, "0:", 1);if(res != FR_OK){printf("文件系统挂载失败,f_mount = %d\r\n", res);}else{printf("文件系统挂载成功\r\n");}}}else printf("文件系统挂载成功\r\n");printf("csize = 0x%X = %d\r\n", spi_fs.csize, spi_fs.csize);printf("ssize = 0x%X = %d\r\n", spi_fs.ssize, spi_fs.ssize);printf("n_fatent = 0x%lX = %lu\r\n", spi_fs.n_fatent, spi_fs.n_fatent);printf("database = 0x%lX = %lu\r\n", spi_fs.database, spi_fs.database);printf("fsize = 0x%lX = %lu\r\n", spi_fs.fsize, spi_fs.fsize);printf("last_clst = 0x%lX = %lu\r\n", spi_fs.last_clst, spi_fs.last_clst);printf("free_clst = 0x%lX = %lu\r\n", spi_fs.free_clst, spi_fs.free_clst);/*----------------------- 文件系统测试:写测试 -----------------------------*/printf("\r\n****** 即将进行文件写入测试... ******\r\n");while(1){res = f_open(&fil, "0:sudaroot.txt", FA_OPEN_APPEND | FA_WRITE);if(res == FR_OK){res = f_write(&fil, write_buf, 4096, &count);if(res != FR_OK){printf("f_write 发生错误,err = %d, count = %d\r\n", res, count);printf("关闭打开的sudaroot.txt文件\r\n");count = 0;f_close(&fil);break;}else{if(count == 0){printf("没有空间写入数据了\r\n");break;}sum++;count = 0;f_close(&fil);}}else{printf("打开/创建sudaroot.txt文件失败,err = %d\r\n", res);break;}HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);HAL_Delay(1);}printf("总计:sum = %u\r\n", sum);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);HAL_Delay(500);}/* USER CODE END 3 */
}

效果:

电脑也能读取。

上图等值写入了8104次数据。每次写4KB字节。那么共写入了 8104 * 4KB = 32,416KB数据。

计算一下,和上面数据对比。

我们上面计算出n_fatent = 1015;但是n_fatent会多加比实际可用簇多加2个簇。

故实际的簇是n_fatent - 2 = 1015 - 2 = 1013;

因为一簇 = 32KB; 

故32MB实际可用 = 1013 * 32KB = 32416KB;

对比发现,和计算结果和测试效果一致。

扩容成功。

  全篇完。

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。
笔记是以最简单的方式,只展示最核心的原理。
若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。
若无积分等无法下载源码,可加入QQ群657407920下载交流经验。感谢~!

 

 

这篇关于STM32CbueMX之SPI_FLASH + FATFS + USB MSC + 虚拟扩容的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/697391

相关文章

HDFS—集群扩容及缩容

白名单:表示在白名单的主机IP地址可以,用来存储数据。 配置白名单步骤如下: 1)在NameNode节点的/opt/module/hadoop-3.1.4/etc/hadoop目录下分别创建whitelist 和blacklist文件 (1)创建白名单 [lytfly@hadoop102 hadoop]$ vim whitelist 在whitelist中添加如下主机名称,假如集群正常工作的节

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

什么是 Flash Attention

Flash Attention 是 由 Tri Dao 和 Dan Fu 等人在2022年的论文 FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness 中 提出的, 论文可以从 https://arxiv.org/abs/2205.14135 页面下载,点击 View PDF 就可以下载。 下面我

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑燃料电池和电解槽虚拟惯量支撑的电力系统优化调度方法》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源程序擅长文章解读,论文与完整源程序,等方面的知识,电网论文源程序关注python

Usb Audio Device Descriptor(10) Hid Device

对于 Standard Interface Descriptor, 当 bInterfaceClass=0x03时,即为HID设备。Standard Interface Descriptor如下 struct usb_standard_interface_descriptor{U8 bLength; /*Size of this descriptor in bytes*/U8 bDescrip

mysql动态扩容调研

MySQL动态扩容方案 目前可用方案 MySQL的复制: 一个Master数据库,多个Salve,然后利用MySQL的异步复制能力实现读写分离,这个方案目前应用比较广泛,这种技术对于以读为主的应用很有效。数据切分(MySQL的Sharding策略): 垂直切分:一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分;垂直切分的思路就是分析

AF透明模式/虚拟网线模式组网部署

透明模式组网 实验拓扑  防火墙基本配置 接口配置 eth1  eth3   放通策略  1. 内网用户上班时间(9:00-17:00)不允许看视频、玩游戏及网上购物,其余时 间访问互联网不受限制;(20 分) 应用控制策略   2. 互联网用户只允许访问内网两台服务器的 WEB、SSH 和远程桌面服务,其余 服务均不允许访问;(20 分) 外网访问内网的限制策略   虚拟网线模式组网 实