5.16 SPI Guide

This chapter describes the file layout, the 22 public functions, and the 5 examples (FullDuplex_SoftNSS, Simplex_Interrupt, SPI_CRC, SPI_DMA, and SPI_FLASH) of the SPI driver in the N32G033 standard peripheral library. Read the “SPI” chapter of the user manual for the clock polarity and phase, NSS management, and CRC, and the errata sheet for known chip issues.

5.16.1 Related files

Relative path Purpose
firmware/n32g033_std_periph_driver/inc/n32g033_spi.h Defines the SPI_InitType structure and the macros for data direction, master/slave mode, data length, clock polarity and phase, NSS, baud-rate prescaler, and CRC, and declares 22 public functions in sections such as reset and initialization, interrupt and DMA, NSS and data length, and CRC and status
firmware/n32g033_std_periph_driver/src/n32g033_spi.c Implements those 22 functions: SPI reset and initialization, data transfer, NSS and data length configuration, CRC computation and transfer, and status flag and interrupt handling
projects/n32g033_EVAL/examples/SPI/ SPI example root directory, with 5 examples: FullDuplex_SoftNSS, Simplex_Interrupt, SPI_CRC, SPI_DMA, and SPI_FLASH
projects/n32g033_EVAL/examples/SPI/SPI_FLASH/ Project layout of this example: MDK-ARM/, EWARM/, inc/, src/, and readme.txt, with spi_flash.h and spi_flash.c under inc/ and src/
projects/n32g033_EVAL/bsp/ Board support files shared by the examples: the SysTick delay in delay.h and the UART log interface in log.h

5.16.2 API reference

Search API22 functions

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

Reset and initialization

SPI_DeInit, SPI_Init, SPI_InitStruct, SPI_Enable

Interrupts, DMA, and data transfer

SPI_EnableInt, SPI_EnableDma, SPI_TransmitData, SPI_ReceiveData

NSS and data length configuration

SPI_SetNssLevel, SPI_SSOutputEnable, SPI_ConfigDataLen

CRC and bidirectional mode

SPI_TransmitCrcNext, SPI_EnableCalculateCrc, SPI_GetCRCDat, SPI_GetCRCPoly, SPI_ConfigBidirectionalMode

Status and flags

SPI_GetStatus, SPI_ClrCRCErrFlag, SPI_GetIntStatus, SPI_ClrITPendingBit

Host clock delay and CRC failure control

SPI_Configdelaytime, SPI_NSSFailCRCEnable

void SPI_DeInit(SPI_Module* SPIx)Reset the SPI registers.
Source comment
name    SPI_DeInit.
fun     Reset the SPI registers.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
return  none
void SPI_Init(SPI_Module* SPIx, SPI_InitType* SPI_InitStruct)Initializes the SPI according to SPI_InitStruct.
Source comment
name    SPI_Init.
fun     Initializes the SPI according to SPI_InitStruct.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_InitStruct (The input parameters must be the following values):
          - 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
            - 0x01 ~ 0xffff
