5.12 I2S Guide

This chapter describes how to use the I2S driver and examples in the N32G41x standard peripheral library. I2S shares n32g41x_spi.h and n32g41x_spi.c with SPI; its 4 public functions fill the I2S initialization structure with default values, compute the divider from the audio frequency and configure the I2S mode and data format, enable the I2S, and select the 13-bit PCM long-frame bypass. The examples cover DMA transfer, interrupt transfer and SPI/I2S mode switching. Read the audio standards, clock generator and register definitions together with the “Serial Peripheral Interface/Inter-IC Sound (SPI/ I2S)” chapter of the user manual, and take the errata sheet as the authority on known chip issues.

5.12.1 Related files

Relative path Purpose
firmware/n32g41x_std_periph_driver/inc/n32g41x_spi.h Defines the I2S_InitType structure and the I2S mode, standard, data-format, audio-frequency and clock-polarity macros, and declares 4 I2S functions in the /* I2S init */ section
firmware/n32g41x_std_periph_driver/src/n32g41x_spi.c Implements those 4 functions: computes the I2SPREDIV linear divider from the audio frequency and clock source, writes the I2S mode, standard and data-format fields of SPI_I2S_CFGR, and sets or clears the I2SEN and PCMBYPASS bits
projects/n32g41x_EVAL/examples/I2S/ I2S example root directory, with 6 examples: I2S_DMA_Master, I2S_DMA_Slave, I2S_Interrupt_Master, I2S_Interrupt_Slave, SPI_I2S_Switch_Master, SPI_I2S_Switch_Slave
projects/n32g41x_EVAL/examples/I2S/I2S_DMA/I2S_DMA_Master/ Project layout of that example: MDK-ARM/, EWARM/, inc/, src/, readme.txt
projects/n32g41x_EVAL/bsp/ Board support files shared by the examples: log output and millisecond delay functions

5.12.2 API reference

Search API4 functions

The following 4 public functions are listed in the declaration order of n32g41x_spi.h in 1 group; each entry gives a summary and the original comment above the function in n32g41x_spi.c.

I2S initialization

I2S_Init, I2S_InitStruct, I2S_Enable, I2S_Enable13BitPCMLongBypass

void I2S_Init(SPI_Module *SPIx, const I2S_InitType *I2S_InitParam)Initializes the SPIx peripheral according to the specified parameters in the I2S_InitStruct.
Source comment
name    I2S_Init.
fun     Initializes the SPIx peripheral according to the specified parameters in the I2S_InitStruct.
param   SPIx :
          - SPI1
param   I2S_InitParam :
          - I2sMode
           - I2S_MODE_SlAVE_TX
           - I2S_MODE_SlAVE_RX
           - I2S_MODE_MASTER_TX
           - I2S_MODE_MASTER_RX
          - Standard
           - I2S_STD_PHILLIPS
           - I2S_STD_MSB_ALIGN
           - I2S_STD_LSB_ALIGN
           - I2S_STD_PCM_SHORTFRAME
           - I2S_STD_PCM_LONGFRAME
          - DataFormat
           - I2S_DATA_FMT_16BITS
           - I2S_DATA_FMT_16BITS_EXTENDED
           - I2S_DATA_FMT_24BITS
           - I2S_DATA_FMT_32BITS
          - MCLKEnable
           - I2S_MCLK_ENABLE
           - I2S_MCLK_DISABLE
          - AudioFrequency
           - I2S_AUDIO_FREQ_192K
           - I2S_AUDIO_FREQ_96K
           - I2S_AUDIO_FREQ_48K
           - I2S_AUDIO_FREQ_44K
           - I2S_AUDIO_FREQ_32K
           - I2S_AUDIO_FREQ_22K
           - I2S_AUDIO_FREQ_16K
           - I2S_AUDIO_FREQ_11K
           - I2S_AUDIO_FREQ_8K
           - I2S_AUDIO_FREQ_DEFAULT
          - CLKPOL
           - I2S_CLKPOL_LOW
           - I2S_CLKPOL_HIGH
          - ClkSrcFrequency
           - RCC_Clocks.SysclkFreq
           - user defined
return  none
void I2S_InitStruct(I2S_InitType *I2S_StructInit)Fills each I2S_InitStruct member with its default value.
Source comment
name    I2S_InitStruct.
fun     Fills each I2S_InitStruct member with its default value.
param   I2S_StructInit :
          - I2sMode
          - Standard
          - DataFormat
          - MCLKEnable
          - AudioFrequency
          - CLKPOL
return  none
void I2S_Enable(SPI_Module *SPIx, FunctionalState Cmd)Enables or disables the specified SPI peripheral (in I2S mode).
Source comment
name    I2S_Enable.
fun     Enables or disables the specified SPI peripheral (in I2S mode).
param   SPIx :
          - SPI1
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2S_Enable13BitPCMLongBypass(SPI_Module *SPIx, FunctionalState Cmd)pcm long for 13bit is bypass select.
Source comment
name    I2S_Enable13BitPCMLongBypass.
fun     pcm long for 13bit is bypass select.
param   SPIx (The input parameters must be the following values):
          - SPI1
param   Cmd (The input parameters must be the following values):
          - ENABLE
          - DISABLE
return  none

5.12.3 Demo descriptions

This section describes the 6 I2S examples: I2S_DMA_Master and I2S_DMA_Slave transfer data with DMA, I2S_Interrupt_Master and I2S_Interrupt_Slave transfer data with the TxE and RxNE interrupts, and SPI_I2S_Switch_Master and SPI_I2S_Switch_Slave show how to switch the same SPI1 between SPI and I2S mode; the first two pairs and the last pair are board-to-board tests.

5.12.3.1 I2S_DMA_Master DMA master transmit

Example directory: projects/n32g41x_EVAL/examples/I2S/I2S_DMA/I2S_DMA_Master/

Enable the DMA, GPIO and SPI1/I2S1 clocks→Configure PA4, PA5 and PA7 as alternate-function push-pull→Set up DMA_CH3 to move 32 half-words→Enable I2S master TX and wait for the DMA transfer→Print the completion message over USART

