【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+文件系统(FATFS)

it2022-12-27  83

  这一张我们主要讲解一下STM32CUBEMX新版本 片外FLASH+FATFS文件系统。

一、准备工作

这里我们要想配置SPI和文件系统 并验证需要的准备工作如下:

1、MDK for ARM(KEIL5)或者IAR FOR ARM(这个是软件必备开发平台) (必须)

2、一块STM32最小系统开发板 (必须)

3、一块片外FLASH可以在开发板上面或者是自己买的模块

二、具体的操作

1、工程建立

1)、在Pinout&Configuration菜单栏下,配置硬件SPI的基本参数如图

这里配置的是SPI2,还需要软件控制片选增加一个PB12作为输出的片选脚,这里SPI2就配置好了。

2)、在Pinout&Configuration菜单栏下,配置FATFS基本信息

在MiddleWare下FATFS勾选User-define,在底下参数栏里面设置简体中文以及块大小,我们选取的是W25Q128,块的大小是4096。如果是SD卡就不需要更改大小为512。这里文件系统就配置好了。

3)、生成工程配置如图

生成成功后打开工程。

2、工程测试

1)、SPI & FLASH测试

void MX_SPI2_Init(void) { hspi2.Instance = SPI2; hspi2.Init.Mode = SPI_MODE_MASTER; hspi2.Init.Direction = SPI_DIRECTION_2LINES; hspi2.Init.DataSize = SPI_DATASIZE_8BIT; hspi2.Init.CLKPolarity = SPI_POLARITY_LOW; hspi2.Init.CLKPhase = SPI_PHASE_1EDGE; hspi2.Init.NSS = SPI_NSS_SOFT; hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi2.Init.TIMode = SPI_TIMODE_DISABLE; hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi2.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi2) != HAL_OK) { Error_Handler(); } SPI2->CR1 |= SPI_CR1_SPE; }

SPI2初始化代码添加SPI使能代码。 FLASH.C

#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(SPI2_CS_GPIO_Port,SPI2_CS_Pin,GPIO_PIN_RESET); } //片选CS拉高 void sFLASH_CS_HIGH(void) { HAL_GPIO_WritePin(SPI2_CS_GPIO_Port,SPI2_CS_Pin,GPIO_PIN_SET); } //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_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; } //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; //下一个扇区可以写完了 } } } }

FLASH.H

#ifndef _H_SPIFLASH_H #define _H_SPIFLASH_H /**Includes************************************************************************************/ #include "stm32f1xx_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); #endif

main.c 检测FLASH_ID判断FLASH是否初始化成功

uint32_t Flash_ID = 0; Flash_ID = sFLASH_ReadID();

2)、文件系统代码添加及测试

在user_diskio.c增加如下代码

#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[]= "WH is the high hand\n"; BYTE work[512]; 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); } 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(); read_file(); } //初始化函数修改如下 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 */ } //状态函修改如下 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 */ } //读取函数修改 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 ); } res = RES_OK; return res; /* USER CODE END READ */ } //写入函数修改 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 */ } //枚举函数修改 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 */ }

最后在main.c文件下 调用测试代码

FileTest();

运行成功

3、总结

这样SPI FLASH+FATFS配置就完成了!~~~

如有什么不懂联系加QQ群欢迎大家学习交流!

QQ:1320300083

最新回复(0)