观海 发表于 2025-5-7 19:35

stm32F103、GD32F103读写Nor Flash实战指南

一、NOR Flash基础与硬件连接
1. NOR Flash简介
NOR Flash是一种非易失性存储器,支持随机访问和XIP(Execute In Place)特性,常用于存储固件代码、特定数据。典型型号如GD25Q128(128Mbit容量,SPI接口),其擦写寿命约10万次,适合嵌入式系统扩展存储。

2. GD32F103硬件设计
以SPI接口的GD25Q系列为例:
引脚连接:



二、SPI外设配置与驱动开发
1. SPI初始化代码
void SPI_FLASH_Init(void)
{
u32 temp;
SPI_InitTypeDefSPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

/* Enable SPI1 and GPIO clocks */
/*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO,
       SPI_FLASH_SPI_MISO_GPIO, SPI_FLASH_SPI_DETECT_GPIO
       and SPI_FLASH_SPI_SCK_GPIO Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD, ENABLE);

/*!< SPI_FLASH_SPI Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);


/*!< Configure SPI_FLASH_SPI pins: SCK */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_SCK;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_FONT_SPI_SCK_FLAG;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/*!< Configure SPI_FLASH_SPI pins: MISO */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_MISO;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/*!< Configure SPI_FLASH_SPI pins: MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_MOSI;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/*!< Configure SPI_FLASH_SPI_CS_PIN pin: SPI_FLASH Card CS pin */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_NSS;
GPIO_InitStructure.GPIO_Mode = GPIO_FONT_SPI_NSS_FLAG;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();

/* SPI1 configuration */
// W25X16: data input on the DIO pin is sampled on the rising edge of the CLK.
// Data on the DO and DIO pins are clocked out on the falling edge of CLK.
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);

/* Enable SPI1*/
SPI_Cmd(SPI2, ENABLE);
//temp = SPI_FLASH_ReadID();
}

根据temp = SPI_FLASH_ReadID()返回的值,可以判断Nor Flash的一些信息,比如型号、容量大小等。

2. NOR Flash控制函数
发送一个字节数据或命令

u8 SPI_FLASH_SendByte(u8 byte)
{
/* Loop while DR register in not emplty */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_TXE) == RESET);

/* Send byte through the SPI1 peripheral */
SPI_I2S_SendData(FONT_SPI, byte);

/* Wait to receive a byte */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_RXNE) == RESET);

/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(FONT_SPI);
}
发送半字数据

u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
{
/* Loop while DR register in not emplty */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_TXE) == RESET);

/* Send Half Word through the SPI1 peripheral */
SPI_I2S_SendData(FONT_SPI, HalfWord);

/* Wait to receive a Half Word */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_RXNE) == RESET);

/* Return the Half Word read from the SPI bus */
return SPI_I2S_ReceiveData(FONT_SPI);
}
读取一个字节


u8 SPI_FLASH_ReadByte(void)
{
return (SPI_FLASH_SendByte(Dummy_Byte));
}
三、关键操作实现
1. 数据读取(Read Data)
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "Read from Memory " instruction */
SPI_FLASH_SendByte(W25X_ReadData);

/* Send ReadAddr high nibble address byte to read from */
SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
/* Send ReadAddr medium nibble address byte to read from */
SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
/* Send ReadAddr low nibble address byte to read from */
SPI_FLASH_SendByte(ReadAddr & 0xFF);

while (NumByteToRead--) /* while there is data to be read */
{
    /* Read a byte from the FLASH */
    *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    /* Point to the next location where the byte read will be saved */
    pBuffer++;
}

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
}

2. 扇区擦除(Sector Erase)
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
/* Send write enable instruction */
SPI_FLASH_WriteEnable();
SPI_FLASH_WaitForWriteEnd();
/* Sector Erase */
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();
/* Send Sector Erase instruction */
SPI_FLASH_SendByte(W25X_SectorErase);
/* Send SectorAddr high nibble address byte */
SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
/* Send SectorAddr medium nibble address byte */
SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
/* Send SectorAddr low nibble address byte */
SPI_FLASH_SendByte(SectorAddr & 0xFF);
/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
}

