[MCU] 【HC32F460开发板测评】09.通过硬件QSPI来实现对W25Q64的读写操作

xld0932   2021-5-7 21:39 楼主

HC32F460 系列的四线式串行外设接口(QSPI)是一个存储器控制模块,主要用于和带 SPI 兼容接口的串行 ROM 进行通信,其对象主要包括有串行闪存、串行 EEPROM 以及串行FeRAM。本例程通过QSPI接口实现对QSPI FLASH进行擦除、写入数据和读取数据的操作,将写入和读取出的数据进行对比,将结果通过串口进行打印输出。
 

QSPI接口的主要特性:

  • 支持扩展 SPI,二线式 SPI 和四线式 SPI 等多种协议
  • 地址线宽度可选择 8 位/16 位/24 位/32 位
  • 可通过时序调整以支持各种串行闪存
  • 支持多种读取方式

--- 标准读/快速读

--- 二线式输出快速读取/二线式输入输出快速读取

--- 四线式输出快速读取/四线式输入输出快速读取

  • 数量可调的虚拟周期
  • 16 字节的预读取功能
  • 总线周期延长功能
  • XIP 控制功能
  • 灵活而广泛的支持大量串行闪存软件控制指令,包括擦、写、 ID 读取及掉电控制等
     

内存映射:

22.png

HC32F460开发板配置了1颗8MB板载QSPI FLASH芯片W25Q64,硬件原理图如下图所示:

21.png 程序头文件:

 

/*******************************************************************************
 * @file    QSPI_FLASH.h
 * @author  xld0932
 * [url=home.php?mod=space&uid=252314]@version[/url] V1.00
 * [url=home.php?mod=space&uid=311857]@date[/url] 31-Mar-2021
 * [url=home.php?mod=space&uid=159083]@brief[/url] ......
*******************************************************************************/


/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __QSPI_FLASH_H__
#define __QSPI_FLASH_H__


#ifdef __cplusplus
extern "C" {
#endif


#undef  EXTERN


#ifdef  __QSPI_FLASH_C__
#define EXTERN
#else
#define EXTERN extern
#endif


/* Includes ------------------------------------------------------------------*/
#include "config.h"


/* Exported constants --------------------------------------------------------*/
/* Exported types ------------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/


/* Exported functions --------------------------------------------------------*/
EXTERN void QSPI_FLASH_Init(void);
EXTERN void QSPI_FLASH_Demo(void);


#ifdef __cplusplus
}
#endif


#endif


/******************* (C) COPYRIGHT 2021 *************************END OF FILE***/

 

程序源文件:

/*******************************************************************************
 * @file    QSPI_FLASH.c
 * @author  xld0932
 * @version V1.00
 * @date    31-Mar-2021
 * @brief   ......
*******************************************************************************/


/* Define to prevent recursive inclusion -------------------------------------*/
#define __QSPI_FLASH_C__


/* Includes ------------------------------------------------------------------*/
#include "QSPI_FLASH.h"


/* Private typedef -----------------------------------------------------------*/


/* Private define ------------------------------------------------------------*/

/* QSPI memory bus address definition */
#define QSPI_BUS_ADDRESS                (0x98000000ul)

/* FLASH parameters definition */
#define FLASH_PAGE_SIZE                 (0x100u)
#define FLASH_SECTOR_SIZE               (0x1000u)
#define FLASH_MAX_ADDR                  (0x800000ul)
#define FLASH_DUMMY_BYTE_VALUE          (0xFFu)
#define FLASH_BUSY_BIT_MASK             (0x01u)

/* FLASH instruction definition */
#define FLASH_INSTR_WRITE_ENABLE        (0x06u)
#define FLASH_INSTR_PAGE_PROGRAM        (0x02u)
#define FLASH_INSTR_ERASE_4KB_SECTOR    (0x20u)
#define FLASH_INSTR_ERASE_CHIP          (0xC7u)
#define FLASH_INSTR_READ_SR1            (0x05u)
#define FLASH_INSTR_READ_SR2            (0x35u)
#define FLASH_INSTR_READ_SR3            (0x15u)


