5.26 SPI Guide

This chapter covers the SPI driver files in the N32H49x standard peripheral library, its 32 public functions without the I2S_ prefix, and the 8 examples CRC, CRC_FIFO, FullDuplex_SoftNSS, Simplex_Interrupt, SPI_DMA_T&R, SPI_FLASH, SPI_FLASH_DMA, and SPI_Simplex_DMA_Recv. SPI shares n32h49x_spi.h and n32h49x_spi.c with I2S; the 11 functions with the I2S_ prefix are documented in the I2S chapter. Read it together with the “Serial peripheral interface/Inter-IC sound (SPI/I2S)” chapter of the user manual for chip-select management, four-wire timing, CRC, and FIFO details.

5.26.1 Related files

Relative path Purpose
firmware/n32h49x_std_periph_driver/inc/n32h49x_spi.h Defines the SPI_InitType structure and the data-direction, master/slave mode, data length, clock polarity/phase, NSS, baud-rate prescaler, bit order, and CRC polynomial macros, and declares the 32 functions without the I2S_ prefix;
the 11 functions with the I2S_ prefix in this header belong to the I2S chapter
firmware/n32h49x_std_periph_driver/src/n32h49x_spi.c Implements the 32 functions: initialization and enable, interrupt and DMA enable, data transfer, NSS and data-length configuration, CRC computation and check, flag and interrupt status queries, and FIFO and transfer control
projects/n32h49x_EVAL/examples/SPI/ Root of the SPI examples, holding 8 examples: CRC, CRC_FIFO, FullDuplex_SoftNSS, Simplex_Interrupt, SPI_DMA_T&R, SPI_FLASH, SPI_FLASH_DMA, SPI_Simplex_DMA_Recv
projects/n32h49x_EVAL/examples/SPI/CRC/ Project layout of that example: MDK-ARM/, EWARM/, inc/, src/, readme.txt
projects/n32h49x_EVAL/bsp/ Board support files referenced by some examples: UART log printing (log.c/log.h) and printf redirection (print_remap.c)

5.26.2 API reference

Search API32 functions

The following 32 public functions are listed in the declaration order of n32h49x_spi.h in 5 groups; each entry gives a summary and the original comment above the function in n32h49x_spi.c.

SPI initialization and enable

SPI_I2S_DeInit, SPI_Init, SPI_InitStruct, SPI_Enable

Interrupts, DMA, and data transfer

SPI_I2S_EnableInt, SPI_I2S_EnableDma, SPI_I2S_TransmitData, SPI_I2S_ReceiveData

NSS, data length, CRC, and bidirectional mode

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

Flags and interrupt status

SPI_I2S_GetStatus, SPI_I2S_ClrCRCErrFlag, SPI_I2S_GetIntStatus, SPI_I2S_ClrITPendingBit

FIFO and transfer control

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)Deinitializes the SPIx peripheral registers to their default reset values (Affects also the I2Ss)
Source comment
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)Initializes the SPIx peripheral according to the specified parameters in the SPI_InitStruct.
Source comment
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)Fills each SPI_InitStruct member with its default value.
Source comment
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)Enables or disables the specified SPI peripheral.
Source comment
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)Enables or disables the specified SPI/I2S interrupts.
Source comment
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)Enables or disables the SPIx/I2Sx DMA interface.
Source comment
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)Transmits a Data through the SPIx/I2Sx peripheral.
Source comment
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)Returns the most recent received data by the SPIx/I2Sx peripheral.
Source comment
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)Configures internally by software the NSS pin for the selected SPI.
Source comment
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)Enables or disables the SS output for the selected SPI.
Source comment
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)Configures the data size for the selected SPI.
Source comment
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)Transmit the SPIx CRC value.
Source comment
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)Enables or disables the CRC value calculation of the transferred bytes.
Source comment
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)Returns the transmit or the receive CRC register value for the specified SPI.
Source comment
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)Returns the CRC Polynomial register value for the specified SPI.
Source comment
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)Selects the data transfer direction in bi-directional mode for the specified SPI.
Source comment
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)Checks whether the specified SPI/I2S flag is set or not.
Source comment
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)Clears the SPIx CRC Error (CRCERR) flag.
Source comment
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)Checks whether the specified SPI/I2S interrupt has occurred or not.
Source comment
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)Clears the SPIx CRC Error (CRCERR) interrupt pending bit.
Source comment
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)Enables or disables the FIFO (in SPI/I2S mode).
Source comment
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)Clears the SPIx FIFO bit.
Source comment
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)Configures the Rx FIFO size for the selected SPI.
Source comment
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)Configures the Tx FIFO size for the selected SPI.
Source comment
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)In FIFO mode, the current number of valid FIFOs.
Source comment
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)In FIFO mode, the current number of valid FIFOs.
Source comment
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)When in FIFO mode with CRC function, the user needs to write the number of data to be transmitted in advance.
Source comment
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)When in FIFO mode with CRC function, the user write the number of data to be transmitted in advance.
Source comment
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 master clock delay time configuration.
Source comment
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)Get SPI master clock delay time.
Source comment
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)In FIFIO mode, write the data in the receiving FIFO.
Source comment
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)In FIFIO mode, read the data in the receiving FIFO.
Source comment
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 descriptions

