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_CONFIGis 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- bufferyou specified in- lp_i2s_trans_t. The API will try to receive the data as the- buflenyou specified. Check the- received_sizeto 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- buflenbytes are received.
- For both of the two APIs, if - lp_i2s_evt_cbs_t::on_request_new_transis 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- bufferin 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
- 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_i2scomponent. 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_DELAYto 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_transcallback 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_transcallback 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_metcallback will be triggered. Must be in multiple of 4
 
- 
int id
- 
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_readto get data, you can get data via this callback asynchronously.
 
- 
lp_i2s_callback_t on_thresh_met
Macros
- 
LP_I2S_MAX_DELAY
- LP_I2S max timeout value. 
Header File
- 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_i2scomponent. 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. 
- 
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 
 
- 
i2s_data_bit_width_t data_bit_width
- 
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 
 
- 
lp_i2s_std_gpio_config_t pin_cfg
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
- 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_i2scomponent. 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. 
- 
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_numadditionally 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
 
- 
i2s_data_bit_width_t data_bit_width
- 
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 
 
- 
lp_i2s_pdm_rx_gpio_config_t pin_cfg
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