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

物联沃分享整理
物联沃-IOTWORD物联网 » stm32 hal NANDFLASH+FATFS+U盘

发表回复