这两天终于把SPI FLASH的FATFS文件系统搞好了。总结这一过程,得出以下经验:
1. 先将SPI FALSH底层驱动调好,并好好做测试。自己写一个程序,写入然后读出进行校验,确认数据是OK,并且确认整片FLASH是可用的。 之前驱动没写好,PAGE和SECTOR搞错了,导致部分文件只能写入一部分,后面部分不正确,一开始还以为是内存异常,找了好久。
2. user_diskio.c文件里的USER_initialize、USER_status、USER_read、USER_write这4大函数,一定要写好。前面2个函数没什么,主要是后面两个函数容易出错,或者容易让人大意出问题。因为USER_read和USER_write这两个函数传递进来的sector就是扇区(@param sector: Sector address (LBA)),而不是扇区地址。而底层驱动(我从网上下载的)采用的是扇区所在的物理地址,两种要做好对应关系。否则,可能出现可以绑定、但是无法读写的情况。
3. USER_ioctl函数中,获取命令为GET_BLOCK_SIZE是,根据字面理解返回的是块的长度尺寸,例如64K,但是应该返回的是块里面扇区的数量。
注意以上问题后,FATFS就可以用了。
相关代码:
/** ****************************************************************************** * @file user_diskio.c * @brief This file includes a diskio driver skeleton to be completed by the user. ****************************************************************************** * This notice applies to any and all portions of this file * that are not between comment pairs USER CODE BEGIN and * USER CODE END. Other portions of this file, whether * inserted by the user or by software development tools * are owned by their respective copyright owners. * * Copyright (c) 2018 STMicroelectronics International N.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted, provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of other * contributors to this software may be used to endorse or promote products * derived from this software without specific written permission. * 4. This software, including modifications and/or derivative works of this * software, must execute solely and exclusively on microcontroller or * microprocessor devices manufactured by or for STMicroelectronics. * 5. Redistribution and use of this software other than as permitted under * this license is void and will automatically terminate your rights under * this license. * * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ #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 <string.h> #include "ff_gen_drv.h" #include "w25qx.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Disk status */ static volatile DSTATUS Stat = STA_NOINIT; extern void MX_SPI1_Init(void); /* 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 */ ) { switch (pdrv) { case 0 : MX_SPI1_Init(); //初始化SPI FLASH return RES_OK; case 1 : return RES_OK; case 2 : return RES_OK; case 3 : return RES_OK; default: return STA_NOINIT; } } /** * @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 */ ) { switch (pdrv) { case 0 : return RES_OK; case 1 : return RES_OK; case 2 : return RES_OK; default: return STA_NOINIT; } } /** * @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 HERE */ //HUANGDADAO UINT i = 0; for(i = 0; i < count; i ++) { BSP_W25Qx_Read_Sector(buff, sector*SECTOR_SIZE); sector++; buff+=SECTOR_SIZE; } return RES_OK; } /** * @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 HERE */ UINT i = 0; for(i = 0; i < count; i ++) { BSP_W25Qx_Write_Sector((uint8_t*)buff,sector*SECTOR_SIZE); sector ++; buff += SECTOR_SIZE; } return RES_OK; } #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 */ ) { DRESULT res = RES_OK; switch(cmd) { case CTRL_SYNC : break; case CTRL_TRIM: break; case GET_BLOCK_SIZE: *(DWORD*)buff = BLOCK_SIZE; break; case GET_SECTOR_SIZE: *(DWORD*)buff = SECTOR_SIZE; break; case GET_SECTOR_COUNT: *(DWORD*)buff = SECTOR_COUNT; break; default: res = RES_PARERR; break; } return res; } #endif /* _USE_IOCTL == 1 */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/