Item Source analysis
Function Configures I2S1 as master TX and sends 32 16-bit data words with DMA;
tested board-to-board against the I2S_DMA_Slave slave project.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, DMA_Configuration, I2S_Configuration;
inc/main.h: I2S pin, DMA channel and clock macros;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function and test steps;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Clock source HSI plus PLL, system clock 80 MHz for N32G412 and 96 MHz for N32G415;
I2S clock source is the system clock, I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_MCLK_DISABLE, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW, mode I2S_MODE_MASTER_TX;
WS on PA4, CK on PA5, SD on PA7, with GPIO_AF0, GPIO_MODE_AF_PP, GPIO_NO_PULL;
TX uses DMA_CH3 (DMA_REMAP_SPI1_TX), peripheral address SPI1->DAT, half-word transfer, normal mode.
Program and data path main calls log_init and then runs RCC_Configuration, a 1 s delay, GPIO_Configuration, DMA_Configuration and I2S_Configuration;
it then enables the DMA TX request, the DMA channel and the I2S;
the I2S_MASTER_Buffer_Tx data is moved by DMA into the I2S data register and sent out on SD;
after the DMA transfer completes, TE is set and BUSY is cleared, the completion message is printed.
Main APIs SPI_I2S_DeInit, RCC_ConfigI2SClk, I2S_InitStruct, I2S_Init, I2S_Enable, SPI_I2S_EnableDma, SPI_I2S_GetStatus, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus
Verification Connect PA4, PA5, PA7 and GND of the two boards one to one, reset the slave first and then the master;
the master prints I2S DMA Master TX transfer completed! and the slave prints the result of comparing the received data;
a logic analyzer can also be used to observe WS, CK and SD.
Use note Only SPI1 of the N32G41x supports I2S, SPI2 does not;
the I2S clock source is set to the system clock and the pins are shared with SPI1 with AF0 selected;
the development environment is KEIL MDK-ARM V5.34 or IAR EWARM 8.50.1, for N32G412x8L7, N32G412xBL7, N32G415x8L7 and N32G415xBL7.
Core-code walkthrough and porting

The example first prepares the DMA, GPIO and I2S resources, then main starts one DMA transmit.

Data path: RCC_Configuration enables the clocks → GPIO_Configuration sets PA4, PA5 and PA7 to alternate-function push-pull → DMA_Configuration connects I2S_MASTER_Buffer_Tx to SPI1->DAT → I2S_Configuration fills I2S_InitType and calls I2S_Init → main enables the DMA and the I2S → the completion message is printed over USART

1. Enable the DMA, GPIO and SPI1/I2S1 clocks. All three clocks are required: the DMA clock for DMA_CH3, the GPIO clock for the alternate function of PA4, PA5 and PA7, and I2S_MASTER_CLK for SPI1, which is I2S1. Porting to another peripheral instance means changing all three macros.

src/main.csrc/main.c · RCC_Configuration
void RCC_Configuration(void)
{
    /* Enable DMA clock */
    RCC_EnableAHBPeriphClk(I2S_MASTER_DMA_CLK, ENABLE);

    /* Enable GPIO clock */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_GPIO, ENABLE);

    /* Enable SPI1/I2S1 clock */
    RCC_EnableAPB2PeriphClk(I2S_MASTER_CLK, ENABLE);
}

When switching to another SPI instance or DMA clock, update I2S_MASTER_CLK and I2S_MASTER_DMA_CLK together.

2. Configure the WS, CK and SD pins as alternate-function push-pull. GPIO_InitStruct is called first, then PA4 gets its pin, alternate-function number, push-pull mode and no pull-up/pull-down settings; PA5 and PA7 reuse the same structure and only change Pin and GPIO_Alternate. The pin numbers and alternate-function numbers are collected in the macros of inc/main.h and must be changed together on a different board.

src/main.csrc/main.c · GPIO_Configuration
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);

    /* I2S_MASTER: WS(PA4) as AF_PP */
    GPIO_InitStructure.Pin            = I2S_MASTER_WS_PIN;
    GPIO_InitStructure.GPIO_Pull      = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = I2S_MASTER_WS_AF;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(I2S_MASTER_WS_GPIO, &GPIO_InitStructure);

When changing the pins, update the pin-number macros and the GPIO_AF0 alternate-function number in inc/main.h.

3. Configure DMA_CH3 to move the transmit data. The peripheral address points to SPI1->DAT, the memory address points to I2S_MASTER_Buffer_Tx, the direction is peripheral destination, the data width is half-word, BufSize is 32 and the mode is normal so the transfer runs once; DMA_RequestRemap maps DMA_CH3 to the SPI1 transmit request. The buffer size and data width must match the 16-bit I2S format.

