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
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)
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
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.
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.
/* 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.
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.
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.
/* 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.
/* 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.
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.
/* 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.
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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
/* 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.
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.
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.
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.
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.
/* 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.
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.
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.
/* 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.
/* 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.
/* 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 |