stm32 hal NANDFLASH+FATFS+U盘
工作中用到到NandFlash,之前用的SDIO+FATFS+U盘的方案,想延续这个方案,但是发现网上的案例并不多,最后自己手撸,给大家分享一下。
代码有个bug,并且做了一些整理和优化,我更新了一版
1.NANDFLASH基础
【正点原子STM32】FSMC_FMC——NAND FLASH实验(存储原理、存储结构、坏块管理和磨损均衡、控制命令、FMC-NAND FLASH接口、NAND FLASH驱动步骤)_stm32 fmc-CSDN博客
不熟悉NANDFLASH 和 FMC的建议先去看一下正点原子的文章,基础知识不再赘述,网上的好文章不少,我这边着重强调几个特性。
1.NandFlash 由PAGE,BLOCK,PLAN,LUN构成,为包含关系,即一个BLOCK = N * PAGE,
PLAN = N * BLOCK, LUN = N * PLAN,这边的N代表的是对应组成一个模块需要的个数,不同芯片的构建不一样,需要自行查看
2.NandFlash 的写最小单位是擦除最小单位是BOCK,读最小单位是PAGE,并且如果你需要写入的PAGE已经被写入过了,则你在写入前需要擦除整个BLOCK,而且擦除前需要将需要写入的其他地方读取下来,否则其他内容会消失。
2.FATFS基础
https://blog.csdn.net/ZHONGCAI0901/article/details/115196445
FATFS文件系统详解-CSDN博客
网上也有不少讲FATFS的文章,但要么浮于表面,光讲应用层,要么太低层了,难以理解,第一篇文章是讲的文件系统的组成,第二篇文章讲的是fatfs的应用端移植,我这边强调一下重点。
1.fatfs有一个组织架构,在单片机挂载,或者电脑挂载时,会通过读存储器的组织架构,看是否按照这个形式存储,如果没有,则会调用对应的写入程序,写入存储架构,一般来说,文件系统能成功挂载基本读写功能就没有问题了。
2.fatfs即使你不理解他的文件架构也没关系,只需要理解扇区概念,扇区在软件逻辑上即是一个可以读写的最小单元
3.fatfs需要移植的函数有六个
以上图片即是需要移植的函数,图片来自于网络
这几个函数中,实际只需要重点关注 read 和 write ,后续我会来描述
3.stm32cubemx U盘移植
STM32-外部FLASH(W25Q64)模拟U盘 – 不要让自己太懒 – 博客园
usb协议比较复杂,如果开始就专注底层协议那工作没办法开展了,这边用的方案是用stm32cubemx提供的U盘的方案也只需要实现几个接口函数,和fatfs提供的接口十分接近,后续会看到。
4.hal NandFlash接口
STM32CbueMX之NAND FLASH_stm32 nandflash时序配置-CSDN博客
nandflash一般是采用的fmc接口来控制,fmc如何使用我这边不赘述,我这边主要强调一下fmc的时序问题。
上面一篇文章说的是 HCLK cycles 如何通过当前的始终频率计算,我这边主要是验证功能,所以给的比较大,还有就是 page size 等参数的配置,这边的参数要根据实际芯片手册来,我来解释这些参数的含义
1.page size 一个page的字节数
2.spare area size 空闲内存的 字节数
3.block size 一个block = N * Page ,这个N就是 block size
4.block number 一个Plane = N * block ,这边的N就是Block number
5.Plane number 一个Lun = N * plane ,这边的N就是 plane number
6.这边的Plane size 有争议,我在网上也没看到一个合理的解释,我个人理解为 应该是
block size * block number
这边有个提醒,第一栏NAND CONTROL 中别打开ECC功能,否则后续写入的时候会写入ECC字节,影响fatfs文件系统的创建,理论上这个功能肯定是没问题,为啥有问题我后续在调试
我们再看一下hal 提供的nandflash 接口
HAL_StatusTypeDef HAL_NAND_Reset(NAND_HandleTypeDef *hnand);
HAL_StatusTypeDef HAL_NAND_Read_Page_8b(NAND_HandleTypeDef *hnand, const NAND_AddressTypeDef *pAddress,
uint8_t *pBuffer, uint32_t NumPageToRead);
HAL_StatusTypeDef HAL_NAND_Write_Page_8b(NAND_HandleTypeDef *hnand, const NAND_AddressTypeDef *pAddress,
const uint8_t *pBuffer, uint32_t NumPageToWrite);
HAL_StatusTypeDef HAL_NAND_Erase_Block(NAND_HandleTypeDef *hnand, const NAND_AddressTypeDef *pAddress);
主要是以上四个函数,分别是复位,读,写,以及擦除,后续我会给大家看如何使用。
5.Fatfs移植
基本不要修改什么内容,主要是三点
1.把code_page 修改成simplifed chinese ,这是用来支持中文的
2.把MAX_SS 和 MIN_SS修改成2048 ,这点很重要,我使用的芯片一个page是2048,读者要根据自己使用的芯片来
3.FS_NORTC 改成FIXED timestamp ,用来给文件设置一个固定的时间
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file user_diskio.c
* @brief This file includes a diskio driver skeleton to be completed by the user.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/*
* Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
* To be suppressed in the future.
* Kept to ensure backward compatibility with previous CubeMx versions when
* migrating projects.
* User code previously added there should be copied in the new user sections before
* the section contents can be deleted.
*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif
/* USER CODE BEGIN DECL */
/* Includes ------------------------------------------------------------------*/
#include "fmc.h"
#include <string.h>
#include "ff_gen_drv.h"
#include "bsp_nandflash.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;
/* USER CODE END DECL */
/* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */
Diskio_drvTypeDef USER_Driver =
{
USER_initialize,
USER_status,
USER_read,
#if _USE_WRITE
USER_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes a Drive
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
/* USER CODE BEGIN INIT */
Stat = STA_NOINIT;
Stat = RES_OK;
return Stat;
/* USER CODE END INIT */
}
/**
* @brief Gets Disk Status
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_status (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
/* USER CODE BEGIN STATUS */
Stat = STA_NOINIT;
Stat = RES_OK;
return Stat;
/* USER CODE END STATUS */
}
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
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 */
)
{
/* USER CODE BEGIN READ */
NandFlash_ReadPages(buff, sector, count);
return RES_OK;
/* USER CODE END READ */
}
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
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 */
NandFlash_WritePages(buff, sector, count);
return RES_OK;
/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @param pdrv: Physical drive number (0..)
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
/* USER CODE BEGIN IOCTL */
DRESULT res = RES_OK;
switch(cmd)
{
case CTRL_SYNC:
break;
case GET_SECTOR_COUNT:
*(DWORD *)buff = hnand1.Config.BlockSize * hnand1.Config.BlockNbr;
break;
case GET_SECTOR_SIZE:
*(DWORD *)buff = 2048;
break;
case GET_BLOCK_SIZE:
*(DWORD *)buff = 64;
break;
default:
res = RES_ERROR;
}
return res;
/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
以上内容是fatfs移植文件,主要是五个函数的的实现
1.USER_initialize 这个我们在cubemx 默认配置,不要管理
2.USER_status 这个实际上是判断读写状态,因为我这边使用的是阻塞时的读写,所以不要管理
3.NandFlash_ReadPages read实际上就是对一个或多个存储块进行读,这个芯片你的最小读单位位2048byte
4.NandFlash_WritePageswrite和read略微不一样,nandflash的最小写单元可以是一个page,也就是说可以和读的字节一样是2048byte,但如果当前需要写入的page已经被写入了,需要将其擦除,并重新写入。
5.USER_ioctl 这个里面的多数参数没有被用到
1.CTRL_SYNC 这个是用来做延迟挂载的,什么是延迟挂载后续在fatfs应用层会说
2.GET_SECTOR_COUNT 获取扇区个数,实际上就是block Num* block size,理论上还要乘以
plan num,我这个芯片有连个plan,但我只是用了一个plan,所以暂时无需管理
3. GET_BLOCK_SIZE 描述块的个数 block = N*page ,这个的N就是这边block size
#include "fmc.h"
#include "string.h"
#include "bsp_nandflash.h"
#define MIN(a,b) ((a)>(b)?(b):(a))
#define BLN_SIZE (4096)
#define PAGE_SIZE (2048)
#define BLOCK_NUM (64)
#define TMP_BLOCK_SIZE (PAGE_SIZE*BLOCK_NUM)
static uint8_t block_Buff[TMP_BLOCK_SIZE];
uint32_t NandFlash_GetBlock(uint32_t sector)
{
return (sector / hnand1.Config.BlockSize);
}
uint32_t NandFlash_CheckSpare(uint32_t sector)
{
uint8_t *page_buff = block_Buff;
NandFlash_Read(page_buff, sector);
for(int i=0; i<2048; i++)
{
if(page_buff[i] != 0xFF)
{
return 0;
}
}
return 1;
}
void NandFlash_Read(uint8_t *buff, uint32_t sector)
{
HAL_StatusTypeDef flag;
NAND_AddressTypeDef tmp_addr = {0};
tmp_addr.Page = sector % hnand1.Config.BlockSize;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PlaneSize;
flag = HAL_NAND_Read_Page_8b(&hnand1, &tmp_addr, buff, 1);
}
void NandFlash_ReadPages(uint8_t *buff, uint32_t sector, uint32_t count)
{
for(int i=0; i<count; i++)
{
NandFlash_Read(buff,sector);
buff += PAGE_SIZE;
sector++;
}
}
void NandFlash_Read_4k(uint8_t *buff, uint32_t sector)
{
NandFlash_Read(buff, sector*2);
NandFlash_Read(buff+PAGE_SIZE, sector*2+1);
}
void NandFlash_ReadPages_4k(uint8_t *buff, uint32_t sector, uint32_t count)
{
for(int i=0; i<count; i++)
{
NandFlash_Read_4k(buff,sector);
buff += BLN_SIZE;
sector++;
}
}
void NandFlash_ReadBlock(uint8_t *buff, uint32_t sector)
{
NAND_AddressTypeDef tmp_addr = {0};
tmp_addr.Page = 0;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PlaneSize;
HAL_NAND_Read_Page_8b(&hnand1, &tmp_addr, buff, BLOCK_NUM);
}
void NandFlash_WriteNoCheck(const uint8_t *buff, uint32_t sector)
{
NAND_AddressTypeDef tmp_addr = {0};
tmp_addr.Page = sector % hnand1.Config.BlockSize;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PlaneSize;
HAL_NAND_Write_Page_8b(&hnand1, &tmp_addr, buff, 1);
}
void NandFlash_WriteBlock(const uint8_t *buff, uint32_t sector)
{
NAND_AddressTypeDef tmp_addr = {0};
tmp_addr.Page = 0;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PlaneSize;
HAL_NAND_Erase_Block(&hnand1, &tmp_addr);
HAL_NAND_Write_Page_8b(&hnand1, &tmp_addr, buff, BLOCK_NUM);
}
void NandFlash_EraseBlock(uint32_t sector)
{
NAND_AddressTypeDef tmp_addr = {0};
tmp_addr.Page = 0;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PlaneSize;
HAL_NAND_Erase_Block(&hnand1, &tmp_addr);
}
void NandFlash_Write(const uint8_t *buff, uint32_t sector)
{
HAL_StatusTypeDef flag;
uint32_t buff_addr;
NAND_AddressTypeDef tmp_addr = {0};
if(NandFlash_CheckSpare(sector))
{
NandFlash_WriteNoCheck(buff, sector);
return ;
}
NandFlash_ReadBlock(block_Buff, sector);
tmp_addr.Page = sector % hnand1.Config.BlockSize;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PlaneSize;
buff_addr = tmp_addr.Page * PAGE_SIZE;
memcpy(&block_Buff[buff_addr], buff, PAGE_SIZE);
tmp_addr.Page = 0;
HAL_NAND_Erase_Block(&hnand1, &tmp_addr);
HAL_NAND_Write_Page_8b(&hnand1, &tmp_addr, block_Buff, BLOCK_NUM);
}
void NandFlash_WritePages(const uint8_t *buff, uint32_t sector, uint32_t count)
{
for(int i=0; i<count; i++)
{
NandFlash_Write(buff,sector);
buff += PAGE_SIZE;
sector++;
}
}
void NandFlash_Write_4k(const uint8_t *buff, uint32_t sector)
{
NandFlash_Write(buff, sector*2);
NandFlash_Write(buff+PAGE_SIZE, sector*2+1);
}
void NandFlash_WritePages_4k(const uint8_t *buff, uint32_t sector, uint32_t count)
{
for(int i=0; i<count; i++)
{
NandFlash_Write_4k(buff,sector);
buff += BLN_SIZE;
sector++;
}
}
NandFlash_Read 和 NandFlash_Write 是这段程序的核心逻辑
先说 NandFlash_Read,核心逻辑是将sector与实际的pages对应,并且读出来放到buff里,其他
read函数都是在此基础上读多或者多少,以及读多大进行扩展,不再赘述
void NandFlash_Read(uint8_t *buff, uint32_t sector)
{
NAND_AddressTypeDef tmp_addr = {0};
tmp_addr.Page = sector % hnand1.Config.BlockSize;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PageSize;
HAL_NAND_Read_Page_8b(&hnand1, &tmp_addr, buff, 1);
}
NandFlash_Write是整个程序的核心
1.读取整个块上的内容
2.将sector 和 对应的page逻辑转换,并且拷贝到读取的块上
3.擦除整个块
4.将对应的内容写入
必须要强调,这个写入方式是不合理的,因为如果连续写入多个page,那这样会进行多次无效的擦除读写,降低了读写速率以及使用寿命,但本文意在讲解nandflash 基础用法,所以也就不在考量范围内
void NandFlash_Write(const uint8_t *buff, uint32_t sector)
{
HAL_StatusTypeDef flag;
uint32_t buff_addr;
NAND_AddressTypeDef tmp_addr = {0};
if(NandFlash_CheckSpare(sector))
{
NandFlash_WriteNoCheck(buff, sector);
return ;
}
NandFlash_ReadBlock(block_Buff, sector);
tmp_addr.Page = sector % hnand1.Config.BlockSize;
tmp_addr.Block = sector / hnand1.Config.BlockSize % hnand1.Config.BlockNbr;
tmp_addr.Plane = sector / hnand1.Config.PlaneSize;
buff_addr = tmp_addr.Page * PAGE_SIZE;
memcpy(&block_Buff[buff_addr], buff, PAGE_SIZE);
tmp_addr.Page = 0;
HAL_NAND_Erase_Block(&hnand1, &tmp_addr);
HAL_NAND_Write_Page_8b(&hnand1, &tmp_addr, block_Buff, BLOCK_NUM);
}
以上我这边你的fatfs的移植已经实现。
接下来是fatfs的应用层
我这边推荐STM32CubeMX教程26 FatFs 文件系统 – W25Q128读写 – OSnotes – 博客园
这边的虽然存储器类型不一样,但是 FATFS应用层操作是一样的,我这边的测试用例会放在文章末尾。
最后是u盘的移植,实在是没什么好说的,实现u盘的读写操作即可,和fatfs没什么区别,我这边就贴一下配置和代码
设备选择device
设备选择大存储设备,设置读取的字节为2048
代码部分,主要实现了u盘的读写,以及u盘的大小,在STORAGE_GetCapacity_FS设置
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : usbd_storage_if.c
* @version : v1.0_Cube
* @brief : Memory management layer.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"
/* USER CODE BEGIN INCLUDE */
#include "bsp_nandflash.h"
/* USER CODE END INCLUDE */
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
* @brief Usb device.
* @{
*/
/** @defgroup USBD_STORAGE
* @brief Usb mass storage device module
* @{
*/
/** @defgroup USBD_STORAGE_Private_TypesDefinitions
* @brief Private types.
* @{
*/
/* USER CODE BEGIN PRIVATE_TYPES */
/* USER CODE END PRIVATE_TYPES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_Defines
* @brief Private defines.
* @{
*/
#define STORAGE_LUN_NBR 1
#define STORAGE_BLK_NBR 0x10000
#define STORAGE_BLK_SIZ 0x200
/* USER CODE BEGIN PRIVATE_DEFINES */
#define NANDFLASH_BLK_NBR (131072)
#define NANDFLASH_BLK_SIZ (2048)
/* USER CODE END PRIVATE_DEFINES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_Macros
* @brief Private macros.
* @{
*/
/* USER CODE BEGIN PRIVATE_MACRO */
/* USER CODE END PRIVATE_MACRO */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_Variables
* @brief Private variables.
* @{
*/
/* USER CODE BEGIN INQUIRY_DATA_FS */
/** USB Mass storage Standard Inquiry Data. */
const int8_t STORAGE_Inquirydata_FS[] = {/* 36 */
/* LUN 0 */
0x00,
0x80,
0x02,
0x02,
(STANDARD_INQUIRY_DATA_LEN - 5),
0x00,
0x00,
0x00,
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0' ,'1' /* Version : 4 Bytes */
};
/* USER CODE END INQUIRY_DATA_FS */
/* USER CODE BEGIN PRIVATE_VARIABLES */
/* USER CODE END PRIVATE_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Exported_Variables
* @brief Public variables.
* @{
*/
extern USBD_HandleTypeDef hUsbDeviceFS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_FunctionPrototypes
* @brief Private functions declaration.
* @{
*/
static int8_t STORAGE_Init_FS(uint8_t lun);
static int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
static int8_t STORAGE_IsReady_FS(uint8_t lun);
static int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
static int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_GetMaxLun_FS(void);
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
/**
* @}
*/
USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
{
STORAGE_Init_FS,
STORAGE_GetCapacity_FS,
STORAGE_IsReady_FS,
STORAGE_IsWriteProtected_FS,
STORAGE_Read_FS,
STORAGE_Write_FS,
STORAGE_GetMaxLun_FS,
(int8_t *)STORAGE_Inquirydata_FS
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes the storage unit (medium) over USB FS IP
* @param lun: Logical unit number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Init_FS(uint8_t lun)
{
/* USER CODE BEGIN 2 */
UNUSED(lun);
return (USBD_OK);
/* USER CODE END 2 */
}
/**
* @brief Returns the medium capacity.
* @param lun: Logical unit number.
* @param block_num: Number of total block number.
* @param block_size: Block size.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
/* USER CODE BEGIN 3 */
UNUSED(lun);
*block_num = NANDFLASH_BLK_NBR;
*block_size = NANDFLASH_BLK_SIZ;
return (USBD_OK);
/* USER CODE END 3 */
}
/**
* @brief Checks whether the medium is ready.
* @param lun: Logical unit number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
/* USER CODE BEGIN 4 */
UNUSED(lun);
return (USBD_OK);
/* USER CODE END 4 */
}
/**
* @brief Checks whether the medium is write protected.
* @param lun: Logical unit number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
{
/* USER CODE BEGIN 5 */
UNUSED(lun);
return (USBD_OK);
/* USER CODE END 5 */
}
/**
* @brief Reads data from the medium.
* @param lun: Logical unit number.
* @param buf: data buffer.
* @param blk_addr: Logical block address.
* @param blk_len: Blocks number.
* @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 */
NandFlash_ReadPages(buf, blk_addr, blk_len);
return (USBD_OK);
/* USER CODE END 6 */
}
/**
* @brief Writes data into the medium.
* @param lun: Logical unit number.
* @param buf: data buffer.
* @param blk_addr: Logical block address.
* @param blk_len: Blocks number.
* @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 */
NandFlash_WritePages(buf, blk_addr, blk_len);
return (USBD_OK);
/* USER CODE END 7 */
}
/**
* @brief Returns the Max Supported LUNs.
* @param None
* @retval Lun(s) number.
*/
int8_t STORAGE_GetMaxLun_FS(void)
{
/* USER CODE BEGIN 8 */
return (STORAGE_LUN_NBR - 1);
/* USER CODE END 8 */
}
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
/**
* @}
*/
/**
* @}
*/
以上就是nandflash+fatfs+U盘的方案了,不知不觉写了很多,这个方案里面要熟悉的基础知识很多,如果不明白也可以留言,我这边说几点我这边没做好的地方
1.目前我这个nandflash 只使用了 256mb,即只是用了一半的大小
2.这个nandflash读写驱动不合适,合理的方案应该是在读写page的基础上加上ftl,进行负载均衡以及坏块处理。
3.目前这个移植后,只能正常使用fat32格式,原因未知
后续有空我会在这三个地方进行修改
我把样例代码提交给了gitee上,有需要的人自取,感谢阅读
zxy/H743_NANDFLASH_FATFS
作者:qq_45019496