src/main.csrc/main.c · DMA_Configuration
    DMA_InitStructure.PeriphAddr     = (uint32_t)&I2S_MASTER->DAT;
    DMA_InitStructure.MemAddr        = (uint32_t)I2S_MASTER_Buffer_Tx;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize        = BufferSize;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(I2S_MASTER_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(I2S_MASTER_Tx_DMA_Remap, DMA, I2S_MASTER_Tx_DMA_Channel, ENABLE);

The buffer length, data width and the DMA_REMAP_SPI1_TX mapping must be checked together with the channel and the I2S data format.

4. Configure the I2S master TX for the sampling frequency. SPI_I2S_DeInit first resets SPI1/I2S1 and the clock source is set to the system clock; I2S_InitStruct then sets the Phillips standard, the 16-bit extended format, MCLK disabled, 48 kHz and clock polarity low, and the system clock frequency from RCC_GetClocksFreqValue is used for the divider calculation; finally the mode is set to master TX and I2S_Init is called. The slave must use exactly the same set of parameters for the board-to-board test to pass.

src/main.csrc/main.c · I2S_Configuration
    I2S_InitType I2S_InitStructure;
    RCC_ClocksType RCC_Clocks;

    SPI_I2S_DeInit(I2S_MASTER);

    /* Configure I2S clock source as system clock */
    RCC_ConfigI2SClk(RCC_I2S_CLKSEL_SYS);

    /* I2S configuration */
    I2S_InitStruct(&I2S_InitStructure);
    I2S_InitStructure.Standard       = I2S_STD_PHILLIPS;
    I2S_InitStructure.DataFormat     = I2S_DATA_FMT_16BITS_EXTENDED;
    I2S_InitStructure.MCLKEnable     = I2S_MCLK_DISABLE;
    I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
    I2S_InitStructure.CLKPOL         = I2S_CLKPOL_LOW;

    /* Get system clock frequency for I2S clock calculation */
    RCC_GetClocksFreqValue(&RCC_Clocks);
    I2S_InitStructure.ClkSrcFrequency = RCC_Clocks.SysclkFreq;

    /* Configure I2S1 as Master TX */
    I2S_InitStructure.I2sMode = I2S_MODE_MASTER_TX;
    I2S_Init(I2S_MASTER, &I2S_InitStructure);

The standard, data format, sampling frequency and clock polarity must be identical on both ends, and ClkSrcFrequency must match the actual system clock.

5. Enable the DMA and the I2S and wait for the transmit to complete. main enables the I2S DMA TX request, then the DMA channel, then the I2S, so the transfer requests are ready before the I2S runs; it then waits for the DMA transfer-complete flag and confirms that the transmit buffer is empty and the bus is not busy before printing the completion message.

src/main.csrc/main.c · main
    /* Enable I2S DMA TX request */
    SPI_I2S_EnableDma(I2S_MASTER, SPI_I2S_DMA_TX, ENABLE);

    /* Enable DMA TX channel */
    DMA_EnableChannel(I2S_MASTER_Tx_DMA_Channel, ENABLE);

    /* Enable I2S */
    I2S_Enable(I2S_MASTER, ENABLE);

    /* Wait for DMA TX transfer complete */
    while (!DMA_GetFlagStatus(I2S_MASTER_Tx_DMA_FLAG, DMA))
        ;

    /* Wait until I2S TX buffer is empty and bus is not busy */
    while (SPI_I2S_GetStatus(I2S_MASTER, SPI_I2S_TE_FLAG) == RESET)
        ;
    while (SPI_I2S_GetStatus(I2S_MASTER, SPI_I2S_BUSY_FLAG) != RESET)
        ;

The exit conditions depend on DMA_FLAG_TC3, SPI_I2S_TE_FLAG and SPI_I2S_BUSY_FLAG, which must be updated together when changing the channel or the instance.

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

5.12.3.2 I2S_DMA_Slave DMA slave receive

Example directory: projects/n32g41x_EVAL/examples/I2S/I2S_DMA/I2S_DMA_Slave/

Enable the DMA, GPIO and SPI1/I2S1 clocks→Configure PA4, PA5 and PA7 as alternate-function push-pull→Set up DMA_CH2 to receive 32 half-words→Enable I2S slave RX and wait for the DMA transfer→Compare the received data and print the result over USART

Item Source analysis
Function Configures I2S1 as slave RX and receives 32 16-bit data words with DMA;
the received data is compared with the expected data and the result is printed;
tested board-to-board against the I2S_DMA_Master master project.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, DMA_Configuration, I2S_Configuration, Buffercmp;
inc/main.h: I2S pin, DMA channel and clock macros;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function and test steps;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Clock source HSI plus PLL, system clock 80 MHz for N32G412 and 96 MHz for N32G415;
I2S clock source is the system clock, I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_MCLK_DISABLE, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW, mode I2S_MODE_SlAVE_RX;
WS on PA4, CK on PA5, SD on PA7, with GPIO_AF0, GPIO_MODE_AF_PP;
RX uses DMA_CH2 (DMA_REMAP_SPI1_RX), peripheral address SPI1->DAT, half-word transfer, normal mode.
Program and data path main calls log_init and then runs RCC_Configuration, GPIO_Configuration, DMA_Configuration and I2S_Configuration;
it then enables the DMA RX request, the DMA channel and the I2S;
data enters the I2S data register from SD and is stored into I2S_Buffer_Rx by DMA;
after the DMA transfer completes, Buffercmp compares the buffer with I2S_Expected_Buffer and prints the result.
Main APIs SPI_I2S_DeInit, RCC_ConfigI2SClk, I2S_InitStruct, I2S_Init, I2S_Enable, SPI_I2S_EnableDma, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus
Verification Connect PA4, PA5, PA7 and GND of the two boards one to one, reset the slave first and then the master;
the slave prints I2S DMA Slave RX completed, data match! Test PASS!! when the data is correct and Test FAIL!! when the comparison fails.
Use note The slave must enable the I2S and DMA and enter the waiting state first, because the master sends after a 1 s delay;
the master and slave settings must match (Phillips, 16-bit extended, 48 kHz);
only SPI1 of the N32G41x supports I2S.
Core-code walkthrough and porting

The example first prepares the DMA, GPIO and I2S resources, then main starts the DMA receive and compares the data.

Data path: RCC_Configuration enables the clocks → GPIO_Configuration configures PA4, PA5 and PA7 → DMA_Configuration connects SPI1->DAT to I2S_Buffer_Rx → I2S_Configuration sets slave RX → main enables the DMA and the I2S → Buffercmp compares and the result is printed over USART

1. Enable the DMA, GPIO and SPI1/I2S1 clocks. As in the master project, the DMA clock, the GPIO clock and the SPI1 clock must all be enabled, only with the slave-side macros. Changing the peripheral instance means replacing this whole group of macros.

src/main.csrc/main.c · RCC_Configuration
void RCC_Configuration(void)
{
    /* Enable DMA clock */
    RCC_EnableAHBPeriphClk(I2S_SLAVE_DMA_CLK, ENABLE);

    /* Enable GPIO clock */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_GPIO, ENABLE);

    /* Enable SPI1/I2S1 clock */
    RCC_EnableAPB2PeriphClk(I2S_SLAVE_CLK, ENABLE);
}

I2S_SLAVE_CLK and I2S_SLAVE_DMA_CLK must correspond to the selected peripheral instance and DMA clock.

2. Configure the WS, CK and SD pins as alternate-function push-pull. The pins are PA4, PA5 and PA7 as in the master project and are configured in the same way; on the slave the clock and WS are supplied by the external master, but the pins are still configured as alternate-function push-pull. Changing the pins again means changing the macros of inc/main.h.

src/main.csrc/main.c · GPIO_Configuration
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);

    /* I2S_SLAVE: WS(PA4) as AF_PP */
    GPIO_InitStructure.Pin            = I2S_SLAVE_WS_PIN;
    GPIO_InitStructure.GPIO_Pull      = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = I2S_SLAVE_WS_AF;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(I2S_SLAVE_WS_GPIO, &GPIO_InitStructure);

The pin numbers and the GPIO_AF0 alternate-function number are defined in inc/main.h and must be updated together on a different board.

3. Configure DMA_CH2 to move the received data. The direction is peripheral source, the peripheral address still points to SPI1->DAT, the memory address points to I2S_Buffer_Rx and the width is half-word; DMA_RequestRemap maps DMA_CH2 to the SPI1 receive request. The buffers on both ends must have the same length of 32 for the comparison to be item by item.

src/main.csrc/main.c · DMA_Configuration
    DMA_InitStructure.PeriphAddr     = (uint32_t)&I2S_SLAVE->DAT;
    DMA_InitStructure.MemAddr        = (uint32_t)I2S_Buffer_Rx;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize        = BufferSize;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(I2S_SLAVE_Rx_DMA_Channel, &DMA_InitStructure);
    DMA_RequestRemap(I2S_SLAVE_Rx_DMA_Remap, DMA, I2S_SLAVE_Rx_DMA_Channel, ENABLE);

The receive buffer size, data width and the DMA_REMAP_SPI1_RX mapping must be checked together with the channel and the data format.

4. Configure the I2S slave RX for the sampling frequency. The parameters are identical to the master and only the mode is changed to I2S_MODE_SlAVE_RX; the slave does not output CK and WS, which come from the external master, but the sampling frequency, standard and data format still have to match. Changing any of them requires changing the master project as well.

src/main.csrc/main.c · I2S_Configuration
    I2S_InitType I2S_InitStructure;
    RCC_ClocksType RCC_Clocks;

    SPI_I2S_DeInit(I2S_SLAVE);

    /* Configure I2S clock source as system clock */
    RCC_ConfigI2SClk(RCC_I2S_CLKSEL_SYS);

    /* I2S configuration - must match Master settings */
    I2S_InitStruct(&I2S_InitStructure);
    I2S_InitStructure.Standard       = I2S_STD_PHILLIPS;
    I2S_InitStructure.DataFormat     = I2S_DATA_FMT_16BITS_EXTENDED;
    I2S_InitStructure.MCLKEnable     = I2S_MCLK_DISABLE;
    I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
    I2S_InitStructure.CLKPOL         = I2S_CLKPOL_LOW;

    /* Get system clock frequency for I2S clock calculation */
    RCC_GetClocksFreqValue(&RCC_Clocks);
    I2S_InitStructure.ClkSrcFrequency = RCC_Clocks.SysclkFreq;

    /* Configure I2S1 as Slave RX */
    I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_RX;
    I2S_Init(I2S_SLAVE, &I2S_InitStructure);

