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:

    1. Enable I2S DMA request

    2. Configure DMAMUX, select I2S as DMA request source

    3. Configure DMA channel parameters

    4. 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

22.6. I2S Samples