5.26 SPI 使用说明

本章说明 N32H49x 标准外设库中 SPI 的驱动文件、32 个非 I2S_ 前缀公开函数,以及 CRC、CRC_FIFO、FullDuplex_SoftNSS、Simplex_Interrupt、SPI_DMA_T&R、SPI_FLASH、SPI_FLASH_DMA、SPI_Simplex_DMA_Recv 共 8 个示例。SPI 与 I2S 共用 n32h49x_spi.h、n32h49x_spi.c,以 I2S_ 为前缀的 11 个函数收录在 I2S 篇。片选管理、四线时序、CRC 与 FIFO 等细节请配合用户手册的「串行外设接口/内置音频总线(SPI/I2S)」章节阅读。

5.26.1 相关文件

相对路径 作用
firmware/n32h49x_std_periph_driver/inc/n32h49x_spi.h 定义 SPI_InitType 结构体与数据方向、主从模式、数据长度、时钟极性/相位、NSS、波特率预分频、位序、CRC 多项式等宏,并声明 32 个非 I2S_ 前缀函数;
同一头文件以 I2S_ 为前缀的 11 个函数归 I2S 篇
firmware/n32h49x_std_periph_driver/src/n32h49x_spi.c 实现上述 32 个函数:初始化与使能、中断与 DMA 使能、数据收发、NSS 与数据长度配置、CRC 计算与校验、标志与中断状态查询、FIFO 与传输控制
projects/n32h49x_EVAL/examples/SPI/ SPI 示例根目录,含 CRC、CRC_FIFO、FullDuplex_SoftNSS、Simplex_Interrupt、SPI_DMA_T&R、SPI_FLASH、SPI_FLASH_DMA、SPI_Simplex_DMA_Recv 共 8 个示例
projects/n32h49x_EVAL/examples/SPI/CRC/ 该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt
projects/n32h49x_EVAL/bsp/ 部分示例引用的公共板级支持文件:串口日志打印(log.c/log.h)与 printf 重定向(print_remap.c)

5.26.2 API 参考

搜索 API共 32 个函数

以下 32 个公开函数按 n32h49x_spi.h 的声明顺序分为 5 组;每个条目给出功能摘要与 n32h49x_spi.c 中该函数正上方的注释原文。

SPI 初始化与开关

SPI_I2S_DeInit、SPI_Init、SPI_InitStruct、SPI_Enable

中断、DMA 与数据收发

SPI_I2S_EnableInt、SPI_I2S_EnableDma、SPI_I2S_TransmitData、SPI_I2S_ReceiveData

NSS、数据长度、CRC 与双向模式

SPI_SetNssLevel、SPI_SSOutputEnable、SPI_ConfigDataLen、SPI_TransmitCrcNext、SPI_EnableCalculateCrc、SPI_GetCRCDat、SPI_GetCRCPoly、SPI_ConfigBidirectionalMode

标志与中断状态

SPI_I2S_GetStatus、SPI_I2S_ClrCRCErrFlag、SPI_I2S_GetIntStatus、SPI_I2S_ClrITPendingBit

FIFO 与传输控制

SPI_I2S_FIFO_Cmd、SPI_I2S_ClearFIFOBit、SPI_RxFIFOSizeConfig、SPI_TxFIFOSizeConfig、SPI_RX_FIFO_CNT_GET、SPI_TX_FIFO_CNT_GET、SPI_TRANSNUM_SET、SPI_TRANSNUM_GET、SPI_DELAYTIME_SET、SPI_DELAYTIME_GET、SPI_RX_FIFO_SET、SPI_RX_FIFO_GET

void SPI_I2S_DeInit(const SPI_Module* SPIx)将 SPIx 外设寄存器复位为默认值(同时影响 I2S)。
源码注释
name    SPI_I2S_DeInit.
fun     Deinitializes the SPIx peripheral registers to their default reset values (Affects also the I2Ss)
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
return  none
void SPI_Init(SPI_Module* SPIx,const SPI_InitType* SPI_InitStruct)根据 SPI_InitStruct 中指定的参数初始化 SPIx 外设。
源码注释
name    SPI_Init.
fun     Initializes the SPIx peripheral according to the specified parameters in the SPI_InitStruct.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   SPI_InitStruct :
          - DataDirection
            - SPI_DIR_DOUBLELINE_FULLDUPLEX
            - SPI_DIR_DOUBLELINE_RONLY
            - SPI_DIR_SINGLELINE_RX
            - SPI_DIR_SINGLELINE_TX
          - SpiMode
            - SPI_MODE_MASTER
            - SPI_MODE_SLAVE
          - DataLen
            - SPI_DATA_SIZE_16BITS
            - SPI_DATA_SIZE_8BITS
          - CLKPOL
            - SPI_CLKPOL_LOW
            - SPI_CLKPOL_HIGH
          - CLKPHA
            - SPI_CLKPHA_FIRST_EDGE
            - SPI_CLKPHA_SECOND_EDGE
          - NSS
            - SPI_NSS_SOFT
            - SPI_NSS_HARD
          - BaudRatePres
            - SPI_BR_PRESCALER_2
            - SPI_BR_PRESCALER_4
            - SPI_BR_PRESCALER_8
            - SPI_BR_PRESCALER_16
            - SPI_BR_PRESCALER_32
            - SPI_BR_PRESCALER_64
            - SPI_BR_PRESCALER_128
            - SPI_BR_PRESCALER_256
          - FirstBit
            - SPI_FB_MSB
            - SPI_FB_LSB
          - CRCPoly                 default 0x0007, max 0xffff
return  none
void SPI_InitStruct(SPI_InitType* SPI_InitStruct)为 SPI_InitStruct 的每个成员填入默认值。
源码注释
name    SPI_InitStruct.
fun     Fills each SPI_InitStruct member with its default value.
param   SPI_InitStruct :
          - DataDirection
          - SpiMode
          - DataLen
          - CLKPOL
          - CLKPHA
          - NSS
          - BaudRatePres
          - FirstBit
          - CRCPoly
return  none
void SPI_Enable(SPI_Module* SPIx, FunctionalState Cmd)使能或禁用指定的 SPI 外设。
源码注释
name    SPI_Enable.
fun     Enables or disables the specified SPI peripheral.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void SPI_I2S_EnableInt(SPI_Module* SPIx, uint8_t SPI_I2S_IT, FunctionalState Cmd)使能或禁用指定的 SPI/I2S 中断。
源码注释
name    SPI_I2S_EnableInt.
fun     Enables or disables the specified SPI/I2S interrupts.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_I2S_IT :
         - SPI_I2S_INT_TE
         - SPI_I2S_INT_RNE
         - SPI_I2S_INT_ERR
         - SPI_I2S_INT_RXONLYC
         - SPI_I2S_INT_RXFIFOF
         - SPI_I2S_INT_RXFIFOHF
         - SPI_I2S_INT_TXFIFOHE
param   Cmd :
         - ENABLE
         - DISABLE
return  none
void SPI_I2S_EnableDma(SPI_Module* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState Cmd)使能或禁用 SPIx/I2Sx 的 DMA 接口。
源码注释
name    SPI_I2S_EnableDma.
fun     Enables or disables the SPIx/I2Sx DMA interface.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_I2S_DMAReq :
         - SPI_I2S_DMA_TX
         - SPI_I2S_DMA_RX
param   Cmd :
         - ENABLE
         - DISABLE
return  none
void SPI_I2S_TransmitData(SPI_Module* SPIx, uint16_t Data)通过 SPIx/I2Sx 外设发送一个数据。
源码注释
name    SPI_I2S_TransmitData.
fun     Transmits a Data through the SPIx/I2Sx peripheral.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   Data            \* Data to be transmitted *\
return  none
uint16_t SPI_I2S_ReceiveData(const SPI_Module* SPIx)返回 SPIx/I2Sx 外设最近接收到的数据。
源码注释
name    SPI_I2S_ReceiveData.
fun     Returns the most recent received data by the SPIx/I2Sx peripheral.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
return  The data in the SPI_DAT register.
void SPI_SetNssLevel(SPI_Module* SPIx, uint16_t SPI_NSSInternalSoft)通过软件在内部配置所选 SPI 的 NSS 引脚。
源码注释
name    SPI_SetNssLevel.
fun     Configures internally by software the NSS pin for the selected SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   SPI_NSSInternalSoft :
                 - SPI_NSS_HIGH
                 - SPI_NSS_LOW