3. 写数据
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

Addr = WriteAddr % SPI_FLASH_PageSize;
count = SPI_FLASH_PageSize - Addr;
NumOfPage =NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

if (Addr == 0) /* WriteAddr is SPI_FLASH_PageSize aligned*/
{
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      while (NumOfPage--)
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
      WriteAddr +=SPI_FLASH_PageSize;
      pBuffer += SPI_FLASH_PageSize;
      }

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
}
else /* WriteAddr is not SPI_FLASH_PageSize aligned*/
{
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
      {
      temp = NumOfSingle - count;

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
      WriteAddr +=count;
      pBuffer += count;

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      NumByteToWrite -= count;
      NumOfPage =NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
      WriteAddr +=count;
      pBuffer += count;

      while (NumOfPage--)
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
      WriteAddr +=SPI_FLASH_PageSize;
      pBuffer += SPI_FLASH_PageSize;
      }

      if (NumOfSingle != 0)
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
}
}

四、完整代码
/**********************************************************************************
* ÎļþÃû£ºspi_flash.c
* ÃèÊö    £ºspi µ×²ãÓ¦Óú¯Êý¿â         
* ʵÑéÆ½Ì¨£ºÒ°»ðSTM32¿ª·¢°å
* Ó²¼þÁ¬½Ó ----------------------------
*         | PA4-SPI1-NSS: W25X16-CS|
*         | PA5-SPI1-SCK: W25X16-CLK |
*         | PA6-SPI1-MISO : W25X16-DO|
*         | PA7-SPI1-MOSI : W25X16-DIO |
*          ----------------------------
* ¿â°æ±¾£ºST3.5.0
* ×÷Õß    £º±£Áô
* ÂÛ̳    £ºhttp://www.amobbs.com/forum-1008-1.html
* ÌÔ±¦    £ºhttp://firestm32.taobao.com
**********************************************************************************/
#include "spi_flash.h"



/* Private typedef -----------------------------------------------------------*/
//#define SPI_FLASH_PageSize      4096
#define SPI_FLASH_PageSize      256
#define SPI_FLASH_PerWritePageSize      256

/* Private define ------------------------------------------------------------*/
#define W25X_WriteEnable                      0x06
#define W25X_WriteDisable                      0x04
#define W25X_ReadStatusReg                  0x05
#define W25X_WriteStatusReg                  0x01
#define W25X_ReadData                                0x03
#define W25X_FastReadData                      0x0B
#define W25X_FastReadDual                      0x3B
#define W25X_PageProgram                      0x02
#define W25X_BlockErase                              0xD8
#define W25X_SectorErase                      0x20
#define W25X_ChipErase                              0xC7
#define W25X_PowerDown                              0xB9
#define W25X_ReleasePowerDown          0xAB
#define W25X_DeviceID                                0xAB
#define W25X_ManufactDeviceID           0x90
#define W25X_JedecDeviceID                  0x9F

#define WIP_Flag                  0x01/* Write In Progress (WIP) flag */

#define Dummy_Byte                0xFF

/*******************************************************************************
* Function Name: SPI_FLASH_Init
* Description    : Initializes the peripherals used by the SPI FLASH driver.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_Init(void)
{
#ifdef _DRIVER_IN_
        u32 temp;
#endif       
SPI_InitTypeDefSPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

/* Enable SPI1 and GPIO clocks */
/*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO,
       SPI_FLASH_SPI_MISO_GPIO, SPI_FLASH_SPI_DETECT_GPIO
       and SPI_FLASH_SPI_SCK_GPIO Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD, ENABLE);

/*!< SPI_FLASH_SPI Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);


/*!< Configure SPI_FLASH_SPI pins: SCK */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_SCK;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_FONT_SPI_SCK_FLAG;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/*!< Configure SPI_FLASH_SPI pins: MISO */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_MISO;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/*!< Configure SPI_FLASH_SPI pins: MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_MOSI;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/*!< Configure SPI_FLASH_SPI_CS_PIN pin: SPI_FLASH Card CS pin */
GPIO_InitStructure.GPIO_Pin = GPIO_FONT_SPI_NSS;
GPIO_InitStructure.GPIO_Mode = GPIO_FONT_SPI_NSS_FLAG;
GPIO_Init(GPIO_FONT_SPI, &GPIO_InitStructure);

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();

