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 thebuffer
you specified inlp_i2s_trans_t
. The API will try to receive the data as thebuflen
you specified. Check thereceived_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 untiltimeout_ms
.For
lp_i2s_channel_read_until_bytes()
, this API is a wrapper of thelp_i2s_channel_read()
. The difference is, thelp_i2s_channel_read_until_bytes()
will block untilbuflen
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, thebuffer
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
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 onesp_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
-
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_read
to 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_i2s
component. To declare that your component depends onesp_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_i2s
component. To declare that your component depends onesp_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_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
-
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