return  none
void SPI_SSOutputEnable(SPI_Module* SPIx, FunctionalState Cmd)使能或禁用所选 SPI 的 SS 输出。
源码注释
name    SPI_SSOutputEnable.
fun     Enables or disables the SS output for the selected SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   Cmd :
                 - ENABLE
                 - DISABLE
return  none
void SPI_ConfigDataLen(SPI_Module* SPIx, uint16_t DataLen)配置所选 SPI 的数据长度。
源码注释
name    SPI_ConfigDataLen.
fun     Configures the data size for the selected SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   DataLen :
                 - SPI_DATA_SIZE_16BITS
                 - SPI_DATA_SIZE_8BITS
return  none
void SPI_TransmitCrcNext(SPI_Module* SPIx, FunctionalState Cmd)发送 SPIx 的 CRC 值。
源码注释
name    SPI_TransmitCrcNext.
fun     Transmit the SPIx CRC value.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   Cmd :
                 - ENABLE
                 - DISABLE
return  none
void SPI_EnableCalculateCrc(SPI_Module* SPIx, FunctionalState Cmd)使能或禁用传输字节的 CRC 值计算。
源码注释
name    SPI_EnableCalculateCrc.
fun     Enables or disables the CRC value calculation of the transferred bytes.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   Cmd :
                 - ENABLE
                 - DISABLE
return  none
uint16_t SPI_GetCRCDat(const SPI_Module* SPIx, uint8_t SPI_CRC)返回指定 SPI 的发送或接收 CRC 寄存器值。
源码注释
name    SPI_GetCRCDat.
fun     Returns the transmit or the receive CRC register value for the specified SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   Cmd :
                 - SPI_CRC_TX
                 - SPI_CRC_RX
return  Tx/Rx CRC register value.
uint16_t SPI_GetCRCPoly(const SPI_Module* SPIx)返回指定 SPI 的 CRC 多项式寄存器值。
源码注释
name    SPI_GetCRCPoly.
fun     Returns the CRC Polynomial register value for the specified SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
return  The CRC Polynomial register value.
void SPI_ConfigBidirectionalMode(SPI_Module* SPIx, uint16_t DataDirection)选择指定 SPI 在双向模式下的数据传输方向。
源码注释
name    SPI_ConfigBidirectionalMode.
fun     Selects the data transfer direction in bi-directional mode for the specified SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   DataDirection :
                 - SPI_BIDIRECTION_TX
                 - SPI_BIDIRECTION_RX
return  none.
FlagStatus SPI_I2S_GetStatus(const SPI_Module* SPIx, uint16_t SPI_I2S_FLAG)检查指定的 SPI/I2S 标志是否置位。
源码注释
name    SPI_I2S_GetStatus.
fun     Checks whether the specified SPI/I2S flag is set or not.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_I2S_FLAG :
                 - SPI_I2S_BUSY_FLAG
                 - SPI_I2S_OVER_FLAG
                 - SPI_MODERR_FLAG
                 - SPI_CRCERR_FLAG
                 - I2S_UNDER_FLAG
                 - I2S_CHSIDE_FLAG
                 - SPI_I2S_TE_FLAG
                 - SPI_I2S_RNE_FLAG
                 - SPI_I2S_RXONLYC_FLAG
                 - SPI_I2S_RXFIFOF_FLAG
                 - SPI_I2S_TXFIFOE_FLAG
                 - SPI_I2S_RXFIFOHF_FLAG
                 - SPI_I2S_TXFIFOHE_FLAG
return  The new state of SPI_I2S_FLAG (SET or RESET).
void SPI_I2S_ClrCRCErrFlag(SPI_Module* SPIx, uint16_t SPI_I2S_FLAG)清除 SPIx 的 CRC 错误(CRCERR)标志。
源码注释
name    SPI_I2S_ClrCRCErrFlag.
fun     Clears the SPIx CRC Error (CRCERR) flag.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6
param   SPI_I2S_FLAG :
                 - SPI_CRCERR_FLAG
return  none.
INTStatus SPI_I2S_GetIntStatus(const SPI_Module* SPIx, uint8_t SPI_I2S_IT)检查指定的 SPI/I2S 中断是否发生。
源码注释
name    SPI_I2S_GetIntStatus.
fun     Checks whether the specified SPI/I2S interrupt has occurred or not.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_I2S_IT :
         - SPI_I2S_INT_TE
         - SPI_I2S_INT_RNE
         - SPI_I2S_INT_RXONLYC
         - SPI_I2S_INT_RXFIFOF
         - SPI_I2S_INT_RXFIFOHF
         - SPI_I2S_INT_TXFIFOHE
         - SPI_I2S_INT_CRCERR
         - SPI_I2S_INT_MODERR
         - SPI_I2S_INT_OVERERR
         - I2S_I2S_INT_UNDER
return  The new state of SPI_I2S_IT (SET or RESET).
void SPI_I2S_ClrITPendingBit(SPI_Module* SPIx, uint8_t SPI_I2S_IT)清除 SPIx 的 CRC 错误(CRCERR)中断挂起位。
源码注释
name    SPI_I2S_ClrITPendingBit.
fun     Clears the SPIx CRC Error (CRCERR) interrupt pending bit.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_I2S_IT :
                 - SPI_INT_CRCERR
return  none.
void SPI_I2S_FIFO_Cmd(SPI_Module* SPIx, FunctionalState NewState)使能或禁用 FIFO(SPI/I2S 模式)。
源码注释
name    SPI_I2S_FIFO_Cmd.
fun     Enables or disables the FIFO (in SPI/I2S mode).
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   Cmd :
                 - ENABLE
                 - DISABLE
return  none.
void SPI_I2S_ClearFIFOBit(SPI_Module* SPIx, uint16_t SPI_I2S_FIFO_Clear)清除 SPIx 的 FIFO 位。
源码注释
name    SPI_I2S_ClearFIFOBit.
fun     Clears the SPIx FIFO bit.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_I2S_FIFO_Clear :
                 - SPI_I2S_FIFO_CLR
return  none.
void SPI_RxFIFOSizeConfig(SPI_Module* SPIx, uint16_t SPI_FIFOSize)配置所选 SPI 的接收 FIFO 大小。
源码注释
name    SPI_RxFIFOSizeConfig.
fun     Configures the Rx FIFO size for the selected SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_FIFOSize :
                 - SPI_RX_FIF0NUM_7
                 - SPI_RX_FIF0NUM_6
                 - SPI_RX_FIF0NUM_5
                 - SPI_RX_FIF0NUM_4
                 - SPI_RX_FIF0NUM_3
                 - SPI_RX_FIF0NUM_2
                 - SPI_RX_FIF0NUM_1
                 - SPI_RX_FIF0NUM_0
return  none.
void SPI_TxFIFOSizeConfig(SPI_Module* SPIx, uint16_t SPI_FIFOSize)配置所选 SPI 的发送 FIFO 大小。
源码注释
name    SPI_TxFIFOSizeConfig.
fun     Configures the Tx FIFO size for the selected SPI.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   SPI_FIFOSize :
                 - SPI_TX_FIF0NUM_7
                 - SPI_TX_FIF0NUM_6
                 - SPI_TX_FIF0NUM_5
                 - SPI_TX_FIF0NUM_4
                 - SPI_TX_FIF0NUM_3
                 - SPI_TX_FIF0NUM_2
                 - SPI_TX_FIF0NUM_1
                 - SPI_TX_FIF0NUM_0
return  none.
uint16_t SPI_RX_FIFO_CNT_GET(const SPI_Module* SPIx)在 FIFO 模式下,当前有效 FIFO 的个数。
源码注释
name    SPI_RX_FIFO_CNT_GET.
fun     In FIFO mode, the current number of valid FIFOs.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
return  Current effective number of FIFOs.
uint16_t SPI_TX_FIFO_CNT_GET(const SPI_Module* SPIx)在 FIFO 模式下,当前有效 FIFO 的个数。
源码注释
name    SPI_TX_FIFO_CNT_GET.
fun     In FIFO mode, the current number of valid FIFOs.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
return  Current effective number of FIFOs.
void SPI_TRANSNUM_SET(SPI_Module* SPIx, uint16_t Data)FIFO 模式且启用 CRC 功能时,用户需提前写入待传输数据的个数。
源码注释
name    SPI_TRANSNUM_SET.
fun     When in FIFO mode with CRC function,
                the user needs to write the number of data to be transmitted in advance.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   Data                                \* Data to be transmitted *\
return  none.
uint16_t SPI_TRANSNUM_GET(const SPI_Module* SPIx)FIFO 模式且启用 CRC 功能时,用户提前写入的待传输数据个数。
源码注释
name    SPI_TRANSNUM_GET.
fun     When in FIFO mode with CRC function,
                the user write the number of data to be transmitted in advance.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
