# Low Power Inter-IC Sound (LP I2S)

## Introduction

LP I2S (Low Power Inter-IC Sound) is a synchronous protocol which can be used for audio data transmission. It also provides a data reception communication interface for Voice Activity Detection (VAD) and some digital audio applications in low power mode. For more details about VAD, see Voice Activity Detection.

Note

For I2S documentation, see Inter-IC Sound.

The I2S standard bus defines three signals, - BCK: bit clock - WS: word select - SD: serial data

A basic I2S data bus has one master and one slave. The roles remain unchanged throughout the communication.

LP I2S on ESP32-P4 only supports working as an I2S Slave.

The LP I2S module on ESP32-P4 provides an independent RX unit, which supports receiving data when the chip is running under sleep modes. Compared to HP I2S, LP I2S does not support DMA access. Instead, it uses a piece of separate internal memory to store data.

## I2S Communication Mode

### Standard Mode

In standard mode, there are always two sound channels, i.e., the left and right channels, which are called "slots". These slots support 16-bit-width sample data. The communication format for the slots can be found in this I2S Communication Mode section.

### PDM Mode (RX)

PDM (Pulse-density Modulation) mode for RX channel can receive PDM-format data. Only 16-bit-width sample data are supported. The communication format for the slots can be found in this I2S Communication Mode section.

## Functional Overview

### Resource Allocation

To create a LP I2S channel handle, you should set up the LP I2S channel configuration structure `lp_i2s_chan_config_t`, and call `lp_i2s_new_channel()` with the prepared configuration structure.

If the LP I2S channel is no longer used, you should recycle the allocated resource by calling `lp_i2s_del_channel()`.

    //initialization
    lp_i2s_chan_handle_t rx_handle = NULL;
    lp_i2s_chan_config_t config = {
        .id = 0,
        .role = I2S_ROLE_SLAVE,
        .threshold = 512,
    };
    ESP_ERROR_CHECK(lp_i2s_new_channel(&config, NULL, &rx_handle));

    //deinitialization
    ESP_ERROR_CHECK(lp_i2s_del_channel(rx_chan));

### Register Event Callbacks

By calling `lp_i2s_register_event_callbacks()`, you can hook your own function to the driver ISR. Supported event callbacks are listed in `lp_i2s_evt_cbs_t`.

As the above callbacks are called in an ISR context, you should always ensure the callback function is suitable for an ISR context. Blocking logic should not appear in these callbacks. The callback function prototype is declared in `lp_i2s_callback_t`.

You can also register your own context when calling `lp_i2s_register_event_callbacks()` by the parameter `user_data`. This user data will be passed to the callback functions directly.

This function may fail due to reasons like `ESP_ERR_INVALID_ARG`, especially, this error may indicate that the callback functions are not in the internal RAM. Callbacks should be placed in IRAM since the default ISR handler is allocated with the ESP_INTR_FLAG_IRAM flag.

Please check the error log for more details. If it fails due to `ESP_ERR_INVALID_STATE`, it indicates that the LP I2S channel is enabled, and you cannot add a callback at this moment.

    lp_i2s_evt_cbs_t cbs = {
            .on_thresh_met = s_lp_i2s_on_thresh_met,
            .on_request_new_trans = s_lp_i2s_on_request_new_trans,
    };
    ESP_ERROR_CHECK(lp_i2s_register_event_callbacks(rx_chan, &cbs, &trans));

### Enable and Disable LP I2S

Before using LP I2S to receive data, you need to enable the LP I2S channel by calling `lp_i2s_channel_enable()`, this function switches the driver state from init to enable. Calling `lp_i2s_channel_disable()` does the opposite, that is, puts the driver back to the init state.

### Communication Mode

- Calling `lp_i2s_channel_init_std_mode()` can help you initialize the LP I2S channel to STD mode. Some initialization helpers are listed below: - `LP_I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG` - `LP_I2S_STD_MSB_SLOT_DEFAULT_CONFIG` - `LP_I2S_STD_PCM_SHORT_SLOT_DEFAULT_CONFIG`

- Calling `lp_i2s_channel_init_pdm_rx_mode()` can help you initialize the LP I2S channel to PDM mode. `LP_I2S_PDM_RX_SLOT_DEFAULT_CONFIG` is an initialization helper.

### Read Data via LP I2S

After the LP I2S channel is enabled, `lp_i2s_channel_read()` and `lp_i2s_channel_read_until_bytes()` will be available.

- For `lp_i2s_channel_read()`, if there are new data received by the LP I2S channel, this API will move the received data to the `buffer` you specified in `lp_i2s_trans_t`. The API will try to receive the data as the `buflen` you specified. Check the `received_size` to know how many bytes you received, in case there are no enough received data. If no new received data, the API will block until `timeout_ms`.

- For `lp_i2s_channel_read_until_bytes()`, this API is a wrapper of the `lp_i2s_channel_read()`. The difference is, the `lp_i2s_channel_read_until_bytes()` will block until `buflen` bytes are received.