The standard, data format, sampling frequency and clock polarity must be changed on both ends as a pair.

5. Enable the DMA and the I2S, then compare the received data. main first enables the DMA RX request and the DMA channel, then enables the I2S and waits; when the DMA transfer completes, Buffercmp compares the received words with I2S_Expected_Buffer and prints the result. The contents of I2S_Expected_Buffer must match what the master sends in I2S_MASTER_Buffer_Tx.

src/main.csrc/main.c · main
    /* Enable I2S DMA RX request */
    SPI_I2S_EnableDma(I2S_SLAVE, SPI_I2S_DMA_RX, ENABLE);

    /* Enable DMA RX channel */
    DMA_EnableChannel(I2S_SLAVE_Rx_DMA_Channel, ENABLE);

    /* Enable I2S Slave - must be enabled before Master starts */
    I2S_Enable(I2S_SLAVE, ENABLE);

    log_info(" Waiting for Master to send data...\r\n");

    /* Wait for DMA RX transfer complete */
    while (!DMA_GetFlagStatus(I2S_SLAVE_Rx_DMA_FLAG, DMA))
        ;

    /* Check the correctness of received data */
    TransferStatus = Buffercmp(I2S_Buffer_Rx, I2S_Expected_Buffer, BufferSize);

    if (TransferStatus == PASSED)
    {
        log_info("\r\n I2S DMA Slave RX completed, data match! Test PASS!!\r\n");
    }
    else
    {
        log_info("\r\n I2S DMA Slave RX completed but data mismatch! Test FAIL!!\r\n");
    }

The expected data I2S_Expected_Buffer, the transfer-complete flag DMA_FLAG_TC2 and the buffer size must be checked together with the master data.

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

5.12.3.3 I2S_Interrupt_Master interrupt master transmit

Example directory: projects/n32g41x_EVAL/examples/I2S/I2S_Interrupt/I2S_Interrupt_Master/

Enable the GPIO and SPI1/I2S1 clocks→Configure PA4, PA5 and PA7 as alternate-function push-pull→Set the SPI1 interrupt priority→Enable I2S master TX and the TxE interrupt→Send 32 words one by one in the interrupt and then disable it→Print the completion message over USART

Item Source analysis
Function Configures I2S1 as master TX and writes the 32 16-bit words to the data register one by one in the TxE interrupt;
the TxE interrupt is disabled in the handler after all data is sent;
tested board-to-board against the I2S_Interrupt_Slave slave project.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, I2S_Configuration;
src/n32g41x_it.c: SPI1_IRQHandler interrupt handler;
inc/main.h: I2S pin, IRQ number and clock macros;
readme.txt: function and test steps;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Clock source HSI plus PLL, system clock 80 MHz for N32G412 and 96 MHz for N32G415;
I2S clock source is the system clock, I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_MCLK_DISABLE, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW, mode I2S_MODE_MASTER_TX;
WS on PA4, CK on PA5, SD on PA7, with GPIO_AF0, GPIO_MODE_AF_PP;
interrupt SPI1_IRQn with preemption priority 1 and subpriority 0, group NVIC_PriorityGroup_0.
Program and data path main calls log_init and then runs RCC_Configuration, a 1 s delay, NVIC_Configuration, GPIO_Configuration and I2S_Configuration;
it then enables the TxE interrupt and the I2S;
each TxE interrupt writes I2S_Buffer_Tx[TxIdx++] to the data register and disables the TxE interrupt in the handler once TxIdx reaches 32;
after the main loop exits, the code confirms that the buffer is empty, that the bus is not busy, and prints the completion message.
Main APIs SPI_I2S_DeInit, RCC_ConfigI2SClk, I2S_InitStruct, I2S_Init, I2S_Enable, SPI_I2S_EnableInt, SPI_I2S_GetIntStatus, SPI_I2S_TransmitData, SPI_I2S_GetStatus, NVIC_Init
Verification Connect PA4, PA5, PA7 and GND of the two boards and power them on or reset them together;
the master prints I2S Interrupt TX transfer completed!! and the slave prints Test PASS!! when the test passes;
a logic analyzer can also be used to observe the I2S output.
Use note The master sends after a 1 s delay so that the slave is ready to receive;
keep the wires between two boards short when they face each other;
only SPI1 of the N32G41x supports I2S and the pins are shared with SPI1 with AF0 selected.
Core-code walkthrough and porting

The example first configures the pins and the interrupt, then the TxE interrupt sends the data word by word.

Data path: RCC_Configuration enables the clocks → GPIO_Configuration configures PA4, PA5 and PA7 → NVIC_Configuration enables the SPI1 interrupt → I2S_Configuration sets master TX → main enables the TxE interrupt and the I2S → SPI1_IRQHandler sends I2S_Buffer_Tx one word at a time → the status is printed over USART

1. Enable the GPIO and SPI1/I2S1 clocks. The interrupt approach does not need the DMA clock, only the GPIO clock and the SPI1 clock. Changing the instance means changing I2S_MASTER_CLK and RCC_AHB_PERIPH_GPIO.

src/main.csrc/main.c · RCC_Configuration
void RCC_Configuration(void)
{
    /* Enable GPIO clock */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_GPIO, ENABLE);

    /* Enable SPI1/I2S1 clock */
    RCC_EnableAPB2PeriphClk(I2S_MASTER_CLK, ENABLE);
}

These two clock macros must correspond to the selected GPIO port and SPI instance.

2. Configure the WS, CK and SD pins as alternate-function push-pull. Same three-pin configuration as the DMA example: PA5 and PA7 reuse the push-pull mode and no pull-up/pull-down settings of PA4. The pin definitions are collected in inc/main.h.

src/main.csrc/main.c · GPIO_Configuration
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);

    /* I2S_MASTER: WS(PA4) as AF_PP */
    GPIO_InitStructure.Pin            = I2S_MASTER_WS_PIN;
    GPIO_InitStructure.GPIO_Pull      = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = I2S_MASTER_WS_AF;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(I2S_MASTER_WS_GPIO, &GPIO_InitStructure);

The pin numbers and alternate-function numbers are defined in inc/main.h and must be changed together on a different board.

3. Configure the SPI1/I2S1 interrupt priority. The priority group is set to NVIC_PriorityGroup_0, SPI1_IRQn gets preemption priority 1 and subpriority 0, and the channel is enabled. The priority values must be planned together with the other interrupts in the same project.

src/main.csrc/main.c · NVIC_Configuration
    NVIC_InitType NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

    /* SPI1/I2S1 IRQ channel configuration */
    NVIC_InitStructure.NVIC_IRQChannel                   = I2S_MASTER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

The interrupt channel is selected by I2S_MASTER_IRQn, which must be changed when switching to another SPI instance.

4. Configure the I2S master TX for the sampling frequency. The same initialization sequence as the DMA example: reset the peripheral, select the system clock source, fill the defaults and set the Phillips standard, the 16-bit extended format, MCLK disabled, 48 kHz and clock polarity low, and finally set master TX.