return  The number of data transferred.
void SPI_DELAYTIME_SET(SPI_Module* SPIx, uint16_t Data)配置 SPI 主时钟延迟时间。
源码注释
name    SPI_DELAYTIME_SET.
fun     SPI master clock delay time configuration.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
param   Data                                \* Data to be transmitted *\
return  none.
uint16_t SPI_DELAYTIME_GET(const SPI_Module* SPIx)获取 SPI 主时钟延迟时间。
源码注释
name    SPI_DELAYTIME_GET.
fun     Get SPI master clock delay time.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
return  The clock delay time.
void SPI_RX_FIFO_SET(SPI_Module* SPIx, uint16_t Data)在 FIFO 模式下,写入接收 FIFO 的数据。
源码注释
name    SPI_RX_FIFO_SET.
fun     In FIFIO mode, write the data in the receiving FIFO.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
param   Data                                \* Data to be transmitted *\
return  none.
uint16_t SPI_RX_FIFO_GET(const SPI_Module* SPIx)在 FIFO 模式下,读取接收 FIFO 的数据。
源码注释
name    SPI_RX_FIFO_GET.
fun     In FIFIO mode, read the data in the receiving FIFO.
param   SPIx :
                 - 1, 2, 3, 4, 5 or 6 in SPI mode
                 - 2 or 3 in I2S mode
return  Receive data in FIFO.

5.26.3 Demo 示例说明

本节说明 SPI 的 8 个示例:CRC 与 CRC_FIFO 在两块 SPI 之间做带 CRC 的全双工互传(后者经 FIFO),FullDuplex_SoftNSS 用软件 NSS 分两阶段交换主从,Simplex_Interrupt 用 TE、RNE 中断做单线收发,SPI_DMA_T&R 与 SPI_Simplex_DMA_Recv 用 DMA 做单线发送与接收,SPI_FLASH 与 SPI_FLASH_DMA 读写 W25Q128。SPI5 作主机使用 PF6~PF9,SPI2 作从机使用 PC2、PC7、PD1、PD2,两两之间需用跳线连接。

5.26.3.1 CRC 双机全双工 CRC 校验

工程目录: projects/n32h49x_EVAL/examples/SPI/CRC/

配置系统时钟与 GPIO 复用→初始化主机与从机→使能两端 CRC 计算与 SPI→主从同时全双工互发数据→末帧一并发送 CRC→比对数据与 CRC 并检查校验错误

项目 源码解读
功能 SPI5 作主机、SPI2 作从机,以 16 位数据全双工互传 32 帧,收发结束后发送 CRC 值并比对,检查数据与 CRC 状态。
源码组成 src/main.c:收发缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;
src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;
inc/main.h:外设、引脚与复用宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 数据方向为 SPI_DIR_DOUBLELINE_FULLDUPLEX,16 位数据,主机 SPI5、从机 SPI2;
时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,NSS 为 SPI_NSS_SOFT,预分频 128,位序 MSB,CRC 多项式为 7;
SPI5 引脚 SCK:PF7、MISO:PF8、MOSI:PF9(AF11),SPI2 引脚 SCK:PC7、MISO:PC2、MOSI:PD2;
系统时钟为 HSI+PLL、240 MHz。
程序与数据路径 main 初始化两端并调用 SPI_EnableCalculateCrc 打开 CRC 计算;
循环内 SPI5 与 SPI2 同时收发,写 SPI_I2S_TransmitData、读 SPI_I2S_ReceiveData;
发送最后一帧时以 SPI_TransmitCrcNext 触发 CRC 发送;
最后用 Buffercmp 比对收发数据并检查 SPI_CRCERR_FLAG。
主要 API SPI_InitStruct、SPI_Init、SPI_Enable、SPI_EnableCalculateCrc、SPI_TransmitCrcNext、SPI_I2S_TransmitData、SPI_I2S_ReceiveData、SPI_I2S_GetStatus
验证方法 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK、MISO、MOSI;
TransferStatus1、TransferStatus2 均为 PASSED 且未置 CRCERR,表示数据与 CRC 校验正确。
使用提示 须用跳线在同一块板上对连 SPI5 与 SPI2;
从机使能 CRC 计算前要先等时钟稳定;
基于 N32H497ZGL7_STB 评估板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例把 SPI5 配成主机、SPI2 配成从机,两端都打开 CRC 计算,在最后一帧发送前把 CRCNEXT 置位,让硬件在数据后附发 CRC 值。

数据路径: SPI_Master_Buffer_Tx → SPI_I2S_TransmitData → SPI5 SPI_DAT → PF9(MOSI)→ SPI2 → PC2(MISO)→ SPI2_Buffer_Rx → Buffercmp

1. 初始化主机与从机。 先填默认值,再按主机参数配置 SPI5;从机只改工作模式后复用同一结构体。

src/main.csrc/main.c · main
    SPI_InitStruct(&SPI_InitStructure);
    /* SPI_Master configuration ------------------------------------------------------*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_16BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_FIRST_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_128;
    SPI_InitStructure.FirstBit      = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly       = 7;
    SPI_Init(SPI_Master, &SPI_InitStructure);

    /* SPI2 configuration ------------------------------------------------------*/
    SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
    SPI_Init(SPI2, &SPI_InitStructure);

2. 打开两端 CRC 计算并使能。 CRC 使能位置位即清零收发 CRC 寄存器。

src/main.csrc/main.c · main
    /* Enable SPI_Master CRC calculation */
    SPI_EnableCalculateCrc(SPI_Master, ENABLE);
    /* Enable SPI2 CRC calculation */
    SPI_EnableCalculateCrc(SPI2, ENABLE);

    /* Enable SPI_Master */
    SPI_Enable(SPI_Master, ENABLE);
    /* Enable SPI2 */
    SPI_Enable(SPI2, ENABLE);

3. 全双工互传并在末帧发送 CRC。 主从在同一循环里各写各发、各读各收。

src/main.csrc/main.c · main
        /* Wait for SPI_Master Tx buffer empty */
        while (SPI_I2S_GetStatus(SPI_Master, SPI_I2S_TE_FLAG) == RESET)
            ;
        /* Send SPI2 data */
        SPI_I2S_TransmitData(SPI2, SPI2_Buffer_Tx[TxIdx]);
        /* Send SPI_Master data */
        SPI_I2S_TransmitData(SPI_Master, SPI_Master_Buffer_Tx[TxIdx++]);

4. 检查 CRC 结果并读出接收 CRC 值。 任一端的 CRCERR 置位即判失败,随后读取两端接收到的 CRC 寄存器值。

src/main.csrc/main.c · main
    /* Test on the SPI_Master CRC Error flag */
    if ((SPI_I2S_GetStatus(SPI_Master, SPI_CRCERR_FLAG)) == SET)
    {
        TransferStatus2 = FAILED;
    }

    /* Test on the SPI2 CRC Error flag */
    if ((SPI_I2S_GetStatus(SPI2, SPI_CRCERR_FLAG)) == SET)
    {
        TransferStatus1 = FAILED;
    }

    /* Read SPI_Master received CRC value */
    CRC1Value = SPI_I2S_ReceiveData(SPI_Master);
    /* Read SPI2 received CRC value */
    CRC2Value = SPI_I2S_ReceiveData(SPI2);

对应源码: src/main.c、inc/main.h

5.26.3.2 CRC_FIFO 经 FIFO 的 CRC 校验收发

工程目录: projects/n32h49x_EVAL/examples/SPI/CRC_FIFO/

配置系统时钟与 GPIO 复用→初始化主机与从机→设置收发 FIFO 水位并使能 FIFO→使能两端 CRC 计算与 SPI→经 FIFO 全双工互发数据→末帧发送 CRC 并从接收 FIFO 取数比对

