5.17 I2S Guide
This chapter covers the I2S driver files in the N32H49x standard peripheral library, its 11 public functions with the I2S_ prefix, and the 4 examples I2S_DMA, I2S_Fullduplex, I2S_Interrupt, and SPI_I2S_Switch. I2S shares n32h49x_spi.h and n32h49x_spi.c with SPI; the shared SPI_I2S_ functions used for data transfer, DMA, interrupts, and status queries are documented in the SPI chapter. Read it together with the “Serial peripheral interface/Inter-IC sound (SPI/I2S)” chapter of the user manual for audio protocols, the clock generator, and the enable/disable procedures.
5.17.1 Related files
| Relative path | Purpose |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_spi.h |
Defines the I2S_InitType and I2S_EXT_InitType structures and the master/slave mode, audio standard, data format, and sample-rate macros, and declares the 11 functions with the I2S_ prefix;the other 32 functions in this header belong to the SPI chapter |
firmware/n32h49x_std_periph_driver/src/n32h49x_spi.c |
Implements the 11 functions: linear prescaler computation and configuration-register writes, structure defaults, initialization and enable of I2S and I2SEXT, and data transfer plus flag/interrupt status queries |
projects/n32h49x_EVAL/examples/I2S/ |
Root of the I2S examples, holding 4 examples: I2S_DMA, I2S_Fullduplex, I2S_Interrupt, SPI_I2S_Switch |
projects/n32h49x_EVAL/examples/I2S/I2S_DMA/ |
Project layout of that example: MDK-ARM/, EWARM/, inc/, src/, readme.txt |
projects/n32h49x_EVAL/bsp/ |
Board support files shared by all 4 examples: UART log printing |
5.17.2 API reference
The following 11 public functions are listed in the declaration order of n32h49x_spi.h in 2 groups; each entry gives a summary and the original comment above the function in n32h49x_spi.c.
I2S initialization and enable
I2S_InitStruct, I2S_Enable, I2S_Init
I2S_EXT slave-side init, transfer, and status
I2S_EXT_Init, I2S_EXT_Enable, I2S_EXT_TransmitData, I2S_EXT_ReceiveData, I2S_EXT_EnableDma, I2S_EXT_EnableInt, I2S_EXT_GetStatus, I2S_EXT_GetIntStatus
void I2S_InitStruct(I2S_InitType* I2S_InitStruct)Fills each I2S_InitStruct member with its default value.
name I2S_InitStruct.
fun Fills each I2S_InitStruct member with its default value.
param I2S_InitStruct :
- 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).
name I2S_Enable.
fun Enables or disables the specified SPI peripheral (in I2S mode).
param SPIx :
- SPI2
- SPI3
param Cmd :
- ENABLE
- DISABLE
return none
void I2S_Init(SPI_Module* SPIx,const I2S_InitType* I2S_InitStruct)Initializes the SPIx peripheral according to the specified parameters in the I2S_InitStruct.
name I2S_Init.
fun Initializes the SPIx peripheral according to the specified parameters in the I2S_InitStruct.
param SPIx :
- SPI2
- SPI3
param I2S_InitStruct :
- 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
- RCC_Clocks.HclkFreq
- RCC_Clocks.Pclk1Freq
- RCC_Clocks.Pclk2Freq
- RCC_Clocks.AdcPllClkFreq
- RCC_Clocks.AdcHclkFreq
- RCC_Clocks.extpllFreq
- user defined
void I2S_EXT_Init(I2S_EXT_Module* I2Sx_EXT,const I2S_InitType* I2S_EXT_InitStruct)Initializes the I2Sx_EXT peripheral according to the specified parameters in the I2S_EXT_InitStruct.
name I2S_EXT_Init.
fun Initializes the I2Sx_EXT peripheral according to the specified parameters in the I2S_EXT_InitStruct.
param I2Sx_EXT :
- I2S2_EXT
- I2S3_EXT
param I2S_InitStruct:
- 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
- CLKPOL
- I2S_CLKPOL_LOW
- I2S_CLKPOL_HIGH
return none
void I2S_EXT_Enable(I2S_EXT_Module* I2Sx_EXT, FunctionalState Cmd)Enables or disables the specified I2S EXT peripheral.
name I2S_EXT_Enable.
fun Enables or disables the specified I2S EXT peripheral.
param I2Sx_EXT :
- I2S2_EXT
- I2S3_EXT
param Cmd :
- ENABLE
- DISABLE
return none
void I2S_EXT_TransmitData(I2S_EXT_Module* I2Sx, uint16_t Data)Transmits a Data through the I2Sx_EXT peripheral.
name I2S_EXT_TransmitData.
fun Transmits a Data through the I2Sx_EXT peripheral.
param I2Sx :
- I2S2_EXT
- I2S3_EXT
param Data \* Data to be transmitted *\
return none
uint16_t I2S_EXT_ReceiveData(const I2S_EXT_Module* I2Sx)Returns the most recent received data by the I2Sx_EXT peripheral.
name I2S_EXT_ReceiveData.
fun Returns the most recent received data by the I2Sx_EXT peripheral.
param I2Sx :
- I2S2_EXT
- I2S3_EXT
return The data in the I2S_DAT register.
void I2S_EXT_EnableDma(I2S_EXT_Module* I2Sx, uint16_t I2S_EXT_DMAReq, FunctionalState Cmd)Enables or disables the I2Sx DMA interface.
name I2S_EXT_EnableDma.
fun Enables or disables the I2Sx DMA interface.
param I2Sx :
- I2S2_EXT
- I2S3_EXT
param I2S_EXT_DMAReq :
- I2S_EXT_DMA_TX
- I2S_EXT_DMA_RX
param Cmd :
- ENABLE
- DISABLE
return none
void I2S_EXT_EnableInt(I2S_EXT_Module* I2Sx, uint8_t I2S_EXT_IT, FunctionalState Cmd)Enables or disables the specified I2S_EXT interrupts.
name I2S_EXT_EnableInt.
fun Enables or disables the specified I2S_EXT interrupts.
param I2Sx :
- I2S2_EXT
- I2S3_EXT
param I2S_EXT_IT :
- I2S_EXT_INT_TEINTEN
- I2S_EXT_INT_RNEINTEN
- I2S_EXT_INT_ERRINTEN
param Cmd :
- ENABLE
- DISABLE
return none
FlagStatus I2S_EXT_GetStatus(const I2S_EXT_Module* I2Sx, uint16_t I2S_EXT_FLAG)Checks whether the specified I2S_EXT flag is set or not.
name I2S_EXT_GetStatus.
fun Checks whether the specified I2S_EXT flag is set or not.
param I2Sx :
- I2S2_EXT
- I2S3_EXT
param I2S_EXT_FLAG :
- I2S_EXT_BUSY_FLAG
- I2S_EXT_OVER_FLAG
- I2S_EXT_UNDER_FLAG
- I2S_EXT_CHSIDE_FLAG
- I2S_EXT_TE_FLAG
- I2S_EXT_RNE_FLAG
return The new state of I2S_EXT_FLAG (SET or RESET).
INTStatus I2S_EXT_GetIntStatus(const I2S_EXT_Module* I2Sx, uint8_t I2S_EXT_IT)Checks whether the specified I2S_EXT interrupt has occurred or not.
name I2S_EXT_GetIntStatus.
fun Checks whether the specified I2S_EXT interrupt has occurred or not.
param I2Sx :
- I2S2_EXT
- I2S3_EXT
param I2S_EXT_IT :
- I2S_EXT_TE_FLAG
- I2S_EXT_RNE_FLAG
- I2S_EXT_BUSY_FLAG
- I2S_EXT_OVER_FLAG
- I2S_EXT_UNDER_FLAG
- I2S_EXT_CHSIDE_FLAG
return The new state of I2S_EXT_IT (SET or RESET).
5.17.3 Demo descriptions
This section describes the 4 I2S examples, all tested on a single board with I2S2 and I2S3: I2S_DMA transfers data with DMA, I2S_Fullduplex combines I2S and I2SEXT into full duplex, I2S_Interrupt transfers data in the TE and RNE interrupts, and SPI_I2S_Switch alternates between I2S and SPI mode. I2S2 always uses CK:PC7, WS:PC6, SD:PC9, and I2S3 uses CK:PC3, SD:PC1, and WS:PC2, connected to each other with jumpers.
5.17.3.1 I2S_DMA DMA transfer
Example directory: projects/n32h49x_EVAL/examples/I2S/I2S_DMA/
Enable the GPIO, DMA, and two I2S clocks→Configure the slave RX and master TX DMA channels→Configure the audio parameters of both I2S peripherals→Enable the DMA requests, DMA channels, and I2S→Wait for both DMA transfers to finish→Compare the received data and print the result
| Item | Source analysis |
|---|---|
| Function | I2S3 acts as master transmitter and I2S2 as slave receiver; through DMA2_CH2 and DMA1_CH4 they exchange 32 16-bit values on a single board, then the received data is compared and printed. |
| Source files | src/main.c: transmit buffer I2S_MASTER_Buffer_Tx, receive buffer I2S_SLAVE_Buffer_Rx, main and GPIO_Configuration, DMA_Configuration, I2S_Configuration, Buffercmp;src/n32h49x_it.c, inc/n32h49x_it.h: interrupt handlers;inc/main.h: SPI2/SPI3, pin, alternate-function, and DMA macros;readme.txt: function, environment, and wiring instructions;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | I2S uses I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW, and I2S_MCLK_DISABLE, with I2S3 as master TX and I2S2 as slave RX, clocked from the system clock;DMA uses half-word width, no peripheral increment, memory increment, normal mode, and very high priority, with DMA2_CH2 for I2S3 TX and DMA1_CH4 for I2S2 RX; the pins are I2S2 CK:PC7, WS:PC6, SD:PC9 (AF13) and I2S3 CK:PC3, SD:PC1, WS:PC2 (AF2, AF1, AF12); the system clock is HSI+PLL at 240 MHz. |
| Program and data path | main configures GPIO, DMA, and I2S, then enables both DMA channels;I2S_Configuration has already enabled the TX/RX DMA requests and enabled I2S2 and I2S3;DMA2_CH2 writes I2S_MASTER_Buffer_Tx into SPI3->DAT, the data leaves I2S3, is received by I2S2, and is written into I2S_SLAVE_Buffer_Rx;main waits for both DMA completion flags, then calls Buffercmp and prints the result. |
| Main APIs | I2S_InitStruct, I2S_Init, I2S_Enable, SPI_I2S_DeInit, SPI_I2S_EnableDma, DMA_Init, DMA_RequestRemap, DMA_EnableChannel, DMA_GetFlagStatus, RCC_ConfigI2S2Clk, RCC_ConfigI2S3Clk |
| Verification | Wire I2S2 to I2S3 WS, SCK, and SD with jumpers as described in the readme; after download and reset, TransferStatus is PASSED and the UART prints Test PASS!!, meaning the transferred data matches. |
| Use note | I2S2 and I2S3 must be connected with jumpers on the same board; both I2S peripherals are clocked from the system clock, so the I2S clock source must be configured before initialization. It targets the N32H497ZGL7_STB board; the toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example configures I2S3 as master transmitter and I2S2 as slave receiver on a single board and uses two DMA channels to move the data from the transmit buffer into the receive buffer.
Data path: I2S_MASTER_Buffer_Tx → DMA2_CH2 → SPI3->DAT → I2S3 shift register → PC1 (SD), PC3 (CK), PC2 (WS) → I2S2 shift register → SPI2->DAT → DMA1_CH4 → I2S_SLAVE_Buffer_Rx → Buffercmp
1. Select the clock source before initializing I2S. Both I2S peripherals are clocked from the system clock, so the clock source must be selected before I2S_Init.
/* CLK source must be configured before I2S initialization */
RCC_ConfigI2S2Clk(RCC_I2S2_CLKSRC_SYSCLK); /* I2S2(I2S_SLAVE) clk source select */
RCC_ConfigI2S3Clk(RCC_I2S3_CLKSRC_SYSCLK); /* I2S3(I2S_MASTER) clk source select */
When another clock source is used, ClkSrcFrequency must be written with the actual frequency of that source.
2. Configure the slave receive DMA channel. The direction is peripheral to memory: half-words are read from SPI2->DAT into the receive buffer, and the channel is mapped to the I2S2 receive request.
/* I2S_SLAVE_Rx_DMA_Channel configuration ---------------------------------------------*/
DMA_DeInit(I2S_SLAVE_Rx_DMA_Channel);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (uint32_t)&I2S_SLAVE->DAT;
DMA_InitStructure.MemAddr = (uint32_t)I2S_SLAVE_Buffer_Rx;
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
DMA_InitStructure.BufSize = BufferSize;
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
DMA_InitStructure.MemoryInc = DMA_MEM_INC_ENABLE;
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_HALFWORD;;
DMA_InitStructure.MemDataSize = DMA_MEM_DATA_WIDTH_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, I2S_SLAVE_Rx_DMA_Channel, ENABLE);
Transmit and receive use different channels; the channel number, completion flag, and remap are all given by the macros in inc/main.h. The half-word width matches one data-register access per frame in the 16-bit extended format.
3. Configure the master transmit DMA channel. The same structure is reused; only the direction is changed to memory-to-peripheral and the addresses to SPI3->DAT and the transmit buffer.
/* I2S_MASTER_Tx_DMA_Channel configuration ---------------------------------------------*/
DMA_DeInit(I2S_MASTER_Tx_DMA_Channel);
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_Init(I2S_MASTER_Tx_DMA_Channel, &DMA_InitStructure);
DMA_RequestRemap(I2S_MASTER_Tx_DMA_Remap, I2S_MASTER_Tx_DMA_Channel, ENABLE);
4. Initialize and enable both I2S peripherals. I2S2 is set up as slave RX and I2S3 as master TX, then the TX/RX DMA requests are enabled and both ends are enabled.
/* I2S2 configuration */
I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_RX;
I2S_Init(SPI2, &I2S_InitStructure);
/* I2S3 configuration */
I2S_InitStructure.I2sMode = I2S_MODE_MASTER_TX;
I2S_Init(SPI3, &I2S_InitStructure);
/* Enable I2S_SLAVE Rx request */
SPI_I2S_EnableDma(I2S_SLAVE, SPI_I2S_DMA_RX, ENABLE);
/* Enable I2S_MASTER Tx request */
SPI_I2S_EnableDma(I2S_MASTER, SPI_I2S_DMA_TX, ENABLE);
/* Enable the I2S2 */
I2S_Enable(I2S_SLAVE, ENABLE);
/* Enable the I2S3 */
I2S_Enable(I2S_MASTER, ENABLE);
The DMA requests are enabled before I2S, otherwise the first items may not be moved in time.
Corresponding source files: src/main.c, inc/main.h
5.17.3.2 I2S_Fullduplex I2S and I2SEXT full duplex
Example directory: projects/n32h49x_EVAL/examples/I2S/I2S_Fullduplex/
Configure the GPIO and free the debug pin→Initialize I2S2, I2S2_EXT, I2S3, and I2S3_EXT as a full-duplex pair→Enable the extended peripherals and both I2S peripherals→Poll-send and poll-receive 32 items on the master and slave sides→Disable both I2S peripherals and the extended peripherals→Compare the two links and print the result
| Item | Source analysis |
|---|---|
| Function | I2S3 master TX paired with I2S2 slave RX, and I2S2_EXT slave TX paired with I2S3_EXT slave RX, form a full-duplex link on a single board; each end sends 32 16-bit values and checks them against the other. |
| Source files | src/main.c: the master and slave transmit buffers Master_Buffer_Tx and Slave_Buffer_Tx, the receive buffers Master_Buffer_Rx and Slave_Buffer_Rx, main and GPIO_Configuration, I2S_Configuration, Buffercmp;src/n32h49x_it.c, inc/n32h49x_it.h: interrupt handlers;inc/main.h: SPI2/SPI3, pin, alternate-function, and AUX_SD macros;readme.txt: function, environment, and wiring instructions;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | I2S uses I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW, and I2S_MCLK_DISABLE, clocked from the system clock;I2S3 master TX is paired with I2S3_EXT slave RX, and I2S2 slave RX with I2S2_EXT slave TX; the pins are I2S2 CK:PC7, WS:PC6, SD:PC9 with AUX_SD:PC2, and I2S3 CK:PC3, SD:PC1, WS:PA4 with AUX_SD:PB4; the system clock is HSI+PLL at 240 MHz. |
| Program and data path | main configures GPIO and I2S, then enters the loop;each round it first waits for the I2S3 transmit buffer to empty and writes the master item, then waits for the I2S2_EXT transmit buffer to empty and writes the slave item; it then waits for I2S2 receive-not-empty to read the slave-side received item and for I2S3_EXT receive-not-empty to read the master-side item, exiting after 32 items; after disabling I2S and the extended peripherals it calls Buffercmp on both links and prints the result. |
| Main APIs | I2S_InitStruct, I2S_Init, I2S_EXT_Init, I2S_Enable, I2S_EXT_Enable, SPI_I2S_DeInit, SPI_I2S_TransmitData, SPI_I2S_ReceiveData, I2S_EXT_TransmitData, I2S_EXT_ReceiveData, SPI_I2S_GetStatus, I2S_EXT_GetStatus, RCC_ConfigI2S2Clk, RCC_ConfigI2S3Clk |
| Verification | Wire I2S2 to I2S3 WS, CK, and the two data lines with jumpers; after download and reset, both TransferStatus1 and TransferStatus2 are PASSED and the UART prints Test PASS!!. |
| Use note | I2S3 WS is on PA4 and I2S3_EXT AUX_SD on PB4, so both data lines must be connected; PB4 is a default debug pin, so the example remaps SWJ to SWD only before using it as the extended data line. It targets the N32H497ZGL7_STB board; the toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example combines I2S with its extended peripheral into two transmit/receive pairs and completes a full-duplex check on a single board by polling.
Data path: Master_Buffer_Tx → SPI3->DAT → I2S3 (master TX) → I2S2 (slave RX) → SPI2->DAT → Slave_Buffer_Rx; at the same time Slave_Buffer_Tx → I2S2_EXT->DAT → I2S2_EXT (slave TX) → I2S3_EXT (slave RX) → I2S3_EXT->DAT → Master_Buffer_Rx
1. Free the debug pin and configure the pin alternate functions. The I2S3_EXT data line falls on the default debug pin PB4, so SWJ must first be remapped to SWD only.
/* GPIOB, GPIOA and GPIOC clocks enable */
RCC_EnableAHB1PeriphClk(RCC_AHB_PERIPHEN_GPIOA | RCC_AHB_PERIPHEN_GPIOB | RCC_AHB_PERIPHEN_GPIOC, ENABLE);
/* Enable peripheral clocks ------------------------------------------------*/
/* AFIO clocks enable */
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPHEN_AFIO, ENABLE);
GPIO_ConfigPinRemap(GPIOB_PORT_SOURCE, GPIO_PIN_SOURCE4, GPIO_RMP_SWJ_SWD);
/* Configure I2S2_EXT pins: AUX_SD:PC2---------------------------------*/
GPIO_InitStructure.Pin = I2S_SLAVE_PIN_AUX_SD;
GPIO_InitStructure.GPIO_Alternate = I2S_SLAVE_AF_AUX_SD;
GPIO_InitPeripheral(I2S_SLAVE_GPIO_AUX_SD, &GPIO_InitStructure);
/* Configure I2S3_EXT pins: AUX_SD -------------------------------------------*/
GPIO_InitStructure.Pin = GPIO_PIN_4;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF15;
GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);
The CK, WS, and SD of I2S2 and I2S3 and both AUX_SD lines are configured as alternate-function push-pull; the pin macros are given in inc/main.h.
2. Initialize I2S and the extended peripherals as a full-duplex pair. The same audio parameters are written to I2S2, I2S2_EXT, I2S3, and I2S3_EXT in turn, and then all are enabled together.
/* I2S3 Master Transmitter to I2S2 Slave Receiver communication -----------*/
/* I2S2 configuration */
I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_RX;
I2S_Init(SPI2, &I2S_InitStructure);
/* I2S2_EXT configuration */
I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_TX;
I2S_EXT_Init(I2S2_EXT, &I2S_InitStructure);
/* I2S3 configuration */
I2S_InitStructure.I2sMode = I2S_MODE_MASTER_TX;
I2S_Init(SPI3, &I2S_InitStructure);
/* I2S3_EXT configuration */
I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_RX;
I2S_EXT_Init(I2S3_EXT, &I2S_InitStructure);
/* Enable the I2S3_EXT */
I2S_EXT_Enable(I2S3_EXT, ENABLE);
/* Enable the I2S2_EXT */
I2S_EXT_Enable(I2S2_EXT, ENABLE);
/* Enable the I2S3 */
I2S_Enable(SPI3, ENABLE);
/* Enable the I2S2 */
I2S_Enable(SPI2, ENABLE);
3. Send first, then receive, each round. The master side and the slave side each send one item, and then one item is taken back from each receive channel; both are polled inside the main loop.
//master send
/* Wait the Tx buffer to be empty */
while (SPI_I2S_GetStatus(SPI3, SPI_I2S_TE_FLAG) == RESET)
{
}
/* Send a data from I2S3 */
SPI_I2S_TransmitData(SPI3, Master_Buffer_Tx[TxIdx]);
//slave send
/* Wait the Tx buffer to be empty */
while (I2S_EXT_GetStatus(I2S2_EXT, I2S_EXT_TE_FLAG) == RESET)
{
}
/* Send a data from I2S2_EXT */
I2S_EXT_TransmitData(I2S2_EXT, Slave_Buffer_Tx[TxIdx++]);
//slave recv
/* Wait the Rx buffer to be full */
while (SPI_I2S_GetStatus(SPI2, SPI_I2S_RNE_FLAG) == RESET)
{
}
/* Store the I2S2 received data in the relative data table */
Slave_Buffer_Rx[RxIdx] = SPI_I2S_ReceiveData(SPI2);
//master recv
/* Wait the Rx buffer to be full */
while (I2S_EXT_GetStatus(I2S3_EXT, I2S_EXT_RNE_FLAG) == RESET)
{
}
/* Store the I2S2 received data in the relative data table */
Master_Buffer_Rx[RxIdx++] = I2S_EXT_ReceiveData(I2S3_EXT);
The polling is keyed on the transmit buffer being empty and the receive buffer being not empty, so the pace of the main loop is set by the data flow on both ends together.
4. Disable the peripherals and compare both links. After the transfer, the two I2S peripherals and the two extended peripherals are disabled, and then the data received on the slave side and the master side is compared separately.
/* Disable the I2S3 */
I2S_Enable(SPI3, DISABLE);
/* Disable the I2S2 */
I2S_Enable(SPI2, DISABLE);
/* Enable the I2S3_EXT */
I2S_EXT_Enable(I2S3_EXT, DISABLE);
/* Enable the I2S2_EXT */
I2S_EXT_Enable(I2S2_EXT, DISABLE);
TransferStatus1 = Buffercmp((uint16_t*)Slave_Buffer_Rx, Master_Buffer_Tx, BufferSize);
Corresponding source files: src/main.c, inc/main.h
5.17.3.3 I2S_Interrupt interrupt transfer
Example directory: projects/n32h49x_EVAL/examples/I2S/I2S_Interrupt/
Configure the SPI3 and SPI2 interrupt priorities→Configure the pins→Phase one initializes at 16-bit, 48K and enables the TX/RX interrupts→The interrupts transfer item by item and compare after 32 items→After clearing the buffer, phase two switches to 24-bit, 16K for another round→Compare with a mask and print the result
| Item | Source analysis |
|---|---|
| Function | The transmit-buffer-empty interrupt drives I2S3 master TX and the receive-not-empty interrupt drives I2S2 slave RX; first 32 items are sent at 16-bit, 48K, then the configuration is changed to 24-bit, 16K for another round, and each round is compared and printed. |
| Source files | src/main.c: transmit buffer I2S3_Buffer_Tx, receive buffer I2S2_Buffer_Rx, main and GPIO_Configuration, NVIC_Configuration, I2S_Configuration_1, I2S_Configuration_2, Buffercmp, Buffercmp24bits;src/n32h49x_it.c: SPI3_I2S3_IRQHandler and SPI2_I2S2_IRQHandler;inc/main.h: SPI2/SPI3, pin, and alternate-function macros;inc/n32h49x_it.h: interrupt handler declarations;readme.txt: function, environment, and behavior instructions;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | Phase one uses I2S_DATA_FMT_16BITS_EXTENDED and I2S_AUDIO_FREQ_48K, phase two uses I2S_DATA_FMT_24BITS and I2S_AUDIO_FREQ_16K, both with I2S_STD_PHILLIPS, I2S_CLKPOL_LOW, and I2S_MCLK_DISABLE;the NVIC priority group is NVIC_PriorityGroup_0, with SPI3_I2S3_IRQn at preemption priority 1 and subpriority 0 and SPI2_I2S2_IRQn at preemption priority 0;the interrupt sources are SPI_I2S_INT_TE and SPI_I2S_INT_RNE;the system clock is HSI+PLL at 240 MHz. |
| Program and data path | main configures NVIC, GPIO, and phase-one I2S, then waits for RxIdx to reach 32;on each transmit interrupt it writes the next master item with SPI_I2S_TransmitData, and disables that interrupt once 32 items are written;on each receive interrupt it stores one item with SPI_I2S_ReceiveData;after comparing phase one it clears the buffer and reconfigures to 24-bit, 16K for another round, checked with Buffercmp24bits. |
| Main APIs | I2S_InitStruct, I2S_Init, I2S_Enable, SPI_I2S_DeInit, SPI_I2S_EnableInt, SPI_I2S_GetIntStatus, SPI_I2S_TransmitData, SPI_I2S_ReceiveData, NVIC_PriorityGroupConfig, NVIC_Init, RCC_ConfigI2S2Clk, RCC_ConfigI2S3Clk |
| Verification | Connect I2S2 to I2S3 WS, CK, and SD with jumpers; after download and reset, both TransferStatus1 and TransferStatus2 are PASSED and the UART prints Test PASS!!. |
| Use note | The 24-bit phase two accesses the data register twice for each transmit and receive, and the comparison may match only the high byte; the two peripherals share the SPI and I2S interrupt vectors, so the handler names must match the startup file. It targets the N32H497ZGL7_STB board; the toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example drives each end with its own interrupt and lets the main loop wait on a counter, demonstrating interrupt transfer in both the 16-bit and 24-bit data formats.
Data path: I2S3_Buffer_Tx[TxIdx] → transmit interrupt → SPI3_I2S3_IRQHandler writes SPI3->DAT → I2S3 → I2S2 → receive interrupt → SPI2_I2S2_IRQHandler reads SPI2->DAT → I2S2_Buffer_Rx[RxIdx] → Buffercmp or Buffercmp24bits
1. Configure the two interrupt vectors. The priority group is 0, the master side has preemption priority 1 and the slave side 0, and the handler names must match the startup file.
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* SPI3 IRQ Channel configuration */
NVIC_InitStructure.NVIC_IRQChannel = SPI3_I2S3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* SPI2 IRQ channel configuration */
NVIC_InitStructure.NVIC_IRQChannel = SPI2_I2S2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_Init(&NVIC_InitStructure);
2. Enable the interrupts before the peripherals. Both interrupts are enabled before I2S, so the first transmit interrupt fires as soon as the transmit buffer is empty after the endpoints are enabled.
/* Enable the I2S3 TxE interrupt */
SPI_I2S_EnableInt(SPI3, SPI_I2S_INT_TE, ENABLE);
/* Enable the I2S2 RxNE interrupt */
SPI_I2S_EnableInt(SPI2, SPI_I2S_INT_RNE, ENABLE);
/* Enable the I2S2 */
I2S_Enable(SPI2, ENABLE);
/* Enable the I2S3 */
I2S_Enable(SPI3, ENABLE);
3. The transmit side disables its interrupt when full; the receive side stores each item. The transmit interrupt disables itself after writing the 32nd item, and the receive interrupt reads one item per trigger and increments the counter.
void SPI3_I2S3_IRQHandler(void)
{
/* Check the interrupt source */
if (SPI_I2S_GetIntStatus(SPI3, SPI_I2S_INT_TE) == SET)
{
/* Check the end of buffer transfer */
if (TxIdx == 32)
{
/* Disable the I2S3 TXE interrupt to end the communication */
SPI_I2S_EnableInt(SPI3, SPI_I2S_INT_TE, DISABLE);
}
else
{
/* Send a data from I2S3 */
SPI_I2S_TransmitData(SPI3, I2S3_Buffer_Tx[TxIdx++]);
}
}
}
void SPI2_I2S2_IRQHandler(void)
{
/* Check the interrupt source */
if (SPI_I2S_GetIntStatus(SPI2, SPI_I2S_INT_RNE) == SET)
{
/* Store the I2S2 received data in the relative data table */
I2S2_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI2);
}
}
The transmit interrupt uses the counter to decide when to stop, while the receive interrupt does not bound its index and relies on the main loop waiting for RxIdx to reach 32.
4. Phase two switches to 24-bit and allows a masked comparison. Phase two uses 24-bit and 16K; when the data differ it compares the high byte once more to tolerate low-bit differences.
I2S_InitStructure.DataFormat = I2S_DATA_FMT_24BITS;
I2S_InitStructure.MCLKEnable = I2S_MCLK_DISABLE;
I2S_InitStructure.AudioFrequency = I2S_AUDIO_FREQ_16K;
I2S_InitStructure.CLKPOL = I2S_CLKPOL_LOW;
/* I2S Clock source is System clock: Get System Clock frequency */
RCC_GetClocksFreqValue(&RCC_Clocks);
while (BufferLength--)
{
if (*pBuffer1 != *pBuffer2)
{
if (*pBuffer1 != (*pBuffer2 & 0xFF00))
{
return FAILED;
}
}
pBuffer1++;
pBuffer2++;
}
24-bit data needs two data-register accesses per frame, so the read and write counts on both the transmit and receive sides must match the data format to stay aligned.
Corresponding source files: src/main.c, src/n32h49x_it.c
5.17.3.4 SPI_I2S_Switch switching between I2S and SPI
Example directory: projects/n32h49x_EVAL/examples/I2S/SPI_I2S_Switch/
Enable both peripheral clocks and configure the pins→Step one sends and receives 32 items with I2S3 as master TX and I2S2 as slave RX→After a reset, step two switches to SPI master TX and slave RX→After clearing the buffer, step three uses I2S3 slave TX and I2S2 master RX→Compare and print the result for each of the three steps
| Item | Source analysis |
|---|---|
| Function | On the same SPI2 and SPI3 pair it sends 32 16-bit values in each of three ways in turn: I2S master TX with slave RX, SPI single-line master TX with double-line slave RX, and I2S slave TX with master RX, comparing and printing each step. |
| Source files | src/main.c: the I2S and SPI transmit buffers I2S3_Buffer_Tx and SPI3_Buffer_Tx, the receive buffers I2S2_Buffer_Rx and SPI2_Buffer_Rx, main and GPIO_Configuration, SPI_Configuration, two I2S_Configuration functions, three transfer functions, and Buffercmp;src/n32h49x_it.c, inc/n32h49x_it.h: interrupt handlers;inc/main.h: SPI2/SPI3, pin, and alternate-function macros;readme.txt: function, environment, and behavior instructions;MDK-ARM/, EWARM/: Keil and IAR projects. |
| Key configuration | I2S uses I2S_STD_PHILLIPS, I2S_DATA_FMT_16BITS_EXTENDED, I2S_AUDIO_FREQ_48K, I2S_CLKPOL_LOW, and I2S_MCLK_DISABLE, with I2S3 master TX paired with I2S2 slave RX in step one and I2S3 slave TX with I2S2 master RX in step three;SPI uses SPI3 single-line master TX SPI_DIR_SINGLELINE_TX, SPI2 double-line slave RX-only SPI_DIR_DOUBLELINE_RONLY, 16 bits, SPI_CLKPHA_SECOND_EDGE, software NSS, SPI_BR_PRESCALER_4, and MSB first;the pins are I2S2 CK:PC7, WS:PC6, SD:PC9 and I2S3 CK:PC3, SD:PC1, WS:PC2; the system clock is HSI+PLL at 240 MHz. |
| Program and data path | main enables both peripheral clocks and configures GPIO, then calls the three transfer functions in order;I2S3_TransmitData_funtion makes I2S3 master TX and I2S2 slave RX transfer 32 items by polling and disables both ends;SPI_TransmitData_funtion reinitializes SPI master and slave after a reset and also transfers 32 items by polling;I2S2_TransmitData_funtion changes to I2S3 slave TX and I2S2 master RX for another round, and each step is compared and printed. |
| Main APIs | I2S_InitStruct, I2S_Init, I2S_Enable, SPI_I2S_DeInit, SPI_InitStruct, SPI_Init, SPI_Enable, SPI_I2S_TransmitData, SPI_I2S_ReceiveData, SPI_I2S_GetStatus, RCC_ConfigI2S2Clk, RCC_ConfigI2S3Clk |
| Verification | Connect I2S2 to I2S3 WS, CK, and SD with jumpers; after download and reset, TransferStatus1, TransferStatus2, and TransferStatus3 are all PASSED and the UART prints 1 Test PASS!!, 2 Test PASS!!, and 3 Test PASS!! in order. |
| Use note | Every switch calls SPI_I2S_DeInit to reset, so no configuration from the previous mode remains;before step three it clears I2S2_Buffer_Rx so residue from step one is not taken as a pass.It targets the N32H497ZGL7_STB and N32H492ZGL7_STB boards; the toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example transfers data in I2S and SPI mode in turn on the same peripherals and switches modes by resetting and reinitializing.
Data path: step one I2S3_Buffer_Tx → SPI3->DAT → I2S3 (master TX) → I2S2 (slave RX) → SPI2->DAT → I2S2_Buffer_Rx; step two SPI3_Buffer_Tx → SPI3->DAT → SPI3 (master TX) → SPI2 (slave RX) → SPI2->DAT → SPI2_Buffer_Rx; step three I2S3_Buffer_Tx → SPI3->DAT → I2S3 (slave TX) → I2S2 (master RX) → SPI2->DAT → I2S2_Buffer_Rx
1. The main program switches in three steps and resets the counters and buffers. Between steps the transmit and receive counters are reset, and between steps two and three the receive buffer is also cleared.
/* SPI3 and SPI2 clocks enable */
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPHEN_SPI3 | RCC_APB1_PERIPHEN_SPI2, ENABLE);
log_init();
log_info("\r\n This is SPI I2S switch demo!!\r\n");
/* GPIO configuration ------------------------------------------------------*/
GPIO_Configuration();
I2S3_TransmitData_funtion();
/* Reset TxIdx, RxIdx indexes */
TxIdx = 0;
RxIdx = 0;
SPI_TransmitData_funtion();
/* Reset TxIdx, RxIdx indexes and receive table values */
for (TxIdx = 0; TxIdx < BufferSize; TxIdx++)
{
I2S2_Buffer_Rx[TxIdx] = 0;
}
TxIdx = 0;
RxIdx = 0;
I2S2_TransmitData_funtion();
2. Step one sends as I2S3 master and polls. It writes the next item when the transmit buffer is empty and takes back one item when the receive buffer is not empty, exiting after 32 items.
while (RxIdx < BufferSize)
{
/* Wait the Tx buffer to be empty */
while (SPI_I2S_GetStatus(SPI3, SPI_I2S_TE_FLAG) == RESET)
{
}
/* Send a data from I2S3 */
SPI_I2S_TransmitData(SPI3, I2S3_Buffer_Tx[TxIdx++]);
/* Wait the Rx buffer to be full */
while (SPI_I2S_GetStatus(SPI2, SPI_I2S_RNE_FLAG) == RESET)
{
}
/* Store the I2S2 received data in the relative data table */
I2S2_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI2);
}
3. Step two switches to SPI master and slave. Both peripherals are first reset, SPI3 is set up as single-line master TX and SPI2 as double-line slave RX-only, and then SPI is enabled.
SPI_I2S_DeInit(SPI3);
SPI_I2S_DeInit(SPI2);
SPI_InitStruct(&SPI_InitStructure);
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_4;
SPI_InitStructure.FirstBit = SPI_FB_MSB;
SPI_InitStructure.CRCPoly = 7;
SPI_Init(SPI3, &SPI_InitStructure);
/* SPI2 configuration ------------------------------------------------------*/
SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_RONLY;
SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
SPI_Init(SPI2, &SPI_InitStructure);
/* Enable SPI2 */
SPI_Enable(SPI2, ENABLE);
/* Enable SPI3 */
SPI_Enable(SPI3, ENABLE);
4. Step three changes to I2S3 slave TX and I2S2 master RX and pre-writes the first item. The slave transmitter writes one item after being enabled and then enables the master receiver, so the master side does not wait idle.
/* I2S3 configuration */
I2S_InitStructure.I2sMode = I2S_MODE_SlAVE_TX;
I2S_Init(SPI3, &I2S_InitStructure);
/* I2S2 configuration */
I2S_InitStructure.I2sMode = I2S_MODE_MASTER_RX;
I2S_Init(SPI2, &I2S_InitStructure);
/* Enable the I2S3 */
I2S_Enable(SPI3, ENABLE);
After switching to SPI mode SPI_Init writes back the SPI configuration, while switching from I2S mode back requires the audio parameters to be set again; the two modes do not interfere.
Corresponding source files: src/main.c, inc/main.h
5.17.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| I2S linear prescaler values are constrained | The clock generator’s linear prescaler register must not be written with a divider of 0 or 1; the data length field must not be configured as 11. When setting the sample rate, keep the computed divider within the allowed range, otherwise the audio clock is wrong. |
User manual |
| Frame and channel length can only change while I2S is off | Channel length, data length, audio standard, I2S configuration, and clock polarity can only be configured while I2S is disabled; the channel length bit is effective only when the data length is 16 bits; for the other data formats the frame length is fixed at 32 bits. |
User manual |
| Extended I2S (I2SEXT) usage limits | The extended I2S can only act as a slave, and its configuration field can only take the two slave values; the extended peripheral shares the bit clock and word select with the corresponding I2S, and only the data line is separate. |
User manual |
| Order of disabling I2S | For transmit directions, wait until the transmit buffer is empty and the bus is not busy, then clear the I2S enable bit; for receive directions, read the required number of items per the data format and audio standard before closing; a slave receiver may close at any time but must wait for the bus to be idle; in master receive mode the busy flag stays low and cannot mark the end of transfer. |
User manual |
| Read/write count for 24-bit or 32-bit data | 16-bit data (including 16 bits extended to a 32-bit frame) needs one data-register access per frame; 24-bit and 32-bit data need two data-register accesses per frame, and the DMA length is counted in half-words. |
User manual |
| Slave overrun or underrun | If a slave receiver does not read the previous item before new data arrives it overruns, which is cleared by reading the data register and then the status register; if a slave transmitter fails to write data in time it underruns, the channel side then becomes invalid, and I2S must be disabled and restarted from the channel. |
User manual |