This section covers the 8 SPI examples: CRC and CRC_FIFO do CRC-checked full-duplex transfers between two SPIs (the latter through FIFO), FullDuplex_SoftNSS swaps master and slave in two phases with software NSS, Simplex_Interrupt does single-line transfer with TE/RNE interrupts, SPI_DMA_T&R and SPI_Simplex_DMA_Recv use DMA for single-line transmit and receive, and SPI_FLASH and SPI_FLASH_DMA access the W25Q128. SPI5 acts as master on PF6~PF9 and SPI2 as slave on PC2, PC7, PD1, PD2; the two must be wired together with jumpers.

5.26.3.1 CRC Full-duplex CRC check on two SPIs

Example directory: projects/n32h49x_EVAL/examples/SPI/CRC/

Configure the system clocks and GPIO alternate functions→Initialize the master and slave→Enable CRC computation and SPI on both ends→Master and slave transfer in full duplex together→Send the CRC with the last frame→Compare the data and CRC and check for CRC errors

Item Source analysis
Function SPI5 as master and SPI2 as slave transfer 32 frames of 16-bit data in full duplex;
after the transfer they send a CRC value and compare it, checking the data and CRC status.
Source files src/main.c: transmit/receive buffers, main with RCC_Configuration, GPIO_Configuration, and Buffercmp;
src/n32h49x_it.c, inc/n32h49x_it.h: interrupt handlers;
inc/main.h: peripheral, pin, and alternate-function macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Data direction SPI_DIR_DOUBLELINE_FULLDUPLEX, 16-bit data, master SPI5, slave SPI2;
clock polarity/phase SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE, NSS SPI_NSS_SOFT, prescaler 128, bit order MSB, CRC polynomial 7;
SPI5 pins SCK:PF7, MISO:PF8, MOSI:PF9 (AF11) and SPI2 pins SCK:PC7, MISO:PC2, MOSI:PD2;
system clock HSI+PLL at 240 MHz.
Program and data path main initializes both ends and calls SPI_EnableCalculateCrc to turn on CRC computation;
in the loop SPI5 and SPI2 transfer at the same time, writing SPI_I2S_TransmitData and reading SPI_I2S_ReceiveData;
on the last frame SPI_TransmitCrcNext triggers the CRC transmission;
finally Buffercmp compares the data and checks SPI_CRCERR_FLAG.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_EnableCalculateCrc, SPI_TransmitCrcNext, SPI_I2S_TransmitData, SPI_I2S_ReceiveData, SPI_I2S_GetStatus
Verification Wire SPI5 and SPI2 SCK, MISO, and MOSI together with jumpers as in the readme;
TransferStatus1 and TransferStatus2 are both PASSED with no CRCERR set, meaning the data and CRC both check out.
Use note Jumpers must connect SPI5 and SPI2 on the same board;
the slave must wait for the clock to be stable before enabling CRC computation;
based on the N32H497ZGL7_STB evaluation board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example configures SPI5 as master and SPI2 as slave, enables CRC computation on both ends, and sets CRCNEXT before the last frame so the hardware appends the CRC value after the data.

Data path: SPI_Master_Buffer_Tx → SPI_I2S_TransmitData → SPI5 SPI_DAT → PF9 (MOSI) → SPI2 → PC2 (MISO) → SPI2_Buffer_Rx → Buffercmp

1. Initialize master and slave. Fill in defaults, configure SPI5 with the master parameters, then reuse the same structure for SPI2 changing only the mode.

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. Enable CRC computation on both ends and enable SPI. Setting the CRC enable bit clears the transmit and receive CRC registers.

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. Full-duplex transfer with CRC on the last frame. Master and slave write, send, read, and receive in the same loop.

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. Check the CRC result and read the received CRC values. A set CRCERR on either end marks failure, then the received CRC registers of both ends are read.

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);