项目 源码解读
功能 在 CRC 全双工互传的基础上启用 FIFO,SPI5 与 SPI2 经收发 FIFO 互传 32 帧 16 位数据,末帧发送 CRC 并从接收 FIFO 读出比对。
源码组成 src/main.c:收发缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;
src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;
inc/main.h:外设、引脚与复用宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 数据方向为 SPI_DIR_DOUBLELINE_FULLDUPLEX,16 位数据,主机 SPI5、从机 SPI2;
两端收发 FIFO 水位均设为 SPI_RX_FIF0NUM_1/SPI_TX_FIF0NUM_1 并以 SPI_I2S_FIFO_Cmd 使能 FIFO;
时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,NSS 为 SPI_NSS_SOFT,预分频 128,位序 MSB,CRC 多项式为 7;
系统时钟为 HSI+PLL、240 MHz。
程序与数据路径 main 初始化两端后先设置收发 FIFO 水位再使能 FIFO,并调用 SPI_EnableCalculateCrc 打开 CRC;
循环内主从同时收发,接收改用 SPI_RX_FIFO_GET 从接收 FIFO 取数;
末帧以 SPI_TransmitCrcNext 触发 CRC 发送;
最后用 Buffercmp 比对收发数据并检查 SPI_CRCERR_FLAG。
主要 API SPI_InitStruct、SPI_Init、SPI_Enable、SPI_RxFIFOSizeConfig、SPI_TxFIFOSizeConfig、SPI_I2S_FIFO_Cmd、SPI_EnableCalculateCrc、SPI_TransmitCrcNext、SPI_RX_FIFO_GET
验证方法 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK、MISO、MOSI;
TransferStatus1、TransferStatus2 均为 PASSED 且未置 CRCERR,表示数据与 CRC 校验正确。
使用提示 须用跳线在同一块板上对连 SPI5 与 SPI2;
使能 FIFO 后接收须读接收 FIFO,未使能时读数据寄存器;
基于 N32H497ZGL7_STB 评估板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例在 CRC 互传的基础上打开 8 字节收发 FIFO,并把读取接收数据的方式从数据寄存器改为接收 FIFO。

数据路径: SPI_Master_Buffer_Tx → SPI_I2S_TransmitData → SPI5 TX_FIFO → PF9(MOSI)→ SPI2 RX_FIFO → SPI_RX_FIFO_GET → SPI2_Buffer_Rx → Buffercmp

1. 设置收发 FIFO 水位并使能 FIFO。 两端先配水位再打开 FIFO,水位决定半满/半空标志的触发点。

src/main.csrc/main.c · main
    /* Set the FIFO half-full watermark */
    SPI_RxFIFOSizeConfig(SPI_Master, SPI_RX_FIF0NUM_1);
    SPI_TxFIFOSizeConfig(SPI_Master, SPI_TX_FIF0NUM_1);
    /* Enable FIFO */
    SPI_I2S_FIFO_Cmd(SPI_Master, ENABLE);

    /* Set the FIFO half-full watermark */
    SPI_RxFIFOSizeConfig(SPI2, SPI_RX_FIF0NUM_1);
    SPI_TxFIFOSizeConfig(SPI2, SPI_TX_FIF0NUM_1);
    /* Enable FIFO */
    SPI_I2S_FIFO_Cmd(SPI2, ENABLE);

2. 使能两端 CRC 计算与 SPI。 CRC 寄存器在使能位一置位时被清零。

src/main.csrc/main.c · main
    /* Enable SPI_Master CRC calculation */
    SPI_EnableCalculateCrc(SPI_Master, ENABLE);
    /* Enable SPI2 CRC calculation */
    SPI_EnableCalculateCrc(SPI2, ENABLE);

    /* Enable SPI_Master */
    SPI_Enable(SPI_Master, ENABLE);
    /* Enable SPI2 */
    SPI_Enable(SPI2, ENABLE);

3. 经接收 FIFO 读取数据。 使能 FIFO 后接收数据不再读数据寄存器,而是从接收 FIFO 取出。

src/main.csrc/main.c · main
        /* Wait for SPI2 data reception */
        while (SPI_I2S_GetStatus(SPI2, SPI_I2S_RNE_FLAG) == RESET)
            ;

        /* Read SPI2 received data */
        SPI2_Buffer_Rx[RxIdx] = SPI_RX_FIFO_GET(SPI2);

        /* Wait for SPI_Master data reception */
        while (SPI_I2S_GetStatus(SPI_Master, SPI_I2S_RNE_FLAG) == RESET)
            ;

        /* Read SPI_Master received data */
        SPI_Master_Buffer_Rx[RxIdx++] = SPI_RX_FIFO_GET(SPI_Master);

4. 读出接收 CRC 值。 末帧 CRC 发送完成后,同样从接收 FIFO 读取两端收到的 CRC 值。

src/main.csrc/main.c · main
    /* Read SPI_Master received CRC value */
    CRC1Value = SPI_RX_FIFO_GET(SPI_Master);
    /* Read SPI2 received CRC value */
    CRC2Value = SPI_RX_FIFO_GET(SPI2);

对应源码: src/main.c、inc/main.h

5.26.3.3 FullDuplex_SoftNSS 软件 NSS 全双工主从互换

工程目录: projects/n32h49x_EVAL/examples/SPI/FullDuplex_SoftNSS/

第一阶段配置 SPI5 主机、SPI2 从机→软件 NSS 下全双工互传并比对→停止并复位两端→交换主从后重新配置复用→第二阶段 SPI2 主机、SPI5 从机再互传→复位计数与缓冲区并比对结果

项目 源码解读
功能 SPI5 与 SPI2 用软件 NSS 全双工互传 32 字节,第一阶段 SPI5 主、SPI2 从,第二阶段交换为主从后再传一次,两阶段各自比对收发数据。
源码组成 src/main.c:收发缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;
src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;
inc/main.h:外设、引脚与复用宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 数据方向为 SPI_DIR_DOUBLELINE_FULLDUPLEX,8 位数据,NSS 为 SPI_NSS_SOFT;
时钟极性/相位为 SPI_CLKPOL_LOW/SPI_CLKPHA_FIRST_EDGE,预分频 8,位序 LSB,CRC 多项式为 7;
两阶段仅交换 SpiMode,其余参数相同,GPIO_Configuration 按主从模式切换 SCK、MOSI 引脚为复用输出或输入;
系统时钟为 HSI+PLL、240 MHz。
程序与数据路径 第一阶段 main 配置 SPI5 主、SPI2 从后全双工互传,用 Buffercmp 得到 TransferStatus1、TransferStatus2;
SPI_I2S_DeInit 复位两端后交换主从,再次配置并重传;
第二阶段前清零收发索引与接收缓冲区,比对得到 TransferStatus3、TransferStatus4。
主要 API SPI_InitStruct、SPI_Init、SPI_Enable、SPI_I2S_DeInit、SPI_I2S_TransmitData、SPI_I2S_ReceiveData、SPI_I2S_GetStatus
验证方法 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK、MISO、MOSI;
四个 TransferStatus 全为 PASSED,表示两阶段主从互换收发均正确。
使用提示 须用跳线在同一块板上对连 SPI5 与 SPI2;
切换主从前调用 SPI_I2S_DeInit 复位寄存器,并重配 GPIO 复用方向;
基于 N32H497ZGL7_STB 评估板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例分两个阶段做全双工对传,阶段之间复位两端并交换主从,用来验证软件 NSS 下角色可以互换。

数据路径: SPI_Master_Buffer_Tx ⇄ SPI5 ⇄ PF7/PF8/PF9 ⇄ SPI2 ⇄ SPI2_Buffer_Rx,第二阶段主从互换后数据流向相反

1. 第一阶段配置主机与从机。 SPI5 配成主机,SPI2 只改工作模式后沿用同一参数。

src/main.csrc/main.c · main
    SPI_InitStruct(&SPI_InitStructure);
    /* SPI_Master Config -------------------------------------------------------------*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_LOW;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_FIRST_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_8;
    SPI_InitStructure.FirstBit      = SPI_FB_LSB;
    SPI_InitStructure.CRCPoly       = 7;
    SPI_Init(SPI_Master, &SPI_InitStructure);

    /* SPI2 Config -------------------------------------------------------------*/
    SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
    SPI_Init(SPI2, &SPI_InitStructure);

2. 复位两端并交换主从。 两阶段之间先用 SPI_I2S_DeInit 把寄存器复位,再把 SPI5 改从、SPI2 改主。

src/main.csrc/main.c · main
    SPI_I2S_DeInit(SPI_Master);
    SPI_I2S_DeInit(SPI2);

    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_InitStructure.SpiMode       = SPI_MODE_SLAVE;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_LOW;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_FIRST_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_8;
    SPI_InitStructure.FirstBit      = SPI_FB_LSB;
    SPI_InitStructure.CRCPoly       = 7;
    SPI_Init(SPI_Master, &SPI_InitStructure);

    /* SPI2 Re-configuration ---------------------------------------------------*/
    SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
    SPI_Init(SPI2, &SPI_InitStructure);

3. 第二阶段收发。 主从互换后,收发顺序随之改变,但读写数据的写法不变。

