feat(lp_i2s): lp_i2s driver

This commit is contained in:
Armando
2024-07-03 15:39:23 +08:00
parent b5ab82ce3c
commit ee8933f651
29 changed files with 1494 additions and 59 deletions

View File

@@ -62,6 +62,15 @@ typedef enum {
I2S_MCLK_MULTIPLE_1152 = 1152, /*!< MCLK = sample_rate * 1152 (24-bit compatible) */
} i2s_mclk_multiple_t;
/**
* @brief LP I2S transaction type
*/
typedef struct {
void *buffer; ///< Pointer to buffer
size_t buflen; ///< Buffer len, this should be in the multiple of 4
size_t received_size; ///< Received size
} lp_i2s_trans_t;
/**
* @brief Event structure used in I2S event queue
*/
@@ -78,7 +87,15 @@ typedef struct {
*/
} i2s_event_data_t;
typedef struct i2s_channel_obj_t *i2s_chan_handle_t; /*!< I2S channel object handle, the control unit of the I2S driver*/
/**
* @brief Event data structure for LP I2S
*/
typedef struct {
lp_i2s_trans_t trans; ///< LP I2S transaction
} lp_i2s_evt_data_t;
typedef struct i2s_channel_obj_t *i2s_chan_handle_t; /*!< I2S channel object handle, the control unit of the I2S driver*/
typedef struct lp_i2s_channel_obj_t *lp_i2s_chan_handle_t; /*!< I2S channel object handle, the control unit of the I2S driver*/
/**
* @brief I2S event callback
@@ -90,6 +107,17 @@ typedef struct i2s_channel_obj_t *i2s_chan_handle_t; /*!< I2S channel object
*/
typedef bool (*i2s_isr_callback_t)(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
/**
* @brief LP I2S event callback type
*
* @param[in] handle LP I2S channel handle
* @param[in] event Event data
* @param[in] user_ctx User data
*
* @return Whether a high priority task has been waken up by this callback function
*/
typedef bool (*lp_i2s_callback_t)(lp_i2s_chan_handle_t handle, lp_i2s_evt_data_t *event, void *user_ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/i2s_types.h"
#include "hal/i2s_types.h"
#include "esp_types.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LP_I2S max timeout value
*/
#define LP_I2S_MAX_DELAY UINT32_MAX
/**
* @brief LP I2S controller channel configuration
*/
typedef struct {
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 */
} lp_i2s_chan_config_t;
/**
* @brief LP I2S event callbacks
*/
typedef struct {
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_evt_cbs_t;
/**
* @brief Allocate new LP I2S channel(s)
*
* @param[in] chan_cfg LP I2S controller channel configurations
* @param[out] ret_tx_handle LP I2S channel handler used for managing the sending channel(optional), this one is not supported and is kept here for future-proof.
* @param[out] ret_rx_handle LP I2S channel handler used for managing the receiving channel(optional)
* @return
* - 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_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);
/**
* @brief Register LP I2S event callbacks
*
* @param[in] handle LP I2S channel handle
* @param[in] cbs Callbacks
* @param[in] user_data User data
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Invalid state
*/
esp_err_t lp_i2s_register_event_callbacks(lp_i2s_chan_handle_t handle, const lp_i2s_evt_cbs_t *cbs, void *user_data);
/**
* @brief Enable LP I2S driver
*
* @param[in] handle LP I2S channel handle
*
* @return
* - 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);
/**
* @brief Read LP I2S received data
*
* @param[in] handle LP I2S channel handle
* @param[in] trans LP I2S transaction
* @param[in] timeout_ms Timeout in ms, set to `LP_I2S_MAX_DELAY` to wait until read is done
*
* @return
* - 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(lp_i2s_chan_handle_t chan, lp_i2s_trans_t *trans, uint32_t timeout_ms);
/**
* @brief Read LP I2S received data until certain bytes
*
* @param[in] handle LP I2S channel handle
* @param[in] trans LP I2S transaction
*
* @return
* - 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);
/**
* @brief Disable LP I2S driver
*
* @param[in] handle LP I2S channel handle
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Invalid state
*/
esp_err_t lp_i2s_channel_disable(lp_i2s_chan_handle_t chan);
/**
* @brief Delete the LP I2S channel
*
* @param[in] handle LP I2S channel handler
*
* @return
* - ESP_OK Delete successfully
* - ESP_ERR_INVALID_ARG NULL pointer
*/
esp_err_t lp_i2s_del_channel(lp_i2s_chan_handle_t handle);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,100 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_types.h"
#include "esp_err.h"
#include "driver/i2s_types.h"
#include "hal/i2s_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief PDM format in 2 slots(RX)
* @param bits_per_sample I2S data bit width, only support 16 bits for PDM mode
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define LP_I2S_PDM_RX_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = bits_per_sample, \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = (mono_or_stereo == I2S_SLOT_MODE_MONO) ? \
I2S_PDM_SLOT_LEFT : I2S_PDM_SLOT_BOTH, \
.hp_en = true, \
.hp_cut_off_freq_hz = 35.5, \
.amplify_num = 1, \
}
/**
* @brief LP I2S pin configurations
*/
typedef struct {
int clk; /*!< clk pin number */
int din; /*!< din pin number */
} lp_i2s_pdm_rx_gpio_config_t;
/*
High Pass Filter Cut-off Frequency Sheet
+----------------+------------------+----------------+------------------+----------------+------------------+
| param0, param5 | cut-off freq(Hz) | param0, param5 | cut-off freq(Hz) | param0, param5 | cut-off freq(Hz) |
+----------------+------------------+----------------+------------------+----------------+------------------+
| (0, 0) | 185 | (3, 3) | 115 | (5, 5) | 69 |
| (0, 1) | 172 | (1, 7) | 106 | (4, 7) | 63 |
| (1, 1) | 160 | (2, 4) | 104 | (5, 6) | 58 |
| (1, 2) | 150 | (4, 4) | 92 | (5, 7) | 49 |
| (2, 2) | 137 | (2, 7) | 91.5 | (6, 6) | 46 |
| (2, 3) | 126 | (4, 5) | 81 | (6, 7) | 35.5 |
| (0, 3) | 120 | (3, 7) | 77.2 | (7, 7) | 23.3 |
+----------------+------------------+----------------+------------------+----------------+------------------+
*/
/**
* @brief I2S slot configuration for PDM RX mode
*/
typedef struct {
/* General fields */
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 */
/* Particular fields */
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 */
} lp_i2s_pdm_rx_slot_config_t;
/**
* @brief LP I2S PDM configuration
*/
typedef struct {
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 only support slave mode, not support to configure the clock */
} lp_i2s_pdm_rx_config_t;
/**
* @brief Init LP I2S to PDM mode
*
* @param[in] handle LP I2S channel handle
* @param[in] pdm_cfg PDM configuration
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Invalid state
*/
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);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_types.h"
#include "esp_err.h"
#include "driver/i2s_types.h"
#include "hal/i2s_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Philips format in active slot that enabled by mask
* @param bits_per_sample I2S data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define LP_I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = (bits_per_sample), \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = true, \
.left_align = true, \
.big_endian = false, \
.bit_order_lsb = false, \
}
/**
* @brief MSB format in active slot enabled that by mask
* @param bits_per_sample I2S data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define LP_I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = (bits_per_sample), \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = I2S_STD_SLOT_BOTH, \
.ws_width = bits_per_sample, \
.ws_pol = false, \
.bit_shift = false, \
.left_align = true, \
.big_endian = false, \
.bit_order_lsb = false, \
}
/**
* @brief PCM(short) format in active slot that enabled by mask
* @param bits_per_sample I2S data bit width
* @param mono_or_stereo I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
*/
#define LP_I2S_STD_PCM_SHORT_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo) { \
.data_bit_width = (bits_per_sample), \
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, \
.slot_mode = mono_or_stereo, \
.slot_mask = I2S_STD_SLOT_BOTH, \
.ws_width = 1, \
.ws_pol = true, \
.bit_shift = true, \
.left_align = true, \
.big_endian = false, \
.bit_order_lsb = false, \
}
/**
* @brief LP I2S pin configurations
*/
typedef struct {
int bck; /*!< bck pin number */
int ws; /*!< ws pin number */
int din; /*!< din pin number */
} lp_i2s_std_gpio_config_t;
/**
* @brief LP I2S slot configuration for standard mode
*/
typedef struct {
/* General fields */
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 */
/* Particular fields */
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 */
} lp_i2s_std_slot_config_t;
/**
* @brief LP I2S STD configuration
*/
typedef struct {
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 only support slave mode, not support to configure the clock */
} lp_i2s_std_config_t;
/**
* @brief Init LP I2S to STD mode
*
* @param[in] handle LP I2S channel handle
* @param[in] std_cfg STD configuration
*
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_INVALID_STATE: Invalid state
*/
esp_err_t lp_i2s_channel_init_std_mode(lp_i2s_chan_handle_t handle, const lp_i2s_std_config_t *std_cfg);
#ifdef __cplusplus
}
#endif