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

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

一、准备工作

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

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

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

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

二、具体的操作

1、工程建立

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

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+文件系统(FATFS)_第1张图片
【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+文件系统(FATFS)_第2张图片
这里配置的是SPI2,还需要软件控制片选增加一个PB12作为输出的片选脚,这里SPI2就配置好了。

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

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+文件系统(FATFS)_第3张图片
在MiddleWare下FATFS勾选User-define,在底下参数栏里面设置简体中文以及块大小,我们选取的是W25Q128,块的大小是4096。如果是SD卡就不需要更改大小为512。这里文件系统就配置好了。

3)、生成工程配置如图

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+文件系统(FATFS)_第4张图片
生成成功后打开工程。

2、工程测试

1)、SPI & FLASH测试

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

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)、文件系统代码添加及测试

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+文件系统(FATFS)_第6张图片
在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群欢迎大家学习交流!
【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+文件系统(FATFS)_第7张图片

QQ:1320300083

你可能感兴趣的:(STM32CubeMx,嵌入式,单片机,stm32)