src/main.csrc/main.c · main
        /* Send SPI_Master data */
        SPI_I2S_TransmitData(SPI_Master, SPI_Master_Buffer_Tx[TxIdx]);
        /* Send SPI2 data */
        SPI_I2S_TransmitData(SPI2, SPI2_Buffer_Tx[TxIdx++]);

        /* Wait for SPI_Master data reception */
        while (SPI_I2S_GetStatus(SPI_Master, SPI_I2S_RNE_FLAG) == RESET)
            ;

        /* Read SPI_Master received data */
        SPI_Master_Buffer_Rx[RxIdx] = SPI_I2S_ReceiveData(SPI_Master);

        /* Wait for SPI2 data reception */
        while (SPI_I2S_GetStatus(SPI2, SPI_I2S_RNE_FLAG) == RESET)
            ;

        /* Read SPI2 received data */
        SPI2_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI2);

4. 比对第二阶段结果。 每个阶段都用 Buffercmp 比较接收缓冲区与对端发送缓冲区。

src/main.csrc/main.c · main
    TransferStatus3 = Buffercmp(SPI2_Buffer_Rx, SPI_Master_Buffer_Tx, BufferSize);
    TransferStatus4 = Buffercmp(SPI_Master_Buffer_Rx, SPI2_Buffer_Tx, BufferSize);

对应源码: src/main.c、inc/main.h

5.26.3.4 Simplex_Interrupt 单线中断收发

工程目录: projects/n32h49x_EVAL/examples/SPI/Simplex_Interrupt/

配置系统时钟、NVIC 与 GPIO 复用→初始化主机单线只发、从机单线只收→使能 NSS 输出与两端收发中断→使能 SPI 进入主循环等待→主机在发送中断里逐帧发出、从机在接收中断里逐帧接收→收满缓冲后比对并打印结果

项目 源码解读
功能 SPI5 作主机单线只发、SPI2 作从机单线只收,主机以 TE 中断逐帧发送、从机以 RNE 中断逐帧接收 32 字节,收满后比对并打印结果。
源码组成 src/main.c:发送缓冲区、接收缓冲区、main 与 RCC_Configuration、GPIO_Configuration、NVIC_Configuration、Buffercmp;
src/n32h49x_it.c、inc/n32h49x_it.h:SPI5 与 SPI2 中断服务函数;
inc/main.h:外设、引脚与复用宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 主机数据方向为 SPI_DIR_SINGLELINE_TX、从机为 SPI_DIR_SINGLELINE_RX,8 位数据,NSS 为 SPI_NSS_HARD;
时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,预分频 8,位序 MSB;
主机使能 SPI_I2S_INT_TE、从机使能 SPI_I2S_INT_RNE,NVIC 中 SPI5 抢占 1、SPI2_I2S2 抢占 0;
SPI5 引脚 SCK:PF7、MOSI:PF9,SPI2 引脚 SCK:PC7、MISO:PC2。
程序与数据路径 main 配置 GPIO 与 NVIC 后初始化两端并使能中断与 SPI;
SPI5_IRQHandler 在 TE 中断里发送 SPI_Master_Buffer_Tx[TxIdx++],发满后关 TE 中断;
SPI2_I2S2_IRQHandler 在 RNE 中断里把 SPI_I2S_ReceiveData 存入 SPI2_Buffer_Rx[RxIdx++];
主循环等到收满后 Buffercmp 比对并打印结果。
主要 API SPI_InitStruct、SPI_Init、SPI_SSOutputEnable、SPI_I2S_EnableInt、SPI_Enable、SPI_I2S_GetIntStatus、SPI_I2S_TransmitData、SPI_I2S_ReceiveData
验证方法 按 readme 用跳线连接 SPI5 与 SPI2 的 SCK 与单线数据线;
串口打印 Test PASS!! 表示 TransferStatus 为 PASSED,收发数据一致。
使用提示 单线数据线在主机端为 MOSI 引脚、在从机端为 MISO 引脚;
主循环用收满计数退出、不轮询中断标志;
基于 N32H497ZGL7_STB 评估板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用中断而不是查询方式收发:主机在发送缓冲空时进中断发下一字节,从机在接收缓冲非空时进中断收字节。

数据路径: SPI_Master_Buffer_Tx → SPI5_IRQHandler(TE 中断)→ SPI5 SPI_DAT → PF9(MOSI)→ 单线 → PC2(MISO)→ SPI2_I2S2_IRQHandler(RNE 中断)→ SPI2_Buffer_Rx

1. 配置单线发送与接收。 主机配成单线只发、从机配成单线只收,NSS 采用硬件模式。

src/main.csrc/main.c · main
    SPI_InitStruct(&SPI_InitStructure);
    /* SPI_Master configuration ------------------------------------------------*/
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_FIRST_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_HARD;
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_8;
    SPI_InitStructure.FirstBit      = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly       = 7;
    SPI_Init(SPI_Master, &SPI_InitStructure);

    /* SPI2 configuration -------------------------------------------------*/
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX;
    SPI_InitStructure.SpiMode       = SPI_MODE_SLAVE;
    SPI_Init(SPI2, &SPI_InitStructure);

2. 打开两端中断并使能 SPI。 主机使能发送中断、从机使能接收中断,再使能两个外设。

src/main.csrc/main.c · main
    /* Enable SPI_Master TXE interrupt */
    SPI_I2S_EnableInt(SPI_Master, SPI_I2S_INT_TE, ENABLE);
    /* Enable SPI2 RXNE interrupt */
    SPI_I2S_EnableInt(SPI2, SPI_I2S_INT_RNE, ENABLE);

    /* Enable SPI2 */
    SPI_Enable(SPI2, ENABLE);
    /* Enable SPI_Master */
    SPI_Enable(SPI_Master, ENABLE);

3. 配置并打开 NVIC。 采用 1 位抢占、3 位子优先级,SPI5 与 SPI2 分设不同抢占级。

src/main.csrc/main.c · NVIC_Configuration
    /* 1 bit for pre-emption priority, 3 bits for subpriority */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    /* Configure and enable SPI_Master interrupt -------------------------------*/
    NVIC_InitStructure.NVIC_IRQChannel                   = SPI5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* Configure and enable SPI2 interrupt --------------------------------*/
    NVIC_InitStructure.NVIC_IRQChannel                   = SPI2_I2S2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
    NVIC_Init(&NVIC_InitStructure);

4. 主机 TE 中断发送。 每次发送缓冲空时写入下一字节,发满后关闭发送中断。

src/n32h49x_it.csrc/n32h49x_it.c · SPI5_IRQHandler
    if (SPI_I2S_GetIntStatus(SPI5, SPI_I2S_INT_TE) != RESET)
    {
        /* Send SPI5 data */
        SPI_I2S_TransmitData(SPI5, SPI_Master_Buffer_Tx[TxIdx++]);

        /* Disable SPI5 TXE interrupt */
        if (TxIdx == BufferSize)
        {
            SPI_I2S_EnableInt(SPI5, SPI_I2S_INT_TE, DISABLE);
        }
    }

5. 从机 RNE 中断接收。 每次接收缓冲非空时读出一字节存入接收缓冲区。

src/n32h49x_it.csrc/n32h49x_it.c · SPI2_I2S2_IRQHandler
    /* Check the interrupt source */
    if (SPI_I2S_GetIntStatus(SPI2, SPI_I2S_INT_RNE) == SET)
    {
        /* Store SPI2 received data */
        SPI2_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI2);
    }

对应源码: src/main.c、src/n32h49x_it.c、inc/main.h

5.26.3.5 SPI_DMA_T&R DMA 单线发送与接收

工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_DMA_T&R/

配置时钟、GPIO 与片选空闲电平→配置从机接收 DMA 通道→配置主机发送 DMA 通道→初始化主机单线只发、从机单线只收→打开收发 DMA 请求并使能 SPI 与 DMA 通道→等 DMA 完成并比对数据打印结果

