赞
踩
最近有一个关于SPIFLASH嵌入FATFS的需求,自己也是第一次搞,在此做一个实验记录,方便后期查看。
注:本博客借鉴了Carry_王的博客,修正了一下小错误之后,能够直接实现FATFS的移植和使用,大部分代码是参照上面码哥的,大家也可以自行对比一下。
顺便一提,所有动作的本质还是MCU通过SPI总线与Flash通信,只是借助了FATFS协议栈对Flash进行管理,FATFS由纯软件构成。
这里我使用的平台是硬石的YS-F4Pro运动控制板,MCU是STM32F407IGTb,片外Flash使用的是16M的W25Q128,具体思路如下:
打开STM32CubeMX,选择芯片:
配置系统Debug方式以及时钟来源
配置外部时钟
配置SPI(这里要根据硬件电路对应的SPI来进行匹配,片选PI0,输出PC2,输入PI3,时钟PI1)
配置片选信号PI0,该处不具体将SPI的通信,如有不解需参考SPI的通信说明
为了后面不麻烦,这里把文件系统也配置进去,因为使用的是W25Q128,每个扇区大小为4096,所以此处将最大最小扇区定义为4096
时钟树配置,主频为72M
完成代码配置(选择必要的文件这项点上之后不会添加额外库文件,能减少无谓的编译时间)
内嵌Flash芯片的驱动文件(这里直接贴我修改后的源码)
代码的具体意思我就不分析了,注释大部分都有
SPIFLASH.c
#ifndef __SPIFLASH_H__ #define __SPIFLASH_H__ #ifdef __cplusplus extern "C" { #endif #include "main.h" #include "SPIFLASH.h" #include "spi.h" #define W25Q80 0XEF13 #define W25Q16 0XEF14 #define W25Q32 0XEF15 #define W25Q64 0XEF16 #define W25Q128 0XEF17 #define M25P64_FLASH_ID 0x202017//C22017 #define M25P40_FLASH_ID 0x202013 /* USER CODE BEGIN Includes */ #define sFLASH_CMD_WRITE 0x02 /*!< Write to Memory instruction */ #define sFLASH_CMD_WRSR 0x01 /*!< Write Status Register instruction */ #define sFLASH_CMD_WREN 0x06 /*!< Write enable instruction */ #define sFLASH_CMD_READ 0x03 /*!< Read from Memory instruction */ #define sFLASH_CMD_RDSR 0x05 /*!< Read Status Register instruction */ #define sFLASH_CMD_RDID 0x9F /*!< Read identification */ #define sFLASH_CMD_SE 0x20 /*!< Sector Erase instruction (4k)*/ #define sFLASH_CMD_BE 0xD8 /*!< Block Erase instruction (64k)*/ #define sFLASH_CMD_CE 0xC7 /*!< Chip Erase instruction (Chip Erase)*/ #define sFLASH_WIP_FLAG 0x01 /*!< Write In Progress (WIP) flag */ #define sFLASH_CMD_RDID 0x9F /*!< Read identification */ #define sFLASH_CMD_DeviceID 0xAB #define sFLASH_CMD_ManufactDeviceID 0x90 #define sFLASH_CMD_JedecDeviceID 0x9F #define sFLASH_DUMMY_BYTE 0xFF uint8_t sFlashBuff[4096]; //片选CS拉低 void sFLASH_CS_LOW(void) { HAL_GPIO_WritePin(SPIFLASH_CS_GPIO_Port,SPIFLASH_CS_Pin,GPIO_PIN_RESET); } //片选CS拉高 void sFLASH_CS_HIGH(void) { HAL_GPIO_WritePin(SPIFLASH_CS_GPIO_Port,SPIFLASH_CS_Pin,GPIO_PIN_SET); } //FLASH 发送一个字节 char sFLASH_SendByte(uint8_t byte) { // unsigned char dr; // /*!< Loop while DR register in not emplty */ // //while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // while((SPI2->SR & SPI_SR_TXE) == 0); // /*!< Send byte through the SPI1 peripheral */ // //SPI_I2S_SendData(SPI1, byte); // SPI2->DR = byte; // /*!< Wait to receive a byte */ // //while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); // while((SPI2->SR & SPI_SR_RXNE) == 0); // /*!< Return the byte read from the SPI bus */ // //return SPI_I2S_ReceiveData(SPI1); // dr = SPI2->DR; // return dr; uint8_t d_read,d_send = byte; if(HAL_SPI_TransmitReceive(&hspi2,&d_send,&d_read,1,0xFFFF) != HAL_OK) { d_read = sFLASH_DUMMY_BYTE; } return d_read; } //FLASH写使能 void sFLASH_WriteEnable(void) { /*!< Select the FLASH: Chip Select low */ sFLASH_CS_LOW(); /*!< Send "Write Enable" instruction */ sFLASH_SendByte(sFLASH_CMD_WREN); /*!< Deselect the FLASH: Chip Select high */ sFLASH_CS_HIGH(); } //FLASH读取一个字节 uint8_t sFLASH_ReadByte(void) { return (sFLASH_SendByte(sFLASH_DUMMY_BYTE)); } //FLASH等待写完成 void sFLASH_WaitForWriteEnd(void) { uint8_t flashstatus = 0; HAL_Delay(1); /*!< Select the FLASH: Chip Select low */ sFLASH_CS_LOW(); HAL_Delay(1); /*!< Send "Read Status Register" instruction */ sFLASH_SendByte(sFLASH_CMD_RDSR); /*!< Loop as long as the memory is busy with a write cycle */ do { /*!< Send a dummy byte to generate the clock needed by the FLASH and put the value of the status register in FLASH_Status variable */ flashstatus = sFLASH_SendByte(sFLASH_DUMMY_BYTE); } while ((flashstatus & sFLASH_WIP_FLAG) == SET); /* Write in progress */ /*!< Deselect the FLASH: Chip Select high */ sFLASH_CS_HIGH(); } //FLASH擦除一个扇区 void sFLASH_EraseSector(uint32_t SectorAddr) { /*!< Send write enable instruction */ sFLASH_WriteEnable(); /*!< Sector Erase */ /*!< Select the FLASH: Chip Select low */ sFLASH_CS_LOW(); /*!< Send Sector Erase instruction */ sFLASH_SendByte(sFLASH_CMD_SE); /*!< Send SectorAddr high nibble address byte */ sFLASH_SendByte((SectorAddr & 0xFF0000) >> 16); /*!< Send SectorAddr medium nibble address byte */ sFLASH_SendByte((SectorAddr & 0xFF00) >> 8); /*!< Send SectorAddr low nibble address byte */ sFLASH_SendByte(SectorAddr & 0xFF); /*!< Deselect the FLASH: Chip Select high */ sFLASH_CS_HIGH(); /*!< Wait the end of Flash writing */ sFLASH_WaitForWriteEnd(); } //FLASH擦除整个片 void sFLASH_EraseChip(void) { /*!< Send write enable instruction */ sFLASH_WriteEnable(); /*!< Bulk Erase */ /*!< Select the FLASH: Chip Select low */ sFLASH_CS_LOW(); /*!< Send Bulk Erase instruction */ sFLASH_SendByte(sFLASH_CMD_CE); /*!< Deselect the FLASH: Chip Select high */ sFLASH_CS_HIGH(); /*!< Wait the end of Flash writing */ sFLASH_WaitForWriteEnd(); } //FLASH写一个页 void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) { sFLASH_WriteEnable(); sFLASH_CS_LOW(); sFLASH_SendByte(sFLASH_CMD_WRITE); sFLASH_SendByte((WriteAddr & 0xFF0000) >> 16); sFLASH_SendByte((WriteAddr & 0xFF00) >> 8); sFLASH_SendByte(WriteAddr & 0xFF); while (NumByteToWrite--) { sFLASH_SendByte(*pBuffer); pBuffer++; } sFLASH_CS_HIGH(); sFLASH_WaitForWriteEnd(); } //FLASH读取0-65536个 void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) { sFLASH_CS_LOW(); sFLASH_SendByte(sFLASH_CMD_READ); sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16); sFLASH_SendByte((ReadAddr& 0xFF00) >> 8); sFLASH_SendByte(ReadAddr & 0xFF); while (NumByteToRead--) /*!< while there is data to be read */ { *pBuffer = sFLASH_SendByte(sFLASH_DUMMY_BYTE); pBuffer++; } sFLASH_CS_HIGH(); } //读取FLASH ID uint32_t sFLASH_ReadID(void) { uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0; sFLASH_CS_LOW(); HAL_Delay(1); sFLASH_SendByte(sFLASH_CMD_RDID); Temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE); Temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE); Temp2 = sFLASH_SendByte(sFLASH_DUMMY_BYTE); sFLASH_CS_HIGH(); Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2; return Temp; } //无检验写SPI FLASH void sFLASH_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite) { uint16_t pageremain; pageremain=256-WriteAddr%256; //单页剩余的字节数 if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节 while(1) { sFLASH_WritePage(pBuffer,WriteAddr,pageremain); if(NumByteToWrite==pageremain) { break;//写入结束了 } else //NumByteToWrite>pageremain { pBuffer+=pageremain; WriteAddr+=pageremain; NumByteToWrite-=pageremain; //减去已经写入了的字节数 if(NumByteToWrite>256) { pageremain=256; //一次可以写入256个字节 } else { pageremain=NumByteToWrite; //不够256个字节了 } } } } //带擦除的写0-65536个字节函数 void sFLASH_WriteBuffer(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite) { uint32_t secpos; uint16_t secoff; uint16_t secremain; uint16_t i; uint8_t * psFlashBuff; psFlashBuff=sFlashBuff; secpos=WriteAddr/4096;//扇区地址 secoff=WriteAddr%4096;//在扇区内的偏移 secremain=4096-secoff;//扇区剩余空间大小 if(NumByteToWrite<=secremain) { secremain=NumByteToWrite;//不大于4096个字节 } while(1) { sFLASH_ReadBuffer(psFlashBuff,secpos*4096,4096);//读出整个扇区的内容 sFLASH_EraseSector(secpos*4096); //擦除这个扇区 for(i=0;i<secremain;i++) //复制 { psFlashBuff[i+secoff]=pBuffer[i]; } sFLASH_Write_NoCheck(psFlashBuff,secpos*4096,4096);//写入整个扇区 if(NumByteToWrite==secremain) { break;//写入结束了 } else//写入未结束 { secpos++;//扇区地址增1 secoff=0;//偏移位置为0 pBuffer+=secremain; //指针偏移 WriteAddr+=secremain; //写地址偏移 NumByteToWrite-=secremain; //字节数递减 if(NumByteToWrite>4096) { secremain=4096;//下一个扇区还是写不完 } else { secremain=NumByteToWrite; //下一个扇区可以写完了 } } } } #endif
SPIFLASH.h
#ifndef __SPIFLASH_H__ #define __SPIFLASH_H__ /**Includes************************************************************************************/ #include "stm32f4xx_hal.h" #include "spi.h" /**Function declaration************************************************************************/ void sFLASH_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite); void sFLASH_EraseChip(void); void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead); uint32_t sFLASH_ReadID(void); char sFLASH_SendByte(uint8_t byte); void FileTest(void);//该句代码为测试代码,函数在user_diskio.c中定义 #endif
uint32_t Flash_ID = 0;
Flash_ID = sFLASH_ReadID();
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file user_diskio.c * @brief This file includes a diskio driver skeleton to be completed by the user. ****************************************************************************** * @attention * * <h2><center>© Copyright (c) 2021 STMicroelectronics. * All rights reserved.</center></h2> * * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** */ /* 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 <string.h> #include "ff_gen_drv.h" #include "spi.h" #include "SPIFLASH.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Disk status */ static volatile DSTATUS Stat = STA_NOINIT; #define PAGE_SIZE 256 #define SECTOR_SIZE 4096 #define SECTOR_COUNT 200 #define BLOCK_SIZE 65536 #define FLASH_PAGES_PER_SECTOR SECTOR_SIZE/PAGE_SIZE FATFS fs; FIL file; /* 文件对象 */ FRESULT f_res; /* 文件操作结果 */ UINT fnum; /* 文件成功读写数量 */ BYTE ReadBuffer[1024]={0}; /* 读缓冲区 */ BYTE WriteBuffer[]= "WWB is the high hand\n"; BYTE work[4096]; char USER_Path[4]; void mount_disk(void) { f_res = f_mount(&fs, USER_Path, 0); return; } void format_disk(void) { uint8_t res = 0; //这里根据版本不同函数输入参数不一样 f_res = f_mkfs(USER_Path, 1, 4096, work, sizeof(work)); } UINT bw; void create_file(void) { FIL file; FIL *pf = &file; uint8_t res; f_res = f_open(pf, "0:/test.txt", FA_OPEN_ALWAYS | FA_WRITE); f_res = f_write(&file, WriteBuffer, sizeof(WriteBuffer), &bw); f_res = f_close(pf); } void read_file(void) { FIL file; FRESULT res; uint8_t rbuf[100] = {0}; f_res = f_open(&file, "0:/test.txt", FA_READ); f_res = f_read(&file, ReadBuffer, sizeof(WriteBuffer), &bw); f_res = f_close(&file); } void FileTest(void) { mount_disk(); //文件系统注册 format_disk(); //格式化文件系统 create_file(); //建立文件并写入"WWB is the high hand\n" read_file(); //读取文件放到ReadBuffer中 } /* 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 == 1 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; if(sFLASH_ReadID() != 0) { Stat &= ~STA_NOINIT; } 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; 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 */ DRESULT res = RES_ERROR; UINT i; for(i = 0;i < count;i++) { sFLASH_ReadBuffer(buff + i * 4096,sector * 4096 + i * 4096,4096 ); } 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 */ DRESULT res = RES_ERROR; UINT i; for(i = 0;i < count;i++) { sFLASH_WriteBuffer((void *)(buff + i * 4096),sector * 4096 + i * 4096,4096 ); } res = RES_OK; /* USER CODE HERE */ return res; /* 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 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; /* USER CODE END IOCTL */ } #endif /* _USE_IOCTL == 1 */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
编译下载后进入Debug模式,监视ReadBuffer数组【数据为空】和WriteBuffer数组配【数据为需要写入的数据】
点击左上角运行代码,查看ReadBuffer数组是否读取到WriteBuffer中的写入值
点击之后,稍微等待2秒左右【期间要初始化各种外设以及文件系统,再与Flash通信】得到如下结果,ReadBuffer内读取到了之前写入的内容
到此为止,打完收工,顺便祝大家端午假快乐
Good Game!!!!!!
ASWaterbenben专注于STM32CubeMX的使用研究
有需要的猿们敬请关注!!!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。