Corresponding source files: src/main.c, inc/main.h

5.26.3.2 CRC_FIFO CRC-checked transfer through FIFO

Example directory: projects/n32h49x_EVAL/examples/SPI/CRC_FIFO/

Configure the system clocks and GPIO alternate functions→Initialize the master and slave→Set the FIFO watermarks and enable FIFO→Enable CRC computation and SPI on both ends→Transfer in full duplex through FIFO→Send the CRC on the last frame and read the result from the receive FIFO

Item Source analysis
Function On top of the CRC full-duplex transfer, FIFO is enabled so SPI5 and SPI2 exchange 32 frames of 16-bit data through the transmit and receive FIFOs, sending the CRC on the last frame and reading the result from the receive FIFO to compare.
Source files src/main.c: transmit/receive buffers, main with RCC_Configuration, GPIO_Configuration, and Buffercmp;
src/n32h49x_it.c, inc/n32h49x_it.h: interrupt handlers;
inc/main.h: peripheral, pin, and alternate-function macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Data direction SPI_DIR_DOUBLELINE_FULLDUPLEX, 16-bit data, master SPI5, slave SPI2;
both ends set the Tx/Rx FIFO watermark to SPI_RX_FIF0NUM_1/SPI_TX_FIF0NUM_1 and enable FIFO with SPI_I2S_FIFO_Cmd;
clock polarity/phase SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE, NSS SPI_NSS_SOFT, prescaler 128, bit order MSB, CRC polynomial 7;
system clock HSI+PLL at 240 MHz.
Program and data path main initializes both ends, sets the FIFO watermarks and enables FIFO, then calls SPI_EnableCalculateCrc to turn on CRC;
in the loop master and slave transfer together and reception switches to SPI_RX_FIFO_GET reading the receive FIFO;
on the last frame SPI_TransmitCrcNext triggers the CRC transmission;
finally Buffercmp compares the data and checks SPI_CRCERR_FLAG.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_RxFIFOSizeConfig, SPI_TxFIFOSizeConfig, SPI_I2S_FIFO_Cmd, SPI_EnableCalculateCrc, SPI_TransmitCrcNext, SPI_RX_FIFO_GET
Verification Wire SPI5 and SPI2 SCK, MISO, and MOSI together with jumpers as in the readme;
TransferStatus1 and TransferStatus2 are both PASSED with no CRCERR set, meaning the data and CRC both check out.
Use note Jumpers must connect SPI5 and SPI2 on the same board;
with FIFO enabled, reception must read the receive FIFO instead of the data register;
based on the N32H497ZGL7_STB evaluation board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example enables the 8-byte transmit and receive FIFOs on top of the CRC transfer and switches reception from the data register to the receive FIFO.

Data path: SPI_Master_Buffer_Tx → SPI_I2S_TransmitData → SPI5 TX_FIFO → PF9 (MOSI) → SPI2 RX_FIFO → SPI_RX_FIFO_GET → SPI2_Buffer_Rx → Buffercmp

1. Set the FIFO watermarks and enable FIFO. Both ends set the watermarks first, then enable FIFO; the watermark sets the trigger point of the half-full and half-empty flags.

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. Enable CRC computation and SPI on both ends. The CRC registers are cleared when the enable bit is set.

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. Read data through the receive FIFO. With FIFO enabled, reception no longer reads the data register but pulls from the receive 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. Read the received CRC values. After the last frame’s CRC is sent, the CRC values received by both ends are also read from the receive FIFO.

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);

Corresponding source files: src/main.c, inc/main.h

5.26.3.3 FullDuplex_SoftNSS Full-duplex with software NSS and role swap

Example directory: projects/n32h49x_EVAL/examples/SPI/FullDuplex_SoftNSS/

Phase 1: configure SPI5 as master and SPI2 as slave→Full-duplex transfer under software NSS and compare→Stop and reset both ends→Swap roles and reconfigure the alternate functions→Phase 2: SPI2 as master and SPI5 as slave transfer again→Reset the counters and buffers and compare the result