/* SPI1 configuration */
// W25X16: data input on the DIO pin is sampled on the rising edge of the CLK.
// Data on the DO and DIO pins are clocked out on the falling edge of CLK.
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);

/* Enable SPI1*/
SPI_Cmd(SPI2, ENABLE);
#ifdef _DRIVER_IN_
        temp = SPI_FLASH_ReadID();
        if ((temp == GD25Q32)||(temp == PY25Q32))
                MassType = MASS_25Q32;
        else
                MassType = MASS_25Q16;
#else
                MassType = MASS_25Q16;       
#endif       
}
/*******************************************************************************
* Function Name: SPI_FLASH_SectorErase
* Description    : Erases the specified FLASH sector.
* Input          : SectorAddr: address of the sector to erase.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
/* Send write enable instruction */
SPI_FLASH_WriteEnable();
SPI_FLASH_WaitForWriteEnd();
/* Sector Erase */
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();
/* Send Sector Erase instruction */
SPI_FLASH_SendByte(W25X_SectorErase);
/* Send SectorAddr high nibble address byte */
SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
/* Send SectorAddr medium nibble address byte */
SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
/* Send SectorAddr low nibble address byte */
SPI_FLASH_SendByte(SectorAddr & 0xFF);
/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
}

/*******************************************************************************
* Function Name: SPI_FLASH_BlockErase
* Description    : Erases the specified FLASH sector.
* Input          : SectorAddr: address of the sector to erase.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_BlockErase(u32 BlockAddr)
{
/* Send write enable instruction */
SPI_FLASH_WriteEnable();
SPI_FLASH_WaitForWriteEnd();
/* Sector Erase */
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();
/* Send Sector Erase instruction */
SPI_FLASH_SendByte(W25X_BlockErase);
/* Send SectorAddr high nibble address byte */
SPI_FLASH_SendByte((BlockAddr & 0xFF0000) >> 16);
/* Send SectorAddr medium nibble address byte */
SPI_FLASH_SendByte((BlockAddr & 0xFF00) >> 8);
/* Send SectorAddr low nibble address byte */
SPI_FLASH_SendByte(BlockAddr & 0xFF);
/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
}
/*******************************************************************************
* Function Name: SPI_FLASH_BulkErase
* Description    : Erases the entire FLASH.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_BulkErase(void)
{
/* Send write enable instruction */
SPI_FLASH_WriteEnable();

/* Bulk Erase */
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();
/* Send Bulk Erase instruction*/
SPI_FLASH_SendByte(W25X_ChipErase);
/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();

/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
}

/*******************************************************************************
* Function Name: SPI_FLASH_PageWrite
* Description    : Writes more than one byte to the FLASH with a single WRITE
*                  cycle(Page WRITE sequence). The number of byte can't exceed
*                  the FLASH page size.
* Input          : - pBuffer : pointer to the buffercontaining the data to be
*                  written to the FLASH.
*                  - WriteAddr : FLASH's internal address to write to.
*                  - NumByteToWrite : number of bytes to write to the FLASH,
*                  must be equal or less than "SPI_FLASH_PageSize" value.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
/* Enable the write access to the FLASH */
SPI_FLASH_WriteEnable();

/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();
/* Send "Write to Memory " instruction */
SPI_FLASH_SendByte(W25X_PageProgram);
/* Send WriteAddr high nibble address byte to write to */
SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
/* Send WriteAddr medium nibble address byte to write to */
SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
/* Send WriteAddr low nibble address byte to write to */
SPI_FLASH_SendByte(WriteAddr & 0xFF);

if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
{
   NumByteToWrite = SPI_FLASH_PerWritePageSize;
   //printf("\n\r Err: SPI_FLASH_PageWrite too large!");
}