- For both of the two APIs, if `lp_i2s_evt_cbs_t::on_request_new_trans` is set, the driver will each time requesting a new LP I2S transaction descriptor (`lp_i2s_trans_t`) from the callback event data structure (`lp_i2s_evt_data_t`). This also means, the `buffer` in the (`lp_i2s_trans_t`) needs to be ready for receiving data.

### Thread Safety

All the APIs are guaranteed to be thread safe by the driver, which means, you can call them from different RTOS tasks without protection by extra locks.

All the APIs are not allowed to be used in ISR context.

## API Reference

### Header File

- components/esp_driver_i2s/include/driver/lp_i2s.h

- This header file can be included with:

  >     #include "driver/lp_i2s.h"

- This header file is a part of the API provided by the `esp_driver_i2s` component. To declare that your component depends on `esp_driver_i2s`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_i2s

  > or

  >     PRIV_REQUIRES esp_driver_i2s

### Functions

esp_err_t lp_i2s_new_channel(const lp_i2s_chan_config_t *chan_cfg, lp_i2s_chan_handle_t *ret_tx_handle, lp_i2s_chan_handle_t *ret_rx_handle)

  Allocate new LP I2S channel(s)

  Parameters:

    - chan_cfg -- [in] LP I2S controller channel configurations

    - ret_tx_handle -- [out] LP I2S channel handler used for managing the sending channel(optional), this one is not supported and is kept here for future-proof.

    - ret_rx_handle -- [out] LP I2S channel handler used for managing the receiving channel(optional)

  Returns:

    - ESP_OK Allocate new channel(s) success

    - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip

    - ESP_ERR_INVALID_ARG NULL pointer or illegal parameter in lp_i2s_chan_config_t

    - ESP_ERR_NOT_FOUND No available LP I2S channel found

esp_err_t lp_i2s_register_event_callbacks(lp_i2s_chan_handle_t chan, const lp_i2s_evt_cbs_t *cbs, void *user_data)

  Register LP I2S event callbacks.

  Parameters:

    - chan -- [in] LP I2S channel handle

    - cbs -- [in] Callbacks

    - user_data -- [in] User data

  Returns:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Invalid state

esp_err_t lp_i2s_channel_enable(lp_i2s_chan_handle_t chan)

  Enable LP I2S driver.

  Parameters:

    chan -- [in] LP I2S channel handle

  Returns:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Invalid state

esp_err_t lp_i2s_channel_read(lp_i2s_chan_handle_t chan, lp_i2s_trans_t *trans, uint32_t timeout_ms)

  Read LP I2S received data.

  Parameters:

    - chan -- [in] LP I2S channel handle

    - trans -- [in] LP I2S transaction

    - timeout_ms -- [in] Timeout in ms, set to `LP_I2S_MAX_DELAY` to wait until read is done

  Returns:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Invalid state, e.g. `on_request_new_trans` callback is registered

esp_err_t lp_i2s_channel_read_until_bytes(lp_i2s_chan_handle_t chan, lp_i2s_trans_t *trans)

  Read LP I2S received data until certain bytes.

  Parameters:

    - chan -- [in] LP I2S channel handle

    - trans -- [in] LP I2S transaction

  Returns:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Invalid state, e.g. `on_request_new_trans` callback is registered

esp_err_t lp_i2s_channel_disable(lp_i2s_chan_handle_t chan)

  Disable LP I2S driver.

  Parameters:

    chan -- [in] LP I2S channel handle

  Returns:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Invalid state

esp_err_t lp_i2s_del_channel(lp_i2s_chan_handle_t chan)

  Delete the LP I2S channel.

  Parameters:

    chan -- [in] LP I2S channel handler

  Returns:

    - ESP_OK Delete successfully

    - ESP_ERR_INVALID_ARG NULL pointer

### Structures

struct lp_i2s_chan_config_t

  LP I2S controller channel configuration.

  Public Members

  int id

    LP I2S port id

  i2s_role_t role

    LP I2S role, only I2S_ROLE_SLAVE supported

  size_t threshold

    When LP I2S received bytes are bigger than this value, the `on_thresh_met` callback will be triggered. Must be in multiple of 4

struct lp_i2s_evt_cbs_t

  LP I2S event callbacks.

  Public Members

  lp_i2s_callback_t on_thresh_met

    Triggered when the received bytes are bigger than `lp_i2s_chan_config_t:threshold`

  lp_i2s_callback_t on_request_new_trans

    Triggered when a new transaction buffer is needed, when this callback is registered, you don't need to use `lp_i2s_channel_read` to get data, you can get data via this callback asynchronously.

### Macros

LP_I2S_MAX_DELAY

  LP_I2S max timeout value.

### Header File

- components/esp_driver_i2s/include/driver/lp_i2s_std.h

- This header file can be included with:

  >     #include "driver/lp_i2s_std.h"

- This header file is a part of the API provided by the `esp_driver_i2s` component. To declare that your component depends on `esp_driver_i2s`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_i2s

  > or

  >     PRIV_REQUIRES esp_driver_i2s

