本帖最后由 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如果点亮了,通过多次按键不会看出来有什么区别,因为都是同一个灯在点亮,所以我把程序作了一点修改,通过按键来翻转灯的亮灭,这样的话就可以判断出来是什么问题,而且显示的效果也比常亮好
下面是实验的图片
这个是上电后的状态,一直在等待按键按下,所以没有灯点亮
这是按键按下后,判断读取到的数据和写入的数据是一样的,点亮了这个灯,如果再次按下的话这个灯会熄灭
好了,今天就到这里,以后看有什么新的实验,我再发上来和大家一起分享,关于代码中如果有哪里不清楚的可以在本帖下方留言,我看到后会及时回复大家
|