Item Source analysis
Function SPI5 and SPI2 exchange 32 bytes in full duplex with software NSS;
phase 1 has SPI5 as master and SPI2 as slave, phase 2 swaps the roles and transfers again, each phase comparing the data.
Source files src/main.c: transmit/receive buffers, main with RCC_Configuration, GPIO_Configuration, and Buffercmp;
src/n32h49x_it.c, inc/n32h49x_it.h: interrupt handlers;
inc/main.h: peripheral, pin, and alternate-function macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Data direction SPI_DIR_DOUBLELINE_FULLDUPLEX, 8-bit data, NSS SPI_NSS_SOFT;
clock polarity/phase SPI_CLKPOL_LOW/SPI_CLKPHA_FIRST_EDGE, prescaler 8, bit order LSB, CRC polynomial 7;
the two phases differ only in SpiMode while the rest is the same, and GPIO_Configuration switches the SCK and MOSI pins between alternate output and input per role;
system clock HSI+PLL at 240 MHz.
Program and data path In phase 1 main configures SPI5 as master and SPI2 as slave, transfers in full duplex, and Buffercmp produces TransferStatus1 and TransferStatus2;
SPI_I2S_DeInit resets both ends, then the roles are swapped and the transfer repeats;
before phase 2 the indexes and receive buffers are cleared, and comparison produces TransferStatus3 and TransferStatus4.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_I2S_DeInit, SPI_I2S_TransmitData, SPI_I2S_ReceiveData, SPI_I2S_GetStatus
Verification Wire SPI5 and SPI2 SCK, MISO, and MOSI together with jumpers as in the readme;
all four TransferStatus values are PASSED, meaning the swap of roles transferred correctly in both phases.
Use note Jumpers must connect SPI5 and SPI2 on the same board;
call SPI_I2S_DeInit to reset the registers and reconfigure the GPIO alternate directions when swapping roles;
based on the N32H497ZGL7_STB evaluation board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example runs full-duplex transfers in two phases, resetting both ends and swapping the roles in between to verify that the roles can be exchanged under software NSS.

Data path: SPI_Master_Buffer_Tx ⇄ SPI5 ⇄ PF7/PF8/PF9 ⇄ SPI2 ⇄ SPI2_Buffer_Rx; after the role swap in phase 2 the data flows the other way

1. Configure master and slave for phase 1. SPI5 is configured as master and SPI2 reuses the same parameters with only the mode changed.

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. Reset both ends and swap the roles. Between the two phases SPI_I2S_DeInit resets the registers, then SPI5 becomes slave and SPI2 master.

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. Phase-2 transfer. After the swap the send order changes, but the way data is written and read stays the same.

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. Compare the phase-2 result. Each phase compares the receive buffer with the peer’s transmit buffer using 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);

Corresponding source files: src/main.c, inc/main.h

5.26.3.4 Simplex_Interrupt Simplex transfer by interrupt

Example directory: projects/n32h49x_EVAL/examples/SPI/Simplex_Interrupt/

Configure the system clock, NVIC, and GPIO alternate functions→Initialize the master for simplex transmit and the slave for simplex receive→Enable the NSS output and the transfer interrupts of both ends→Enable the SPIs and wait in the main loop→The master transmits frame by frame and the slave receives frame by frame, both driven by interrupts→Compare and print the result once the buffer is full

Item Source analysis
Function SPI5 acts as master with simplex transmit and SPI2 as slave with simplex receive;
the master sends 32 bytes frame by frame on the TE interrupt and the slave receives frame by frame on the RNE interrupt, comparing and printing the result once the buffer is full.
Source files src/main.c: transmit and receive buffers, main with RCC_Configuration, GPIO_Configuration, NVIC_Configuration, and Buffercmp;
src/n32h49x_it.c, inc/n32h49x_it.h: SPI5 and SPI2 interrupt service routines;
inc/main.h: peripheral, pin, and alternate-function macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Master data direction SPI_DIR_SINGLELINE_TX and slave SPI_DIR_SINGLELINE_RX, 8-bit data, NSS SPI_NSS_HARD;
clock polarity/phase SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE, prescaler 8, bit order MSB;
the master enables SPI_I2S_INT_TE and the slave enables SPI_I2S_INT_RNE, with SPI5 preemption 1 and SPI2_I2S2 preemption 0 in the NVIC;
SPI5 pins SCK:PF7, MOSI:PF9 and SPI2 pins SCK:PC7, MISO:PC2.
Program and data path main configures the GPIO and NVIC, then initializes both ends and enables the interrupts and the SPIs;
SPI5_IRQHandler sends SPI_Master_Buffer_Tx[TxIdx++] on the TE interrupt and disables the TE interrupt once full;
SPI2_I2S2_IRQHandler stores SPI_I2S_ReceiveData into SPI2_Buffer_Rx[RxIdx++] on the RNE interrupt;
the main loop waits until the buffer is full, then Buffercmp compares and prints the result.
Main APIs SPI_InitStruct, SPI_Init, SPI_SSOutputEnable, SPI_I2S_EnableInt, SPI_Enable, SPI_I2S_GetIntStatus, SPI_I2S_TransmitData, SPI_I2S_ReceiveData
Verification Wire SPI5 and SPI2 SCK and the simplex data line together with jumpers as in the readme;
the serial port prints Test PASS!!, meaning TransferStatus is PASSED and the transferred data matches.
Use note The simplex data line is the MOSI pin on the master and the MISO pin on the slave;
the main loop exits on the full count instead of polling the interrupt flag;
based on the N32H497ZGL7_STB evaluation board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example transfers by interrupt rather than polling: the master enters the interrupt to send the next byte when the transmit buffer is empty, and the slave enters the interrupt to grab a byte when the receive buffer is not empty.