/* while there is data to be written on the FLASH */
while (NumByteToWrite--)
{
    /* Send the current byte */
    SPI_FLASH_SendByte(*pBuffer);
    /* Point on the next byte to be written */
    pBuffer++;
}

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();

/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
}

/*******************************************************************************
* Function Name: SPI_FLASH_BufferWrite
* Description    : Writes block of data to the FLASH. In this function, the
*                  number of WRITE cycles are reduced, using Page WRITE sequence.
* Input          : - pBuffer : pointer to the buffercontaining the data to be
*                  written to the FLASH.
*                  - WriteAddr : FLASH's internal address to write to.
*                  - NumByteToWrite : number of bytes to write to the FLASH.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

Addr = WriteAddr % SPI_FLASH_PageSize;
count = SPI_FLASH_PageSize - Addr;
NumOfPage =NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

if (Addr == 0) /* WriteAddr is SPI_FLASH_PageSize aligned*/
{
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      while (NumOfPage--)
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
      WriteAddr +=SPI_FLASH_PageSize;
      pBuffer += SPI_FLASH_PageSize;
      }

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
}
else /* WriteAddr is not SPI_FLASH_PageSize aligned*/
{
    if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
    {
      if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
      {
      temp = NumOfSingle - count;

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
      WriteAddr +=count;
      pBuffer += count;

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
      NumByteToWrite -= count;
      NumOfPage =NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
      WriteAddr +=count;
      pBuffer += count;

      while (NumOfPage--)
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
      WriteAddr +=SPI_FLASH_PageSize;
      pBuffer += SPI_FLASH_PageSize;
      }

      if (NumOfSingle != 0)
      {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
}
}
//ÎÞ¼ìÑéдSPI FLASH
//±ØÐëÈ·±£ËùдµÄµØÖ··¶Î§ÄÚµÄÊý¾ÝÈ«²¿Îª0XFF,·ñÔòÔÚ·Ç0XFF´¦Ð´ÈëµÄÊý¾Ý½«Ê§°Ü!
//¾ßÓÐ×Ô¶¯»»Ò³¹¦ÄÜ
//ÔÚÖ¸¶¨µØÖ·¿ªÊ¼Ð´ÈëÖ¸¶¨³¤¶ÈµÄÊý¾Ý,µ«ÊÇҪȷ±£µØÖ·²»Ô½½ç!
//pBuffer:Êý¾Ý´æ´¢Çø
//WriteAddr:¿ªÊ¼Ð´ÈëµÄµØÖ·(24bit)
//NumByteToWrite:ҪдÈëµÄ×Ö½ÚÊý(×î´ó65535)
//CHECK OK
void SPI_Flash_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{                                       
        u16 pageremain;          
        pageremain=256-WriteAddr%256; //µ¥Ò³Ê£ÓàµÄ×Ö½ÚÊý                         
        if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//²»´óÓÚ256¸ö×Ö½Ú
        while(1)
        {          
                SPI_FLASH_PageWrite(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¸ö×Ö½ÚÁË
                }
        };          
}

u8 SPI_FLASH_BUF;
void        SPI_Flash_Read(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead);
void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{
        u32 secpos;
        u16 secoff;
        u16 secremain;          
        u16 i;   

        secpos=WriteAddr/4096;//ÉÈÇøµØÖ· 0~511 for w25x16
        secoff=WriteAddr%4096;//ÔÚÉÈÇøÄ򵀮«ÒÆ
        secremain=4096-secoff;//ÉÈÇøÊ£Óà¿Õ¼ä´óС   

        if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//²»´óÓÚ4096¸ö×Ö½Ú
        while(1)
        {       
                SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//¶Á³öÕû¸öÉÈÇøµÄÄÚÈÝ
                for(i=0;i<secremain;i++)//УÑéÊý¾Ý
                {
                        if(SPI_FLASH_BUF!=0XFF)break;//ÐèÒª²Á³ý          
                }
                if(i<secremain)//ÐèÒª²Á³ý
                {
                        SPI_FLASH_SectorErase(secpos * 4096);//²Á³ýÕâ¸öÉÈÇø
                        for(i=0;i<secremain;i++)           //¸´ÖÆ
                        {
                                SPI_FLASH_BUF=pBuffer;          
                        }
                        SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//дÈëÕû¸öÉÈÇø

                }else SPI_Flash_Write_NoCheck(pBuffer,WriteAddr,secremain);//дÒѾ­²Á³ýÁ˵Ä,Ö±½ÓдÈëÉÈÇøÊ£ÓàÇø¼ä.                                   
                if(NumByteToWrite==secremain)break;//дÈë½áÊøÁË
                else//дÈëδ½áÊø
                {
                        secpos++;//ÉÈÇøµØÖ·Ôö1
                        secoff=0;//Æ«ÒÆÎ»ÖÃΪ0        

                           pBuffer+=secremain;//Ö¸ÕëÆ«ÒÆ
                        WriteAddr+=secremain;//дµØÖ·Æ«ÒÆ          
                           NumByteToWrite-=secremain;                                //×Ö½ÚÊýµÝ¼õ
                        if(NumByteToWrite>4096)secremain=4096;        //ÏÂÒ»¸öÉÈÇø»¹ÊÇд²»Íê
                        else secremain=NumByteToWrite;                        //ÏÂÒ»¸öÉÈÇø¿ÉÒÔдÍêÁË
                }       
        };              
}
/*******************************************************************************
* Function Name: SPI_FLASH_BufferRead
* Description    : Reads a block of data from the FLASH.
* Input          : - pBuffer : pointer to the buffer that receives the data read
*                  from the FLASH.
*                  - ReadAddr : FLASH's internal address to read from.
*                  - NumByteToRead : number of bytes to read from the FLASH.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "Read from Memory " instruction */
SPI_FLASH_SendByte(W25X_ReadData);

/* Send ReadAddr high nibble address byte to read from */
SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
/* Send ReadAddr medium nibble address byte to read from */
SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
/* Send ReadAddr low nibble address byte to read from */
SPI_FLASH_SendByte(ReadAddr & 0xFF);

while (NumByteToRead--) /* while there is data to be read */
{
    /* Read a byte from the FLASH */
    *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    /* Point to the next location where the byte read will be saved */
    pBuffer++;
}

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
}