项目 源码解读
功能 SPI5 作主机单线只发、SPI2 作从机单线只收,发送由 DMA1_CH3 逐字节搬出、接收由 DMA1_CH4 逐字节搬入缓冲,完成后比对并打印结果。
源码组成 src/main.c:发送缓冲区、接收缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;
src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;
inc/main.h:外设、引脚、复用、DMA 通道与完成标志宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 主机数据方向为 SPI_DIR_SINGLELINE_TX、从机为 SPI_DIR_SINGLELINE_RX,8 位数据,NSS 为 SPI_NSS_HARD;
时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,预分频 8,位序 MSB;
DMA 为字节宽度、外设地址不递增、存储器地址递增、普通模式、最高优先级,发送用 DMA1_CH3、接收用 DMA1_CH4;
主机引脚 NSS:PF6、SCK:PF7、MOSI:PF9,从机引脚 NSS:PD1、SCK:PC7、MISO:PC2。
程序与数据路径 main 先配置从机接收与主机发送两条 DMA 通道并重映射请求;
初始化两端后调用 SPI_I2S_EnableDma 打开从机接收与主机发送请求;
使能 SPI 与两条 DMA 通道后等两个完成标志;
最后用 Buffercmp 比对 SPI_SLAVE_Buffer_Rx 与发送缓冲区并打印结果。
主要 API SPI_InitStruct、SPI_Init、SPI_SSOutputEnable、SPI_I2S_EnableDma、SPI_Enable、DMA_Init、DMA_RequestRemap、DMA_EnableChannel、DMA_GetFlagStatus
验证方法 按 readme 用跳线连接 SPI5 与 SPI2 的 NSS、SCK 与单线数据线;
串口打印 Test PASS!! 表示 TransferStatus 为 PASSED,DMA 收发数据一致。
使用提示 单线数据线在主机端为 MOSI 引脚、在从机端为 MISO 引脚;
DMA 请求要先于 SPI 使能打开;
基于 N32H497ZGL7_STB 评估板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例把发送与接收都交给 DMA:主机端一条通道把发送缓冲区写进数据寄存器,从机端另一条通道把接收数据寄存器读进缓冲区。

数据路径: SPI_MASTER_Buffer_Tx → DMA1_CH3 → SPI_MASTER->DAT → SPI5 → PF9(MOSI)→ 单线 → PC2(MISO)→ SPI2 → SPI_SLAVE->DAT → DMA1_CH4 → SPI_SLAVE_Buffer_Rx → Buffercmp

1. 配置从机接收 DMA 通道。 方向为外设到存储器,从 SPI_SLAVE->DAT 读字节写入接收缓冲区。

src/main.csrc/main.c · main
    DMA_DeInit(SPI_SLAVE_Rx_DMA_Channel);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = (uint32_t)&SPI_SLAVE->DAT;
    DMA_InitStructure.MemAddr        = (uint32_t)SPI_SLAVE_Buffer_Rx;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize        = BufferSize;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;

2. 配置主机发送 DMA 通道。 复用同一结构体,只把方向改成存储器到外设并换成发送侧地址。

src/main.csrc/main.c · main
    /* SPI_MASTER_Tx_DMA_Channel configuration ---------------------------------------------*/
    DMA_DeInit(SPI_MASTER_Tx_DMA_Channel);
    DMA_InitStructure.PeriphAddr = (uint32_t)&SPI_MASTER->DAT;
    DMA_InitStructure.MemAddr    = (uint32_t)SPI_MASTER_Buffer_Tx;
    DMA_InitStructure.Direction  = DMA_DIR_PERIPH_DST;
    DMA_Init(SPI_MASTER_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(SPI_MASTER_Tx_DMA_Remap, SPI_MASTER_Tx_DMA_Channel, ENABLE);

3. 初始化主机与从机。 主机配成单线只发、从机配成单线只收,随后打开主机的 NSS 输出。

src/main.csrc/main.c · main
    /* SPI_MASTER configuration ------------------------------------------------------*/
    SPI_InitStruct(&SPI_InitStructure);
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_FIRST_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_HARD;
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_8;
    SPI_InitStructure.FirstBit      = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly       = 7;
    SPI_Init(SPI_MASTER, &SPI_InitStructure);

    /* SPI_SLAVE configuration ------------------------------------------------------*/
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX;
    SPI_InitStructure.SpiMode       = SPI_MODE_SLAVE;
    SPI_Init(SPI_SLAVE, &SPI_InitStructure);

4. 打开收发 DMA 请求。 从机使能接收请求、主机使能发送请求,请求要先于 SPI 使能打开。

src/main.csrc/main.c · main
    /* Enable SPI_MASTER NSS output for master mode */
    SPI_SSOutputEnable(SPI_MASTER, ENABLE);

    /* Enable SPI_SLAVE Rx request */
    SPI_I2S_EnableDma(SPI_SLAVE, SPI_I2S_DMA_RX, ENABLE);
    /* Enable SPI_MASTER Tx request */
    SPI_I2S_EnableDma(SPI_MASTER, SPI_I2S_DMA_TX, ENABLE);

5. 使能 SPI 与 DMA 通道。 先使能两个 SPI,再打开两条 DMA 通道触发搬运。

src/main.csrc/main.c · main
    /* Enable SPI_SLAVE */
    SPI_Enable(SPI_SLAVE, ENABLE);
    /* Enable SPI_MASTER */
    SPI_Enable(SPI_MASTER, ENABLE);

    /* Enable DMA1 Channel3 */
    DMA_EnableChannel(SPI_MASTER_Tx_DMA_Channel, ENABLE);
    /* Enable DMA1 Channel4 */
    DMA_EnableChannel(SPI_SLAVE_Rx_DMA_Channel, ENABLE);

对应源码: src/main.c、inc/main.h

5.26.3.6 SPI_FLASH 读写与擦除 W25Q128

工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_FLASH/

初始化 SPI FLASH 驱动→读取并校验 W25Q128 的 ID→擦除扇区并写入发送缓冲区→读回数据并比对→再擦除扇区并读回校验为全 0xFF→串口打印读写与擦除结果

项目 源码解读
功能 通过 SPI3 驱动外挂 W25Q128,先读 ID 校验型号,再擦除指定扇区、写入一段字符串并读回比对,最后擦除后读回校验全为 0xFF。
源码组成 src/main.c:发送/接收缓冲区、main 与 Buffercmp;
src/spi_flash.c:驱动实现 sFLASH_Init、sFLASH_ReadID、sFLASH_EraseSector、sFLASH_WriteBuffer、sFLASH_ReadBuffer 与底层收发;
inc/spi_flash.h:命令、容量与引脚等宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 驱动内 SPI 为 SPI_DIR_DOUBLELINE_FULLDUPLEX、主机、8 位数据,SPI_CLKPOL_HIGH/SPI_CLKPHA_SECOND_EDGE,NSS 为 SPI_NSS_SOFT,预分频 16,位序 MSB,CRC 多项式为 7;
SPI3 引脚 NSS:PA15、SCK:PB3、MISO:PD11、MOSI:PD12;
串口 TX:PA9、115200、8 位数据、1 位停止;
系统时钟为 HSI+PLL、240 MHz。
程序与数据路径 main 调 sFLASH_Init 初始化驱动,再 sFLASH_ReadID 读 ID 并与 sFLASH_W25Q128_ID 比较;
擦除扇区后 sFLASH_WriteBuffer 写入 Tx_Buffer,sFLASH_ReadBuffer 读回 Rx_Buffer;
Buffercmp 检查读写一致,再擦除并用循环检查读回数据均为 0xFF。
主要 API SPI_InitStruct、SPI_Init、SPI_Enable、SPI_I2S_TransmitData、SPI_I2S_ReceiveData
验证方法 按 readme 连接 J20 跳线帽;
串口打印 Read ID 为 W25Q128 的 ID、Flash write OK.、Sector erase OK.,表示读写与擦除均正确。
使用提示 需连接跳线帽 J20 并外挂 W25Q128;
写前须先擦除对应扇区;
基于 N32H497ZGL7_EVB 全功能板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例用片内 SPI3 以软件片选访问外挂 W25Q128,驱动里封装了读 ID、扇区擦除、页写与读缓冲。

数据路径: Tx_Buffer → sFLASH_WriteBuffer → sFLASH_WritePage → W25Q128 存储单元 → sFLASH_ReadBuffer → Rx_Buffer → Buffercmp

1. 初始化驱动并读取 ID。 先初始化 SPI 与片选,再发 RDID 命令读回三字节组成 ID 并与目标型号比较。

src/main.csrc/main.c · main
    /* Initialize the SPI FLASH driver */
    sFLASH_Init();

    /* Get SPI Flash ID */
    FlashID = sFLASH_ReadID();
    printf("Read ID=0x%06x\r\n", FlashID);

    /* Check the SPI Flash ID */
    if ((FlashID == sFLASH_ID) || (FlashID == sFLASH_W25Q128_ID_DTR))
    {

2. 擦除、写入、读回并比对。 写前必须先擦除扇区,写完成后读回并比对。

src/main.csrc/main.c · main
        /* Erase SPI FLASH Sector to write on */
        sFLASH_EraseSector(FLASH_SectorToErase);

        /* Write Tx_Buffer data to SPI FLASH memory */
        sFLASH_WriteBuffer(Tx_Buffer, FLASH_WriteAddress, BufferSize);

        /* Read data from SPI FLASH memory */
        sFLASH_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);

        /* Check the correctness of written dada */
        TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);

