天天看點

STM32F7xx —— QSPI                                 STM32F7xx —— QSPI

                                 STM32F7xx —— QSPI

目錄

STM32F7xx —— QSPI

一、QSPI

二、幾個重要的函數

三、幾個重要的結構

四、QSPI接口設計(僅供參考)

五、QSPI驅動W25Q256

一、QSPI

        SPI 是 Queued SPI 的簡寫,是 Motorola公司推出的 SPI 接口的擴充,比 SPI 應用更加廣泛。在 SPI 協定的基礎上,Motorola 公司對其功能進行了增強,增加了隊列傳輸機制,推出了隊列串行外圍接口協定(即 QSPI 協定),使用該接口,使用者可以一次性傳輸包含多達16個8位或16位資料的傳輸隊列。一旦傳輸啟動,直到傳輸結束,都不需要CPU幹預,極大的提高了傳輸效率。該協定在ColdFire系列MCU得到廣泛應用。與SPI相比,QSPI的最大結構特點是以80位元組的RAM代替了SPI的發送和接收寄存器。QSPI 是一種專用的通信接口,連接配接單、雙或四(條資料線) SPI Flash 存儲媒體。

 該接口可以在以下三種模式下工作:

① 間接模式:使用 QSPI 寄存器執行全部操作

② 狀态輪詢模式:周期性讀取外部 Flash 狀态寄存器,而且标志位置 1 時會産生中斷(如擦除或燒寫完成,會産生中斷)

③ 記憶體映射模式:外部 Flash 映射到微控制器位址空間,進而系統将其視作内部存儲器。

QSPI通過6根線與SPI晶片通信,下圖是内部框圖:

STM32F7xx —— QSPI                                 STM32F7xx —— QSPI

QSPI每條指令,必須包含一個或多個階段:指令、位址、交替位元組、空指令和資料。

QSPI發送指令:等待QSPI空閑;設定指令參數。

QSPI讀資料:設定資料傳輸長度;設定QSPI工作模式并設定位址;讀取資料。

QSPI寫資料:設定資料傳輸長度;設定QSPI工作模式并設定位址;寫資料。

二、幾個重要的函數

HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化

HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 發送指令

HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 發送資料

HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收資料
 
           

三、幾個重要的結構

// QSPI操作句柄
typedef struct
{
  QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */
  QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */
  uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */
  __IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */
  __IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */
  uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */
  __IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */
  __IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */
  DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */
  __IO HAL_LockTypeDef       Lock;             /* Locking object                     */
  __IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */
  __IO uint32_t              ErrorCode;        /* QSPI Error code                    */
  uint32_t                   Timeout;          /* Timeout for the QSPI memory access */ 
}QSPI_HandleTypeDef;

// Instance:QSPI基位址  ---  QUADSPI
// Init:設定QSPI參數
// pTxBuffPtr,TxXferSize,TxXferCount:發送緩存指針 發送資料量 剩餘資料量
// pRxBuffPtr,RxXferSize,RxXferCount:接收緩存指針 發送資料量 剩餘資料量
// hdma與DMA相關, 其他為過程變量不需要關心
           