Data path: SPI_Master_Buffer_Tx → SPI5_IRQHandler (TE interrupt) → SPI5 SPI_DAT → PF9 (MOSI) → simplex line → PC2 (MISO) → SPI2_I2S2_IRQHandler (RNE interrupt) → SPI2_Buffer_Rx

1. Configure simplex transmit and receive. The master is set for simplex transmit and the slave for simplex receive, with NSS in hardware mode.

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. Enable the interrupts of both ends and the SPIs. The master enables the transmit interrupt and the slave enables the receive interrupt, then both peripherals are enabled.

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. Configure and enable the NVIC. With 1 bit of preemption and 3 bits of subpriority, SPI5 and SPI2 are given different preemption levels.

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. Master TE-interrupt transmit. Each time the transmit buffer is empty the next byte is written, and the transmit interrupt is disabled once full.

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. Slave RNE-interrupt receive. Each time the receive buffer is not empty one byte is read out into the receive buffer.

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);
    }

Corresponding source files: src/main.c, src/n32h49x_it.c, inc/main.h

5.26.3.5 SPI_DMA_T&R DMA simplex transmit and receive

Example directory: projects/n32h49x_EVAL/examples/SPI/SPI_DMA_T&R/

Configure the clock, GPIO, and idle chip-select level→Configure the slave receive DMA channel→Configure the master transmit DMA channel→Initialize the master for simplex transmit and the slave for simplex receive→Enable the transmit and receive DMA requests and the SPI and DMA channels→Wait for the DMA to finish and compare the data

Item Source analysis
Function SPI5 acts as master with simplex transmit and SPI2 as slave with simplex receive;
DMA1_CH3 moves the transmit bytes out one at a time and DMA1_CH4 moves the received bytes into the buffer, comparing and printing the result once done.
Source files src/main.c: transmit and receive buffers, main with RCC_Configuration, GPIO_Configuration, and Buffercmp;
src/n32h49x_it.c, inc/n32h49x_it.h: interrupt service routines;
inc/main.h: peripheral, pin, alternate-function, DMA channel, and completion-flag macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Master data direction SPI_DIR_SINGLELINE_TX and slave SPI_DIR_SINGLELINE_RX, 8-bit data, NSS SPI_NSS_HARD;
clock polarity/phase SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE, prescaler 8, bit order MSB;
DMA is byte-width, with the peripheral address fixed, the memory address incremented, normal mode, and the highest priority, using DMA1_CH3 for transmit and DMA1_CH4 for receive;
master pins NSS:PF6, SCK:PF7, MOSI:PF9 and slave pins NSS:PD1, SCK:PC7, MISO:PC2.
Program and data path main first configures the slave receive and master transmit DMA channels and remaps the requests;
after initializing both ends it calls SPI_I2S_EnableDma to open the slave receive and master transmit requests;
it enables the SPIs and the two DMA channels and then waits for both completion flags;
finally Buffercmp compares SPI_SLAVE_Buffer_Rx with the transmit buffer and prints the result.
Main APIs SPI_InitStruct, SPI_Init, SPI_SSOutputEnable, SPI_I2S_EnableDma, SPI_Enable, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus
Verification Wire SPI5 and SPI2 NSS, SCK, and the simplex data line together with jumpers as in the readme;
the serial port prints Test PASS!!, meaning TransferStatus is PASSED and the DMA-transferred data matches.
Use note The simplex data line is the MOSI pin on the master and the MISO pin on the slave;
the DMA requests must be enabled before the SPIs are enabled;
based on the N32H497ZGL7_STB evaluation board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example hands both transmit and receive to DMA: one channel on the master writes the transmit buffer into the data register, and another channel on the slave reads the receive data register into the buffer.

Data path: SPI_MASTER_Buffer_Tx → DMA1_CH3 → SPI_MASTER->DAT → SPI5 → PF9 (MOSI) → simplex line → PC2 (MISO) → SPI2 → SPI_SLAVE->DAT → DMA1_CH4 → SPI_SLAVE_Buffer_Rx → Buffercmp