3. 驱动内的 SPI 初始化。 片选默认拉高,SPI3 配成主机全双工、软件 NSS。

src/spi_flash.csrc/spi_flash.c · sFLASH_Init
    sFLASH_LowLevel_Init();

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    /*!< SPI configuration */
    SPI_InitStruct(&SPI_InitStructure);
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;

    SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_16;

    SPI_InitStructure.FirstBit = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly  = 7;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

4. 读 ID 的实现。 拉低片选后发 0x9F,连续读回三字节并在退出前拉高片选。

src/spi_flash.csrc/spi_flash.c · sFLASH_ReadID
    sFLASH_CS_LOW();

    /*!< Send "RDID " instruction */
    sFLASH_SendByte(0x9F);
    /*!< Read a byte from the FLASH */
    Temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    /*!< Read a byte from the FLASH */
    Temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    /*!< Read a byte from the FLASH */
    Temp2 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);

    /*!< Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

    return Temp;

对应源码: src/main.c、src/spi_flash.c、inc/spi_flash.h

5.26.3.7 SPI_FLASH_DMA DMA 读写 W25Q128

工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_FLASH_DMA/

初始化 SPI FLASH 驱动→读取并校验 W25Q128 的 ID→填入递增测试数据→擦除扇区并经 DMA 写入→经 DMA 读回并比对→再擦除扇区并读回校验为全 0xFF→串口打印结果

项目 源码解读
功能 通过 SPI3 加 DMA 访问外挂 W25Q128,读 ID 校验型号后擦除扇区、经 DMA 写入 300 字节递增数据并读回比对,再擦除后读回校验全为 0xFF。
源码组成 src/main.c:测试数据与接收缓冲区、main 与 Buffercmp;
src/spi_flash.c:驱动实现 sFLASH_DMA_Init、sFLASH_DMA_WriteBuffer、sFLASH_DMA_ReadBuffer 及底层收发;
inc/spi_flash.h:命令、容量、引脚与 DMA 通道宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 批量读写走 DMA,读时把 SPI 切为只读模式免去持续发送 0xFF;
DMA 为字节宽度、外设地址不递增、存储器地址递增、普通模式、最高优先级;
SPI3 引脚 NSS:PA15、SCK:PB3、MISO:PD11、MOSI:PD12;
串口 TX:PA9、115200、8 位数据、1 位停止,系统时钟 HSI+PLL、240 MHz。
程序与数据路径 main 先 sFLASH_Init 与 sFLASH_ReadID,并把 Tx_Buffer 填成递增序列;
擦除后 sFLASH_DMA_WriteBuffer 经 DMA 写入,sFLASH_DMA_ReadBuffer 经 DMA 读回 Rx_Buffer;
Buffercmp 检查读写一致,再擦除并用循环检查读回数据均为 0xFF。
主要 API SPI_InitStruct、SPI_Init、SPI_Enable、SPI_I2S_DeInit、SPI_I2S_EnableDma、DMA_Init、DMA_RequestRemap、DMA_EnableChannel、DMA_GetFlagStatus
验证方法 按 readme 连接 J20 跳线帽;
串口打印 spi dma write and read flash OK. 与 Sector erase OK.,表示 DMA 读写与擦除均正确。
使用提示 仅大批量读写时用 DMA,读时改为只读模式以提升性能;
需连接跳线帽 J20 并外挂 W25Q128;
基于 N32H497ZGL7_EVB 全功能板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例让 DMA 承担 Flash 的批量读写;写前先擦除扇区,读时把 SPI 改成只读模式,避免为读数据而持续发送 0xFF。

数据路径: Tx_Buffer → sFLASH_DMA_WriteBuffer → sFLASH_DMA_PageWrite → DMA → W25Q128 → DMA → Rx_Buffer → Buffercmp

1. 填数据并调用 DMA 读写。 擦除扇区后用 DMA 写入,再用 DMA 读回并比对。

src/main.csrc/main.c · main
    sFLASH_Init();

    /* Get SPI Flash ID */
    FlashID = sFLASH_ReadID();
    printf("Read ID=0x%06x\r\n", FlashID);

    for(i = 0; i < 512; i++)
    {
        Tx_Buffer[i] = i + 1;
    }

    /* Check the SPI Flash ID */
    if ((FlashID == sFLASH_ID) || (FlashID == sFLASH_W25Q128_ID_DTR))
    {

        /* Perform a write in the Flash followed by a read of the written data */
        /* Erase SPI FLASH Sector to write on */
        sFLASH_EraseSector(FLASH_SectorToErase);

        /* Write Tx_Buffer data to SPI FLASH memory */
        //SPI_DMA_PageWrite(FLASH_WriteAddress);
        sFLASH_DMA_WriteBuffer(Tx_Buffer, FLASH_WriteAddress, BufferSize);

        /* Read data from SPI FLASH memory */
        //SPI_DMA_BufferRead(FLASH_ReadAddress);
        sFLASH_DMA_ReadBuffer(Rx_Buffer, FLASH_ReadAddress, BufferSize);

        /* Check the correctness of written dada */
        TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);

2. 初始化 DMA 通道。 按收发类型选择通道,外设地址固定指向 SPI 数据寄存器,存储器地址递增。

src/spi_flash.csrc/spi_flash.c · sFLASH_DMA_Init
    if(TransmitType == DMA_RECV)
    {
        /* SPI_FLASH_Rx_DMA_Channel configuration ---------------------------------------------*/
        DMA_DeInit(sFLASH_SPI_Rx_DMA_Channel);
        DMA_StructInit(&DMA_InitStructure);
        DMA_InitStructure.PeriphAddr     = (uint32_t)&sFLASH_SPI->DAT;
        DMA_InitStructure.MemAddr        = (uint32_t)MemoryAddr;
        DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
        DMA_InitStructure.BufSize        = BufferLen;
        DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;

3. 经 DMA 读回并切只读模式。 发读命令后把 SPI 复位并改为只读,再开 DMA 接收请求并等完成标志。

src/spi_flash.csrc/spi_flash.c · sFLASH_DMA_ReadBuffer
    /* Select the FLASH: Chip Select low */
    sFLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    sFLASH_SendByte(sFLASH_CMD_READ);

    /* Send ReadAddr high nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* Send ReadAddr medium nibble address byte to read from */
    sFLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* Send ReadAddr low nibble address byte to read from */
    sFLASH_SendByte(ReadAddr & 0xFF);

    /*!< Disable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, DISABLE);
    /*!< DeInitializes the sFLASH_SPI */
    SPI_I2S_DeInit(sFLASH_SPI);

    /*!< Set the sFLASH_SPI ReadOnly*/
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_RONLY;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    sFLASH_DMA_Init(DMA_RECV, (uint32_t)pBuffer, NumByteToRead);
    /* Enable DMA1 Channel2 */
    DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, ENABLE);
    /* Enable sFLASH_SPI Rx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, ENABLE);

    /*!< Enable the sFLASH_SPI  */
    SPI_Enable(sFLASH_SPI, ENABLE);

    /* Wait for sFLASH_SPI_Rx_DMA_FLAG transfer complete */
    while (!DMA_GetFlagStatus(sFLASH_SPI_Rx_DMA_FLAG, sFLASH_SPI_DMA));

    /* Disnable sFLASH_SPI Rx request */
    SPI_I2S_EnableDma(sFLASH_SPI, SPI_I2S_DMA_RX, DISABLE);
    /* Disable DMA1 Channel2 */
    DMA_EnableChannel(sFLASH_SPI_Rx_DMA_Channel, DISABLE);

    /* Deselect the FLASH: Chip Select high */
    sFLASH_CS_HIGH();

4. 写缓冲按页拆分。 写 300 字节超过一页,函数先把要写的字节拆成整页与不足一页两部分。

src/spi_flash.csrc/spi_flash.c · sFLASH_DMA_WriteBuffer
    uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

    Addr        = WriteAddr % sFLASH_SPI_PAGESIZE;
    count       = sFLASH_SPI_PAGESIZE - Addr;
    NumOfPage   = NumByteToWrite / sFLASH_SPI_PAGESIZE;
    NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;

对应源码: src/main.c、src/spi_flash.c、inc/spi_flash.h

5.26.3.8 SPI_Simplex_DMA_Recv DMA 单线接收