// 參數配置 時鐘分頻系數  FIFO門檻值  采樣移位  FLASH大小  片選高電平時間  時鐘模式  閃存ID  雙閃存模式設定
typedef struct
{
  uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.
                                  This parameter can be a number between 0 and 255 */ 
                                  
  uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)
                                  This parameter can be a value between 1 and 32 */
                                  
  uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to 
                                  take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)
                                  This parameter can be a value of @ref QSPI_SampleShifting */
                                  
  uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits 
                                  required to address the flash memory. The flash capacity can be up to 4GB 
                                  (addressed using 32 bits) in indirect mode, but the addressable space in 
                                  memory-mapped mode is limited to 256MB
                                  This parameter can be a number between 0 and 31 */
                                  
  uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number 
                                  of clock cycles which the chip select must remain high between commands.
                                  This parameter can be a value of @ref QSPI_ChipSelectHighTime */ 
                                    
  uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.
                                  This parameter can be a value of @ref QSPI_ClockMode */
                                 
  uint32_t FlashID;            /* Specifies the Flash which will be used,
                                  This parameter can be a value of @ref QSPI_Flash_Select */
                                 
  uint32_t DualFlash;          /* Specifies the Dual Flash Mode State
                                  This parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
}QSPI_InitTypeDef;
           
// 采樣移位
#define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
#define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/
           

四、QSPI接口設計(僅供參考)

#define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();

#define QSPI_BK1_NCS_PORT         GPIOB
#define QSPI_BK1_NCS_PIN          GPIO_PIN_6
#define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
#define QSPI_BK1_NCS_CONFIG()     GPIOConfigExt(QSPI_BK1_NCS_PORT, QSPI_BK1_NCS_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, QSPI_BK1_NCS_AF)

#define QSPI_BK1_CLK_PORT         GPIOB
#define QSPI_BK1_CLK_PIN          GPIO_PIN_2
#define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
#define QSPI_BK1_CLK_CONFIG()     GPIOConfigExt(QSPI_BK1_CLK_PORT, QSPI_BK1_CLK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_CLK_AF)

#define QSPI_BK1_IO0_PORT         GPIOF
#define QSPI_BK1_IO0_PIN          GPIO_PIN_8
#define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI
#define QSPI_BK1_IO0_CONFIG()     GPIOConfigExt(QSPI_BK1_IO0_PORT, QSPI_BK1_IO0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO0_AF)

#define QSPI_BK1_IO1_PORT         GPIOF
#define QSPI_BK1_IO1_PIN          GPIO_PIN_9
#define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI
#define QSPI_BK1_IO1_CONFIG()     GPIOConfigExt(QSPI_BK1_IO1_PORT, QSPI_BK1_IO1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO1_AF)

#define QSPI_BK1_IO2_PORT         GPIOF
#define QSPI_BK1_IO2_PIN          GPIO_PIN_7
#define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
#define QSPI_BK1_IO2_CONFIG()     GPIOConfigExt(QSPI_BK1_IO2_PORT, QSPI_BK1_IO2_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO2_AF)

#define QSPI_BK1_IO3_PORT         GPIOF
#define QSPI_BK1_IO3_PIN          GPIO_PIN_6
#define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
#define QSPI_BK1_IO3_CONFIG()     GPIOConfigExt(QSPI_BK1_IO3_PORT, QSPI_BK1_IO3_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO3_AF)
           
// 封裝幾個必要的接口
static QSPI_HandleTypeDef qspi_handle;

static void qspi_gpio_init(void)
{
  QSPI_CLK_ENABLE();

  QSPI_BK1_NCS_CONFIG();
  QSPI_BK1_CLK_CONFIG();
  QSPI_BK1_IO0_CONFIG();
  QSPI_BK1_IO1_CONFIG();
  QSPI_BK1_IO2_CONFIG();
  QSPI_BK1_IO3_CONFIG();
}

static void qspi_mode_init(void)
{
  qspi_handle.Instance = QUADSPI;                        // QSPI
  qspi_handle.Init.ClockPrescaler = 2;                   // QPSI分頻比,W25Q256最大頻率為104M 是以此處應該為2,QSPI頻率就為216/(2+1)=72MHZ
  qspi_handle.Init.FifoThreshold = 4;                    // FIFO門檻值為4個位元組
  qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采樣移位半個周期(DDR模式下,必須設定為0)
  qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小為32M位元組
  qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片選高電平時間為4個時鐘(13.8*4=55.2ns),即手冊裡面的tSHSL參數
  qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0
  qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flash
  qspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止雙閃存模式
  HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
}

void QSPIInit(void)
{
  qspi_gpio_init();
  qspi_mode_init();
}

// QSPI發送指令
// instruction:要發送的指令
// address:發送到的目的位址
// dummyCycles:空指令周期數
// instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
// addressMode:位址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
// addressSize:位址長度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS
// dataMode:資料模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
{
  QSPI_CommandTypeDef Cmdhandler;

  Cmdhandler.Instruction = instruction;           // 指令
  Cmdhandler.Address = address;                   // 位址
  Cmdhandler.DummyCycles = dummyCycles;           // 設定空指令周期數
  Cmdhandler.InstructionMode = instructionMode;   // 指令模式
  Cmdhandler.AddressMode = addressMode;           // 位址模式
  Cmdhandler.AddressSize = addressSize;           // 位址長度
  Cmdhandler.DataMode = dataMode;                 // 資料模式
  Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都發送指令
  Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 無交替位元組
  Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 關閉DDR模式
  Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;

  HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
}

// 接收指定長度資料
uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
{
  qspi_handle.Instance->DLR = length - 1;
	
  if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK)
  {
    return 0;  //接收資料
  }
  else
  {
    return 1;
  }
}

// 發送指定長度資料
uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)
{
  qspi_handle.Instance->DLR = length - 1;
  if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK)
  {
    return 0;  //發送資料
  }
  else
  {
    return 1;
  }
}
           

五、QSPI驅動W25Q256

W25Q256:32M  512塊,每塊16個扇區,每個扇區4K。具體指令看手冊。封裝了如下接口(初始化讀寫擦除等接口是以後移植檔案系統的基礎):

#define W25QXX_WRITE_ENABLE      0x06
#define W25QXX_WRITE_DISABLE     0x04
#define W25QXX_READ_STATUS_REG1  0x05
#define W25QXX_READ_STATUS_REG2  0x35
#define W25QXX_READ_STATUS_REG3  0x15
#define W25QXX_WRITE_STATUS_REG1 0x01
#define W25QXX_WRITE_STATUS_REG2 0x31
#define W25QXX_WRITE_STATUS_REG3 0x11
#define W25QXX_READ_DATA         0x03
#define W25QXX_FAST_READ_DATA    0x0B
#define W25QXX_FAST_READ_DUAL    0x3B
#define W25QXX_PAGE_PROGRAM      0x02
#define W25QXX_BLOCK_ERASE       0xD8
#define W25QXX_SECTOR_ERASE      0x20
#define W25QXX_CHIP_ERASE        0xC7
#define W25QXX_DEVICEID          0xAB
#define W25QXX_MANUFACT_DEVICEID 0x90
#define W25QXX_JEDEC_DEVICEID    0x9F
#define W25QXX_EABLE_4BYTE_ADDR  0xB7
#define W25QXX_EXIT_4BYTE_ADDR   0xE9
#define W25QXX_SET_READ_PARAM    0xC0
#define W25QXX_ENTER_QPIMODE     0x38
#define W25QXX_EXIT_QPIMODE      0xFF

uint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.

// 讀取W25QXX的狀态寄存器,W25QXX一共有3個狀态寄存器
// 狀态寄存器1:
// BIT7  6   5   4   3   2   1   0
// SPR   RV  TB BP2 BP1 BP0 WEL BUSY
// SPR:預設0,狀态寄存器保護位,配合WP使用
// TB,BP2,BP1,BP0:FLASH區域寫保護設定
// WEL:寫使能鎖定
// BUSY:忙标記位(1,忙;0,空閑)
// 預設:0x00
// 狀态寄存器2:
// BIT7  6   5   4   3   2   1   0
// SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
// 狀态寄存器3:
// BIT7      6    5    4   3   2   1   0
// HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
// reg:狀态寄存器号,範:1~3
// 傳回值:狀态寄存器值
static uint8_t w25qxx_read_status(uint8_t reg)
{
  uint8_t value = 0, command = 0;

  switch(reg)
  {
  case 1:
    command = W25QXX_READ_STATUS_REG1;  // 讀狀态寄存器1指令
    break;

  case 2:
    command = W25QXX_READ_STATUS_REG2;  // 讀狀态寄存器2指令
    break;

  case 3:
    command = W25QXX_READ_STATUS_REG3;  // 讀狀态寄存器3指令
    break;

  default:
    command = W25QXX_READ_STATUS_REG1;
    break;
  }

  if(w25qxx_qpi_mode)
  {
    QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);  //QPI,寫command指令,位址為0,4線傳資料_8位位址_無位址_4線傳輸指令,無空周期,1個位元組資料
  }
  else
  {
    QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);  //SPI,寫command指令,位址為0,單線傳資料_8位位址_無位址_單線傳輸指令,無空周期,1個位元組資料
  }

  QSPIReceive(&value, 1);

  return value;
}

// 寫狀态寄存器
static void w25qxx_write_status(uint8_t reg, uint8_t status)
{
  uint8_t command = 0;

  switch(reg)
  {
  case 1:
    command = W25QXX_WRITE_STATUS_REG1;  //寫狀态寄存器1指令
    break;
  case 2:
    command = W25QXX_WRITE_STATUS_REG2;  //寫狀态寄存器2指令
    break;
  case 3:
    command = W25QXX_WRITE_STATUS_REG3;  //寫狀态寄存器3指令
    break;
  default:
    command = W25QXX_WRITE_STATUS_REG1;
    break;
  }
  if(w25qxx_qpi_mode)
  {
    QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,寫command指令,位址為0,4線傳資料_8位位址_無位址_4線傳輸指令,無空周期,1個位元組資料
  }
  else
  {
    QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); // SPI,寫command指令,位址為0,單線傳資料_8位位址_無位址_單線傳輸指令,無空周期,1個位元組資料
  }
  QSPITransmit(&status, sizeof(status));
}

// 寫使能  将S1寄存器的WEL置位
static void w25qxx_write_enable(void)
{
  if(w25qxx_qpi_mode)
  {
    QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,寫使能指令,位址為0,無資料_8位位址_無位址_4線傳輸指令,無空周期,0個位元組資料
  }
  else
  {
    QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,寫使能指令,位址為0,無資料_8位位址_無位址_單線傳輸指令,無空周期,0個位元組資料
  }
}

// 寫失能  将WEL清零
void W25QXX_Write_Disable(void)
{
  if(w25qxx_qpi_mode)
  {
    QSPISendCMD(W25QXX_WRITE_DISABLE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,寫禁止指令,位址為0,無資料_8位位址_無位址_4線傳輸指令,無空周期,0個位元組資料
  }
  else
  {
    QSPISendCMD(W25QXX_WRITE_DISABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,寫禁止指令,位址為0,無資料_8位位址_無位址_單線傳輸指令,無空周期,0個位元組資料
  }
}

// 等待空閑
void w25qxx_wait_busy(void)
{
  while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
}

// W25QXX進入QSPI模式
static void w25qxx_qspi_init(void)
{
  uint8_t reg2;
  reg2 = w25qxx_read_status(2); // 先讀出狀态寄存器2的原始值
  if((reg2 & 0X02) == 0)  // QE位未使能
  {
    w25qxx_write_enable(); // 寫使能
    reg2 |= 1 << 1; // 使能QE位
    w25qxx_write_status(2, reg2); // 寫狀态寄存器2
  }

  QSPISendCMD(W25QXX_ENTER_QPIMODE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // 寫command指令,位址為0,無資料_8位位址_無位址_單線傳輸指令,無空周期,0個位元組資料

  w25qxx_qpi_mode = 1; // 标記QSPI模式
}

// 0XEF13,表示晶片型号為W25Q80
// 0XEF14,表示晶片型号為W25Q16
// 0XEF15,表示晶片型号為W25Q32
// 0XEF16,表示晶片型号為W25Q64
// 0XEF17,表示晶片型号為W25Q128
// 0XEF18,表示晶片型号為W25Q256
static void w25qxx_id_get(void)
{
  uint8_t temp[2];
  uint16_t device_id;

  if(w25qxx_qpi_mode)
  {
    QSPISendCMD(W25QXX_MANUFACT_DEVICEID, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_24_BITS, QSPI_DATA_4_LINES); // QPI,讀id,位址為0,4線傳輸資料_24位位址_4線傳輸位址_4線傳輸指令,無空周期,2個位元組資料
  }
  else
  {
    QSPISendCMD(W25QXX_MANUFACT_DEVICEID, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_1_LINE, QSPI_ADDRESS_24_BITS, QSPI_DATA_1_LINE); // SPI,讀id,位址為0,單線傳輸資料_24位位址_單線傳輸位址_單線傳輸指令,無空周期,2個位元組資料
  }

  QSPIReceive(temp, 2);

  device_id = (temp[0] << 8) | temp[1];

  printf("QSPI Flash Device ID: %X\r\n", device_id);
}

void W25QXXInit(void)
{
  uint8_t temp;

  QSPIInit();
  w25qxx_qspi_init(); // 使能QSPI模式
  w25qxx_id_get();    // 讀取FLASH ID.

  temp = w25qxx_read_status(3); // 讀取狀态寄存器3,判斷位址模式
  if((temp & 0X01) == 0) // 如果不是4位元組位址模式,則進入4位元組位址模式
  {
    w25qxx_write_enable(); // 寫使能
    QSPISendCMD(W25QXX_EABLE_4BYTE_ADDR, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,使能4位元組位址指令,位址為0,無資料_8位位址_無位址_4線傳輸指令,無空周期,0個位元組資料
  }
  w25qxx_write_enable(); // 寫使能
  QSPISendCMD(W25QXX_SET_READ_PARAM, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,設定讀參數指令,位址為0,4線傳資料_8位位址_無位址_4線傳輸指令,無空周期,1個位元組資料
  temp = 3 << 4;      // 設定P4&P5=11,8個dummy clocks,104M
  QSPITransmit(&temp, 1);
}

// 擦除一塊4096位元組 最少需要150ms
void W25QXXSectorErase(uint32_t addr)
{
  //addr /= 4096;
  addr *= 4096;

  w25qxx_write_enable();
  w25qxx_wait_busy();
  QSPISendCMD(W25QXX_SECTOR_ERASE, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_NONE); // QPI,寫扇區擦除指令,位址為0,無資料_32位位址_4線傳輸位址_4線傳輸指令,無空周期,0個位元組資料
  w25qxx_wait_busy();
}


// 擦除整個晶片
void W25QXXChipErase(void)
{
  w25qxx_write_enable();          //SET WEL
  w25qxx_wait_busy();
  QSPISendCMD(W25QXX_CHIP_ERASE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,寫全片擦除指令,位址為0,無資料_8位位址_無位址_4線傳輸指令,無空周期,0個位元組資料
  w25qxx_wait_busy();           //等待晶片擦除結束
}

// 寫最多256位元組
void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)
{
  w25qxx_write_enable();          //寫使能
  QSPISendCMD(W25QXX_PAGE_PROGRAM, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,頁寫指令,位址為WriteAddr,4線傳輸資料_32位位址_4線傳輸位址_4線傳輸指令,無空周期,NumByteToWrite個資料
  QSPITransmit(buffer, length);
  w25qxx_wait_busy();            //等待寫入結束
}

// 寫最多65536位元組 無擦除動作
void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
{
  uint16_t pageremain = 256 - addr % 256; // 單頁剩餘的位元組數

  if(addr + length - 1 > (32 * 1024 * 1024))
  {
    return;
  }

  if(length <= pageremain)
  {
    pageremain = length;  // 不大于256個位元組
  }
  while(1)
  {
    // 分頁寫入
    W25QXXWritePage(addr, buffer, pageremain);

    if(length == pageremain)
    {
      break;  //寫入結束了
    }
    else
    {
      buffer += pageremain;
      addr += pageremain;

      length -= pageremain; // 減去已經寫入了的位元組數
      if(length > 256)
      {
        pageremain = 256;  // 一次可以寫入256個位元組
      }
      else
      {
        pageremain = length;  // 不夠256個位元組了
      }
    }
  }
}

// 寫最多65536位元組 帶擦除動作 
static uint8_t W25QXX_BUFFER[4096];
void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
{
  uint32_t secpos;
  uint16_t secoff, secremain, i;
  uint8_t * w25q_buf;

  w25q_buf = W25QXX_BUFFER;

  secpos = addr / 4096; // 扇區位址
  secoff = addr % 4096; // 在扇區内的偏移
  secremain = 4096 - secoff; // 扇區剩餘空間大小

  if(length <= secremain)
  {
    secremain = length;  // 不大于4096個位元組
  }
  while(1)
  {
  	WatchdogFeed();
    W25QXXRead(secpos * 4096, w25q_buf, 4096); // 讀出整個扇區的内容
    for(i = 0; i < secremain; ++i)
    {
      if(w25q_buf[secoff + i] != 0XFF)
      {
        break;  // 需要擦除
      }
    }
    if(i < secremain) // 需要擦除
    {
      W25QXXSectorErase(secpos); // 擦除這個扇區
      for(i = 0; i < secremain; ++i)
      {
        w25q_buf[i + secoff] = buffer[i];
      }
      W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 寫入整個扇區

    }
    else
    {
      W25QXXWriteExt(addr, buffer, secremain);  // 寫已經擦除了的,直接寫入扇區剩餘區間.
    }
    if(length == secremain)
    {
      break;
    }
    else
    {
      secpos++;
      secoff = 0;

      buffer += secremain;
      addr += secremain;
      length -= secremain;
      if(length > 4096)
      {
        secremain = 4096;
      }
      else
      {
        secremain = length;
      }
    }
  };
}


// 讀取SPI FLASH,僅支援QPI模式  在指定位址開始讀取指定長度的資料 65536
void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
{
  QSPISendCMD(W25QXX_FAST_READ_DATA, addr, 8, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,快速讀資料,位址為ReadAddr,4線傳輸資料_32位位址_4線傳輸位址_4線傳輸指令,8空周期,length個資料
  QSPIReceive(buffer, length);
}
           

也可以使用SPI操作W25QXX。

繼續閱讀