5.33 XSPI 使用说明
本章说明 N32H49x 标准外设库中 XSPI 驱动的文件组成与 34 个公开函数,以及 XSPI_DUALQUAD、XSPI_QUAD、XSPI_QUAD_DMA 3 个示例。XSPI 支持标准、双线、四线、八线 SPI 通信与内存映射(XIP)访问外扩 Flash,间接模式的 FIFO 收发、中断与 DMA 请配合用户手册的“多线串行外设接口(xSPI)”章节阅读。
5.33.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_xspi.h |
定义帧格式、传输模式、FIFO 阈值、中断与 DMA 选择等宏与 XSPI_InitType 结构,声明 34 个公开函数 |
firmware/n32h49x_std_periph_driver/src/n32h49x_xspi.c |
实现上述 34 个函数:复位与初始化、传输类型与等待周期、FIFO 阈值与数据量、数据收发与状态标志、中断与 DMA 配置 |
projects/n32h49x_EVAL/examples/XSPI/ |
XSPI 示例根目录,含 XSPI_DUALQUAD、XSPI_QUAD、XSPI_QUAD_DMA 3 个示例 |
projects/n32h49x_EVAL/examples/XSPI/XSPI_QUAD/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32h49x_EVAL/bsp/ |
该示例引用的公共板级文件:串口日志函数 |
5.33.2 API 参考
以下 34 个公开函数按 n32h49x_xspi.h 的声明顺序分为 7 组;每个条目给出功能摘要与 n32h49x_xspi.c 中该函数正上方的注释原文。
复位与初始化
XSPI_DeInit、XSPI_Init、XSPI_StructInit、XSPI_Cmd、XSPI_XIP_Cmd
传输类型与等待周期
XSPI_SetTransType、XSPI_SetWaitCycles
FIFO 阈值与数据量
XSPI_SetRXFIFOLevel、XSPI_SetTXFIFOLevel、XSPI_SetTXFIFOStartLevel、XSPI_GetRXFIFOLevel、XSPI_GetTXFIFOLevel、XSPI_ReadRxFifoNum、XSPI_ReadTxFifoNum
数据收发与状态标志
XSPI_GetDataPointer、XSPI_SendData、XSPI_ReceiveData、XSPI_GetFlagStatus、XSPI_GetBusyStatus、XSPI_GetTxDataBusyStatus、XSPI_GetTxDataEmptyStatus、XSPI_GetRxHaveDataStatus、XSPI_GetRxDataFullStatus、XSPI_GetDataConflictErrorStatus
中断
XSPI_GetINTStatus、XSPI_ClearITBit、XSPI_ConfigInt
DMA
XSPI_ConfigDMATxLevel、XSPI_ConfigDMARxLevel、XSPI_EnableDMA
FIFO 数据搬运
XSPI_ClrFifo、XSPI_GetFifoData、XSPI_SendAndGetWords、XSPI_SendWordAndGetWords
void XSPI_DeInit(void)将 XSPI 外设寄存器恢复为默认复位值。
name XSPI_DeInit.
fun Deinitializes the XSPI peripheral registers to its default reset values.
return none
void XSPI_Init(XSPI_InitType* XSPI_InitStruct)合并 XSPI 参数结构体缓冲区中的配置,并写入相关寄存器。
name XSPIInitConfig.
fun Merge configuration from the buffer of XSPI para struct, then write it into related registers.
param XSPI_InitStruct :
- FRF
- SPIFRF
- TMOD
- SCPOL
- SCPH
- DFS
- SSTE
- SEN
- CLKDIV
- WAITCYCLES
- INST_L
- ADDRL
- NDF
- TXFT
- RXFT
- ENHANCED_WAITCYCLES
- ENHANCED_CLKSTREN
- ENHANCED_ADDR_L
- ENHANCED_INST_L
- ENHANCED_TRANSTYPE
- ENHANCED_WRSPIDDREN;
- ENHANCED_WRINDDREN;
- XIP_FRF
- XIP_TRANSTYPE
- XIP_ADDRL
- XIP_INST_L
- XIP_WAITCYCLES
- XIPDFSHC
- XIPINSTEN
- XIPCTEN
- XIPINSTEN
- XIPMBL
- XIP_WRINDDREN
- XIP_DDREN
- XIPDFSHC
- XIPMDBITS
- ITOC
- WTOC
- XTOUT
- CFS
- MWMOD
- MCDIR
- MHSEN
- TXFN
- RXFN
- SDCN
- SES
- TXDE
return none
使用提示: 源码注释的 name 字段写作 XSPIInitConfig.,与函数名 XSPI_Init 不一致,按函数名调用即可。
void XSPI_StructInit(XSPI_InitType* XSPI_InitStruct)把 XSPI_InitStruct 的每个成员填充为默认值。
name XSPI_StructInit.
fun Fills each XSPI_InitStruct member with its default value.
param XSPI_InitStruct :
- FRF
- SPIFRF
- TMOD
- SCPOL
- SCPH
- DFS
- SSTE
- SEN
- CLKDIV
- WAITCYCLES
- INST_L
- ADDRL
- NDF
- TXFT
- RXFT
- ENHANCED_WAITCYCLES
- ENHANCED_CLKSTREN
- ENHANCED_ADDR_L
- ENHANCED_INST_L
- ENHANCED_TRANSTYPE
- ENHANCED_WRSPIDDREN;
- ENHANCED_WRINDDREN;
- XIP_FRF
- XIP_TRANSTYPE
- XIP_ADDRL
- XIP_INST_L
- XIP_WAITCYCLES
- XIPDFSHC
- XIPINSTEN
- XIPCTEN
- XIPINSTEN
- XIPMBL
- XIP_WRINDDREN
- XIP_DDREN
- XIPDFSHC
- XIPMDBITS
- ITOC
- WTOC
- XTOUT
- CFS
- MWMOD
- MCDIR
- MHSEN
- TXFN
- RXFN
- SDCN
- SES
- TXDE
return none
void XSPI_Cmd(FunctionalState cmd)控制 XSPI 功能开关。
name XSPI_Cmd.
fun Control XSPI function switch.
param cmd
- ENABLE
- DISABLE
return none
void XSPI_XIP_Cmd(FunctionalState cmd)控制 XSPI XIP 功能开关。
name XSPI_XIP_Cmd.
fun Control XSPI XIP function switch.
param cmd
- ENABLE
- DISABLE
return none
void XSPI_SetTransType(uint32_t TransType)设置地址与指令的发送格式(1/2/4/8 线模式)。
name XSPI_SetTransType.
fun Set the format of address and instruction sending (1/2/4/8 wire mode)
param TransType
- XSPI_ENH_CTRL0_TRANSTYPE_STANDARD
- XSPI_ENH_CTRL0_TRANSTYPE_ADDRESS_BY_FRF
- XSPI_ENH_CTRL0_TRANSTYPE_ALL_BY_FRF
return none
void XSPI_SetWaitCycles(uint32_t WAITCYCLES)设置需要等待的空指令周期数。
name XSPI_SetWaitCycles.
fun Set the number of empty instruction cycles to wait
param WAITCYCLES:
- XSPI_ENH_CTRL0_WAIT_1CYCLES
- ...
- XSPI_ENH_CTRL0_WAIT_31CYCLES
return none
void XSPI_SetRXFIFOLevel(uint32_t fifo_len)设置接收 FIFO 传输阈值,即触发接收满中断的阈值。
name XSPI_SetRXFIFOLevel.
fun Set Rx fifo transmission threshold, trigger full interrupt threshold
param fifo_len:
- 0x0-0xF
return none
void XSPI_SetTXFIFOLevel(uint32_t fifo_len)设置发送 FIFO 传输阈值,并触发相应中断阈值。
name XSPI_SetTXFIFOLevel.
fun Set the Tx fifo transmission threshold and trigger the interrupt threshold
param fifo_len:
- 0x0-0xF
return none
void XSPI_SetTXFIFOStartLevel(uint32_t fifo_len)设置发送 FIFO 启动传输阈值。
name XSPI_SetTXFIFOStartLevel.
fun Set Tx fifo start transmission threshold
param fifo_len:
- 0x0-0xF
return none
uint8_t XSPI_GetRXFIFOLevel(void)获取接收 FIFO 中的数据量。
name XSPI_GetRXFIFOLevel.
fun Get the amount of data in the Rx fifo
return Amount of data in Rx fifo.
uint8_t XSPI_GetTXFIFOLevel(void)获取发送 FIFO 中的数据量。
name XSPI_GetTXFIFOLevel.
fun Get the amount of data in the Tx fifo
return Amount of data in Tx fifo.
uint32_t XSPI_GetDataPointer(void)获取 XSPI DAT0 寄存器的指针。
name XSPIGetDataPointer.
fun Get Pointer of XSPI DAT0 register.
return The pointer of XSPI DAT0 register.
使用提示: 源码注释的 name 字段写作 XSPIGetDataPointer.,与函数名 XSPI_GetDataPointer 不一致,按函数名调用即可。
uint32_t XSPI_ReadRxFifoNum(void)读取 XSPI RXFN 寄存器的值,该值表示接收 FIFO 中的数据个数。
name XSPI_ReadRxFifoNum.
fun Read value from XSPI RXFN register which shows the number of the data from receive fifo.
return The number of the data from receive fifo.
uint32_t XSPI_ReadTxFifoNum(void)读取 XSPI TXFN 寄存器的值,该值表示发送 FIFO 中的数据个数。
name XSPI_ReadTxFifoNum.
fun Read value from XSPI TXFN register which shows the number of the data from tx fifo.
return The number of the data from tx fifo.
void XSPI_SendData(uint32_t SendData)直接向 XSPI DAT0 寄存器写入一个数据以发送。
name XSPI_SendData.
fun Write one data direct to XSPI DAT0 register to send.
param SendData:
- data to be send
return none
uint32_t XSPI_ReceiveData(void)从 XSPI DAT0 寄存器读取一个数据。
name XSPI_ReceiveData.
fun Read one data from XSPI DAT0 register.
return The value of XSPI DAT0 register.
FlagStatus XSPI_GetFlagStatus(uint32_t XSPI_FLAG)检查指定的 XSPI 标志是否置位。
name XSPI_GetFlagStatus.
fun Checks whether the specified XSPI flag is set or not.
param XSPI_FLAG:
- XSPI_STS_BUSY
- XSPI_STS_TXFNF
- XSPI_STS_TXFE
- XSPI_STS_RXFNE
- XSPI_STS_RXFF
- XSPI_STS_TXE
- XSPI_STS_DCERR
return The new state of XSPI_FLAG (SET or RESET).
uint16_t XSPI_GetINTStatus(uint16_t FLAG)检查指定的 XSPI 中断是否发生。
name XSPI_GetINTStatus.
fun Checks whether the specified XSPI interrupt has occurred or not.
param FLAG:
- XSPI_ISTS_TXFEIS
- XSPI_ISTS_TXFOIS
- XSPI_ISTS_RXFUIS
- XSPI_ISTS_RXFOIS
- XSPI_ISTS_RXFFIS
- XSPI_ISTS_MMCIS
- XSPI_ISTS_XRXOIS
- XSPI_ISTS_TXUIS
return The new state of XSPI_IT (SET or RESET).
void XSPI_ClearITBit(uint16_t XSPI_IT)检查指定的 XSPI 中断是否发生。
name XSPI_ClearITBit.
fun Checks whether the specified XSPI interrupt has occurred or not.
param XSPI_IT:
- XSPI_IT_TXUIM
- XSPI_IT_XRXOIM
- XSPI_IT_MMCIM
- XSPI_IT_RXFFIM
- XSPI_IT_RXFOIM
- XSPI_IT_RXFUIM
- XSPI_IT_TXFOIM
- XSPI_IT_TXFEIM
return none.
void XSPI_ConfigInt(uint16_t XSPI_IT, FunctionalState Cmd)使能或禁止指定的 XSPI 中断。
name XSPI_ConfigInt.
fun Enables or disables the specified XSPI interrupts.
param XSPI_IT:
- XSPI_IT_TXUIM
- XSPI_IT_XRXOIM
- XSPI_IT_MMCIM
- XSPI_IT_RXFFIM
- XSPI_IT_RXFOIM
- XSPI_IT_RXFUIM
- XSPI_IT_TXFOIM
- XSPI_IT_TXFEIM
param Cmd:
- ENABLE
- DISABLE
return none.
void XSPI_ConfigDMATxLevel(uint32_t TxDataLevel)配置 XSPI DMA 发送阈值。
name XSPI_ConfigDMATxLevel.
fun Configuration of XSPI DMA Tx Level.
param TxDataLevel(transmit data):
- 0~0x1F
return none.
void XSPI_ConfigDMARxLevel(uint32_t RxDataLevel)配置 XSPI DMA 接收阈值。
name XSPI_ConfigDMARxLevel.
fun Configuration of XSPI DMA Rx Level.
param RxDataLevel(receive data):
- 0~0x1F
return none.
void XSPI_EnableDMA(uint32_t XSPI_DMAReq, FunctionalState Cmd)使能或禁止 XSPI 的 DMA 接口。
name XSPI_EnableDMA
fun Enables or disables the XSPI's DMA interface.
param XSPI_DMAReq (The input parameters must be the following values):
- XSPI_DMAREQ_TX XSPI DMA transmit request
- XSPI_DMAREQ_RX XSPI DMA receive request
param Cmd (The input parameters must be the following values):
- ENABLE
- DISABLE
return none
note Configured when xSPI_EN.XSPIEN disabled
FlagStatus XSPI_GetBusyStatus(void)获取 XSPI 状态,判断是否忙。
name XSPI_GetBusyStatus.
fun Get XSPI status,busy or not.
return SET:XSPI busy;
RESET:XSPI idle.
FlagStatus XSPI_GetTxDataBusyStatus(void)检查发送 FIFO 是否已满。
name XSPI_GetTxDataBusyStatus.
fun Check transmit fifo full or not.
return SET: Transmit fifo full;
RESET: Transmit fifo not full.
FlagStatus XSPI_GetTxDataEmptyStatus(void)检查发送 FIFO 是否为空。
name XSPI_GetTxDataEmptyStatus.
fun Check transmit fifo empty or not.
return SET: Transmit fifo empty;
RESET: Transmit fifo not empty.
FlagStatus XSPI_GetRxHaveDataStatus(void)检查接收 FIFO 是否有数据。
name XSPI_GetRxHaveDataStatus.
fun Check receive fifo have data or not.
return SET:Receive fifo have data;
RESET:Receive fifo empty.
FlagStatus XSPI_GetRxDataFullStatus(void)检查接收 FIFO 是否已满。
name XSPI_GetRxDataFullStatus.
fun Check receive fifo full or not.
return SET: Receive fifo full;
RESET: Receive fifo not full.
FlagStatus XSPI_GetDataConflictErrorStatus(void)检查是否发生数据冲突错误。
name XSPI_GetDataConflictErrorStatus.
fun Check data conflict error or not.
return SET: Data conflict error;
RESET: No data conflict error.
void XSPI_ClrFifo(void)读取 DAT0 寄存器以清空 FIFO。
name ClrFifo.
fun Read DAT0 register to clear fifo.
return none.
使用提示: 源码注释的 name 字段写作 ClrFifo.,与函数名 XSPI_ClrFifo 不一致,按函数名调用即可。
uint32_t XSPI_GetFifoData(uint32_t* pData, uint32_t Len)从 FIFO 中获取数据。
name GetFifoData
fun Get data from fifo.
param pData pointer to buffer of getting fifo data.
param Len length of getting fifo data.
return XSPI_SUCCESS or XSPI_NULL.
使用提示: 源码注释的 name 字段写作 GetFifoData,与函数名 XSPI_GetFifoData 不一致,按函数名调用即可。
void XSPI_SendAndGetWords(uint32_t* pSrcData, uint32_t* pDstData, uint32_t cnt)从源数据缓冲区发送数据,并把返回的数据取回目标数据缓冲区。
name XSPISendAndGetWords.
fun Send words out from source data buffer and get returned datas into destination data buffer.
param pSrcData:
- pSrcData pointer to buffer of sending datas.
param pDstData:
- pDstData pointer to buffer of getting returned datas.
param cnt:
- cnt number of sending datas.
return none.
使用提示: 源码注释的 name 字段写作 XSPISendAndGetWords.,与函数名 XSPI_SendAndGetWords 不一致,按函数名调用即可。
uint32_t XSPI_SendWordAndGetWords(uint32_t WrData, uint32_t* pRdData, uint8_t LastRd)发送一个数据,并把返回的数据取回目标数据缓冲区。
name XSPISendWordAndGetWords.
fun Send one word data and get returned words into destination data buffer.
param WrData:
- WrData one word to be sent.
param pRdData:
- pRdData pointer to buffer of getting returned datas.
param LastRd:
- 1:go on to get returned datas.
- 0:end to get returned datas.
return none.
使用提示: 源码注释的 name 字段写作 XSPISendWordAndGetWords.,与函数名 XSPI_SendWordAndGetWords 不一致,按函数名调用即可。
5.33.3 Demo 示例说明
本节说明 XSPI 的 3 个示例:XSPI_DUALQUAD 用两片外置 Flash 的片选切换组合成八线数据通路,XSPI_QUAD 用四线模式读写单片外置 Flash,XSPI_QUAD_DMA 在四线模式基础上用 DMA 搬运数据。
5.33.3.1 XSPI_DUALQUAD 双片选八线读写外置 Flash
工程目录: projects/n32h49x_EVAL/examples/XSPI/XSPI_DUALQUAD/
初始化日志、时钟与 GPIO→用标准模式配置 XSPI 并逐片使能四线→分别擦除两片 Flash 并读回校验→切换片选后用八线格式写入数据→用八线格式读回并比较得出结论
| 项目 | 源码解读 |
|---|---|
| 功能 | 用两片外置 Quad Flash 通过片选切换组合为八线数据通路; 先以标准模式逐片擦除并读回校验,再切到八线格式写入并读回比较。 |
| 源码组成 | src/main.c:main、Delay;src/xspi_cfg.c:RCC_Configuration、GPIO_Configuration、XSPI_Configuration;src/spi_flash.c:XSPI_FlashREG_WR、XSPI_FlashSendData、XSPI_FlashWrite、XSPI_FlashRead;inc/main.h:长度与地址宏;readme.txt、MDK-ARM/、EWARM/:说明与工程文件。 |
| 关键配置 | 时钟:RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_XSPI, ENABLE) 与 GPIO、AFIO 时钟;帧格式:寄存器阶段用 STANDRD_SPI_FORMAT,数据阶段用 OCTAL_SPI_FORMAT;片选:由 AFIO_Config_XSPI_DualQuadMode 选择第一片或第二片 Flash;数据: DFS_32_BIT 与 CTRL1_NDF_63、CTRL1_NDF_255 控制帧长与数量。 |
| 程序与数据路径 | main 先配好时钟与引脚,再进入标准模式;循环内逐片选择 Flash,写使能、置 QE、擦除 32KB 扇区并读回校验全 0xFF;填充 XSPI_Wbuffer 后切到八线格式写入数据并读回;main 比较 XSPI_Rbuffer 与 XSPI_Wbuffer 并打印结论。 |
| 主要 API | XSPI_DeInit、XSPI_Cmd、XSPI_SendData、XSPI_ReceiveData、XSPI_GetFlagStatus |
| 验证方法 | 串口打印 Spi_flash write and read demo end - dual quad mode 表示流程正常结束;比较循环未打印 FALSH WR fail 表示写入与读回一致。 |
| 使用提示 | 双片选通过 AFIO 切换,移植时按目标板两片 Flash 的片选引脚调整; 寄存器读写阶段与数据阶段使用不同帧格式,切换时需重新调用 XSPI_Configuration;运行需插好全功能板上的跳线帽。 |
核心代码解读与移植
示例把两片外置 Quad Flash 通过片选切换组合成八线数据通路,先逐片擦除校验,再写入并读回比较。
数据路径: RCC_Configuration 与 GPIO_Configuration 配置时钟与引脚 → XSPI_Configuration 进入标准模式 → AFIO_Config_XSPI_DualQuadMode 选择第一片或第二片 Flash → 写使能、置 QE、擦除并读回校验 → 切到八线格式写入并读回 XSPI_Wbuffer → 比较 XSPI_Rbuffer 与 XSPI_Wbuffer
1. 初始化 XSPI 并选择片选。 main 先配置标准模式,再用 AFIO_Config_XSPI_DualQuadMode 在两片 Flash 之间切换片选。
/* XSPI Configuration */
XSPI_Configuration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,50);
for(flash_cnt = 0; flash_cnt < 2; flash_cnt++) // Erase flash 1 and flash 2 respectively
{
if(flash_cnt == 1)// enable flash 2 and clear buffer
{
AFIO_Config_XSPI_DualQuadMode(AFIO_XSPI_DUAL_QUAD_ENABLE);
}
else
{
AFIO_Config_XSPI_DualQuadMode(AFIO_XSPI_DUAL_QUAD_DISABLE);
}
XSPI_Configuration 直接写 CTRL0、CTRL1、BAUD 等寄存器完成配置;两片 Flash 由 AFIO 的片选选择切换,每次循环只操作被选中的一片。
2. 置 QE 使能四线模式。 在标准模式下写使能并置位状态寄存器 2 的 QE 位,使 Flash 支持四线传输。
do/*set QE*/
{
bufw[0] = SPIFLASH_Write_Reg1;
bufw[1] = 0x00;
bufw[2] = SPIFLASH_REG2CMD_SETQE;
XSPI_FlashREG_WR(bufw,bufr,3,TYPE_REG_WRITE);
bufw[0] = SPIFLASH_Read_Reg2;
XSPI_FlashREG_WR(bufw,bufr,NULL,TYPE_REG_READ);
}while(!(bufr[1]&SPIFLASH_REG2CMD_SETQE));
寄存器读写通过写指令与地址后轮询状态寄存器完成,do 与 while 循环确保 QE 位真正置位。
3. 擦除后读回校验。 逐片擦除 32KB 扇区,再用读命令把数据取回并检查是否为全 0xFF。
/*==========================Read flash===========================*/
XSPI_Wbuffer[0]=SPIFLASH_Read_Data;
XSPI_Wbuffer[1]=(flash_addr & 0xff0000) >> 16;;
XSPI_Wbuffer[2]=(flash_addr & 0xff00) >> 8;;
XSPI_Wbuffer[3]=flash_addr & 0xff;;
XSPI_FlashSendData(XSPI_Wbuffer,XSPI_Rbuffer,0x104);
log_info("============Read flash %X=================\r\n",(flash_cnt+1));
for(i = 4; i < (TEST_WR_BYTE_LEN+4); i++)
{
if(XSPI_Rbuffer[i] != 0xFF)
{
printf("Erase flash %X fail,i=0X%X\r\n",(flash_cnt+1),i);
break;
}
}
4. 写使能两片 Flash。 写入前对两片 Flash 分别发出写使能命令,并轮询状态寄存器确认写使能位。
for(i = 0; i < TEST_WR_WORD_LEN; i++)
{
XSPI_Wbuffer[i] = 0xCC660000+i;
}
for(flash_cnt = 0; flash_cnt < 2; flash_cnt++) // enable write flash 1 and flash 2 respectively
{
if(flash_cnt == 1)// enable flash 2 and clear buffer
{
AFIO_Config_XSPI_DualQuadMode(AFIO_XSPI_DUAL_QUAD_ENABLE);
}
else
{
AFIO_Config_XSPI_DualQuadMode(AFIO_XSPI_DUAL_QUAD_DISABLE);
}
do/*write enable*/
{
bufw[0] = SPIFLASH_Write_Enable;
XSPI_FlashREG_WR(bufw,bufr,1,TYPE_REG_WRITE);
bufw[0] = SPIFLASH_Read_Reg1;
XSPI_FlashREG_WR(bufw,bufr,NULL,TYPE_REG_READ);
}while((bufr[1]&0x03) != 0x02);
}
对应源码: src/main.c
5.33.3.2 XSPI_QUAD 四线读写外置 Flash
工程目录: projects/n32h49x_EVAL/examples/XSPI/XSPI_QUAD/
初始化日志、时钟与 GPIO→使能外置 Flash 四线模式→擦除扇区并写入测试数据→四线读回并与写入比较→擦除整片并校验读回数据
| 项目 | 源码解读 |
|---|---|
| 功能 | 用四线模式访问单片外置 Flash; 先置 QE 使能四线,擦除扇区后写入数据,读回比较一致后再擦除整片并校验读回是否全 0xFF。 |
| 源码组成 | src/main.c:main、Delay、Buffercmp;src/xspi_cfg.c:RCC_Configuration、GPIO_Configuration、GPIO_HD_WP_Configuration、XSPI_Configuration;src/spi_flash.c:XSPI_FlashWrite、sFLASH_QuadEnable、sFLASH_EraseSector、sFLASH_ReadBuffer 等;inc/main.h、inc/spi_flash.h、inc/xspi_cfg.h:宏与函数声明;readme.txt、MDK-ARM/、EWARM/:说明与工程文件。 |
| 关键配置 | 时钟:RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_XSPI, ENABLE);引脚:NSS0 用 PG6、SCK 用 PF10、IO0/IO1 用 PF8/PF9、IO2/IO3 用 PF7/PF6; 帧格式:寄存器阶段用 DFS_08_BIT,数据阶段用 QUAD_SPI_FORMAT 与 DFS_32_BIT;阈值: XSPI_SetTXFIFOStartLevel 按传输长度设置。 |
| 程序与数据路径 | main 先配置时钟与引脚,再置 QE 使能四线模式;填充 XSPI_Wbuffer 后擦除扇区并写入数据;四线读回数据后用 Buffercmp 比较写入与读出;整片擦除后再读回校验是否全 0xFFFFFFFF 并打印结论。 |
| 主要 API | XSPI_DeInit、XSPI_Cmd、XSPI_SetTXFIFOStartLevel、XSPI_SendData、XSPI_GetFlagStatus |
| 验证方法 | 串口打印 Spi_flash write and read demo test success 表示读写一致且擦除后读回全 0xFF;否则打印 Spi_flash write and read demo test failed。 |
| 使用提示 | sFLASH_QuadEnable 会临时把 IO2/IO3 配为普通输出以驱动写保护与保持引脚,置位后再切回复用功能;寄存器读写超过 16 个数据需改用 DMA 方式传输; 运行需外置 Flash 与全功能板跳线帽就位。 |
核心代码解读与移植
示例先用标准模式置 QE 使能四线,再擦除扇区写入数据、读回比较,最后整片擦除校验。
数据路径: RCC_Configuration 与 GPIO_Configuration 配置时钟与引脚 → sFLASH_QuadEnable 置 QE 使能四线 → sFLASH_EraseSector 擦除扇区 → sFLASH_WriteBuffer 写入 XSPI_Wbuffer → sFLASH_ReadBuffer 四线读回 → Buffercmp 比较 → sFLASH_EraseChip 整片擦除并校验读回全 0xFFFFFFFF
1. 直接写寄存器配置 XSPI。 XSPI_Configuration 先复位外设,再把帧格式、传输模式、帧长、帧数与波特率写入相关寄存器。
void XSPI_Configuration(uint32_t frame_format,uint32_t transfer_mode,uint32_t data_frame_size,uint16_t number_data_frame,uint16_t baudr)
{
XSPI_DeInit();
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_XSPI, ENABLE);
/*disable XSPI*/
XSPI->SLAVE_EN = (0x0);
XSPI_Cmd(DISABLE);
/*set MST, SPI_FRF, TMOD ,SCPOL, SCPH, DFS, SSTE*/
XSPI->CTRL0 = (XSPI_Mode_Master|frame_format|transfer_mode|SCPOL_LOW|START_BIT|data_frame_size|SSTE_BIT0);
/*set NDF*/
XSPI->CTRL1 = (number_data_frame);
/*set CLKDIV */
XSPI->BAUD = ((uint32_t)baudr<<1);
/*set TRANSTYPE, ADDRLEN, INSTL, WAITCYCLES*/
XSPI->ENH_CTRL0 = (TRANS_TYPE_STANDRD|ENSPI_ADDR_24BIT|ENSPI_INST_L_08BIT|ENSPI_WAIT_8CYCLES);
if(transfer_mode == TX_ONLY || transfer_mode == RX_ONLY)
{
XSPI->RX_DELAY = (XSPI->BAUD)/2;
XSPI->ENH_CTRL0 |= CLK_STRETCH_EN;
}
/*set TXFTTEI*/
XSPI->TXFT = ((0x00<<16)|0x00)&0x1f001f;/* level = 0*/
/*set RXFTTEI*/
XSPI->RXFT = (0x08&0x1f);/* level = 8*/
XSPI->IMASK = 0;
/*enable XSPI*/
XSPI->SLAVE_EN = (0x1);
XSPI_Cmd(ENABLE);
}
2. 置 QE 使能四线模式。 sFLASH_QuadEnable 先把 IO2/IO3 配为普通输出驱动写保护与保持引脚,置位状态寄存器 2 的 QE 位后再切回复用功能。
GPIO_HD_WP_Configuration();
XSPI_Configuration(STANDRD_SPI_FORMAT,TX_AND_RX,DFS_08_BIT,CTRL1_NDF_255,TEST_WR_CMDCLKDIV);
flag |= sFLASH_WriteEnable();
do/*set QE*/
{
#if EXTERNAL_FLASH == FLASH_P25Q40
bufw[0] = SPIFLASH_Write_Reg1;
bufw[1] = 0x00;
bufw[2] = SPIFLASH_REG2CMD_SETQE;
XSPI_FlashREG_WR(bufw,bufr,3,TYPE_REG_WRITE);
#else
bufw[0] = SPIFLASH_Write_Reg2;
bufw[1] = SPIFLASH_REG2CMD_SETQE;
XSPI_FlashREG_WR(bufw,bufr,2,TYPE_REG_WRITE);
#endif
bufw[0] = SPIFLASH_Read_Reg2;
XSPI_FlashREG_WR(bufw,bufr,NULL,TYPE_REG_READ);
}while(!(bufr[1]&SPIFLASH_REG2CMD_SETQE) && timeout--);
if(!(bufr[1]&SPIFLASH_REG2CMD_SETQE) && (timeout == 0))
{
flag |= FAILED_STATUS;
}
else
{
flag |= sFLASH_WaitForWriteEnd();
}
GPIO_Configuration();
3. 四线写页。 sFLASH_WritePage 先在四线模式下写使能,再配置帧格式并把页数据写入 Flash,最后等待写结束。
TestResultStatus sFLASH_WritePage(uint32_t* pBuffer, uint32_t WriteAddr, uint32_t NumWordToWrite)
{
TestResultStatus flag = SUCCESS_STATUS;
if(NumWordToWrite != 0)
{
flag |= sFLASH_WriteEnable();
XSPI_Configuration(QUAD_SPI_FORMAT,TX_ONLY,DFS_32_BIT,(NumWordToWrite-1),TEST_WR_CLKDIV);
flag |= XSPI_FlashWrite(SPIFLASH_QuadPage_Pro,WriteAddr,NumWordToWrite,pBuffer);
/* Wait the end of Flash writing */
flag |= sFLASH_WaitForWriteEnd();
}
return flag;
}
4. 四线读回。 sFLASH_ReadBuffer 把传输模式设为只接收、帧格式设为四线,再读回指定字数的数据。
TestResultStatus sFLASH_ReadBuffer(uint32_t* pBuffer, uint32_t ReadAddr, uint32_t NumWordToRead)
{
TestResultStatus flag = SUCCESS_STATUS;
XSPI_Configuration(QUAD_SPI_FORMAT,RX_ONLY,DFS_32_BIT,(NumWordToRead-1),TEST_WR_CLKDIV);
flag |= XSPI_FlashRead(SPIFLASH_Read_QUAD,ReadAddr,NumWordToRead,pBuffer);
return flag;
}
对应源码: src/xspi_cfg.c、src/spi_flash.c
5.33.3.3 XSPI_QUAD_DMA 四线 DMA 读写外置 Flash
工程目录: projects/n32h49x_EVAL/examples/XSPI/XSPI_QUAD_DMA/
初始化日志、时钟、GPIO 与 DMA→使能外置 Flash 四线模式→擦除扇区并写入测试数据→用 DMA 读回并与写入比较→打印读写结论
| 项目 | 源码解读 |
|---|---|
| 功能 | 与四线示例流程一致,但读写改用 DMA 搬运数据; 在 XSPI->DAT0 与缓冲区之间搬运,提高大数据量传输效率。 |
| 源码组成 | src/main.c:main、Delay、Buffercmp;src/xspi_cfg.c:RCC_Configuration、GPIO_Configuration、GPIO_HD_WP_Configuration、XSPI_Configuration、DMA_Configuration;src/spi_flash.c:XSPI_FlashWrite、XSPI_FlashRead(含 DMA 使能与等待)等;inc/main.h、inc/spi_flash.h、inc/xspi_cfg.h:宏与函数声明;readme.txt、MDK-ARM/、EWARM/:说明与工程文件。 |
| 关键配置 | DMA 通道:发送用 DMA1_CH1 映射 DMA_REMAP_XSPI_TX,接收用 DMA1_CH2 映射 DMA_REMAP_XSPI_RX;DMA 参数:WORD 数据宽度、存储器递增、突发使能(近似模式、长度 8); XSPI: XSPI_ConfigDMATxLevel、XSPI_ConfigDMARxLevel 与 XSPI_EnableDMA 设置阈值与请求。 |
| 程序与数据路径 | main 配置时钟与引脚并置 QE,擦除扇区后写入 XSPI_Wbuffer;XSPI_FlashWrite 关闭 XSPI、设 DMA 阈值并使能发送请求,再用 DMA_Configuration 搬运数据;XSPI_FlashRead 使能接收请求,用 DMA 把 XSPI->DAT0 读入 XSPI_Rbuffer;main 比较读回与写入并打印结论。 |
| 主要 API | XSPI_EnableDMA、XSPI_ConfigDMATxLevel、XSPI_ConfigDMARxLevel、XSPI_SendData、XSPI_GetFlagStatus |
| 验证方法 | 串口打印 Spi_flash write and read demo test success 表示 DMA 写入与读回一致;否则打印 Spi_flash write and read demo test failed。 |
| 使用提示 | XSPI 的 DMA 阈值与使能须在 XSPI 关闭时配置,示例先 XSPI_Cmd(DISABLE) 再设置;发送等待 DMA_FLAG_TC1、接收等待 DMA_FLAG_TC2,移植时按通道核对标志;DMA 请求映射固定为发送 DMA_REMAP_XSPI_TX、接收 DMA_REMAP_XSPI_RX。 |
核心代码解读与移植
示例在四线模式基础上把数据搬运交给 DMA,DMA 配置通过请求映射把 XSPI->DAT0 与外设数据流连接。
数据路径: DMA_Configuration 按发送或接收选择通道与请求映射 → XSPI_FlashWrite 关闭 XSPI、设阈值并使能发送请求 → DMA 把缓冲区数据写入 XSPI->DAT0 → XSPI_FlashRead 使能接收请求 → DMA 把 XSPI->DAT0 读入缓冲区 → 等待传输完成标志
1. DMA 通道与请求映射。 DMA_Configuration 依据发送或接收方向选择 DMA1_CH1 或 DMA1_CH2,并设置对应的 XSPI 请求映射。
DMA_ChannelType* DMAy_Channelx;
uint32_t DMA_Direction;
uint32_t DMA_RemapNum = 0;
if (TxRx == DMA_TX)/*DMA tx*/
{
DMAy_Channelx = DMA1_CH1;
DMA_Direction = DMA_DIR_PERIPH_DST;
DMA_RemapNum = DMA_REMAP_XSPI_TX;/*DMA remap*/
}
else if (TxRx == DMA_RX)/*DMA rx*/
{
DMAy_Channelx = DMA1_CH2;
DMA_Direction = DMA_DIR_PERIPH_SRC;
DMA_RemapNum = DMA_REMAP_XSPI_RX;/*DMA remap*/
}
DMA_DeInit(DMAy_Channelx);
DMA_StructInit(&DMA_InitStructure);
/* config DMA channel*/
DMA_InitStructure.PeriphAddr = PeripheralAddr;
DMA_InitStructure.MemAddr = MemoryAddr;
DMA_InitStructure.Direction = DMA_Direction;
DMA_InitStructure.BufSize = Len;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_WORD;
DMA_InitStructure.MemDataSize = DMA_MEM_DATA_WIDTH_WORD;
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
DMA_InitStructure.Priority = DMA_PRIORITY_HIGH;
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
DMA_InitStructure.BurstCmd = DMA_BURST_ENABLE;
DMA_InitStructure.BurstMode = DMA_BURST_MODE_ALMOST;
DMA_InitStructure.BurstLen = DMA_BURST_LEN_8;
DMA_Init(DMAy_Channelx, &DMA_InitStructure);
DMA_RequestRemap(DMA_RemapNum, DMAy_Channelx, NewState);
/* enable DMA channel*/
DMA_EnableChannel(DMAy_Channelx, NewState);
2. DMA 发送。 XSPI_FlashWrite 关闭 XSPI 后设置 DMA 收发阈值并使能发送请求,再启动 DMA 搬运并等待发送与传输完成。
XSPI_Cmd(DISABLE);
/*enable DMA */
XSPI_ConfigDMATxLevel(0x08);
XSPI_ConfigDMARxLevel(0x08);
XSPI_EnableDMA(XSPI_DMAREQ_TX,ENABLE);
XSPI_Cmd(ENABLE);
XSPI_SendData(PrgCmd);
XSPI_SendData(addr);
DMA_Configuration((uint32_t)&XSPI->DAT0, (uint32_t)Wbuf, (count), DMA_TX, ENABLE);
/*wait TX end */
while(XSPI_GetFlagStatus(XSPI_STS_TXFE) == RESET)/*wait TX FIFO empty*/
{
if ((wtime--) == 0)
{
result |= FAILED_STATUS;
break;
}
}
wtime = FLASH_WAIT_CNT*1000;
while(DMA_GetFlagStatus(DMA_FLAG_TC1,DMA1) == RESET)/*wait DMA transfer complete*/
{
if ((wtime--) == 0)
{
result |= FAILED_STATUS;
break;
}
}
/*disable DMA*/
XSPI_Cmd(DISABLE);
DMA_Configuration((uint32_t)&XSPI->DAT0, (uint32_t)Wbuf, (count), DMA_TX, DISABLE);
XSPI_EnableDMA(XSPI_DMAREQ_TX,DISABLE);
3. DMA 接收。 XSPI_FlashRead 使能接收请求,启动 DMA 把 XSPI->DAT0 的数据读入缓冲区,等待传输完成标志后关闭 DMA。
XSPI_ConfigDMATxLevel(0x08);
XSPI_ConfigDMARxLevel(0x08);
XSPI_EnableDMA(XSPI_DMAREQ_RX,ENABLE);
XSPI_Cmd(ENABLE);
XSPI_SendData(PrgCmd);
XSPI_SendData(StrAddr);
DMA_Configuration((uint32_t)&XSPI->DAT0, (uint32_t)DstBuf, (Len), DMA_RX, ENABLE);
/*wait RX end */
while(DMA_GetFlagStatus(DMA_FLAG_TC2,DMA1) == RESET)/*wait DMA transfer complete*/
{
if ((wtime--) == 0)
{
result |= FAILED_STATUS;
break;
}
}
/*disable DMA*/
XSPI_Cmd(DISABLE);
DMA_Configuration((uint32_t)&XSPI->DAT0, (uint32_t)DstBuf, (Len), DMA_RX, DISABLE);
XSPI_EnableDMA(XSPI_DMAREQ_TX,DISABLE);
对应源码: src/xspi_cfg.c、src/spi_flash.c
5.33.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| 命令阶段 | 每条命令包含 Instruction、Address、Mode bits、Wait cycles、Data 五个阶段,任一阶段可跳过,但至少要保留 Instruction、Address、Mode bits、Data 之一。 | 用户手册 |
| Mode bits | Mode bits 阶段只在 XIP 模式使用; 使能后会在地址阶段后插入模式位,位宽为 2、4、8 或 16。 |
用户手册 |
| DDR 波特率 | DDR 模式下波特率值应为 4 的倍数。 | 用户手册 |
| XIP 帧格式 | XIP 操作只支持 Dual、Quad、Octal 模式,XSPI_CTRL0.SPIFRF 不能配置为 0。 |
用户手册 |
| Tx and Rx 模式 | Tx and Rx 模式只有在标准 SPI 模式(XSPI_CTRL0.SPIFRF 为 00)下才可使用。 |
用户手册 |
| EEPROM read 模式 | EEPROM read 模式只有在标准 SPI 模式(XSPI_CTRL0.SPIFRF 为 00)下才可使用,读取字节数在 XSPI_CTRL1.NDF 中指定。 |
用户手册 |
| 指令与地址 | 一条指令占用 1 个 FIFO 地址,地址可以占用多个 FIFO 位置,指令和地址都需写入 XSPI_DATx 寄存器。 |
用户手册 |
| XIP 接收 FIFO 溢出 | XIP 传输中若 RX FIFO 深度不足可能溢出导致数据错误,软件检测到 RX FIFO 溢出后必须重新尝试数据传输。 | 用户手册 |