1. Configure the slave receive DMA channel. The direction is peripheral to memory, reading bytes from SPI_SLAVE->DAT into the receive buffer.

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. Configure the master transmit DMA channel. The same structure is reused, changing only the direction to memory-to-peripheral and the addresses to the transmit side.

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. Initialize the master and slave. The master is set for simplex transmit and the slave for simplex receive, then the master NSS output is enabled.

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. Enable the transmit and receive DMA requests. The slave enables the receive request and the master enables the transmit request, and the requests must be opened before the SPIs are enabled.

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. Enable the SPIs and the DMA channels. Both SPIs are enabled first, then the two DMA channels are opened to trigger the transfer.

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);

Corresponding source files: src/main.c, inc/main.h

5.26.3.6 SPI_FLASH Read, write, and erase W25Q128

Example directory: projects/n32h49x_EVAL/examples/SPI/SPI_FLASH/

Initialize the SPI FLASH driver→Read and check the W25Q128 ID→Erase the sector and write the transmit buffer→Read back the data and compare→Erase the sector again and read back to verify it is all 0xFF→Print the read/write and erase result

Item Source analysis
Function Drives an external W25Q128 through SPI3: it first reads the ID to check the model, then erases the given sector, writes a string and reads it back for comparison, and finally erases and reads back to verify the data is all 0xFF.
Source files src/main.c: transmit/receive buffers, main, and Buffercmp;
src/spi_flash.c: driver implementation sFLASH_Init, sFLASH_ReadID, sFLASH_EraseSector, sFLASH_WriteBuffer, sFLASH_ReadBuffer, and the low-level transfer;
inc/spi_flash.h: command, capacity, and pin macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration SPI inside the driver is SPI_DIR_DOUBLELINE_FULLDUPLEX, master, 8-bit data, SPI_CLKPOL_HIGH/SPI_CLKPHA_SECOND_EDGE, NSS SPI_NSS_SOFT, prescaler 16, bit order MSB, CRC polynomial 7;
SPI3 pins NSS:PA15, SCK:PB3, MISO:PD11, MOSI:PD12;
serial TX:PA9, 115200, 8-bit data, 1 stop bit;
system clock HSI+PLL at 240 MHz.
Program and data path main calls sFLASH_Init to initialize the driver, then sFLASH_ReadID reads the ID and compares it with sFLASH_W25Q128_ID;
after erasing the sector sFLASH_WriteBuffer writes Tx_Buffer and sFLASH_ReadBuffer reads it back into Rx_Buffer;
Buffercmp checks that read and write match, then the sector is erased again and a loop checks that all read-back data is 0xFF.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_I2S_TransmitData, SPI_I2S_ReceiveData
Verification Connect jumper J20 as in the readme;
the serial port prints the W25Q128 Read ID, Flash write OK., and Sector erase OK., meaning read/write and erase are all correct.
Use note Jumper J20 must be connected and a W25Q128 attached;
the sector must be erased before writing;
based on the N32H497ZGL7_EVB full-featured board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example uses the on-chip SPI3 with a software chip select to access the external W25Q128, and the driver wraps ID read, sector erase, page write, and buffer read.

Data path: Tx_Buffer → sFLASH_WriteBuffer → sFLASH_WritePage → W25Q128 storage cells → sFLASH_ReadBuffer → Rx_Buffer → Buffercmp

1. Initialize the driver and read the ID. The SPI and chip select are initialized first, then the RDID command is sent and three bytes are read back to form the ID and compare it with the target model.

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. Erase, write, read back, and compare. The sector must be erased before writing, and once writing is done the data is read back and compared.

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 initialization inside the driver. The chip select is pulled high by default, and SPI3 is set as master full-duplex with software 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. Implementing the ID read. After pulling the chip select low it sends 0x9F, reads three bytes in a row, and raises the chip select before returning.

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;

Corresponding source files: src/main.c, src/spi_flash.c, inc/spi_flash.h

5.26.3.7 SPI_FLASH_DMA DMA read and write of W25Q128

Example directory: projects/n32h49x_EVAL/examples/SPI/SPI_FLASH_DMA/

Initialize the SPI FLASH driver→Read and check the W25Q128 ID→Fill in incrementing test data→Erase the sector and write through DMA→Read back through DMA and compare→Erase the sector again and read back to verify it is all 0xFF→Print the result