return  none
void SPI_InitStruct(SPI_InitType* SPI_StructInit)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 (The input parameters must be the following values):
          - DataDirection
          - SpiMode
          - DataLen
          - CLKPOL
          - CLKPHA
          - NSS
          - BaudRatePres
          - FirstBit
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 (The input parameters must be the following values):
          - SPI1
          - SPI2
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void SPI_EnableInt(SPI_Module* SPIx, uint32_t SPI_IT, FunctionalState Cmd)Enable/disable SPI interrupts.
Source comment
name    SPI_EnableInt.
fun     Enable/disable SPI interrupts.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_IT (The input parameters must be the following values):
          - SPI_INT_TE
          - SPI_INT_RNE
          - SPI_INT_ERR
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void SPI_EnableDma(SPI_Module* SPIx, uint32_t SPI_DMAReq, FunctionalState Cmd)Enable SPI DMA transfer.
Source comment
name    SPI_EnableDma.
fun     Enable SPI DMA transfer.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_DMAReq (The input parameters must be the following values):
          - SPI_DMA_TX
          - SPI_DMA_RX
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void SPI_TransmitData(SPI_Module* SPIx, uint16_t Data)Transmits a Data through the SPI peripheral.
Source comment
name    SPI_TransmitData.
fun     Transmits a Data through the SPI peripheral.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   Data: data to be transmitted
return  none
uint16_t SPI_ReceiveData(SPI_Module* SPIx)Get SPI data from SPI_DAT register.
Source comment
name    SPI_ReceiveData.
fun     Get SPI data from SPI_DAT register.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
return  The data in the SPI_DAT register
void SPI_SetNssLevel(SPI_Module* SPIx, uint32_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 (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_NSSInternalSoft (The input parameters must be the following values):
          - SPI_NSS_HIGH Set NSS pin internally
          - SPI_NSS_LOW  Reset NSS pin internally
return  none
void SPI_SSOutputEnable(SPI_Module* SPIx, FunctionalState Cmd)Configures internally by software the NSS pin for the selected SPI.
Source comment
name    SPI_SSOutputEnable.
fun     Configures internally by software the NSS pin for the selected SPI.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
void SPI_ConfigDataLen(SPI_Module* SPIx, uint32_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 (The input parameters must be the following values):
          - SPI1
          - SPI2
param   DataLen (The input parameters must be the following values):
          - SPI_DATA_SIZE_16BITS
          - SPI_DATA_SIZE_8BITS
return  none
void SPI_TransmitCrcNext(SPI_Module* SPIx)Transmit the SPIx CRC value.
Source comment
name    SPI_TransmitCrcNext.
fun     Transmit the SPIx CRC value.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
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 (The input parameters must be the following values):
          - SPI1
          - SPI2
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none
uint32_t SPI_GetCRCDat(SPI_Module* SPIx, uint8_t SPI_CRC)Get SPI CRC data from SPI_CRCTDAT/SPI_CRCRDAT register.
Source comment
name    SPI_GetCRCDat.
fun     Get SPI CRC data from SPI_CRCTDAT/SPI_CRCRDAT register.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_CRC (The input parameters must be the following values):
          - SPI_CRC_TX
          - SPI_CRC_RX
return  Tx/Rx CRC register value.
uint32_t SPI_GetCRCPoly(SPI_Module* SPIx)Get CRC Polynomial from SPI_CRC_POLY register.
Source comment
name    SPI_GetCRCPoly.
fun     Get CRC Polynomial from SPI_CRC_POLY register.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
return  The CRC Polynomial register value.
void SPI_ConfigBidirectionalMode(SPI_Module* SPIx, uint32_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 (The input parameters must be the following values):
          - SPI1
          - SPI2
param   DataDirection (The input parameters must be the following values):
          - SPI_BIDIRECTION_RX
          - SPI_BIDIRECTION_TX
return  none
FlagStatus SPI_GetStatus(SPI_Module* SPIx, uint32_t SPI_FLAG)Get SPI flag status.
Source comment
name    SPI_GetStatus.
fun     Get SPI flag status.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_FLAG (The input parameters must be the following values):
          - SPI_TE_FLAG
          - SPI_RNE_FLAG
          - SPI_CRCERR_FLAG
          - SPI_MODERR_FLAG
          - SPI_OVER_FLAG
          - SPI_BUSY_FLAG
return  SET or RESET
void SPI_ClrCRCErrFlag(SPI_Module* SPIx, uint32_t SPI_FLAG)Clear SPI flag status.
Source comment
name    SPI_ClrCRCErrFlag.
fun     Clear SPI flag status.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_FLAG (The input parameters must be the following values):
          - SPI_CRCERR_FLAG
return  none
note
        - OVER (OverRun error) flag is cleared by software sequence: a read
          operation to SPI_DAT register (SPI_ReceiveData()) followed by a read
          operation to SPI_STS register (SPI_GetStatus()).
        - UNDER (UnderRun error) flag is cleared by a read operation to
          SPI_STS register (SPI_GetStatus()).
        - MODERR (Mode Fault) flag is cleared by software sequence: a read/write
          operation to SPI_STS register (SPI_GetStatus()) followed by a
          write operation to SPI_CTRL1 register (SPI_Enable() to enable the SPI).
INTStatus SPI_GetIntStatus(SPI_Module* SPIx, uint32_t SPI_IT)Get SPI interrupt flag status.
Source comment
name    SPI_GetIntStatus.
fun     Get SPI interrupt flag status.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_IT (The input parameters must be the following values):
          - SPI_INT_FLAG_RNE
          - SPI_INT_FLAG_TE
          - SPI_INT_FLAG_CRCERR
          - SPI_INT_FLAG_MODERR
          - SPI_INT_FLAG_OVER
          - SPI_INT_FLAG_ERR
return  SET or RESET
void SPI_ClrITPendingBit(SPI_Module* SPIx, uint32_t SPI_FLAG)Clear SPI flag status.
Source comment
name    SPI_ClrITPendingBit.
fun     Clear SPI flag status.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   SPI_FLAG (The input parameters must be the following values):
          - SPI_INT_FLAG_CRCERR
return  none
note
        - OVER (OverRun error) flag is cleared by software sequence: a read
          operation to SPI_DAT register (SPI_ReceiveData()) followed by a read
          operation to SPI_STS register (SPI_GetStatus()).
        - UNDER (UnderRun error) flag is cleared by a read operation to
          SPI_STS register (SPI_GetStatus()).
        - MODERR (Mode Fault) flag is cleared by software sequence: a read/write
          operation to SPI_STS register (SPI_GetStatus()) followed by a
          write operation to SPI_CTRL1 register (SPI_Enable() to enable the SPI).
void SPI_Configdelaytime(SPI_Module* SPIx,uint32_t DelayTime)SPI Host Clock Delay Time Configuration.
Source comment
name    SPI_Configdelaytime.
fun     SPI Host Clock Delay Time Configuration.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   DelayTime (The input parameters must be the following values):
          - 0x00 ~ 0x0f  delay DelayTime/2 PCLK2
return  The CRC Polynomial register value.
void SPI_NSSFailCRCEnable(SPI_Module* SPIx,FunctionalState Cmd)NSS failure, CRC calculation stops or continues.
Source comment
name    SPI_NSSFailCRCEnable.
fun     NSS failure, CRC calculation stops or continues.
param   SPIx (The input parameters must be the following values):
          - SPI1
          - SPI2
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none

5.16.3 Demo descriptions

This section describes the 5 SPI examples: FullDuplex_SoftNSS transfers both ways in full duplex with software NSS and swaps the roles, Simplex_Interrupt moves data in single-wire mode with transfer interrupts, SPI_CRC checks a transfer in 16-bit frames, SPI_DMA moves the master-transmit and slave-receive data with DMA, and SPI_FLASH reads, writes, and erases a serial FLASH.

5.16.3.1 FullDuplex_SoftNSS full-duplex transfer with software NSS

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

Enable the GPIO and SPI clocks→Configure the SPI pins→Set the two SPI instances as master and slave→Transfer both ways in full duplex and compare→Swap the roles and transfer again

Item Source analysis
Function Use two SPI instances, one master and one slave, for full-duplex transfer, then swap the roles and repeat, comparing the data each time.
Source files src/main.c holds the two-instance initialization, the transfer flow, and the compare function;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h defines the pin and clock macros for both instances;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCK, MISO, and MOSI on both instances use alternate-function push-pull;
Mode: 8-bit data, full duplex, software NSS, sampled on the second edge;
Bit order: LSB first;
Prescaler: 8;
Clock polarity: idle low.
Program and data path main -> RCC_Configuration enables the clocks -> GPIO_Configuration sets the pins -> SPI_Init sets one master and one slave -> the main loop exchanges data in full duplex -> Buffercmp compares -> the roles are swapped for a second round.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_TransmitData, SPI_ReceiveData, SPI_GetStatus
Verification After compiling and resetting, the serial log prints the master and slave results of both rounds;
both passing means the full-duplex transfer and the role swap are correct.
Use note Wire SCK, MISO, and MOSI of the two instances crosswise, and drive the master chip select in software;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example configures one master and one slave with a shared parameter set, then exchanges data in full duplex with software NSS and polling.

Data path: SPI_InitStruct fills the shared parameters → SPI_Init configures the master and the slave → SPI_Enable enables both → the main loop exchanges bytes through the transmit and receive registers → Buffercmp compares → the roles are swapped for one more round

1. One parameter set configures the master and then the slave, changing only the mode.

src/main.csrc/main.c · main
    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_LOW;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_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(SPIy, &SPI_InitStructure);

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

Sharing the parameters keeps the polarity, phase, and bit order aligned; change them on both sides together.

2. Full-duplex transfer: write after the transmit buffer is empty, and read back after the receive buffer is not empty.

src/main.csrc/main.c · main
    while (TxIdx < BufferSize)
    {
        /* Wait for SPIy Tx buffer empty */
        while (SPI_GetStatus(SPIy, SPI_TE_FLAG) == RESET)
            ;
        /* Send SPIz data */
        SPI_TransmitData(SPIz, SPIz_Buffer_Tx[TxIdx]);
        /* Send SPIy data */
        SPI_TransmitData(SPIy, SPIy_Buffer_Tx[TxIdx++]);
        /* Wait for SPIz data reception */
        while (SPI_GetStatus(SPIz, SPI_RNE_FLAG) == RESET)
            ;
        /* Read SPIz received data */
        SPIz_Buffer_Rx[RxIdx] = SPI_ReceiveData(SPIz);
        /* Wait for SPIy data reception */
        while (SPI_GetStatus(SPIy, SPI_RNE_FLAG) == RESET)
            ;
        /* Read SPIy received data */
        SPIy_Buffer_Rx[RxIdx++] = SPI_ReceiveData(SPIy);
    }

Keep the timing aligned: write the data on both sides first, then read back, so the order does not slip.

3. Swap the master and slave roles, reconfigure, and run the transfer again.

src/main.csrc/main.c · main
    /* SPIy Re-configuration */
    SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
    SPI_Init(SPIy, &SPI_InitStructure);

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

    /* Reset TxIdx, RxIdx indexes and receive tables values */
    TxIdx = 0;
    RxIdx = 0;
    for (k = 0; k < BufferSize; k++)
        SPIz_Buffer_Rx[k] = 0;
    for (k = 0; k < BufferSize; k++)
        SPIy_Buffer_Rx[k] = 0;

Disable the SPI before changing its mode and re-enable it after, so the configuration is not changed mid-transfer.

4. Clocks and pins: enable both SPI clocks and set the pin alternate functions.

src/main.csrc/main.c · RCC_Configuration
void RCC_Configuration(void)
{
    /* Enable peripheral clocks --------------------------------------------------*/
    /* GPIO clock enable */
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);

    /* SPI Periph clock enable */
    RCC_EnableAPBPeriphClk(SPIy_CLK | SPIz_CLK, ENABLE);
}

The two SPI instances sit on their own buses; update the clock and alternate-function macros when you move the pins.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_spi.h

5.16.3.2 Simplex_Interrupt single-wire interrupt transfer

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

Enable the GPIO and SPI clocks→Configure the pins and the interrupt priority→Set the master to single-wire transmit and the slave to single-wire receive→Enable the transfer interrupts and start→Wait for reception and compare in the main loop

Item Source analysis
Function The master uses single-wire transmit and the slave single-wire receive;
the master’s transmit interrupt refills data and the slave’s receive interrupt takes it away.
Source files src/main.c holds the two-instance initialization, the NVIC configuration, and the compare function;
src/n32g033_it.c and inc/n32g033_it.h provide the two interrupt service routines;
inc/main.h defines the pins, interrupt line numbers, and buffers of both instances;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: master SCK on PB3 and MOSI on PB5, slave SCK on PB0 and MISO on PA12;
Mode: 8-bit data, single wire, software NSS, sampled on the second edge;
Transfer interrupts enabled;
Clock polarity: idle low.
Program and data path main -> enables the single-wire transfer interrupts on both instances -> the slave receive interrupt takes each byte and the master transmit interrupt refills each byte -> the main loop waits until the receive count reaches the length -> Buffercmp compares.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_EnableInt, SPI_TransmitData, SPI_ReceiveData, SPI_GetIntStatus, SPI_GetStatus
Verification After compiling and resetting, completion is reached when the master has sent all bytes and the slave receive count reaches the length;
a matching serial log means it passed.
Use note In single-wire mode the data line is MOSI on the master and MISO on the slave, so the two must be wired together;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example hands the data movement to the transfer interrupts and lets the main loop only wait for the receive count to reach the length.

Data path: Enable the two transfer interrupts → the master refills on an empty transmit register through its interrupt → the slave takes data on a non-empty receive register through its interrupt → the receive count reaches the length → the main loop calls Buffercmp

1. The two instances are configured for single-wire transmit and single-wire receive, and each enables its transfer interrupt.

src/main.csrc/main.c · main
    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_LOW;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;
    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);

    /* Enable SPI_MASTER TXE interrupt */
    SPI_EnableInt(SPI_MASTER, SPI_INT_TE, ENABLE);
    /* Enable SPI_SLAVE RXNE interrupt */
    SPI_EnableInt(SPI_SLAVE, SPI_INT_RNE, ENABLE);

    /* Enable SPI_SLAVE */
    SPI_Enable(SPI_SLAVE, ENABLE);
    /* Enable SPI_MASTER */
    SPI_Enable(SPI_MASTER, ENABLE);

The wire direction comes from the data-direction parameter; the master and slave settings are mirror images.

2. Interrupt priority: the master and slave lines are configured separately.

src/main.csrc/main.c · NVIC_Configuration
    /* Configure and enable SPI_MASTER interrupt */
    NVIC_InitStructure.NVIC_IRQChannel                   = SPI_MASTER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority           = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* Configure and enable SPI_SLAVE interrupt */
    NVIC_InitStructure.NVIC_IRQChannel                   = SPI_SLAVE_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority           = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

The slave receive interrupt needs a timely response, so its priority should not be lower than the master transmit one.

3. Master transmit interrupt: write the next byte when the transmit register is empty, and disable the interrupt once done.

src/n32g033_it.csrc/n32g033_it.c · SPI1_IRQHandler
void SPI1_IRQHandler(void)
{
    if (SPI_GetIntStatus(SPI_MASTER, SPI_INT_FLAG_TE) != RESET)
    {
        /* Send SPI_MASTER data */
        SPI_TransmitData(SPI_MASTER, MASTER_Buffer_Tx[TxIdx++]);

        /* Disable SPI_MASTER TE interrupt */
        if (TxIdx == BufferSize)
        {
            SPI_EnableInt(SPI_MASTER, SPI_INT_TE, DISABLE);
        }
    }
}

Disable the transmit interrupt on completion so the handler does not spin on an empty buffer.

4. Slave receive interrupt: store each received byte into the buffer and advance the count.

src/n32g033_it.csrc/n32g033_it.c · SPI2_IRQHandler
void SPI2_IRQHandler(void)
{
    if(SPI_GetIntStatus(SPI_SLAVE, SPI_INT_FLAG_RNE) != RESET)
    {
        /* Store SPI_SLAVE received data */
        SLAVE_Buffer_Rx[RxIdx++] = SPI_ReceiveData(SPI_SLAVE);
    }
}

The receive count is the completion test, so the length must match the sender exactly.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_spi.h

5.16.3.3 SPI_CRC transfer with CRC checking

Example directory: projects/n32g033_EVAL/examples/SPI/SPI_CRC/

Enable the GPIO and SPI clocks→Configure both SPI instances for 16-bit frames→Enable CRC calculation on both→Transfer the data and trigger the CRC on the last byte→Read the CRC and check the error flag

Item Source analysis
Function Transfer 16-bit frames between the master and the slave, trigger the CRC on the last byte after the data, then check the received CRC and the error flag.
Source files src/main.c holds the two-instance initialization, the transfer flow, the CRC computation, and the compare;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h defines the pins, clock macros, and buffers of both instances;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCK, MISO, and MOSI of SPI1 are wired crosswise to SPI2;
Mode: 16-bit data, full duplex, software NSS;
Bit order: MSB first;
Prescaler: 16;
CRC: enabled on both, polynomial 7.
Program and data path main -> SPI_Init sets 16-bit frames on both -> SPI_EnableCalculateCrc enables the CRC -> the main loop sends and receives word by word -> the last word calls SPI_TransmitCrcNext -> SPI_GetCRCDat reads the CRC and the error flag is checked.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_EnableCalculateCrc, SPI_TransmitCrcNext, SPI_TransmitData, SPI_ReceiveData, SPI_GetStatus, SPI_GetCRCDat
Verification After compiling and resetting, the serial log prints the transfer compare and the CRC check;
matching CRC values and no error flag mean it passed.
Use note The 16-bit frames require identical data lengths on both sides, and the CRC polynomial and enable order must follow the manual;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example transfers data in 16-bit frames, triggers the CRC on the last word, and checks both CRC values and the error flag.

Data path: SPI_Init sets 16-bit frames → SPI_EnableCalculateCrc enables the CRC → the main loop sends and receives word by word → the last word calls SPI_TransmitCrcNext → SPI_GetCRCDat reads the local CRC → it is compared with the peer and the error flag is checked

1. Switch the data length to 16 bits and enable CRC calculation on both instances.

src/main.csrc/main.c · main
    SPI_InitStruct(&SPI_InitStructure);
    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_LOW;
    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(SPI1, &SPI_InitStructure);

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

    /* Enable SPI1 CRC calculation */
    SPI_EnableCalculateCrc(SPI1, ENABLE);
    /* Enable SPI2 CRC calculation */
    SPI_EnableCalculateCrc(SPI2, ENABLE);

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

Set the data length before the transfer; both sides must match, and re-check the polynomial when it changes.

2. For the last word, send the data first and then trigger the local CRC transmission.

src/main.csrc/main.c · main
    /* Wait for SPI1 Tx buffer empty */
    while (SPI_GetStatus(SPI1, SPI_TE_FLAG) == RESET)
        ;
    /* Wait for SPI2 Tx buffer empty */
    while (SPI_GetStatus(SPI2, SPI_TE_FLAG) == RESET)
        ;

    /* Send last SPI2_Buffer_Tx data */
    SPI_TransmitData(SPI2, SPI2_Buffer_Tx[TxIdx]);
    /* Enable SPI2 CRC transmission */
    SPI_TransmitCrcNext(SPI2);
    /* Send last SPI1_Buffer_Tx data */
    SPI_TransmitData(SPI1, SPI1_Buffer_Tx[TxIdx]);
    /* Enable SPI1 CRC transmission */
    SPI_TransmitCrcNext(SPI1);

    /* Wait for SPI1 last data reception */
    while (SPI_GetStatus(SPI1, SPI_RNE_FLAG) == RESET)
        ;
    /* Read SPI1 last received data */
    SPI1_Buffer_Rx[RxIdx] = SPI_ReceiveData(SPI1);
    /* Read SPI2 last received data */
    SPI2_Buffer_Rx[RxIdx] = SPI_ReceiveData(SPI2);

Trigger the CRC right after the last data word; a wrong order fails the CRC check.

3. CRC error flag: a set flag in either direction marks a failure.

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

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

Read the flag after the transfer completes, or you may catch an intermediate state.

4. Read the data CRC and the local CRC value and compare them.

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

    /* Get SPI1&SPI2 CRC value */
    SPI1_CRCTX = SPI_GetCRCDat(SPI1,SPI_CRC_TX);
    SPI1_CRCRX = SPI_GetCRCDat(SPI1,SPI_CRC_RX);
    SPI2_CRCTX = SPI_GetCRCDat(SPI2,SPI_CRC_TX);
    SPI2_CRCRX = SPI_GetCRCDat(SPI2,SPI_CRC_RX);

    if((CRC1Value == SPI1_CRCRX) && (SPI1_CRCRX == SPI2_CRCTX))
    {
        log_info("SPI1 CRC is OK! \r\n");
    }
    else
    {
        log_info("SPI1 CRC is Fail! \r\n");
    }

    if((CRC2Value == SPI2_CRCRX) && (SPI2_CRCRX == SPI1_CRCTX))
    {
        log_info("SPI2 CRC is OK! \r\n");
    }
    else
    {
        log_info("SPI2 CRC is Fail! \r\n");
    }

The local transmit CRC should equal the peer receive CRC; the equation follows any polynomial or length change.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_spi.h

5.16.3.4 SPI_DMA transfer with DMA

Example directory: projects/n32g033_EVAL/examples/SPI/SPI_DMA/

Enable the GPIO and SPI clocks→Configure the master and slave SPI instances→Set up the master transmit DMA channel→Set up the slave receive DMA channel→Enable the channels and wait for completion

Item Source analysis
Function The master transmit and the slave receive each use a DMA channel, leaving the main loop to wait for both channels to finish.
Source files src/main.c holds the two-instance initialization, the two DMA channel setups, and the compare function;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h defines the pins, DMA channels, and buffers of both instances;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCK, MISO, and MOSI of the two instances are wired crosswise;
Mode: 8-bit data, full duplex, software NSS;
Bit order: MSB first;
Prescaler: 8;
DMA: the master uses a transmit channel and the slave a receive channel.
Program and data path main -> SPI_Init sets one master and one slave -> DMA_Init sets the master transmit and slave receive channels with request remapping -> SPI_EnableDma enables the DMA requests on both -> the main loop waits for both channels -> Buffercmp compares.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_EnableDma, SPI_TransmitData, SPI_ReceiveData, SPI_GetStatus
Verification After compiling and resetting, the data is compared once both DMA channels complete;
a matching serial log means it passed.
Use note Match the DMA channels to the SPI request mapping for each direction, and update the remap and request macros when the channels change;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example hands both the master transmit and the slave receive to DMA and lets the main loop wait for the two channels.

Data path: SPI_Init sets one master and one slave → the master transmit DMA channel points at the data register → the slave receive DMA channel points at the data register → SPI_EnableDma enables the requests → the channels start → after both completion flags the data is compared by Buffercmp

1. Set up one master and one slave first, sharing one parameter set.

src/main.csrc/main.c · main
    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_LOW;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_SOFT;
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_8;
    SPI_InitStructure.FirstBit      = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly       = CRC_POLYNOMIAL;
    SPI_Init(SPI_MASTER, &SPI_InitStructure);

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

Keep the two instances identical except for the mode, so DMA can attach directly.

2. Master transmit channel: the peripheral address targets the data register and the direction is peripheral destination.

src/main.csrc/main.c · main
    DMA_DeInit(SPI_MASTER_TX_DMA_CHANNEL);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr     = (uint32_t)&SPI1->DAT;
    DMA_InitStructure.MemAddr        = (uint32_t)SPI_Master_Tx_Buffer;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize        = BUFFER_SIZE;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_LOW;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(SPI_MASTER_TX_DMA_CHANNEL, &DMA_InitStructure);
    DMA_RequestRemap(DMA_REMAP_SPI1_TX,DMA,SPI_MASTER_TX_DMA_CHANNEL,ENABLE);

Remap the channel to the SPI request and update the remap argument when the channel changes.

3. Slave receive channel: the peripheral address targets the data register and the direction is peripheral source.

src/main.csrc/main.c · main
    DMA_DeInit(SPI_SLAVE_RX_DMA_CHANNEL);
    DMA_InitStructure.PeriphAddr = (uint32_t)&SPI2->DAT;
    DMA_InitStructure.MemAddr    = (uint32_t)SPI_Slave_Rx_Buffer;
    DMA_InitStructure.Direction  = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.Priority   = DMA_PRIORITY_VERY_HIGH;
    DMA_Init(SPI_SLAVE_RX_DMA_CHANNEL, &DMA_InitStructure);
    DMA_RequestRemap(DMA_REMAP_SPI2_RX,DMA,SPI_SLAVE_RX_DMA_CHANNEL,ENABLE);

Match each direction to its DMA request; a reversed direction leaves the buffer empty.

4. Enable the DMA requests and channels on both sides, then wait for the two completion flags.

src/main.csrc/main.c · main
    /* Enable SPI_MASTER DMA Tx request */
    SPI_EnableDma(SPI_MASTER, SPI_DMA_TX, ENABLE);
    /* Enable SPI_SLAVE DMA Rx request */
    SPI_EnableDma(SPI_SLAVE, SPI_DMA_RX, ENABLE);

    /* Enable SPI_SLAVE */
    SPI_Enable(SPI_SLAVE, ENABLE);
    /* Enable SPI_MASTER */
    SPI_Enable(SPI_MASTER, ENABLE);

    /* Enable DMA channels */
    DMA_EnableChannel(SPI_SLAVE_RX_DMA_CHANNEL, ENABLE);
    DMA_EnableChannel(SPI_MASTER_TX_DMA_CHANNEL, ENABLE);

    /* Transfer complete */
    while (!DMA_GetFlagStatus(SPI_SLAVE_RX_DMA_FLAG, DMA))
        ;
    while (!DMA_GetFlagStatus(SPI_MASTER_TX_DMA_FLAG, DMA))
        ;

Enable the requests and channels before the transfer starts, and wait for both flags to finish.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_spi.h

5.16.3.5 SPI_FLASH read and write of a serial FLASH

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

Enable the GPIO and SPI clocks→Initialize the SPI and the chip-select pin→Read the FLASH device ID→Erase a sector and write the data→Read back and compare, then erase and check the erased state

Item Source analysis
Function Drive an off-chip serial FLASH over SPI: read the device ID, erase a sector, write the data, read it back and compare, then erase and check that the sector is all erased.
Source files src/main.c runs the initialization, the read, write, and erase flow, and the compare;
src/spi_flash.c implements FLASH initialization, command transfer, page write, read-back, and polling;
inc/spi_flash.h defines the commands, device ID, and pin macros;
src/n32g033_it.c provides the interrupt entry;
readme.txt gives the configuration and test steps.
Key configuration Pins: SPI SCK, MISO, and MOSI use alternate-function push-pull, and the chip select is a push-pull output;
Mode: 8-bit data, full duplex, software NSS, sampled on the second edge;
Bit order: MSB first;
Prescaler: 4;
Clock polarity: idle high.
Program and data path main -> sFLASH_Init sets up the SPI and chip select -> sFLASH_ReadID checks the device -> sFLASH_EraseSector erases -> sFLASH_WriteBuffer writes pages -> sFLASH_ReadBuffer reads back -> Buffercmp compares.
Main APIs SPI_InitStruct, SPI_Init, SPI_Enable, SPI_TransmitData, SPI_ReceiveData, SPI_GetStatus
Verification After compiling and resetting, the serial log prints the read-write and erase results;
matching data and an all-0xFF erased sector mean it passed.
Use note Match the FLASH commands and device ID to the real device and keep the chip-select timing per the manual;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example drives the serial FLASH byte by byte over SPI, packing the command, address, and data into one transfer.

Data path: sFLASH_Init sets up the SPI and chip select → sFLASH_SendByte transfers one byte → sFLASH_ReadID issues the read-ID command and takes three bytes → sFLASH_WriteBuffer splits pages into command, address, and data → sFLASH_ReadBuffer reads back → Buffercmp compares

1. Initialize the SPI and chip-select pin and set the chip select to deselected.

src/spi_flash.csrc/spi_flash.c · sFLASH_Init
void sFLASH_Init(void)
{
    SPI_InitType SPI_InitStructure;

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

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

Keep the chip select high when idle; swap the command and pin macros for the target device when you port it.

2. Single-byte transfer: write after the transmit buffer is empty, then read back once the receive buffer is not empty, forming one full-duplex exchange.

src/spi_flash.csrc/spi_flash.c · sFLASH_SendByte
uint8_t sFLASH_SendByte(uint8_t byte)
{
    /* Loop while DAT register in not emplty */
    while (SPI_GetStatus(sFLASH_SPI, SPI_TE_FLAG) == RESET)
        ;

    /* Send byte through the SPI1 peripheral */
    SPI_TransmitData(sFLASH_SPI, byte);

    /* Wait to receive a byte */
    while (SPI_GetStatus(sFLASH_SPI, SPI_RNE_FLAG) == RESET)
        ;

    /* Return the byte read from the SPI bus */
    return SPI_ReceiveData(sFLASH_SPI);
}

Every FLASH command is built on this byte exchange, so a change here affects all commands.

3. Read device ID: issue the read-ID command, take three bytes, and combine them into a 24-bit value.

src/spi_flash.csrc/spi_flash.c · sFLASH_ReadID
uint32_t sFLASH_ReadID(void)
{
    uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

    /* Select the FLASH: Chip Select low */
    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;
}

When the device ID does not match the expected value the later steps are skipped, so check the ID macro first when porting.

4. Main flow: read the ID, erase, write, read back and compare, then erase and check the erased state.

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

    /* Get SPI Flash ID */
    FlashID = sFLASH_ReadID();

    /* Check the SPI Flash ID */
    if (FlashID == sFLASH_ID)
    {
        /* 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 */
        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);
        /* TransferStatus1 = PASSED, if the transmitted and received data by SPI1
           are the same */
        /* TransferStatus1 = FAILED, if the transmitted and received data by SPI1
           are different */

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

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

Erase before writing and wait for the internal write to finish, or the read-back returns wrong data.

Corresponding source files: src/main.c, src/spi_flash.c, inc/spi_flash.h, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_spi.h

5.16.4 Notes and troubleshooting

Scenario Check & action Source
NSS management The SPI chapter of the N32G033 user manual states that NSS has software and hardware management modes, with the master NSS held high and the slave NSS held low;
for multi-master or external chip-select cases, drive NSS by the internal software bit or the external pin as the manual describes.
User manual
Mode fault in master mode The SPI chapter of the N32G033 user manual states that pulling the master NSS low externally triggers a mode fault, and the hardware clears SPIEN and MSEL and sets MODERR;
the software must re-initialize before the next transfer.
User manual
Changing the configuration during a transfer The SPI chapter of the N32G033 user manual states that bits such as LSBFF, MSEL, CLKPHA, CLKPOL, and BR must not change during a transfer;
disable the SPI before changing the configuration.
User manual
When to disable the SPI The SPI chapter of the N32G033 user manual states that before disabling the SPI you must wait until the transmit-empty, not-busy, and receive-not-empty flags are all satisfied, so no data is lost. User manual
When to enable CRC calculation The SPI chapter of the N32G033 user manual states that DATFF and CRCEN are writable only while SPIEN is 0, with the order SPIEN=0, CRCEN=0, CRCEN=1, then SPIEN=1;
the next CRC value must be triggered right after the last data word is written.
User manual
Slave CRC The SPI chapter of the N32G033 user manual states that with CRC enabled in slave mode, received clocks participate in the CRC computation even while NSS is high. User manual

Errata sheet V1.0.0 (2025.9.25) covers revision A silicon and lists no SPI entries.

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.