本帖最后由 binoo7 于 2021-2-8 15:39 编辑
@小跑堂
第一期测评用了GPIO,这次我们来说说QSPI。其实前几天写过一篇原创的**就是关于QSPI的,那篇**是对华大HC32F460的简介及配置的一个说明。
今天是来做一个QSPI的实验,在做实验之前,再说一下关于华大QSPI的通讯,
首先,与其他的芯片的QSPI类似,华大的QSPI支持的通讯方式有两种,按照手册的说法,
一种叫做直接通讯,
一种叫做ROM映射,
这两个通讯方式有什么区别呢?
首先说ROM映射,这方面的**我找到的资料比较少,只能根据自己的实验过程理解和大家做一个分享,还望大家海涵。
ROM映射是把片外的flash映射到内存中,可以和读取内存类似的访问片外的flash,
在访问的时候有多种的访问方式:
• 支持多种读取方式
a.标准读/快速读
b.二线式输出快速读取/二线式输入输出快速读取
c.四线式输出快速读取/四线式输入输出快速读取
• 自由设置指令
• 数量可调的虚拟周期
• 16字节的预读取功能
• 状态查询功能
• SPI总线周期延长功能
• XIP控制功能
而且,相关的寄存器也比较丰富
几乎是除了直接通讯指令寄存器以外的,他都可以设置了
ROM映射的好处是,读取数据是硬件自己完成的,不需要额外的在写读取的函数,这样直接访问响应的地址,就可以读取到片外的存储的数据了
如下图所示
看到红色方框内的内容了吧,这就是关键的地方,内存映射的起始地址是固定的,也就是可以访问这么多的内存空间,
在应用中这样就可以直接读取到地址中的数据了
看到图片中绿色的部分了吗?读取数据真的挺方便的
写入数据不可以直接对该地址进行赋值,需要通过特定的函数进行写入的操作。
是不是感觉很神奇呢,和SPI一比是不是有很大的优势啊
写入和读取都可以通过4根数据线同时进行,这样读写的速度会大大提高说完了ROM映射的功能,下面说说直接通讯的方式。
QSPI 可以通过自动将 MCU 的外部 ROM 读取总线周期转换为 QSPI 总线周期来对串行闪存进行读取。
但串行闪存还有很多不同的追加功能,诸如 ID 信息读取,擦除,写入及状态信息读取等。
这些功能并没有一套标准的指令来进行设置,并且随着串行闪存新功能的迅速增加,硬件层面上的对应变得愈发的困难。
针对这种情况,QSPI 提供了直接通信模式,用户可通过软件直接对串行闪存进行控制。
由此模式软件可以产生任意所需的 QSPI 总线周期。
将 QSCR 寄存器的 DCOME 位设成 1 可以进入直接通信模式。一旦进入直接通信模式,将无法进行通常的闪存读取操作,
如果要进行常规的闪存读取,需要将 DCOME位清零退出直接通信模式。
注意:
- 如果处于 XIP 模式,则需要先退出 XIP 模式再启动直接通信模式。
- 在直接通信模式下是无法对 QSCR 和 QSDCOM 以外的寄存器进行写操作的。对其他寄存器
的写操作将会退出直接通信模式。
下面看一下实验的代码,方式通过的是直接内存映射的方式来做的实验
- #include "hc32_ddl.h"
- /*******************************************************************************
- * Local type definitions ('typedef')
- ******************************************************************************/
- /*******************************************************************************
- * Local pre-processor symbols/macros ('#define')
- ******************************************************************************/
- /* LED0 Port/Pin definition */
- #define LED0_PORT (PortE)
- #define LED0_PIN (Pin06)
- #define LED0_ON() (PORT_SetBits(LED0_PORT, LED0_PIN))
- #define LED0_OFF() (PORT_ResetBits(LED0_PORT, LED0_PIN))
- #define LED0_TOGGLE() (PORT_Toggle(LED0_PORT, LED0_PIN))
- /* LED1 Port/Pin definition */
- #define LED1_PORT (PortA)
- #define LED1_PIN (Pin07)
- #define LED1_ON() (PORT_SetBits(LED1_PORT, LED1_PIN))
- #define LED1_OFF() (PORT_ResetBits(LED1_PORT, LED1_PIN))
- #define LED1_TOGGLE() (PORT_Toggle(LED1_PORT, LED1_PIN))
- /* KEY0 Port/Pin definition */
- #define KEY0_PORT (PortD)
- #define KEY0_PIN (Pin03)
- /* QSPCK Port/Pin definition */
- #define QSPCK_PORT (PortC)
- #define QSPCK_PIN (Pin06)
- /* QSNSS Port/Pin definition */
- #define QSNSS_PORT (PortC)
- #define QSNSS_PIN (Pin07)
- /* QSIO0 Port/Pin definition */
- #define QSIO0_PORT (PortD)
- #define QSIO0_PIN (Pin08)
- /* QSIO1 Port/Pin definition */
- #define QSIO1_PORT (PortD)
- #define QSIO1_PIN (Pin09)
- /* QSIO2 Port/Pin definition */
- #define QSIO2_PORT (PortD)
- #define QSIO2_PIN (Pin10)
- /* QSIO3 Port/Pin definition */
- #define QSIO3_PORT (PortD)
- #define QSIO3_PIN (Pin11)
- /* 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)
- /*******************************************************************************
- * Global variable definitions (declared in header file with 'extern')
- ******************************************************************************/
- /*******************************************************************************
- * Local function prototypes ('static')
- ******************************************************************************/
- /*******************************************************************************
- * Local variable definitions ('static')
- ******************************************************************************/
- static uint8_t u8ExIntFlag = 0u;
- /*******************************************************************************
- * Function implementation - global ('extern') and local ('static')
- ******************************************************************************/
- /**
- *******************************************************************************
- ** \brief ExtInt3 callback function
- **
- ** \param [in] None
- **
- ** \retval None
- **
- ******************************************************************************/
- void ExtInt03_Callback(void)
- {
- if (Set == EXINT_Irq**Get(ExtiCh03))
- {
- u8ExIntFlag = 1u;
- EXINT_Irq**Clr(ExtiCh03);
- }
- }
- /**
- *******************************************************************************
- ** \brief KEY0(SW2) init function
- **
- ** \param [in] None
- **
- ** \retval None
- **
- ******************************************************************************/
- void Sw2_Init(void)
- {
- stc_port_init_t stcPortInit;
- stc_exint_config_t stcExtiConfig;
- stc_irq_regi_conf_t stcIrqRegiConf;
- /* configure structure initialization */
- MEM_ZERO_STRUCT(stcPortInit);
- MEM_ZERO_STRUCT(stcExtiConfig);
- MEM_ZERO_STRUCT(stcIrqRegiConf);
- /* Set PD03 as External Int Ch.3 input */
- stcPortInit.enExInt = Enable;
- PORT_Init(KEY0_PORT, KEY0_PIN, &stcPortInit);
- stcExtiConfig.enExitCh = ExtiCh03;
- /* Filter setting */
- stcExtiConfig.enFilterEn = Enable;
- stcExtiConfig.enFltClk = Pclk3Div8;
- /* Both edge */
- stcExtiConfig.enExtiLvl = ExIntFallingEdge;
- EXINT_Init(&stcExtiConfig);
- /* Select External Int Ch.3 */
- stcIrqRegiConf.enIntSrc = INT_PORT_EIRQ3;
- /* Register External Int to Vect.No.007 */
- stcIrqRegiConf.enIRQn = Int007_IRQn;
- /* Callback function */
- stcIrqRegiConf.pfnCallback = &ExtInt03_Callback;
- /* Registration IRQ */
- enIrqRegistration(&stcIrqRegiConf);
- /* Clear pending */
- NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
- /* Set priority */
- NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);
- /* Enable NVIC */
- NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
- }
- /**
- *******************************************************************************
- ** \brief QSPI flash init function
- **
- ** \param [in] None
- **
- ** \retval None
- **
- ******************************************************************************/
- void QspiFlash_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(QSPCK_PORT, QSPCK_PIN, Func_Qspi, Disable);
- PORT_SetFunc(QSNSS_PORT, QSNSS_PIN, Func_Qspi, Disable);
- PORT_SetFunc(QSIO0_PORT, QSIO0_PIN, Func_Qspi, Disable);
- PORT_SetFunc(QSIO1_PORT, QSIO1_PIN, Func_Qspi, Disable);
- PORT_SetFunc(QSIO2_PORT, QSIO2_PIN, Func_Qspi, Disable);
- PORT_SetFunc(QSIO3_PORT, QSIO3_PIN, 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 QSPI flash write enable function
- **
- ** \param [in] None
- **
- ** \retval None
- **
- ******************************************************************************/
- void QspiFlash_WriteEnable(void)
- {
- QSPI_EnterDirectCommMode();
- QSPI_WriteDirectCommValue(FLASH_INSTR_WRITE_ENABLE);
- QSPI_ExitDirectCommMode();
- }
- /**
- *******************************************************************************
- ** \brief QSPI flash wait for write operation end function
- **
- ** \param [in] None
- **
- ** \retval Ok Flash internal operation finish
- ** \retval ErrorTimeout Flash internal operation timeout
- **
- ******************************************************************************/
- en_result_t QspiFlash_WaitForWriteEnd(void)
- {
- en_result_t enRet = Ok;
- uint8_t u8Status = 0u;
- uint32_t u32Timeout;
- stc_clk_freq_t stcClkFreq;
- CLK_GetClockFreq(&stcClkFreq);
- u32Timeout = stcClkFreq.sysclkFreq / 1000u;
- QSPI_EnterDirectCommMode();
- QSPI_WriteDirectCommValue(FLASH_INSTR_READ_SR1);
- do
- {
- u8Status = QSPI_ReadDirectCommValue();
- u32Timeout--;
- } while ((u32Timeout != 0u) &&
- ((u8Status & FLASH_BUSY_BIT_MASK) == FLASH_BUSY_BIT_MASK));
- if (FLASH_BUSY_BIT_MASK == u8Status)
- {
- enRet = ErrorTimeout;
- }
- QSPI_ExitDirectCommMode();
- return enRet;
- }
- /**
- *******************************************************************************
- ** \brief QSPI flash page write program function
- **
- ** \param [in] u32Addr Valid flash address
- **
- ** \param [in] pData Pointer to send data buffer
- **
- ** \param [in] len Send data length
- **
- ** \retval Error Page write program failed
- ** \retval Ok Page write program success
- **
- ******************************************************************************/
- en_result_t QspiFlash_WritePage(uint32_t u32Addr, const uint8_t pData[], uint16_t len)
- {
- en_result_t enRet = Ok;
- uint16_t u16Index = 0u;
- if ((u32Addr > FLASH_MAX_ADDR) || (NULL == pData) || (len > FLASH_PAGE_SIZE))
- {
- enRet = Error;
- }
- else
- {
- QspiFlash_WriteEnable();
- /* Send data to flash */
- QSPI_EnterDirectCommMode();
- QSPI_WriteDirectCommValue(FLASH_INSTR_PAGE_PROGRAM);
- QSPI_WriteDirectCommValue((uint8_t)((u32Addr & 0xFF0000ul) >> 16));
- QSPI_WriteDirectCommValue((uint8_t)((u32Addr & 0xFF00u) >> 8));
- QSPI_WriteDirectCommValue((uint8_t)(u32Addr & 0xFFu));
- while (len--)
- {
- QSPI_WriteDirectCommValue(pData[u16Index]);
- u16Index++;
- }
- QSPI_ExitDirectCommMode();
- /* Wait for flash idle */
- enRet = QspiFlash_WaitForWriteEnd();
- }
- return enRet;
- }
- /**
- *******************************************************************************
- ** \brief QSPI flash erase 4Kb sector function
- **
- ** \param [in] u32Addr Valid flash address
- **
- ** \retval Error Sector erase failed
- ** \retval Ok Sector erase success
- **
- ******************************************************************************/
- en_result_t QspiFlash_Erase4KbSector(uint32_t u32Addr)
- {
- en_result_t enRet = Ok;
- if (u32Addr >= FLASH_MAX_ADDR)
- {
- enRet = Error;
- }
- else
- {
- QspiFlash_WriteEnable();
- /* Send instruction to flash */
- QSPI_EnterDirectCommMode();
- QSPI_WriteDirectCommValue(FLASH_INSTR_ERASE_4KB_SECTOR);
- QSPI_WriteDirectCommValue((uint8_t)((u32Addr & 0xFF0000ul) >> 16));
- QSPI_WriteDirectCommValue((uint8_t)((u32Addr & 0xFF00u) >> 8));
- QSPI_WriteDirectCommValue((uint8_t)(u32Addr & 0xFFu));
- QSPI_ExitDirectCommMode();
- /* Wait for flash idle */
- enRet = QspiFlash_WaitForWriteEnd();
- }
- return enRet;
- }
- /**
- *******************************************************************************
- ** \brief QSPI flash erase chip function
- **
- ** \param [in] None
- **
- ** \retval None
- **
- ******************************************************************************/
- void QspiFlash_EraseChip(void)
- {
- QspiFlash_WriteEnable();
- /* Send instruction to flash */
- QSPI_EnterDirectCommMode();
- QSPI_WriteDirectCommValue(FLASH_INSTR_ERASE_CHIP);
- QSPI_ExitDirectCommMode();
- /* Wait for flash idle */
- QspiFlash_WaitForWriteEnd();
- }
- /**
- *******************************************************************************
- ** \brief QSPI flash read status register function
- **
- ** \param [in] u8Reg Need to get status register
- ** \arg FLASH_INSTR_READ_SR1 Status register 1
- ** \arg FLASH_INSTR_READ_SR2 Status register 2
- ** \arg FLASH_INSTR_READ_SR3 Status register 3
- **
- ** \retval uint8_t Current register value
- **
- ******************************************************************************/
- uint8_t QspiFlash_ReadStatusRegister(uint8_t u8Reg)
- {
- uint8_t regSta = 0u;
- QSPI_EnterDirectCommMode();
- QSPI_WriteDirectCommValue(u8Reg);
- regSta = QSPI_ReadDirectCommValue();
- QSPI_ExitDirectCommMode();
- return regSta;
- }
- /**
- *******************************************************************************
- ** \brief System clock init function
- **
- ** \param [in] None
- **
- ** \retval None
- **
- ******************************************************************************/
- void SystemClk_Init(void)
- {
- stc_clk_sysclk_cfg_t stcSysClkCfg;
- stc_clk_xtal_cfg_t stcXtalCfg;
- stc_clk_mpll_cfg_t stcMpllCfg;
- MEM_ZERO_STRUCT(stcSysClkCfg);
- MEM_ZERO_STRUCT(stcXtalCfg);
- MEM_ZERO_STRUCT(stcMpllCfg);
- /* Set bus clk div. */
- stcSysClkCfg.enHclkDiv = ClkSysclkDiv1; // 168MHz
- stcSysClkCfg.enExclkDiv = ClkSysclkDiv2; // 84MHz
- stcSysClkCfg.enPclk0Div = ClkSysclkDiv1; // 168MHz
- stcSysClkCfg.enPclk1Div = ClkSysclkDiv2; // 84MHz
- stcSysClkCfg.enPclk2Div = ClkSysclkDiv4; // 42MHz
- stcSysClkCfg.enPclk3Div = ClkSysclkDiv4; // 42MHz
- stcSysClkCfg.enPclk4Div = ClkSysclkDiv2; // 84MHz
- CLK_SysClkConfig(&stcSysClkCfg);
- /* Switch system clock source to MPLL. */
- /* Use Xtal as MPLL source. */
- stcXtalCfg.enMode = ClkXtalModeOsc;
- stcXtalCfg.enDrv = ClkXtalLowDrv;
- stcXtalCfg.enFastStartup = Enable;
- CLK_XtalConfig(&stcXtalCfg);
- CLK_XtalCmd(Enable);
- /* MPLL config. */
- stcMpllCfg.pllmDiv = 1u;
- stcMpllCfg.plln = 42u;
- stcMpllCfg.PllpDiv = 2u;
- stcMpllCfg.PllqDiv = 2u;
- stcMpllCfg.PllrDiv = 2u;
- CLK_SetPllSource(ClkPllSrcXTAL);
- CLK_MpllConfig(&stcMpllCfg);
- /* flash read wait cycle setting */
- EFM_Unlock();
- EFM_SetLatency(EFM_LATENCY_4);
- EFM_Lock();
- /* Enable MPLL. */
- CLK_MpllCmd(Enable);
- /* Wait MPLL ready. */
- while (Set != CLK_GetFlagStatus(ClkFlagMPLLRdy))
- {
- }
- /* Switch system clock source to MPLL. */
- CLK_SetSysClkSource(CLKSysSrcMPLL);
- }
- /**
- *******************************************************************************
- ** \brief main function for QSPI four wire i/o fast read function
- **
- ** \param [in] None
- **
- ** \retval int32_t Return value, if needed
- **
- ******************************************************************************/
- int32_t main(void)
- {
- uint32_t flashAddr = 0u;
- uint8_t *pFlashReadAddr;
- uint16_t bufferLen = 0u;
- char txBuffer[] = "QSPI read and write flash example: Welcome to use HDSC micro chip";
- stc_port_init_t stcPortInit;
- stc_qspi_comm_protocol_t stcQspiCommProtocol;
- /* configure structure initialization */
- MEM_ZERO_STRUCT(stcPortInit);
- MEM_ZERO_STRUCT(stcQspiCommProtocol);
- /* Configure system clock frequency */
- SystemClk_Init();
- /* LED0 Port/Pin initialization */
- LED0_OFF();
- stcPortInit.enPinMode = Pin_Mode_Out;
- PORT_Init(LED0_PORT, LED0_PIN, &stcPortInit);
- /* LED1 Port/Pin initialization */
- LED1_OFF();
- stcPortInit.enPinMode = Pin_Mode_Out;
- PORT_Init(LED1_PORT, LED1_PIN, &stcPortInit);
- /* Key0 Port/Pin initialization */
- Sw2_Init();
- /* Flash initialization */
- QspiFlash_Init();
- /* Get tx buffer length */
- bufferLen = (uint16_t)sizeof(txBuffer);
- while (1)
- {
- if (1u == u8ExIntFlag)
- {
- u8ExIntFlag = 0u;
- // LED0_OFF();
- // LED1_OFF();
- /* Switch to standard read mode */
- stcQspiCommProtocol.enReadMode = QspiReadModeStandard;
- QSPI_CommProtocolConfig(&stcQspiCommProtocol);
- /* Erase sector */
- QspiFlash_Erase4KbSector(flashAddr);
- /* Write data to flash */
- QspiFlash_WritePage(flashAddr, (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 + flashAddr);
- /* Compare txBuffer and flash */
- if (memcmp(txBuffer, pFlashReadAddr, (uint32_t)bufferLen) != 0)
- {
- LED0_TOGGLE();
- }
- else
- {
- LED1_TOGGLE();
- }
- /* Flash address offset */
- flashAddr += FLASH_SECTOR_SIZE;
- if (flashAddr >= FLASH_MAX_ADDR)
- {
- flashAddr = 0u;
- }
- }
- }
- }
- /*******************************************************************************
- * EOF (not truncated)
- ******************************************************************************/
在给的例程中是判断读取到的数据和写入的数据是否一样来点亮LED0或者LED1,这样的话应用起来效果不明显,因为LED1如果点亮了,通过多次按键不会看出来有什么区别,因为都是同一个灯在点亮,所以我把程序作了一点修改,通过按键来翻转灯的亮灭,这样的话就可以判断出来是什么问题,而且显示的效果也比常亮好
下面是实验的图片
这个是上电后的状态,一直在等待按键按下,所以没有灯点亮
这是按键按下后,判断读取到的数据和写入的数据是一样的,点亮了这个灯,如果再次按下的话这个灯会熄灭
好了,今天就到这里,以后看有什么新的实验,我再发上来和大家一起分享,关于代码中如果有哪里不清楚的可以在本帖下方留言,我看到后会及时回复大家
|