Item Source analysis
Function Accesses an external W25Q128 through SPI3 with DMA: after reading the ID to check the model it erases the sector, writes 300 bytes of incrementing data through DMA and reads it back for comparison, then erases and reads back to verify the data is all 0xFF.
Source files src/main.c: test data and receive buffer, main, and Buffercmp;
src/spi_flash.c: driver implementation sFLASH_DMA_Init, sFLASH_DMA_WriteBuffer, sFLASH_DMA_ReadBuffer, and the low-level transfer;
inc/spi_flash.h: command, capacity, pin, and DMA channel macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Bulk read/write goes through DMA, and for reading the SPI is switched to receive-only mode to avoid continuously sending 0xFF;
DMA is byte-width, with the peripheral address fixed, the memory address incremented, normal mode, and the highest priority;
SPI3 pins NSS:PA15, SCK:PB3, MISO:PD11, MOSI:PD12;
serial TX:PA9, 115200, 8-bit data, 1 stop bit, system clock HSI+PLL at 240 MHz.
Program and data path main first calls sFLASH_Init and sFLASH_ReadID, and fills Tx_Buffer with an incrementing sequence;
after erasing, sFLASH_DMA_WriteBuffer writes through DMA and sFLASH_DMA_ReadBuffer reads back into Rx_Buffer through DMA;
Buffercmp checks that read and write match, then the sector is erased again and a loop checks that all read-back data is 0xFF.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_I2S_DeInit, SPI_I2S_EnableDma, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus
Verification Connect jumper J20 as in the readme;
the serial port prints spi dma write and read flash OK. and Sector erase OK., meaning DMA read/write and erase are all correct.
Use note Use DMA only for large bulk transfers, and switch to receive-only mode when reading to improve performance;
jumper J20 must be connected and a W25Q128 attached;
based on the N32H497ZGL7_EVB full-featured board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example lets DMA handle the bulk Flash read/write; the sector is erased before writing, and the SPI is switched to receive-only mode when reading so it need not keep sending 0xFF to read data.

Data path: Tx_Buffer → sFLASH_DMA_WriteBuffer → sFLASH_DMA_PageWrite → DMA → W25Q128 → DMA → Rx_Buffer → Buffercmp

1. Fill the data and call DMA read/write. After erasing the sector it writes through DMA, then reads back through DMA and compares.

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. Initialize the DMA channel. The channel is chosen by transfer type, with the peripheral address fixed at the SPI data register and the memory address incremented.

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. Read back through DMA and switch to receive-only mode. After sending the read command the SPI is reset and set to receive-only, then the DMA receive request is opened and the completion flag is awaited.

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. Splitting the write buffer by page. Writing 300 bytes exceeds one page, so the function first splits the bytes to write into whole pages and a remainder.

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;

Corresponding source files: src/main.c, src/spi_flash.c, inc/spi_flash.h

5.26.3.8 SPI_Simplex_DMA_Recv DMA simplex receive

Example directory: projects/n32h49x_EVAL/examples/SPI/SPI_Simplex_DMA_Recv/

Configure the clock, GPIO, and idle chip-select level→Configure the slave receive DMA channel→Initialize the master for simplex transmit and the slave for simplex receive→Enable the slave receive DMA request and the SPI and DMA channels→The master writes data frame by frame and the slave receives through DMA→Wait for the DMA to finish and compare the data

