22. I2S
22.1. Overview
This document introduces the main driver interfaces and usage of HPM I2S peripheral. For more details, please refer to the API documentation in hpm_i2s_drv.h and related user manuals.
Supports multiple audio transmission protocols:
I2S Philips standard protocol
MSB justified/Left justified protocol
LSB justified/Right justified protocol
PCM protocol
Supports master and slave modes:
Master mode: generates MCLK, BCLK, and FCLK clock signals
Slave mode: uses external clock source
Supports multiple audio formats:
Sample depth (audio_depth):
Supports 16-bit, 24-bit, 32-bit
Represents the actual valid audio data width
Channel length (channel_length):
Supports 16-bit, 32-bit
Must be greater than or equal to sample depth
Represents the total width of each channel
Channel mode:
Standard mode: supports mono and stereo
TDM mode: supports up to 8 channels (4 pairs of stereo)
Supports multiple sampling rates:
Clock division based on system audio master clock (MCLK)
Supports common sampling rates: 192kHz, 96kHz, 48kHz, 44.1kHz, 32kHz, 22.05kHz, 16kHz, 8kHz
Other sampling rates can be achieved through clock division configuration
Supports DMA transfer
Supports multiple interrupts:
FIFO threshold interrupt
FIFO full/empty interrupt
22.2. I2S Initialization
Ensure that the I2S clock source is enabled and the related I2S peripheral pins are initialized.
Use the clock_add_to_group function to add the I2S clock source to the clock group to ensure the I2S clock source is enabled.
Related structure descriptions:
i2s_config_t structure for configuring basic I2S parameters
typedef struct i2s_config { bool invert_mclk_out; /* Enable MCLK output inversion */ bool invert_mclk_in; /* Enable MCLK input inversion */ bool use_external_mclk; /* Use external MCLK */ bool invert_bclk_out; /* Enable BCLK output inversion */ bool invert_bclk_in; /* Enable BCLK input inversion */ bool use_external_bclk; /* Use external BCLK */ bool invert_fclk_out; /* Enable FCLK output inversion */ bool invert_fclk_in; /* Enable FCLK input inversion */ bool use_external_fclk; /* Use external FCLK */ bool enable_mclk_out; /* Enable MCLK output */ bool frame_start_at_rising_edge; /* Frame start edge selection */ uint16_t tx_fifo_threshold; /* TX FIFO threshold */ uint16_t rx_fifo_threshold; /* RX FIFO threshold */ } i2s_config_t;
i2s_transfer_config_t structure for configuring I2S transfer parameters
typedef struct i2s_transfer_config { uint32_t sample_rate; /* Sample rate */ bool enable_tdm_mode; /* Enable TDM mode */ uint8_t channel_num_per_frame; /* Number of channels per frame */ uint8_t channel_length; /* Channel length: 16-bit or 32-bit */ uint8_t audio_depth; /* Audio depth: 16-bit, 24-bit, 32-bit */ bool master_mode; /* Master/Slave mode selection */ uint8_t protocol; /* Protocol selection */ i2s_line_num_t data_line; /* Data line selection */ uint32_t channel_slot_mask; /* Channel slot mask */ } i2s_transfer_config_t;
channel_num_per_frame parameter description:
Used to configure the number of channels per frame:
Parameter value must be even
Fixed to 2 in non-TDM mode
Can be configured as 2, 4, 6, 8, etc. in TDM mode
The specific channel enablement is configured through the channel_slot_mask parameter
channel_slot_mask parameter description:
Used to configure the channel slot enable mask, each bit represents a channel slot:
In non-TDM mode (channel_num_per_frame = 2):
Mono: set to 0x1 (enable one channel only)
Stereo: set to 0x3 (enable two channels)
In TDM mode (e.g., channel_num_per_frame = 8):
Each bit corresponds to a channel
Examples: - 4 channels: set to 0xF (binary 1111) - 8 channels: set to 0xFF (binary 11111111)
22.2.1. Master Mode Initialization
Initialization function API:
void i2s_init(I2S_Type *ptr, i2s_config_t *config);
Parameter description:
Parameter
Type
Description
ptr
I2S_Type*
Pointer to I2S controller base address
config
i2s_config_t*
Pointer to I2S configuration structure
Transfer configuration function API:
hpm_stat_t i2s_config_tx(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config); hpm_stat_t i2s_config_rx(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config); hpm_stat_t i2s_config_transfer(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config);
Parameter description:
Parameter
Type
Description
ptr
I2S_Type*
Pointer to I2S controller base address
mclk_in_hz
uint32_t
MCLK frequency (Hz)
config
i2s_transfer_config_t*
Pointer to I2S transfer configuration structure
Return values:
Return Value
Description
status_success
Configuration successful
status_invalid_argument
Invalid parameter
Example:
/* Step 1: I2S initialization, configure as master mode */ i2s_config_t i2s_config; i2s_transfer_config_t transfer; uint32_t mclk_freq; /* Step 2: Get and set default configuration */ i2s_get_default_config(HPM_I2S0, &i2s_config); i2s_config.enable_mclk_out = true; /* Enable MCLK output */ i2s_init(HPM_I2S0, &i2s_config); /* Step 3: Configure transfer parameters */ i2s_get_default_transfer_config(&transfer); transfer.sample_rate = 48000; /* Set sample rate to 48kHz */ transfer.audio_depth = i2s_audio_depth_16_bits; /* 16-bit audio data */ transfer.channel_num_per_frame = 2; /* 2 channels (stereo) */ transfer.channel_length = i2s_channel_length_32_bits; /* 32-bit channel length */ transfer.master_mode = true; /* Master mode */ transfer.protocol = I2S_PROTOCOL_I2S_PHILIPS; /* Standard I2S protocol */ transfer.data_line = I2S_DATA_LINE_0; /* Use data line 0 */ transfer.channel_slot_mask = 0x3; /* Enable two channels */ /* Step 4: Get MCLK frequency */ mclk_freq = clock_get_frequency(clock_i2s0); /* Step 5: Configure and start transfer */ hpm_stat_t status = i2s_config_tx(HPM_I2S0, mclk_freq, &transfer); if (status != status_success) { printf("I2S TX config failed\n"); }
22.2.2. Slave Mode Initialization
Transfer configuration function API:
hpm_stat_t i2s_config_tx_slave(I2S_Type *ptr, i2s_transfer_config_t *config); hpm_stat_t i2s_config_rx_slave(I2S_Type *ptr, i2s_transfer_config_t *config); hpm_stat_t i2s_config_transfer_slave(I2S_Type *ptr, i2s_transfer_config_t *config);
Parameter description:
Parameter
Type
Description
ptr
I2S_Type*
Pointer to I2S controller base address
config
i2s_transfer_config_t*
Pointer to I2S transfer configuration structure
Return values:
Return Value
Description
status_success
Configuration successful
status_invalid_argument
Invalid parameter
Example:
/* Step 1: I2S initialization, configure as slave mode */ i2s_config_t i2s_config; i2s_transfer_config_t transfer; /* Step 2: Get and set default configuration */ i2s_get_default_config(HPM_I2S0, &i2s_config); i2s_config.use_external_mclk = true; i2s_config.use_external_bclk = true; i2s_config.use_external_fclk = true; i2s_init(HPM_I2S0, &i2s_config); /* Step 3: Configure transfer parameters */ i2s_get_default_transfer_config(&transfer); transfer.sample_rate = 48000; transfer.audio_depth = i2s_audio_depth_16_bits; transfer.channel_num_per_frame = 2; transfer.channel_length = i2s_channel_length_32_bits; transfer.master_mode = false; transfer.protocol = I2S_PROTOCOL_I2S_PHILIPS; transfer.data_line = I2S_DATA_LINE_0; transfer.channel_slot_mask = 0x3; /* Step 4: Configure and start transfer */ hpm_stat_t status = i2s_config_tx_slave(HPM_I2S0, &transfer); if (status != status_success) { printf("I2S TX slave config failed\n"); }
22.2.3. Multi-line Transfer Configuration
Multi-line transfer configuration structure:
typedef struct i2s_multiline_transfer_config { uint32_t sample_rate; /* Sample rate */ bool enable_tdm_mode; /* Enable TDM mode */ uint8_t channel_num_per_frame; /* Number of channels per frame */ uint8_t channel_length; /* Channel length: 16-bit or 32-bit */ uint8_t audio_depth; /* Audio depth: 16-bit, 24-bit, 32-bit */ bool master_mode; /* Master/Slave mode selection */ uint8_t protocol; /* Protocol selection */ bool tx_data_line_en[4]; /* TX data line enable */ bool rx_data_line_en[4]; /* RX data line enable */ uint32_t tx_channel_slot_mask[4];/* Channel slot mask for each TX data line */ uint32_t rx_channel_slot_mask[4];/* Channel slot mask for each RX data line */ } i2s_multiline_transfer_config_t;
Multi-line transfer configuration API:
hpm_stat_t i2s_config_multiline_transfer(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_multiline_transfer_config_t *config);
Parameter description:
Parameter
Type
Description
ptr
I2S_Type*
Pointer to I2S controller base address
mclk_in_hz
uint32_t
MCLK frequency (Hz)
config
i2s_multiline_transfer_config_t*
Pointer to I2S multi-line transfer configuration structure
Return values:
Return Value
Description
status_success
Configuration successful
status_invalid_argument
Invalid parameter
Example:
/* I2S multi-line transfer configuration */ i2s_config_t i2s_config; i2s_multiline_transfer_config_t transfer; uint32_t mclk_freq; /* Get default configuration */ i2s_get_default_config(HPM_I2S0, &i2s_config); i2s_config.enable_mclk_out = true; i2s_init(HPM_I2S0, &i2s_config); /* Configure multi-line transfer parameters */ i2s_get_default_multiline_transfer_config(&transfer); transfer.sample_rate = audio_data.sample_rate; /* Set sample rate */ transfer.channel_num_per_frame = 2; /* Channels per frame */ transfer.audio_depth = audio_data.audio_depth; /* Set bit depth */ transfer.channel_length = i2s_channel_length_32_bits; /* Channel length */ transfer.master_mode = true; /* Master mode */ transfer.protocol = I2S_PROTOCOL_MSB_JUSTIFIED; /* MSB justified protocol */ /* Configure data lines 0-3 for transmission */ transfer.tx_data_line_en[0] = true; transfer.tx_data_line_en[1] = true; transfer.tx_data_line_en[2] = true; transfer.tx_data_line_en[3] = true; transfer.tx_channel_slot_mask[0] = 0x3; /* Data line 0 uses channels 0-1 */ transfer.tx_channel_slot_mask[1] = 0x3; /* Data line 1 uses channels 0-1 */ transfer.tx_channel_slot_mask[2] = 0x3; /* Data line 2 uses channels 0-1 */ transfer.tx_channel_slot_mask[3] = 0x3; /* Data line 3 uses channels 0-1 */ /* Get MCLK frequency */ mclk_freq = clock_get_frequency(clock_i2s0); /* Configure transfer parameters */ hpm_stat_t status = i2s_config_multiline_transfer(HPM_I2S0, mclk_freq, &transfer); if (status != status_success) { printf("I2S multiline transfer config failed\n"); }
22.3. I2S Data Transfer
22.3.1. Polling Transfer
Send data API:
void i2s_send_data(I2S_Type *ptr, i2s_line_num_t tx_line_index, uint32_t data); uint32_t i2s_send_buff(I2S_Type *ptr, i2s_line_num_t tx_line_index, uint8_t samplebits, uint8_t *src, uint32_t size);
Receive data API:
void i2s_receive_data(I2S_Type *ptr, i2s_line_num_t rx_line_index, uint32_t *data); uint32_t i2s_receive_buff(I2S_Type *ptr, i2s_line_num_t rx_line_index, uint8_t samplebits, uint8_t *dst, uint32_t size);
Example:
/* Send single data */ uint32_t data = 0x12345678; i2s_send_data(HPM_I2S0, I2S_DATA_LINE_0, data); /* Send buffer data */ uint8_t tx_buff[32]; uint32_t sent = i2s_send_buff(HPM_I2S0, I2S_DATA_LINE_0, i2s_audio_depth_16_bits, tx_buff, sizeof(tx_buff)); if (sent != sizeof(tx_buff)) { printf("I2S TX failed\n"); } /* Receive single data */ uint32_t rx_data; i2s_receive_data(HPM_I2S0, I2S_DATA_LINE_0, &rx_data); /* Receive buffer data */ uint8_t rx_buff[32]; uint32_t received = i2s_receive_buff(HPM_I2S0, I2S_DATA_LINE_0, i2s_audio_depth_16_bits, rx_buff, sizeof(rx_buff)); if (received != sizeof(rx_buff)) { printf("I2S RX failed\n"); }
22.3.2. DMA Transfer
I2S supports DMA transfer, which can be configured through the following steps:
Enable I2S DMA request
Configure DMAMUX, select I2S as DMA request source
Configure DMA channel parameters
Start DMA transfer
Related API:
static inline void i2s_enable_tx_dma_request(I2S_Type *ptr); static inline void i2s_enable_rx_dma_request(I2S_Type *ptr); static inline void i2s_disable_tx_dma_request(I2S_Type *ptr); static inline void i2s_disable_rx_dma_request(I2S_Type *ptr);
Example:
/* Step 1: Enable I2S DMA request */ i2s_enable_tx_dma_request(HPM_I2S0); /* Step 2: Configure DMAMUX, select I2S as DMA request source */ dmamux_config(HPM_DMAMUX, DMA_MUX_CHANNEL, I2S_DMA_REQ, true); /* Step 3: Configure DMA channel parameters */ dma_channel_config_t config = { .src_width = DMA_TRANSFER_WIDTH_WORD, /* Source data width: 32-bit */ .dst_width = DMA_TRANSFER_WIDTH_WORD, /* Destination data width: 32-bit */ .src_addr = (uint32_t)tx_buff, /* Source buffer address */ .dst_addr = (uint32_t)&HPM_I2S0->TXD[0], /* I2S TX register address */ .src_burst_size = DMA_NUM_TRANSFER_PER_BURST_1T, /* Source burst size: 1 */ .dst_burst_size = DMA_NUM_TRANSFER_PER_BURST_1T, /* Destination burst size: 1 */ .size_in_byte = sizeof(tx_buff) /* Total transfer size in bytes */ }; dma_setup_channel(HPM_DMA, DMA_CHANNEL, &config, true); /* Step 4: Start DMA transfer */ dma_start_channel(HPM_DMA, DMA_CHANNEL);
22.4. I2S Interrupts
I2S supports the following interrupts:
Interrupt Type
Description
TX_FIFO_THRESHOLD
TX FIFO below threshold interrupt
RX_FIFO_THRESHOLD
RX FIFO above threshold interrupt
TX_FIFO_EMPTY
TX FIFO empty interrupt
RX_FIFO_FULL
RX FIFO full interrupt
Related API:
void i2s_enable_irq(I2S_Type *ptr, uint32_t mask); void i2s_disable_irq(I2S_Type *ptr, uint32_t mask); void i2s_clear_irq_status(I2S_Type *ptr, uint32_t mask);
Example:
/* Enable TX FIFO threshold interrupt */ i2s_enable_irq(HPM_I2S0, i2s_tx_fifo_threshold_irq_mask); /* Configure interrupt priority and enable interrupt */ intc_m_enable_irq_with_priority(IRQn_I2S0, 1); /* Interrupt handler */ void isr_i2s(void) { uint32_t status = i2s_get_irq_status(HPM_I2S0); if (status & i2s_tx_fifo_threshold_irq_mask) { /* Handle TX FIFO threshold interrupt */ i2s_clear_irq_status(HPM_I2S0, i2s_tx_fifo_threshold_irq_mask); } }
22.5. Important Notes
I2S Data Width Alignment Rules:
When transferring data with different bit widths, I2S always keeps valid data in the high bits of the 32-bit interface register
For 16-bit data format:
For sending: 16-bit data needs to be left-shifted by 16 bits before writing to TX FIFO register
For receiving: data needs to be right-shifted by 16 bits after reading from RX FIFO register
Data shift requirements:
Data shift operation is required when using i2s_send_data and i2s_receive_data functions
Data shift operation is required when using DMA transfer
Shift bits = 32 - actual data width