工程目录: projects/n32h49x_EVAL/examples/SPI/SPI_Simplex_DMA_Recv/

配置时钟、GPIO 与片选空闲电平→配置从机接收 DMA 通道→初始化主机单线只发、从机单线只收→打开从机接收 DMA 请求并使能 SPI 与 DMA 通道→主机逐帧写数据、从机经 DMA 接收→等 DMA 完成并比对数据打印结果

项目 源码解读
功能 SPI5 作主机单线只发、SPI2 作从机单线只收,主机以查询方式逐字节发送、从机用 DMA1_CH4 把接收数据搬入缓冲,收满后比对并打印结果。
源码组成 src/main.c:发送缓冲区、接收缓冲区、main 与 RCC_Configuration、GPIO_Configuration、Buffercmp;
src/n32h49x_it.c、inc/n32h49x_it.h:中断服务函数;
inc/main.h:外设、引脚、复用、DMA 通道与完成标志宏;
readme.txt:功能、环境与接线说明;
MDK-ARM/、EWARM/:Keil 与 IAR 工程。
关键配置 主机数据方向为 SPI_DIR_SINGLELINE_TX、从机为 SPI_DIR_SINGLELINE_RX,8 位数据,NSS 为 SPI_NSS_HARD;
时钟极性/相位为 SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE,预分频 32,位序 MSB;
接收 DMA 为字节宽度、外设地址不递增、存储器地址递增、普通模式、最高优先级,使用 DMA1_CH4;
主机引脚 NSS:PF6、SCK:PF7、MOSI:PF9,从机引脚 NSS:PD1、SCK:PC7、MISO:PC2。
程序与数据路径 main 先配置从机接收 DMA 通道并重映射请求;
初始化两端后调用 SPI_I2S_EnableDma 打开从机接收请求并使能 SPI 与 DMA 通道;
主机在循环里等 TE 后写 SPI_I2S_TransmitData,从机数据经 DMA 写入 SPI_SLAVE_Buffer_Rx;
等接收完成标志后用 Buffercmp 比对并打印结果。
主要 API SPI_InitStruct、SPI_Init、SPI_SSOutputEnable、SPI_I2S_EnableDma、SPI_Enable、SPI_I2S_GetStatus、SPI_I2S_TransmitData、DMA_Init、DMA_EnableChannel、DMA_GetFlagStatus
验证方法 按 readme 用跳线连接 SPI5 与 SPI2 的 NSS、SCK 与单线数据线;
串口打印 Test PASS!! 表示 TransferStatus 为 PASSED,DMA 接收数据与发送一致。
使用提示 单线数据线在主机端为 MOSI 引脚、在从机端为 MISO 引脚;
发送用查询、接收用 DMA,二者共用一个 SPI 时钟;
基于 N32H497ZGL7_STB 评估板;
开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。
核心代码解读与移植

示例只把接收交给 DMA:主机仍逐字节查询发送,从机的接收数据由 DMA 自动搬进缓冲区。

数据路径: SPI_MASTER_Buffer_Tx → SPI_I2S_TransmitData → SPI5 → PF9(MOSI)→ 单线 → PC2(MISO)→ SPI2 → SPI_SLAVE->DAT → DMA1_CH4 → SPI_SLAVE_Buffer_Rx → Buffercmp

1. 配置从机接收 DMA 通道。 方向为外设到存储器,从 SPI_SLAVE->DAT 读字节写入接收缓冲区。

src/main.csrc/main.c · main
    DMA_DeInit(SPI_SLAVE_Rx_DMA_Channel);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = (uint32_t)&SPI_SLAVE->DAT;
    DMA_InitStructure.MemAddr        = (uint32_t)SPI_SLAVE_Buffer_Rx;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize        = BufferSize;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;

2. 初始化主机与从机并打开请求。 主机配成单线只发、从机配成单线只收,主机打开 NSS 输出、从机打开接收请求。

src/main.csrc/main.c · main
    /* SPI_MASTER configuration ------------------------------------------------------*/
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_FIRST_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_HARD;
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_32;
    SPI_InitStructure.FirstBit      = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly       = 7;
    SPI_Init(SPI_MASTER, &SPI_InitStructure);

    /* SPI_SLAVE configuration ------------------------------------------------------*/
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_RX;
    SPI_InitStructure.SpiMode       = SPI_MODE_SLAVE;
    SPI_Init(SPI_SLAVE, &SPI_InitStructure);

    /* Enable SPI_MASTER NSS output for master mode */
    SPI_SSOutputEnable(SPI_MASTER, ENABLE);

    /* Enable SPI_SLAVE Rx request */
    SPI_I2S_EnableDma(SPI_SLAVE, SPI_I2S_DMA_RX, ENABLE);

3. 使能 SPI 与接收 DMA 通道。 先使能两个 SPI,再打开接收 DMA 通道等待搬运。

src/main.csrc/main.c · main
    /* Enable SPI_SLAVE */
    SPI_Enable(SPI_SLAVE, ENABLE);
    /* Enable SPI_MASTER */
    SPI_Enable(SPI_MASTER, ENABLE);

    /* Enable DMA1 Channel4 */
    DMA_EnableChannel(SPI_SLAVE_Rx_DMA_Channel, ENABLE);

4. 主机逐帧发送并等 DMA 完成。 主机等发送缓冲空后写下一字节,发完等接收 DMA 完成标志。

src/main.csrc/main.c · main
    /* Transfer procedure */
    while (TxIdx < BufferSize)
    {
        /* Wait for SPI_MASTER Tx buffer empty */
        while (SPI_I2S_GetStatus(SPI_MASTER, SPI_I2S_TE_FLAG) == RESET)
            ;

        /* Send SPI_MASTER data */
        SPI_I2S_TransmitData(SPI_MASTER, SPI_MASTER_Buffer_Tx[TxIdx++]);
    }

    /* Wait for DMA1 channel4 transfer complete */
    while (!DMA_GetFlagStatus(SPI_SLAVE_Rx_DMA_FLAG, DMA1))
        ;

对应源码: src/main.c、inc/main.h

5.26.4 注意与排障

场景 检查与建议 来源
关闭 SPI 模块的等待条件 全双工模式关闭前依次等最后一个数据接收完成、TE 置 1、BUSY 清 0;
只发送模式写最后一个数据后先等 TE=1 再等 BUSY=0;
主机只接收模式在倒数第二个数据接收后延迟一个 SPI 时钟再关闭;
从机只接收模式可在任意时刻关闭,进入停机模式前须先等 BUSY=0。
用户手册
主从时钟参数必须一致 主设备与从设备的时钟极性 CLKPOL、时钟相位 CLKPHA 配置必须相同,否则数据采样沿错位。 用户手册
从机 CRC 使能的时机 从机模式下须在时钟稳定之后再使能 CRC 计算,否则 CRC 结果可能出错;
只要 CRCEN 置位且 SCK 上有输入时钟,无论 SPIEN 状态都会进行 CRC 计算。
用户手册
发送 CRC 的时机与开销 CPU 模式(非 FIFO)须在发送最后一个数据前置位 CRCNEXT 以发送 CRC,DMA 模式由硬件自动发送;
FIFO 只收模式按 TRANSNUM 自动发送;
SPI 时钟较高时应减少 CPU 参与并在发送 CRC 期间避免函数调用。
用户手册
多从设备下 CRC 的清除 从设备切换(NSS 由高变低)时,应清除主从两端 CRC 数值以保持下次结果同步,步骤为关 SPI、清 CRCEN、置 CRCEN、再使能 SPI。 用户手册
软件计算 CRC 的参数 软件模拟硬件 CRC 时采用多项式经 CRCPOLY 配置、初始值 0x0000、结果异或 0x0000、输入与输出均不反转。 用户手册
FIFO 模式下的数据读写 使能 FIFO 后从 SPI_RX_FIFO 读接收数据,未使能 FIFO 时读 SPI_DAT;
无论是否使能 FIFO,发送都写 SPI_DAT。
用户手册
溢出与 BUSY 标志的处理 只发送模式不读接收数据会使 OVER 置 1,软件应忽略该标志;
从机模式每个数据项之间 BUSY 会有一个 SPI 时钟周期为低,不要用 BUSY 判断每个数据项的收发。
用户手册
国民技术开发套件文档中心

按芯片系列在线查阅国民技术 MCU 开发套件的芯片资料、SDK、驱动 API 与 Demo 示例说明。

© 2026 国民技术股份有限公司 版权所有粤ICP备11069485号本站使用不含 Cookie 的匿名访问统计