### Functions

esp_err_t lp_i2s_channel_init_std_mode(lp_i2s_chan_handle_t handle, const lp_i2s_std_config_t *std_cfg)

  Init LP I2S to STD mode.

  Parameters:

    - handle -- [in] LP I2S channel handle

    - std_cfg -- [in] STD configuration

  Returns:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Invalid state

### Structures

struct lp_i2s_std_gpio_config_t

  LP I2S pin configurations.

  Public Members

  int bck

    bck pin number

  int ws

    ws pin number

  int din

    din pin number

struct lp_i2s_std_slot_config_t

  LP I2S slot configuration for standard mode.

  Public Members

  i2s_data_bit_width_t data_bit_width

    I2S sample data bit width (valid data bits per sample)

  i2s_slot_bit_width_t slot_bit_width

    I2S slot bit width (total bits per slot)

  i2s_slot_mode_t slot_mode

    Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO

  i2s_std_slot_mask_t slot_mask

    Select the left, right or both slot

  uint32_t ws_width

    WS signal width (i.e. the number of BCLK ticks that WS signal is high)

  bool ws_pol

    WS signal polarity, set true to enable high lever first

  bool bit_shift

    Set true to enable bit shift in Philips mode

  bool left_align

    Set true to enable left alignment

  bool big_endian

    Set true to enable big endian

  bool bit_order_lsb

    Set true to enable lsb first

struct lp_i2s_std_config_t

  LP I2S STD configuration.

  Public Members

  lp_i2s_std_gpio_config_t pin_cfg

    Pin configuration

  lp_i2s_std_slot_config_t slot_cfg

    STD mode slot configuration, can be generated by macros I2S_STD_[mode]_SLOT_DEFAULT_CONFIG, [mode] can be replaced with PHILIPS/MSB/PCM_SHORT/PCM_LONG

### Macros

LP_I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo)

  Philips format in active slot that enabled by mask.

  Parameters:

    - bits_per_sample -- I2S data bit width

    - mono_or_stereo -- I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO

LP_I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo)

  MSB format in active slot enabled that by mask.

  Parameters:

    - bits_per_sample -- I2S data bit width

    - mono_or_stereo -- I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO

LP_I2S_STD_PCM_SHORT_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo)

  PCM(short) format in active slot that enabled by mask.

  Parameters:

    - bits_per_sample -- I2S data bit width

    - mono_or_stereo -- I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO

### Header File

- components/esp_driver_i2s/include/driver/lp_i2s_pdm.h

- This header file can be included with:

  >     #include "driver/lp_i2s_pdm.h"

- This header file is a part of the API provided by the `esp_driver_i2s` component. To declare that your component depends on `esp_driver_i2s`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_i2s

  > or

  >     PRIV_REQUIRES esp_driver_i2s

### Functions

esp_err_t lp_i2s_channel_init_pdm_rx_mode(lp_i2s_chan_handle_t handle, const lp_i2s_pdm_rx_config_t *pdm_cfg)

  Init LP I2S to PDM mode.

  Parameters:

    - handle -- [in] LP I2S channel handle

    - pdm_cfg -- [in] PDM configuration

  Returns:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

    - ESP_ERR_INVALID_STATE: Invalid state

### Structures

struct lp_i2s_pdm_rx_gpio_config_t

  LP I2S pin configurations.

  Public Members

  int clk

    clk pin number

  int din

    din pin number

struct lp_i2s_pdm_rx_slot_config_t

  I2S slot configuration for PDM RX mode.

  Public Members

  i2s_data_bit_width_t data_bit_width

    I2S sample data bit width (valid data bits per sample), only support 16 bits for PDM mode

  i2s_slot_bit_width_t slot_bit_width

    I2S slot bit width (total bits per slot) , only support 16 bits for PDM mode

  i2s_slot_mode_t slot_mode

    Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO

  i2s_pdm_slot_mask_t slot_mask

    Choose the slots to activate

  bool ws_pol

    WS signal polarity, set true to enable high lever first

  bool hp_en

    High pass filter enable

  float hp_cut_off_freq_hz

    High pass filter cut-off frequency, range 23.3Hz ~ 185Hz, see cut-off frequency sheet above

  uint32_t amplify_num

    The amplification number of the final conversion result. The data that have converted from PDM to PCM module, will time `amplify_num` additionally to amplify the final result. Note that it's only a multiplier of the digital PCM data, not the gain of the analog signal range 1~15, default 1

struct lp_i2s_pdm_rx_config_t

  LP I2S PDM configuration.

  Public Members

  lp_i2s_pdm_rx_gpio_config_t pin_cfg

    Pin configuration

  lp_i2s_pdm_rx_slot_config_t slot_cfg

    PDM mode slot configuration

### Macros

LP_I2S_PDM_RX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo)

  PDM format in 2 slots(RX)

  Parameters:

    - bits_per_sample -- I2S data bit width, only support 16 bits for PDM mode

    - mono_or_stereo -- I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.