Item Source analysis
Function SPI5 acts as master with simplex transmit and SPI2 as slave with simplex receive;
the master sends byte by byte by polling and the slave uses DMA1_CH4 to move the received data into the buffer, comparing and printing the result once full.
Source files src/main.c: transmit and receive buffers, main with RCC_Configuration, GPIO_Configuration, and Buffercmp;
src/n32h49x_it.c, inc/n32h49x_it.h: interrupt service routines;
inc/main.h: peripheral, pin, alternate-function, DMA channel, and completion-flag macros;
readme.txt: function, environment, and wiring notes;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Master data direction SPI_DIR_SINGLELINE_TX and slave SPI_DIR_SINGLELINE_RX, 8-bit data, NSS SPI_NSS_HARD;
clock polarity/phase SPI_CLKPOL_HIGH/SPI_CLKPHA_FIRST_EDGE, prescaler 32, bit order MSB;
the receive DMA is byte-width, with the peripheral address fixed, the memory address incremented, normal mode, and the highest priority, using DMA1_CH4;
master pins NSS:PF6, SCK:PF7, MOSI:PF9 and slave pins NSS:PD1, SCK:PC7, MISO:PC2.
Program and data path main first configures the slave receive DMA channel and remaps the request;
after initializing both ends it calls SPI_I2S_EnableDma to open the slave receive request and enables the SPI and DMA channels;
the master waits for TE in the loop and writes with SPI_I2S_TransmitData, while the slave data is moved by DMA into SPI_SLAVE_Buffer_Rx;
after the receive completion flag it uses Buffercmp to compare and print the result.
Main APIs SPI_InitStruct, SPI_Init, SPI_SSOutputEnable, SPI_I2S_EnableDma, SPI_Enable, SPI_I2S_GetStatus, SPI_I2S_TransmitData, DMA_Init, DMA_EnableChannel, DMA_GetFlagStatus
Verification Wire SPI5 and SPI2 NSS, SCK, and the simplex data line together with jumpers as in the readme;
the serial port prints Test PASS!!, meaning TransferStatus is PASSED and the DMA-received data matches the transmitted data.
Use note The simplex data line is the MOSI pin on the master and the MISO pin on the slave;
transmit uses polling and receive uses DMA, and the two share one SPI clock;
based on the N32H497ZGL7_STB evaluation board;
development environment KEIL MDK-ARM 5.34 or IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example hands only the receive to DMA: the master still transmits byte by byte by polling, while the slave’s received data is moved into the buffer automatically by DMA.

Data path: SPI_MASTER_Buffer_Tx → SPI_I2S_TransmitData → SPI5 → PF9 (MOSI) → simplex line → PC2 (MISO) → SPI2 → SPI_SLAVE->DAT → DMA1_CH4 → SPI_SLAVE_Buffer_Rx → Buffercmp

1. Configure the slave receive DMA channel. The direction is peripheral to memory, reading bytes from SPI_SLAVE->DAT into the receive buffer.

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. Initialize the master and slave and open the requests. The master is set for simplex transmit and the slave for simplex receive; the master opens the NSS output and the slave opens the receive request.

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. Enable the SPIs and the receive DMA channel. Both SPIs are enabled first, then the receive DMA channel is opened ready to transfer.

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. The master transmits frame by frame and waits for DMA completion. The master writes the next byte after the transmit buffer is empty, and after sending all bytes it waits for the receive DMA completion flag.

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))
        ;

Corresponding source files: src/main.c, inc/main.h

5.26.4 Notes and troubleshooting

Scenario Check & action Source
Waiting conditions when closing SPI In full-duplex mode, wait for the last data to be received, then TE set, then BUSY cleared before closing;
in transmit-only mode, wait for TE=1 then BUSY=0 after writing the last data;
in master receive-only mode, delay one SPI clock after the second-to-last reception before closing;
in slave receive-only mode, SPI can be closed at any time, but wait for BUSY=0 before entering stop mode.
User manual
Master and slave clock settings must match The master and slave must use the same clock polarity CLKPOL and clock phase CLKPHA, otherwise the sampling edge is misplaced. User manual
When to enable slave CRC In slave mode, enable CRC computation only after the clock is stable, otherwise the CRC result may be wrong;
once CRCEN is set and a clock is present on SCK, CRC is computed regardless of the SPIEN state.
User manual
When and how to transmit CRC In CPU (non-FIFO) mode, set CRCNEXT before sending the last data to transmit the CRC, while in DMA mode the hardware sends it automatically;
FIFO receive-only mode sends the CRC automatically based on TRANSNUM;
at high SPI clock rates, minimize CPU activity and avoid function calls while the CRC is being sent.
User manual
Clearing CRC with multiple slaves When switching slaves (NSS from high to low), clear the CRC values on both sides to keep the next result in sync;
the steps are: disable SPI, clear CRCEN, set CRCEN, then enable SPI.
User manual
Parameters for software CRC To emulate the hardware CRC in software, configure the polynomial via CRCPOLY, use initial value 0x0000 and result XOR value 0x0000, and disable input and output bit reversal. User manual
Reading data with FIFO With FIFO enabled, read received data from SPI_RX_FIFO;
with FIFO disabled, read SPI_DAT;
writes always go to SPI_DAT regardless of the FIFO setting.
User manual
Handling OVER and BUSY flags In transmit-only mode, not reading the received data sets OVER, which software should ignore;
in slave mode, BUSY is low for one SPI clock between data items, so do not use BUSY to track each item’s transfer.
User manual
Nations Technologies DevKit Docs

Datasheets, SDK, driver APIs and demo guides for Nations Technologies MCU development kits, organized by chip series.

© 2026 Nations Technologies Inc. All rights reserved.粤ICP备11069485号This site uses cookie-free anonymous analytics.