src/main.csrc/main.c · I2S_Configuration
    I2S_InitType I2S_InitStructure;
    RCC_ClocksType RCC_Clocks;

    SPI_I2S_DeInit(I2S_MASTER);

    /* Configure I2S clock source as system clock */
    RCC_ConfigI2SClk(RCC_I2S_CLKSEL_SYS);

    /* I2S configuration */
    I2S_InitStruct(&I2S_InitStructure);
    I2S_InitStructure.Standard       = I2S_STD_PHILLIPS;
    I2S_InitStructure.DataFormat     = I2S_DATA_FMT_16BITS_EXTENDED;
    I2S_InitStructure.MCLKEnable     = I2S_MCLK_DISABLE;
    I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
    I2S_InitStructure.CLKPOL         = I2S_CLKPOL_LOW;

    /* Get system clock frequency for I2S clock calculation */
    RCC_GetClocksFreqValue(&RCC_Clocks);
    I2S_InitStructure.ClkSrcFrequency = RCC_Clocks.SysclkFreq;

    /* Configure I2S1 as Master TX */
    I2S_InitStructure.I2sMode = I2S_MODE_MASTER_TX;
    I2S_Init(I2S_MASTER, &I2S_InitStructure);

The slave standard, format, sampling frequency and polarity must match the master.

5. Enable the TxE interrupt and the I2S and send word by word in the handler. main enables the TxE interrupt and the I2S and then waits until TxIdx reaches the buffer size; the handler reads the SPI_I2S_INT_TE state on each entry, writes the next word if data remains, and disables the TxE interrupt after 32 words have been sent. BufferSize, I2S_Buffer_Tx and the index type must be consistent.

src/main.csrc/main.c · main
    /* Enable the I2S1 TxE interrupt */
    SPI_I2S_EnableInt(I2S_MASTER, SPI_I2S_INT_TE, ENABLE);

    /* Enable I2S */
    I2S_Enable(I2S_MASTER, ENABLE);

    /* Wait for all data to be sent */
    while (TxIdx < BufferSize)
    {
    }

    /* Wait until I2S TX buffer is empty and bus is not busy */
    while (SPI_I2S_GetStatus(I2S_MASTER, SPI_I2S_TE_FLAG) == RESET)
        ;
    while (SPI_I2S_GetStatus(I2S_MASTER, SPI_I2S_BUSY_FLAG) != RESET)
        ;
src/n32g41x_it.csrc/n32g41x_it.c · SPI1_IRQHandler
    if (SPI_I2S_GetIntStatus(I2S_MASTER, SPI_I2S_INT_TE) != RESET)
    {
        if (TxIdx == BufferSize)
        {
            /* Disable the I2S1 TxE interrupt to end the communication */
            SPI_I2S_EnableInt(I2S_MASTER, SPI_I2S_INT_TE, DISABLE);
        }
        else
        {
            /* Send data from I2S1 */
            SPI_I2S_TransmitData(I2S_MASTER, I2S_Buffer_Tx[TxIdx++]);
        }
    }

The handler name SPI1_IRQHandler matches the startup file and must be replaced by the corresponding vector name for another instance.

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

5.12.3.4 I2S_Interrupt_Slave interrupt slave receive

Example directory: projects/n32g41x_EVAL/examples/I2S/I2S_Interrupt/I2S_Interrupt_Slave/

Enable the GPIO and SPI1/I2S1 clocks→Configure PA4, PA5 and PA7 as alternate-function push-pull→Set the SPI1 interrupt priority→Enable I2S slave RX and the RxNE interrupt→Receive 32 words in the interrupt→Compare the data and print the result over USART

Item Source analysis
Function Configures I2S1 as slave RX and stores the data into I2S_Buffer_Rx one word at a time in the RxNE interrupt;
after 32 words are received the buffer is compared with the expected data;
tested board-to-board against the I2S_Interrupt_Master master project.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, NVIC_Configuration, I2S_Configuration, Buffercmp;
src/n32g41x_it.c: SPI1_IRQHandler interrupt handler;
inc/main.h: I2S pin, IRQ number and clock macros;
readme.txt: function and test steps;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Clock source HSI plus PLL, system clock 80 MHz for N32G412 and 96 MHz for N32G415;
I2S clock source is the system clock, I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_MCLK_DISABLE, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW, mode I2S_MODE_SlAVE_RX;
WS on PA4, CK on PA5, SD on PA7, with GPIO_AF0, GPIO_MODE_AF_PP;
interrupt SPI1_IRQn with preemption priority 1 and subpriority 0, group NVIC_PriorityGroup_0.
Program and data path main calls log_init and then runs RCC_Configuration, NVIC_Configuration, GPIO_Configuration and I2S_Configuration;
it then enables the RxNE interrupt and the I2S and waits until RxIdx reaches the buffer size;
each RxNE interrupt reads the data register into I2S_Buffer_Rx[RxIdx++];
after 32 words, Buffercmp compares the buffer with I2S_Expected_Buffer and prints the result.
Main APIs SPI_I2S_DeInit, RCC_ConfigI2SClk, I2S_InitStruct, I2S_Init, I2S_Enable, SPI_I2S_EnableInt, SPI_I2S_GetIntStatus, SPI_I2S_ReceiveData, NVIC_Init
Verification Connect PA4, PA5, PA7 and GND of the two boards and power them on or reset them together;
the slave prints I2S Slave RX transfer completed, data match! Test PASS!! when the data is correct and Test FAIL!! when the comparison fails.
Use note The slave enables the I2S and the RxNE interrupt and waits first, and the master sends after a 1 s delay;
the master and slave settings must match (Phillips, 16-bit extended, 48 kHz);
keep the wires between two boards short when they face each other.
Core-code walkthrough and porting

The example first configures the pins, the interrupt and the slave RX parameters, then the RxNE interrupt receives the data word by word and compares it.

Data path: GPIO_Configuration configures PA4, PA5 and PA7 → NVIC_Configuration enables the SPI1 interrupt → I2S_Configuration sets slave RX → main enables the RxNE interrupt and the I2S → SPI1_IRQHandler stores the data into I2S_Buffer_Rx → Buffercmp compares and the result is printed over USART

1. Configure the WS, CK and SD pins as alternate-function push-pull. The pins are PA4, PA5 and PA7 as in the master project; on the slave CK and WS come from the external master, but the pins are still configured as alternate-function push-pull. The pin definitions are collected in inc/main.h.

src/main.csrc/main.c · GPIO_Configuration
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);

    /* I2S_SLAVE: WS(PA4), CK(PA5), SD(PA7) - AF mode, peripheral uses as input from Master */
    GPIO_InitStructure.Pin            = I2S_SLAVE_WS_PIN;
    GPIO_InitStructure.GPIO_Pull      = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = I2S_SLAVE_WS_AF;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(I2S_SLAVE_WS_GPIO, &GPIO_InitStructure);

The pin numbers and alternate-function numbers are defined in inc/main.h and must be changed together on a different board.

