天天看點

【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)一、準備工作二、具體的操作
【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+檔案系統(FATFS)一、準備工作二、具體的操作

這裡配置的是SPI2,還需要軟體控制片選增加一個PB12作為輸出的片選腳,這裡SPI2就配置好了。

2)、在Pinout&Configuration菜單欄下,配置FATFS基本資訊

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+檔案系統(FATFS)一、準備工作二、具體的操作

在MiddleWare下FATFS勾選User-define,在底下參數欄裡面設定簡體中文以及塊大小,我們選取的是W25Q128,塊的大小是4096。如果是SD卡就不需要更改大小為512。這裡檔案系統就配置好了。

3)、生成工程配置如圖

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+檔案系統(FATFS)一、準備工作二、具體的操作

生成成功後打開工程。

2、工程測試

1)、SPI & FLASH測試

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+檔案系統(FATFS)一、準備工作二、具體的操作
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)一、準備工作二、具體的操作

在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檔案下

調用測試代碼

運作成功

3、總結

這樣SPI FLASH+FATFS配置就完成了!~~~

如有什麼不懂聯系加QQ群歡迎大家學習交流!

【STM32CubeMx你不知道的那些事】第九章:STM32CubeMx的SPI外置FLASH+檔案系統(FATFS)一、準備工作二、具體的操作

QQ:1320300083