一、前言
小白为了更了解文件系统,自己尝试移植,记录自己移植的过程,方便复盘。
二、第一次尝试
参考文章链接:
017 - STM32学习笔记 - SPI读写FLASH(二)-flash数据写入与读取_放学校门口见的博客-CSDN博客
020 - STM32学习笔记 - Fatfs文件系统(二) - 移植与测试_stm32移植fat_放学校门口见的博客-CSDN博客
021 - STM32学习笔记 - Fatfs文件系统(三) - 细化与总结_放学校门口见的博客-CSDN博客
失败了哈,还没找到原因,找到了再更新
1.W25Q64_SPI_FLASH
基础的读写功能函数
#include <W25Q64.h>
#include <MYSPI.h>
#include <diskio.h>
uint16_t W25Q64_ReadID(uint8_t *MID,uint16_t *DID)
{
MYSPI_START();
MYSPI_SwapByte(W25Q64_JEDEC_ID);
*MID = MYSPI_SwapByte(W25Q64_DUMMY_BYTE);//厂商id
*DID = MYSPI_SwapByte(W25Q64_DUMMY_BYTE);//芯片型号,高八位
*DID <<= 8;//往前移,给高八位空出位置
*DID |= MYSPI_SwapByte(W25Q64_DUMMY_BYTE);//芯片容量,低八位,或上,避免低八位被置零
MYSPI_END();
return *DID;
}
/**
* @brief Flash进入掉电模式
* @param 无
* @retval 无返回值
*/
void W25Q64_PowerDown(void)
{
MYSPI_START();
MYSPI_SwapByte(W25Q64_POWER_DOWN);
MYSPI_END();
}
/**
* @brief 将Flash从掉电模式唤醒
* @param 无
* @retval 无返回值
*/
void W25Q64_PowerOn(void)
{
MYSPI_START();
MYSPI_SwapByte(W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID);
MYSPI_END();
}
/**
* @brief 写使能
* @param 无
* @retval 无
*/
void W25Q64_WriteEnable(void)
{
MYSPI_START();
MYSPI_SwapByte(W25Q64_WRITE_ENABLE);
MYSPI_END();
}
/**
* @brief 等待状态寄存器busy = 0,芯片不忙
* @param 无
* @retval 无
*/
void W25Q64_WaitBusy(void)
{
uint32_t timeout = 100000;
MYSPI_START();
MYSPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
while((MYSPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
{
timeout--;
if(timeout == 0)
{
break;
}
}
MYSPI_END();
}
/**
* @brief 页编程,一次256个字节
* @param adress:地址
*dataArray:写入数据数组
dataLenth:数组长度
* @retval 无
*/
void W25Q64_PageProgram(uint32_t adress, uint8_t *dataArray, uint32_t dataLenth)
{
int i;
W25Q64_WaitBusy();
W25Q64_WriteEnable();
MYSPI_START();
MYSPI_SwapByte(W25Q64_PAGE_PROGRAM);
MYSPI_SwapByte(adress >> 16);
MYSPI_SwapByte(adress >> 8);
MYSPI_SwapByte(adress);
while(dataLenth--)
{
MYSPI_SwapByte(*dataArray);
dataArray ++ ;
}
MYSPI_END();
}
/**
* @brief 扇区擦除,一次4*1024字节,4KB
* @param adress:地址
*dataArray:写入数据数组
dataLenth:数组长度
* @retval 无
*/
void W25Q64_EraseSector(uint32_t adress)
{
W25Q64_WaitBusy();
W25Q64_WriteEnable();
MYSPI_START();
MYSPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
MYSPI_SwapByte(adress >> 16);
MYSPI_SwapByte(adress >> 8);
MYSPI_SwapByte(adress);
MYSPI_END();
W25Q64_WaitBusy();
}
/**
* @brief 读取存储数据
* @param adress:地址
*dataArray:写入数据数组
dataLenth:数组长度
* @retval 无
*/
uint8_t W25Q64_ReadData(uint32_t adress, uint8_t *dataArray, uint32_t dataLenth)
{
W25Q64_WaitBusy();
MYSPI_START();
MYSPI_SwapByte(W25Q64_READ_DATA);
MYSPI_SwapByte(adress >> 16);
MYSPI_SwapByte(adress >> 8);
MYSPI_SwapByte(adress);
while(dataLenth--)
{
*dataArray = MYSPI_SwapByte(W25Q64_DUMMY_BYTE);
dataArray ++ ;
}
MYSPI_END();
return *dataArray;
}
2、FATFS文件系统
文件系统官网:http://elm-chan.org/fsw/ff/00index_e.html
1.diskio.c
修改了5个功能函数,diskic_status/diskic_initialize/diskic_read/diskic_write/diskic_ioctl,还有一个get_fattime函数。
#include "diskio.h" /* FatFs lower layer API */
#include "W25Q64.h"
#include "main.h"
/* Definitions of physical drive number for each drive */
#define ATA 0 /* Example: Map ATA harddisk to physical drive 0 */
#define MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define USB 2 /* Example: Map USB MSD to physical drive 2 */
#define SPI_FLASH 3 /* Example: Map flash to physical drive 3 */
static uint8_t MID1;
static uint16_t DID1;
/**
* @brief 获取驱动器状态
* @param 驱动器编号
* @retval 驱动器状态
*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv) {
case ATA :
// translate the reslut code here
return stat;
case MMC :
// translate the reslut code here
return stat;
case USB :
// translate the reslut code here
return stat;
case SPI_FLASH :
if(W25Q64_ReadID(&MID1,&DID1) == 0x4017)
{
return 0; /* 若设备能正常读取ID号,则返回0 */
}
else
{
return STA_NOINIT; /* 若读不到ID号,则返回驱动器未初始化,及为0x01 */
}
}
return STA_NOINIT;
}
/**
* @brief 初始化设备
* @param 驱动器编号
* @retval 驱动器状态
*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv) {
case ATA :
// translate the reslut code here
return stat;
case MMC :
// translate the reslut code here
return stat;
case USB :
// translate the reslut code here
return stat;
case SPI_FLASH :
MX_GPIO_Init();
W25Q64_PowerOn();
if(W25Q64_ReadID(&MID1,&DID1) == 0x4017)
{
return 0; /* 若设备能正常读取ID号,则返回0 */
}
else
{
return STA_NOINIT; /* 若读不到ID号,则返回驱动器未初始化,及为0x01 */
}
}
return STA_NOINIT;
}
/**
* @brief 读扇区
* @param
pdrv:驱动器编号
buff:读取到的数据存储缓冲区
sector:读取的目标扇区号
count:读取扇区数量
* @retval 驱动器状态
*/
DRESULT disk_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 */
)
{
DRESULT res;
switch (pdrv) {
case ATA :
// translate the arguments here
return res;
case MMC :
return res;
case USB :
return res;
case SPI_FLASH :
/* FatFs文件系统是以扇区读写内容,我们实现的Flash读写是以字节读取,因此
这里将扇区转换为字节位置,数量转换为字节 */
sector+=512;
W25Q64_ReadData(sector* 4096 ,buff , count * 4096);
return RES_OK;
}
return RES_PARERR;
}
/**
* @brief 写扇区
* @param
pdrv:驱动器编号
buff:写入的数据存储缓冲区
sector:写入的目标扇区号
count:写入扇区数量
* @retval 驱动器状态
*/
#if _USE_WRITE
DRESULT disk_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 */
)
{
DRESULT res;
uint32_t write_addr;
switch (pdrv) {
case ATA :
return res;
case MMC :
return res;
case USB :
return res;
case SPI_FLASH :
sector+=512;
write_addr = (uint32_t)(sector*4096);
W25Q64_EraseSector(write_addr);
W25Q64_PageProgram(write_addr ,(uint8_t*)buff , count * 4096);
return RES_OK;
}
return RES_PARERR;
}
#endif
/**
* @brief 控制函数
* @param
pdrv:驱动器编号
cmd:控制命令
buff:指向缓冲区的指针,取决于命令代码,不使用时可以传个空指针进去
* @retval 控制状态
*/
#if _USE_IOCTL
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
switch (pdrv) {
case ATA :
// Process of the command for the ATA drive
return res;
case MMC :
// Process of the command for the MMC/SD card
return res;
case USB :
// Process of the command the USB drive
return res;
case SPI_FLASH :
switch(cmd)
{
case CTRL_SYNC: /* 是否使用缓存功能,这里我们不适用,所以留空 */
break;
case GET_SECTOR_COUNT: /* 获取磁盘的可用扇区数 */
*(DWORD *)buff = 2048-512;
break;
case GET_SECTOR_SIZE: /* 返回磁盘的扇区大小,FLASH中一个扇区为4096个字节,所以这里直接返回4096即可 */
*(WORD *)buff = 4096;
break;
case GET_BLOCK_SIZE: /* 获取擦除块的大小,一般我们擦除的时候都是整个扇区擦除,此处直接返回1即可 */
*(DWORD *)buff = 1;
break;
}
return RES_OK;
}
return RES_PARERR;
}
2、ffconf.h
主要修改了_USE_MKFS,_VOLUMES,_MIN_SS,_MAX_SS这几个宏定义。
#define _FFCONF 64180 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define _FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define _FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: All basic functions are enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
/ f_truncate() and f_rename() function are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define _USE_STRFUNC 1
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define _USE_FIND 0
/* This option switches filtered directory read feature and related functions,
/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */
#define _USE_MKFS 1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define _USE_FASTSEEK 0
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
#define _USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define _USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable)
/ To enable it, also _FS_TINY need to be set to 1. */
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define _CODE_PAGE 437
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect setting of the code page can cause a file open failure.
/
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
*/
#define _USE_LFN 0
#define _MAX_LFN 255
/* The _USE_LFN option switches the LFN feature.
/
/ 0: Disable LFN feature. _MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree(), must be added to the project. */
#define _LFN_UNICODE 0
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
/ to 1. This option also affects behavior of string I/O functions. */
#define _STRF_ENCODE 3
/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
/
/ 0: ANSI/OEM
/ 1: UTF-16LE
/ 2: UTF-16BE
/ 3: UTF-8
/
/ When _LFN_UNICODE is 0, this option has no effect. */
#define _FS_RPATH 0
/* This option configures relative path feature.
/
/ 0: Disable relative path feature and remove related functions.
/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
/
/ Note that directory items read via f_readdir() are affected by this option. */
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define _VOLUMES 4
/* Number of volumes (logical drives) to be used. */
#define _STR_VOLUME_ID 0
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
/* _STR_VOLUME_ID option switches string volume ID feature.
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
/ the drive ID strings are: A-Z and 0-9. */
#define _MULTI_PARTITION 0
/* This option switches multi-partition feature. By default (0), each logical drive
/ number is bound to the same physical drive number and only an FAT volume found on
/ the physical drive will be mounted. When multi-partition feature is enabled (1),
/ each logical drive number is bound to arbitrary physical drive and partition
/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */
#define _MIN_SS 4096
#define _MAX_SS 4096
/* These options configure the range of sector size to be supported. (512, 1024,
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
/ disk_ioctl() function. */
#define _USE_TRIM 0
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define _FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define _FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
/ bytes. Instead of private sector buffer eliminated from the file object,
/ common sector buffer in the file system object (FATFS) is used for the file
/ data transfer. */
#define _FS_NORTC 0
#define _NORTC_MON 1
#define _NORTC_MDAY 1
#define _NORTC_YEAR 2015
/* The _FS_NORTC option switches timestamp feature. If the system does not have
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
/ to be added to the project to read current time form RTC. _NORTC_MON,
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */
#define _FS_LOCK 0
/* The _FS_LOCK option switches file lock feature to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
/ is 1.
/
/ 0: Disable file lock feature. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock feature. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock feature is independent of re-entrancy. */
#define _FS_REENTRANT 0
#define _FS_TIMEOUT 1000
#define _SYNC_t HANDLE
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this feature.
/
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.c. */
#define _WORD_ACCESS 0
3、main.c
int main(void)
{
/* USER CODE BEGIN 1 */
static FRESULT len;
/* 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 */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
// W25Q64_EraseSector(0x000000);
// W25Q64_PageProgram(0x000000,flash_write,5);
// W25Q64_ReadData(0x000000,flash_read,5);
len = f_mount(&flash_fs,"3:",1);
if(len == FR_NO_FILESYSTEM)
{
len = f_mkfs("3:",0,0);
if(len == FR_OK)
{
len = f_mount(NULL,"3:",1);
len = f_mount(&flash_fs,"3:",1);
}
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// W25Q64_ReadID(&MID,&DID);
}
/* USER CODE END 3 */
}
3、结果——失败
第一次尝试挂载,错误0x0D,没有文件系统,进行格式化,格式化成功,先卸载,再次尝试挂载,仍是错误0x0D,没有文件系统。作为小白,没能找到问题,求大神指教。