void        SPI_Flash_Read(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
                SPI_FLASH_BufferRead(pBuffer,ReadAddr,NumByteToRead);
                return;
}
/*******************************************************************************
* Function Name: SPI_FLASH_ReadID
* Description    : Reads FLASH identification.
* Input          : None
* Output         : None
* Return         : FLASH identification
*******************************************************************************/
u32 SPI_FLASH_ReadID(void)
{
u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "RDID " instruction */
SPI_FLASH_SendByte(W25X_JedecDeviceID);

/* Read a byte from the FLASH */
Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

/* Read a byte from the FLASH */
Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

/* Read a byte from the FLASH */
Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();

Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

return Temp;
}
/*******************************************************************************
* Function Name: SPI_FLASH_ReadID
* Description    : Reads FLASH identification.
* Input          : None
* Output         : None
* Return         : FLASH identification
*******************************************************************************/
u32 SPI_FLASH_ReadDeviceID(void)
{
u32 Temp = 0;

/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "RDID " instruction */
SPI_FLASH_SendByte(W25X_DeviceID);
SPI_FLASH_SendByte(Dummy_Byte);
SPI_FLASH_SendByte(Dummy_Byte);
SPI_FLASH_SendByte(Dummy_Byte);

/* Read a byte from the FLASH */
Temp = SPI_FLASH_SendByte(Dummy_Byte);

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();

return Temp;
}
/*******************************************************************************
* Function Name: SPI_FLASH_StartReadSequence
* Description    : Initiates a read data byte (READ) sequence from the Flash.
*                  This is done by driving the /CS line low to select the device,
*                  then the READ instruction is transmitted followed by 3 bytes
*                  address. This function exit and keep the /CS line low, so the
*                  Flash still being selected. With this technique the whole
*                  content of the Flash is read with a single READ instruction.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_StartReadSequence(u32 ReadAddr)
{
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "Read from Memory " instruction */
SPI_FLASH_SendByte(W25X_ReadData);