2. Configure the SPI1/I2S1 interrupt priority. The priority group is set to NVIC_PriorityGroup_0, SPI1_IRQn gets preemption priority 1 and subpriority 0, and the channel is enabled, symmetric with the master project. The priority values must be planned together with the other interrupts in the same project.

src/main.csrc/main.c · NVIC_Configuration
    NVIC_InitStructure.NVIC_IRQChannel                   = I2S_SLAVE_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

The interrupt channel is selected by I2S_SLAVE_IRQn, which must be changed when switching to another SPI instance.

3. Configure the I2S slave RX for the sampling frequency. The parameters are identical to the master and only the mode is changed to I2S_MODE_SlAVE_RX; the slave does not generate CK and WS, but the sampling frequency and data format still have to match the master. Changing any of them requires changing the master project as well.

src/main.csrc/main.c · I2S_Configuration
    I2S_InitStruct(&I2S_InitStructure);
    I2S_InitStructure.Standard       = I2S_STD_PHILLIPS;
    I2S_InitStructure.DataFormat     = I2S_DATA_FMT_16BITS_EXTENDED;
    I2S_InitStructure.MCLKEnable     = I2S_MCLK_DISABLE;
    I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
    I2S_InitStructure.CLKPOL         = I2S_CLKPOL_LOW;

The standard, data format, sampling frequency and clock polarity must be changed on both ends as a pair.

4. Enable the RxNE interrupt and the I2S and receive and compare in the handler. main enables the RxNE interrupt and the I2S and then waits until RxIdx reaches the buffer size; the handler reads the SPI_I2S_INT_RNE state and stores the data register into I2S_Buffer_Rx; after 32 words, Buffercmp compares the buffer with I2S_Expected_Buffer and prints the result. The expected data must match what the master sends.

src/main.csrc/main.c · main
    /* Enable the I2S1 RxNE interrupt */
    SPI_I2S_EnableInt(I2S_SLAVE, SPI_I2S_INT_RNE, ENABLE);

    /* Enable I2S Slave before Master starts */
    I2S_Enable(I2S_SLAVE, ENABLE);

    /* Wait for all data to be received */
    while (RxIdx < BufferSize)
    {
    }

    TransferStatus = Buffercmp(I2S_Buffer_Rx, I2S_Expected_Buffer, BufferSize);

    if (TransferStatus == PASSED)
    {
        log_info("\r\n I2S Slave RX transfer completed, data match! Test PASS!!\r\n");
    }
    else
    {
        log_info("\r\n I2S Slave RX completed but data mismatch! Test FAIL!!\r\n");
    }
src/n32g41x_it.csrc/n32g41x_it.c · SPI1_IRQHandler
    if (SPI_I2S_GetIntStatus(I2S_SLAVE, SPI_I2S_INT_RNE) != RESET)
    {
        I2S_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(I2S_SLAVE);
    }

The receive buffer I2S_Buffer_Rx, the expected data I2S_Expected_Buffer and the buffer size must be checked together with the master data.

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

5.12.3.5 SPI_I2S_Switch_Master mode-switch master

Example directory: projects/n32g41x_EVAL/examples/I2S/SPI_I2S_Switch/SPI_I2S_Switch_Master/

Enable the GPIO and SPI1 clocks→Configure PA4, PA5 and PA7 as alternate-function push-pull→Send 32 words in I2S master mode by polling→Switch to SPI master single-line TX and send 32 words→Switch back to I2S master and send again→Print the status of each step over USART

Item Source analysis
Function Switches the same SPI1 in three steps: it first sends as I2S master, then switches to SPI master single-line TX, and finally switches back to I2S master;
each step sends 32 16-bit words;
tested board-to-board against the SPI_I2S_Switch_Slave slave project.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, I2S_Master_TX, SPI_Master_TX;
inc/main.h: pin and clock macros;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function and test steps;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Clock source HSI plus PLL, system clock 80 MHz for N32G412 and 96 MHz for N32G415;
I2S mode I2S_MODE_MASTER_TX, I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_MCLK_DISABLE, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW;
SPI mode SPI_DIR_SINGLELINE_TX, SPI_MODE_MASTER, SPI_DATA_SIZE_16BITS, SPI_CLKPOL_LOW, SPI_CLKPHA_SECOND_EDGE, SPI_NSS_SOFT, SPI_BR_PRESCALER_32, SPI_FB_MSB;
pins NSS/WS on PA4, SCK/CK on PA5, MOSI/SD on PA7 with AF0.
Program and data path main calls log_init and then runs RCC_Configuration, a 1 s delay and GPIO_Configuration;
it then calls I2S_Master_TX to send in I2S mode, waits 100 ms and calls SPI_Master_TX to send in SPI single-line mode, waits 100 ms and calls I2S_Master_TX again;
both mode switches reset SPI1 with SPI_I2S_DeInit before reinitializing.
Main APIs SPI_I2S_DeInit, RCC_ConfigI2SClk, I2S_InitStruct, I2S_Init, I2S_Enable, SPI_InitStruct, SPI_Init, SPI_Enable, SPI_I2S_TransmitData, SPI_I2S_GetStatus
Verification Connect PA4, PA5, PA7 and GND of the two boards directly, power the slave on first and then the master;
the master prints Step 1: I2S TX completed!, Step 2: SPI TX completed!, Step 3: I2S TX completed! and All steps done! in turn, and the slave prints the receive-compare result of each step.
Use note The master delays 1 s at startup so that the slave is ready and delays 100 ms between steps so that the slave can switch mode;
the two boards use the same SPI1 pins (PA4, PA5, PA7) connected with jumper wires;
only SPI1 of the N32G41x supports I2S.
Core-code walkthrough and porting

The example configures the pins once and then sends in the order I2S, SPI, I2S on the same SPI1.

Data path: RCC_Configuration enables the clocks → GPIO_Configuration configures PA4, PA5 and PA7 → I2S_Master_TX sends by I2S master polling → SPI_Master_TX switches to SPI master single-line TX → I2S_Master_TX switches back to I2S → the status is printed step by step over USART

1. Enable the GPIO and SPI1 clocks. I2S and SPI share the SPI1 clock, so only the GPIO clock and the SPI1 clock are enabled here. Changing the instance means changing SPI_I2S_MASTER_CLK.

src/main.csrc/main.c · RCC_Configuration
void RCC_Configuration(void)
{
    /* Enable GPIO clock */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_GPIO, ENABLE);

    /* Enable SPI1 clock */
    RCC_EnableAPB2PeriphClk(SPI_I2S_MASTER_CLK, ENABLE);
}

The clock macros must correspond to the selected GPIO port and SPI1 instance.

2. Configure the NSS/WS, SCK/CK and MOSI/SD pins as alternate-function push-pull. The three pins share the same AF0 group in both SPI and I2S mode, so they only need to be configured once. The pin definitions are collected in inc/main.h.

src/main.csrc/main.c · GPIO_Configuration
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);

    /* SPI1/I2S1: NSS/WS(PA4) as AF_PP */
    GPIO_InitStructure.Pin            = SPI_I2S_NSS_WS_PIN;
    GPIO_InitStructure.GPIO_Pull      = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = SPI_I2S_NSS_WS_AF;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(SPI_I2S_NSS_WS_GPIO, &GPIO_InitStructure);