/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * [url=home.php?mod=space&uid=1020061]@attention[/url] *******************************************************************************/
void QSPI_FLASH_Init(void)
{
    stc_qspi_init_t stcQspiInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcQspiInit);

    /* Configuration peripheral clock */
    PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_QSPI, Enable);

    /* Configuration QSPI pin */
    PORT_SetFunc(PortC, Pin06, Func_Qspi, Disable);
    PORT_SetFunc(PortC, Pin07, Func_Qspi, Disable);
    PORT_SetFunc(PortD, Pin08, Func_Qspi, Disable);
    PORT_SetFunc(PortD, Pin09, Func_Qspi, Disable);
    PORT_SetFunc(PortD, Pin10, Func_Qspi, Disable);
    PORT_SetFunc(PortD, Pin11, Func_Qspi, Disable);

    /* Configuration QSPI structure */
    stcQspiInit.enClkDiv                = QspiHclkDiv3;
    stcQspiInit.enSpiMode               = QspiSpiMode3;
    stcQspiInit.enBusCommMode           = QspiBusModeRomAccess;
    stcQspiInit.enPrefetchMode          = QspiPrefetchStopComplete;
    stcQspiInit.enPrefetchFuncEn        = Disable;
    stcQspiInit.enQssnValidExtendTime   = QspiQssnValidExtendSck32;
    stcQspiInit.enQssnIntervalTime      = QspiQssnIntervalQsck8;
    stcQspiInit.enQsckDutyCorr          = QspiQsckDutyCorrHalfHclk;
    stcQspiInit.enVirtualPeriod         = QspiVirtualPeriodQsck6;
    stcQspiInit.enWpPinLevel            = QspiWpPinOutputHigh;
    stcQspiInit.enQssnSetupDelayTime    = QspiQssnSetupDelay1Dot5Qsck;
    stcQspiInit.enQssnHoldDelayTime     = QspiQssnHoldDelay1Dot5Qsck;
    stcQspiInit.enFourByteAddrReadEn    = Disable;
    stcQspiInit.enAddrWidth             = QspiAddressByteThree;
    stcQspiInit.stcCommProtocol.enReadMode           = QspiReadModeFourWiresIO;
    stcQspiInit.stcCommProtocol.enTransInstrProtocol = QspiProtocolExtendSpi;
    stcQspiInit.stcCommProtocol.enTransAddrProtocol  = QspiProtocolExtendSpi;
    stcQspiInit.stcCommProtocol.enReceProtocol       = QspiProtocolExtendSpi;
    stcQspiInit.u8RomAccessInstr        = QSPI_3BINSTR_FOUR_WIRES_IO_READ;
    QSPI_Init(&stcQspiInit);
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void QSPI_FLASH_WriteEnable(void)
{
    QSPI_EnterDirectCommMode();
    QSPI_WriteDirectCommValue(FLASH_INSTR_WRITE_ENABLE);
    QSPI_ExitDirectCommMode();
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
en_result_t QSPI_FLASH_WaitForWriteEnd(void)
{
    en_result_t Result = Ok;
    uint8_t     Status = 0u;
    uint32_t    Timeout;

    stc_clk_freq_t stcClkFreq;

    CLK_GetClockFreq(&stcClkFreq);
    Timeout = stcClkFreq.sysclkFreq / 1000u;

    QSPI_EnterDirectCommMode();
    QSPI_WriteDirectCommValue(FLASH_INSTR_READ_SR1);

    do
    {
        Status = QSPI_ReadDirectCommValue();
        Timeout--;
    } while((Timeout != 0u) &&
           ((Status & FLASH_BUSY_BIT_MASK) == FLASH_BUSY_BIT_MASK));

    if(FLASH_BUSY_BIT_MASK == Status)
    {
        Result = ErrorTimeout;
    }

    QSPI_ExitDirectCommMode();

    return Result;
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
en_result_t QSPI_FLASH_WritePage(uint32_t Address, const uint8_t Buffer[], uint16_t Length)
{
    en_result_t Result = Ok;
    uint16_t    Index  = 0u;

    if((Address > FLASH_MAX_ADDR) || (NULL == Buffer) || (Length > FLASH_PAGE_SIZE))
    {
        Result = Error;
    }
    else
    {
        QSPI_FLASH_WriteEnable();

        /* Send data to flash */
        QSPI_EnterDirectCommMode();
        QSPI_WriteDirectCommValue(FLASH_INSTR_PAGE_PROGRAM);
        QSPI_WriteDirectCommValue((uint8_t)((Address & 0x00FF0000ul) >> 16));
        QSPI_WriteDirectCommValue((uint8_t)((Address & 0x0000FF00ul) >>  8));
        QSPI_WriteDirectCommValue((uint8_t)((Address & 0x000000FFul) >>  0));

        while(Length--)
        {
            QSPI_WriteDirectCommValue(Buffer[Index++]);
        }

        QSPI_ExitDirectCommMode();

        /* Wait for flash idle */
        Result = QSPI_FLASH_WaitForWriteEnd();
    }

    return Result;
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
en_result_t QSPI_FLASH_Erase4KbSector(uint32_t Address)
{
    en_result_t Result = Ok;

    if(Address >= FLASH_MAX_ADDR)
    {
        Result = Error;
    }
    else
    {
        QSPI_FLASH_WriteEnable();

        /* Send instruction to flash */
        QSPI_EnterDirectCommMode();
        QSPI_WriteDirectCommValue(FLASH_INSTR_ERASE_4KB_SECTOR);
        QSPI_WriteDirectCommValue((uint8_t)((Address & 0x00FF0000ul) >> 16));
        QSPI_WriteDirectCommValue((uint8_t)((Address & 0x0000FF00ul) >>  8));
        QSPI_WriteDirectCommValue((uint8_t)((Address & 0x000000FFul) >>  0));

        QSPI_ExitDirectCommMode();

        /* Wait for flash idle */
        Result = QSPI_FLASH_WaitForWriteEnd();
    }

    return Result;
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void QSPI_FLASH_EraseChip(void)
{
    QSPI_FLASH_WriteEnable();

    /* Send instruction to flash */
    QSPI_EnterDirectCommMode();
    QSPI_WriteDirectCommValue(FLASH_INSTR_ERASE_CHIP);
    QSPI_ExitDirectCommMode();

    /* Wait for flash idle */
    QSPI_FLASH_WaitForWriteEnd();
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
uint8_t QSPI_FLASH_ReadStatusRegister(uint8_t RegAddress)
{
    uint8_t Status = 0u;

    QSPI_EnterDirectCommMode();
    QSPI_WriteDirectCommValue(RegAddress);
    Status = QSPI_ReadDirectCommValue();
    QSPI_ExitDirectCommMode();

    return Status;
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void QSPI_FLASH_Demo(void)
{
    char     TxBuffer[] = "QSPI read and write flash example: Welcome to use HDSC micro chip";
    uint8_t *pFlashReadAddr;

    stc_qspi_comm_protocol_t stcQspiCommProtocol;

    /* configure structure initialization */
    MEM_ZERO_STRUCT(stcQspiCommProtocol);

    /* Switch to standard read mode */
    stcQspiCommProtocol.enReadMode = QspiReadModeStandard;
    QSPI_CommProtocolConfig(&stcQspiCommProtocol);

    /* Erase sector */
    QSPI_FLASH_Erase4KbSector(0);

    /* Write data to flash */
    QSPI_FLASH_WritePage(0, (uint8_t *)&TxBuffer[0], sizeof(TxBuffer));

    /* Switch to four wire i/o fast read mode */
    stcQspiCommProtocol.enReadMode = QspiReadModeFourWiresIO;
    QSPI_CommProtocolConfig(&stcQspiCommProtocol);

    /* Pointer to flash address map */
    pFlashReadAddr = (uint8_t *)((uint32_t)QSPI_BUS_ADDRESS + 0);

    if(memcmp(TxBuffer, pFlashReadAddr, sizeof(TxBuffer)) != 0)
    {
        printf("\r\nQSPI Write And Read Failed!!!");
    }
    else
    {
        printf("\r\nQSPI Write And Read Success!\r\n");
    }
}


/******************* (C) COPYRIGHT 2021 *************************END OF FILE***/

 

运行结果:

23.png

工程源码:

Project_QSPI_Flash.zip (845.78 KB)
(下载次数: 44, 2021-5-7 21:39 上传)

 

 

 

We are a team and we work as a team !

回复评论 (5)

QSPI Write And Read Success!

 

点赞  2021-5-7 21:57

谢谢分享,期待后续

默认摸鱼,再摸鱼。2022、9、28
点赞  2021-5-8 09:34
谢谢分享,程序如果有注释更好
点赞  2021-5-8 15:20

qspi会比spi难上一些,原理虽然懂,但是编程的配置会复杂许多!

点赞  2021-5-9 15:44

谢谢分享。

点赞  2021-5-10 14:28
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复