/* Send the 24-bit address of the address to read from -----------------------*/
/* Send ReadAddr high nibble address byte */
SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
/* Send ReadAddr medium nibble address byte */
SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
/* Send ReadAddr low nibble address byte */
SPI_FLASH_SendByte(ReadAddr & 0xFF);
}

/*******************************************************************************
* Function Name: SPI_FLASH_ReadByte
* Description    : Reads a byte from the SPI Flash.
*                  This function must be used only if the Start_Read_Sequence
*                  function has been previously called.
* Input          : None
* Output         : None
* Return         : Byte Read from the SPI Flash.
*******************************************************************************/
u8 SPI_FLASH_ReadByte(void)
{
return (SPI_FLASH_SendByte(Dummy_Byte));
}

/*******************************************************************************
* Function Name: SPI_FLASH_SendByte
* Description    : Sends a byte through the SPI interface and return the byte
*                  received from the SPI bus.
* Input          : byte : byte to send.
* Output         : None
* Return         : The value of the received byte.
*******************************************************************************/
u8 SPI_FLASH_SendByte(u8 byte)
{
/* Loop while DR register in not emplty */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_TXE) == RESET);

/* Send byte through the SPI1 peripheral */
SPI_I2S_SendData(FONT_SPI, byte);

/* Wait to receive a byte */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_RXNE) == RESET);

/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(FONT_SPI);
}

/*******************************************************************************
* Function Name: SPI_FLASH_SendHalfWord
* Description    : Sends a Half Word through the SPI interface and return the
*                  Half Word received from the SPI bus.
* Input          : Half Word : Half Word to send.
* Output         : None
* Return         : The value of the received Half Word.
*******************************************************************************/
u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
{
/* Loop while DR register in not emplty */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_TXE) == RESET);

/* Send Half Word through the SPI1 peripheral */
SPI_I2S_SendData(FONT_SPI, HalfWord);

/* Wait to receive a Half Word */
while (SPI_I2S_GetFlagStatus(FONT_SPI, SPI_I2S_FLAG_RXNE) == RESET);

/* Return the Half Word read from the SPI bus */
return SPI_I2S_ReceiveData(FONT_SPI);
}

/*******************************************************************************
* Function Name: SPI_FLASH_WriteEnable
* Description    : Enables the write access to the FLASH.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_WriteEnable(void)
{
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "Write Enable" instruction */
SPI_FLASH_SendByte(W25X_WriteEnable);

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
}

/*******************************************************************************
* Function Name: SPI_FLASH_WaitForWriteEnd
* Description    : Polls the status of the Write In Progress (WIP) flag in the
*                  FLASH's statusregisterandloopuntil writeopertaion
*                  has completed.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_WaitForWriteEnd(void)
{
u8 FLASH_Status = 0;

/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "Read Status Register" instruction */
SPI_FLASH_SendByte(W25X_ReadStatusReg);

/* 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 */
    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);       
}
while ((FLASH_Status & WIP_Flag) == SET); /* Write in progress */

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
}


//½øÈëµôµçģʽ
void SPI_Flash_PowerDown(void)   
{
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "Power Down" instruction */
SPI_FLASH_SendByte(W25X_PowerDown);

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
}   

//»½ÐÑ
void SPI_Flash_WAKEUP(void)   
{
/* Select the FLASH: Chip Select low */
SPI_FLASH_CS_LOW();

/* Send "Power Down" instruction */
SPI_FLASH_SendByte(W25X_ReleasePowerDown);

/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();                   //µÈ´ýTRES1
}   

/******************************END OF FILE*****************************/

这里说的完整代码并不是整个工程的全部代码,只是与Nor Flash读写有关的代码。

五、总结
通过GD32F103的SPI接口操作NOR Flash,开发者可灵活扩展嵌入式系统的存储能力。本文提供了从底层驱动NOR Flash的方式。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weald2000/article/details/147313523

页: [1]
查看完整版本: stm32F103、GD32F103读写Nor Flash实战指南