The pin numbers and alternate-function numbers are defined in inc/main.h and must be changed together on a different board.

3. Configure the I2S master TX and send data by polling. I2S_Master_TX first resets the peripheral and selects the system clock source, then fills the I2S parameters and calls I2S_Init, enables the I2S with I2S_Enable and writes the data one word at a time after waiting for SPI_I2S_TE_FLAG. The data comes from I2S_Buffer_Tx.

src/main.csrc/main.c · I2S_Master_TX
    I2S_InitStruct(&I2S_InitStructure);
    I2S_InitStructure.Standard       = I2S_STD_PHILLIPS;
    I2S_InitStructure.DataFormat     = I2S_DATA_FMT_16BITS_EXTENDED;
    I2S_InitStructure.MCLKEnable     = I2S_MCLK_DISABLE;
    I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
    I2S_InitStructure.CLKPOL         = I2S_CLKPOL_LOW;

    RCC_GetClocksFreqValue(&RCC_Clocks);
    I2S_InitStructure.ClkSrcFrequency = RCC_Clocks.SysclkFreq;

    I2S_InitStructure.I2sMode = I2S_MODE_MASTER_TX;
    I2S_Init(SPI_I2S_MASTER, &I2S_InitStructure);

    /* Enable I2S */
    I2S_Enable(SPI_I2S_MASTER, ENABLE);

The I2S standard, data format, sampling frequency and polarity must match the I2S configuration of the slave.

4. Switch to SPI master single-line TX. SPI_Master_TX first resets the same SPI1 with SPI_I2S_DeInit, then configures SPI_InitType for single-line TX, 16-bit and software NSS, enables it and sends SPI_Buffer_Tx by polling before disabling the SPI. When switching mode, the I2S configuration bits cannot be reused, so the peripheral must be reinitialized.

src/main.csrc/main.c · SPI_Master_TX
    SPI_InitStruct(&SPI_InitStructure);

    /* SPI1 master: single-line TX, 16-bit */
    SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
    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_32;
    SPI_InitStructure.FirstBit     = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly     = 7;
    SPI_Init(SPI_I2S_MASTER, &SPI_InitStructure);

The SPI direction, width, polarity, phase and baud-rate prescaler must correspond to the SPI configuration of the slave.

5. Call the steps from main and print the status. main calls I2S_Master_TX, SPI_Master_TX and I2S_Master_TX in turn with a 100 ms delay in between; each step logs its state with log_info before and after sending so that the master log can be aligned with the slave. The delays between steps are the key to synchronizing with the mode switch of the slave.

src/main.csrc/main.c · main
    /* Step 1: I2S mode - Master TX */
    log_info("\r\n Step 1: I2S Master TX...\r\n");
    I2S_Master_TX();
    log_info(" Step 1: I2S TX completed!\r\n");

    /* Delay for slave to switch mode */
    SysTick_Delay_Ms(100);

    /* Step 2: Switch to SPI mode - Master TX */
    log_info("\r\n Step 2: SPI Master TX...\r\n");
    SPI_Master_TX();
    log_info(" Step 2: SPI TX completed!\r\n");

    /* Delay for slave to switch mode */
    SysTick_Delay_Ms(100);

    /* Step 3: Switch back to I2S mode - Master TX */
    log_info("\r\n Step 3: I2S Master TX (again)...\r\n");
    I2S_Master_TX();
    log_info(" Step 3: I2S TX completed!\r\n");

The number of steps, the delay of each step and the transmit buffers must correspond one to one with the receive flow of the slave.

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

5.12.3.6 SPI_I2S_Switch_Slave mode-switch slave

Example directory: projects/n32g41x_EVAL/examples/I2S/SPI_I2S_Switch/SPI_I2S_Switch_Slave/

Enable the GPIO and SPI1 clocks→Configure PA4, PA5 and PA7 as alternate-function push-pull→Receive and compare data in I2S slave mode→Switch to SPI slave double-line RX-only and compare→Switch back to I2S slave and compare→Print the result of each step over USART

Item Source analysis
Function Switches the same SPI1 in three steps: it first receives as I2S slave, then switches to SPI slave double-line RX-only, and finally switches back to I2S slave;
each step receives 32 16-bit words and compares them with the expected data;
tested board-to-board against the SPI_I2S_Switch_Master master project.
Source files src/main.c: main, RCC_Configuration, GPIO_Configuration, I2S_Slave_RX, SPI_Slave_RX, Buffercmp;
inc/main.h: pin and clock macros;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt handlers;
readme.txt: function and test steps;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration Clock source HSI plus PLL, system clock 80 MHz for N32G412 and 96 MHz for N32G415;
I2S mode I2S_MODE_SlAVE_RX, I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_MCLK_DISABLE, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW;
SPI mode SPI_DIR_DOUBLELINE_RONLY, SPI_MODE_SLAVE, SPI_DATA_SIZE_16BITS, SPI_CLKPOL_LOW, SPI_CLKPHA_SECOND_EDGE, SPI_NSS_SOFT, SPI_BR_PRESCALER_32, SPI_FB_MSB;
pins NSS/WS on PA4, SCK/CK on PA5, MOSI/SD on PA7 with AF0.
Program and data path main calls log_init and then runs RCC_Configuration and GPIO_Configuration;
it then calls I2S_Slave_RX to receive and compare in I2S mode, calls SPI_Slave_RX to receive and compare in SPI double-line RX-only mode, clears the receive buffer and calls I2S_Slave_RX again;
the three comparison results are stored in TransferStatus1, TransferStatus2 and TransferStatus3, and All Tests PASS!! is printed when all pass.
Main APIs SPI_I2S_DeInit, RCC_ConfigI2SClk, I2S_InitStruct, I2S_Init, I2S_Enable, SPI_InitStruct, SPI_Init, SPI_Enable, SPI_I2S_ReceiveData, SPI_I2S_GetStatus
Verification Connect PA4, PA5, PA7 and GND of the two boards directly, power the slave on first and then the master;
the slave prints Step 1: I2S RX PASS!!, Step 2: SPI RX PASS!! and Step 3: I2S RX PASS!! in turn, and prints All Tests PASS!! when all pass.
Use note The slave has no delay and is always ready before the master, relying on the 1 s startup delay of the master for timing;
the SPI slave uses double-line RX-only mode to receive the master MOSI data;
the two boards use the same SPI1 pins connected with jumper wires.
Core-code walkthrough and porting

The example configures the pins once and then receives and compares in the order I2S, SPI, I2S on the same SPI1.

Data path: RCC_Configuration enables the clocks → GPIO_Configuration configures PA4, PA5 and PA7 → I2S_Slave_RX receives by I2S slave polling → SPI_Slave_RX switches to SPI slave double-line RX-only → I2S_Slave_RX switches back to I2S → Buffercmp compares and the result is printed over USART

1. Enable the GPIO and SPI1 clocks. I2S and SPI share the SPI1 clock, so only the GPIO clock and the SPI1 clock are enabled, symmetric with the master project. Changing the instance means changing SPI_I2S_SLAVE_CLK.

src/main.csrc/main.c · RCC_Configuration
void RCC_Configuration(void)
{
    /* Enable GPIO clock */
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_GPIO, ENABLE);

    /* Enable SPI1 clock */
    RCC_EnableAPB2PeriphClk(SPI_I2S_SLAVE_CLK, ENABLE);
}

The clock macros must correspond to the selected GPIO port and SPI1 instance.

2. Configure the NSS/WS, SCK/CK and MOSI/SD pins as alternate-function push-pull. The three pins share the same AF0 group in both SPI and I2S mode, so they only need to be configured once. The pin definitions are collected in inc/main.h.

src/main.csrc/main.c · GPIO_Configuration
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);

    /* SPI1/I2S1: NSS/WS(PA4) as AF_PP */
    GPIO_InitStructure.Pin            = SPI_I2S_NSS_WS_PIN;
    GPIO_InitStructure.GPIO_Pull      = GPIO_NO_PULL;
    GPIO_InitStructure.GPIO_Alternate = SPI_I2S_NSS_WS_AF;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(SPI_I2S_NSS_WS_GPIO, &GPIO_InitStructure);

The pin numbers and alternate-function numbers are defined in inc/main.h and must be changed together on a different board.

3. Configure the I2S slave RX and read data by polling. I2S_Slave_RX first resets the peripheral, then fills the I2S parameters that match the master and calls I2S_Init, enables the I2S with I2S_Enable and reads the data one word at a time after waiting for SPI_I2S_RNE_FLAG. This data corresponds to what the master sends in I2S_Buffer_Tx.

src/main.csrc/main.c · I2S_Slave_RX
    I2S_InitStruct(&I2S_InitStructure);
    I2S_InitStructure.Standard       = I2S_STD_PHILLIPS;
    I2S_InitStructure.DataFormat     = I2S_DATA_FMT_16BITS_EXTENDED;
    I2S_InitStructure.MCLKEnable     = I2S_MCLK_DISABLE;
    I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_48K;
    I2S_InitStructure.CLKPOL         = I2S_CLKPOL_LOW;

    RCC_GetClocksFreqValue(&RCC_Clocks);
    I2S_InitStructure.ClkSrcFrequency = RCC_Clocks.SysclkFreq;

    I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_RX;
    I2S_Init(SPI_I2S_SLAVE, &I2S_InitStructure);

    /* Enable I2S Slave - must be enabled before Master starts */
    I2S_Enable(SPI_I2S_SLAVE, ENABLE);

The I2S standard, data format, sampling frequency and polarity must match the I2S configuration of the master.

4. Switch to SPI slave double-line RX-only. SPI_Slave_RX first resets the same SPI1 with SPI_I2S_DeInit, then configures double-line RX-only, 16-bit and software NSS, enables it and writes the received data into SPI_Buffer_Rx by polling before disabling the SPI. When switching mode, the peripheral must be reinitialized and the I2S configuration bits cannot be reused.

src/main.csrc/main.c · SPI_Slave_RX
    SPI_InitStruct(&SPI_InitStructure);

    /* SPI1 slave: double-line RX only, 16-bit */
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_RONLY;
    SPI_InitStructure.SpiMode      = SPI_MODE_SLAVE;
    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_32;
    SPI_InitStructure.FirstBit     = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly     = 7;
    SPI_Init(SPI_I2S_SLAVE, &SPI_InitStructure);

The SPI direction, width, polarity, phase and baud-rate prescaler must correspond to the SPI configuration of the master.

5. Receive, compare and print the result step by step from main. main calls I2S_Slave_RX, SPI_Slave_RX and I2S_Slave_RX in turn and compares each result with the corresponding expected buffer using Buffercmp; the I2S receive buffer must be cleared before the second switch so that residual data does not affect the third comparison. The total result is printed when all three pass.

src/main.csrc/main.c · main
    /* Step 2: Switch to SPI mode - Slave RX */
    log_info("\r\n Step 2: Waiting for SPI Master TX...\r\n");
    SPI_Slave_RX();

    TransferStatus2 = Buffercmp((uint16_t*)SPI_Buffer_Rx, SPI_Expected_Buffer, BufferSize);
    if (TransferStatus2 == PASSED)
        log_info(" Step 2: SPI RX PASS!!\r\n");
    else
        log_info(" Step 2: SPI RX FAIL!!\r\n");

    /* Clear I2S RX buffer for step 3 */
    {
        uint8_t i;
        for (i = 0; i < BufferSize; i++)
            I2S_Buffer_Rx[i] = 0;
    }

    /* Step 3: Switch back to I2S mode - Slave RX */
    log_info("\r\n Step 3: Waiting for I2S Master TX (again)...\r\n");
    I2S_Slave_RX((uint16_t*)I2S_Buffer_Rx);

    TransferStatus3 = Buffercmp((uint16_t*)I2S_Buffer_Rx, I2S_Expected_Buffer, BufferSize);
    if (TransferStatus3 == PASSED)
        log_info(" Step 3: I2S RX PASS!!\r\n");

The number of steps, the position where the buffer is cleared and the two expected buffers I2S_Expected_Buffer and SPI_Expected_Buffer must correspond one to one with the master transmit flow.

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

5.12.4 Notes and troubleshooting

Scenario Check & action Source
I2S can only transfer one way The user manual states that I2S only supports half-duplex and not full-duplex, so it can only transmit or receive at a time;
for bidirectional audio use two I2S instances or separate the transmit and receive paths.
User manual
Inaccurate sampling frequency or distorted audio The sampling frequency is determined by LDIV[9:0] and ODDEVEN of SPI_I2S_PREDIV and depends on the I2SxCLK clock source, whether MCLK is output and the channel length;
take the value from the divider formulas or the configuration table, for example with SYSCLK at 72 MHz, MCLK output and a 192 kHz target a LDIV of 1 gives an error of up to 26.76%, so choose a combination with a smaller error from the table.
User manual
Underrun during transmit The user manual states that if no new data is written before the clock edge required by the next data, both master and slave transmit set the UNDER flag of SPI_STS;
in that case disable the I2S and restart the transmit from the left channel.
User manual
Overrun during receive The user manual states that the OVER flag of SPI_STS is set when a new frame arrives before the previous one is read;
reading SPI_DAT clears RNE, so clear the flag in the order given by the manual and make sure the data is taken away before the next frame.
User manual
Disabling the I2S corrupts the last transfer The user manual requires choosing the disable order according to the data length, channel length and audio standard: wait for RNE first, then insert a software delay of one I2S clock period (17 periods when a 16-bit extended format is LSB-aligned) and finally clear I2SEN of SPI_I2S_CFGR;
for continuous transfer, wait for TE and BUSY first.
User manual
The channel-side flag is invalid after an error The user manual states that the CHSIDE flag of SPI_STS is invalid after a transmit underrun or a receive overrun, so the I2S must be disabled and re-enabled before communication resumes. User manual

The errata sheet V1.0.0 (2026.6.30) for the N32G412 and N32G415 series lists only a single A-version device row with an empty errata link column and no I2S-related 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.