mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(parlio_rx): implement parallel io rx driver
This commit is contained in:
parent
188864bcbd
commit
04d267b023
@ -36,7 +36,9 @@ endif()
|
||||
|
||||
# Parallel IO related source files
|
||||
if(CONFIG_SOC_PARLIO_SUPPORTED)
|
||||
list(APPEND srcs "parlio/parlio_common.c" "parlio/parlio_tx.c")
|
||||
list(APPEND srcs "parlio/parlio_common.c"
|
||||
"parlio/parlio_tx.c"
|
||||
"parlio/parlio_rx.c")
|
||||
endif()
|
||||
|
||||
# GPTimer legacy driver
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
@ -19,80 +20,282 @@ extern "C" {
|
||||
* @brief Parallel IO RX unit configuration
|
||||
*/
|
||||
typedef struct {
|
||||
size_t trans_queue_depth; /*!< Depth of internal transaction queue */
|
||||
size_t max_recv_size; /*!< Maximum receive size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
|
||||
size_t data_width; /*!< Parallel IO data width, can set to 1/2/4/8/..., but can't bigger than PARLIO_RX_UNIT_MAX_DATA_WIDTH */
|
||||
parlio_clock_source_t clk_src; /*!< Parallel IO clock source */
|
||||
size_t data_width; /*!< Parallel IO data width */
|
||||
int data_gpio_nums[PARLIO_RX_UNIT_MAX_DATA_WIDTH]; /*!< Parallel IO data GPIO numbers, set to `-1` if it's not used */
|
||||
int clk_gpio_num; /*!< Parallel IO clock GPIO number, set to `-1` if the clock is not clocked from gpio pad*/
|
||||
uint32_t clk_freq_hz; /*!< Parallel IO clock frequency in Hz */
|
||||
uint32_t clk_freq_hz; /*!< The source clock frequency for external when the clock source is selected as PARLIO_CLK_SRC_EXTERNAL;
|
||||
The expected output clock frequency when the clock source is from internal */
|
||||
gpio_num_t clk_gpio_num; /*!< The input or output clock pin. If the clock source is input from external,
|
||||
the clock gpio must be set, otherwise set `-1` can disable the clock output if not needed */
|
||||
gpio_num_t valid_gpio_num; /*!< GPIO number of the valid signal. The signal on this pin is used to indicate whether the data on the data lines are valid.
|
||||
Only takes effect when using level or pulse delimiter, set to `-1` if only use the soft delimiter */
|
||||
gpio_num_t data_gpio_nums[PARLIO_RX_UNIT_MAX_DATA_WIDTH]; /*!< Parallel IO data GPIO numbers, set to `-1` if it's not used,
|
||||
The driver will take [0 .. (data_width - 1)] as the data pins */
|
||||
struct {
|
||||
uint32_t free_clk : 1; /*!< Whether the input external clock is a free-running clock. A free-running clock will always keep running (e.g. I2S bclk),
|
||||
a non-free-running clock will start when there are data transporting and stop when the bus idle (e.g. SPI).
|
||||
This flag only takes effect when the clock source is selected as PARLIO_CLK_SRC_EXTERNAL */
|
||||
uint32_t clk_gate_en : 1; /*!< Enable RX clock gating, only available when the clock direction is output(not supported on ESP32-C6)
|
||||
the output clock will be controlled by the valid gpio,
|
||||
i.e. high level of valid gpio to enable the clock output, low to disable */
|
||||
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
|
||||
uint32_t io_no_init: 1; /*!< Set to skip initializing the GPIO, but only attach the pralio rx signals to those GPIOs via IO Matrix.
|
||||
So that the signals that have attached to those GPIO won't be overwritten. Mainly used for self communication or self monitoring */
|
||||
} flags;
|
||||
} parlio_rx_unit_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a Parallel IO RX unit
|
||||
*
|
||||
* @param[in] config Parallel IO RX unit configuration
|
||||
* @param[out] ret_unit Returned Parallel IO RX unit handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_unit_handle_t *ret_unit);
|
||||
|
||||
esp_err_t parlio_del_rx_unit(parlio_rx_unit_handle_t unit);
|
||||
|
||||
esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t unit);
|
||||
|
||||
esp_err_t parlio_rx_unit_disable(parlio_rx_unit_handle_t unit);
|
||||
/**
|
||||
* @brief Delete a Parallel IO RX unit
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_del_rx_unit(parlio_rx_unit_handle_t rx_unit);
|
||||
|
||||
/**
|
||||
* @brief Configuration of level delimiter
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t data_line_as_enable; /*!< Which IO data line will be used as data enable/valid signal */
|
||||
uint32_t valid_sig_line_id; /*!< The data line id of valid/enable signal.
|
||||
The selected data line will be used as the valid/enable signal (i.e. level signal) in this delimiter.
|
||||
As the line of valid/enable signal is shared with the data line, this line_id will be conflict
|
||||
with the data line if set the id within 'data_width',
|
||||
therefore the range is (data_width, PARLIO_RX_UNIT_MAX_DATA_WIDTH]. */
|
||||
parlio_sample_edge_t sample_edge; /*!< Parallel IO sample edge */
|
||||
parlio_bit_pack_order_t bit_pack_order; /*!< Set how we pack the bits into one bytes */
|
||||
uint32_t eof_data_len; /*!< Set the data length to trigger the End Of Frame (EOF, i.e. transaction done)
|
||||
interrupt, if the data length is set to `0`, that mean the EOF will only triggers
|
||||
when the enable signal inactivated */
|
||||
uint32_t timeout_ticks; /*!< The number of source clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt
|
||||
The timeout counter starts when the valid/enable signal is invalid/disabled. */
|
||||
struct {
|
||||
uint32_t active_level: 1; /*!< Which level indicates the validation of the transmitting data */
|
||||
} flags; /*!< Extra flags */
|
||||
} parlio_rx_level_delimiter_config_t;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @brief Create a level delimiter
|
||||
*
|
||||
* @note The enable signal must be aligned with the valid data.
|
||||
* @note There're at most `SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1` IO pins left for RXD
|
||||
*
|
||||
* @param config
|
||||
* @param ret_delimiter
|
||||
* @param[in] config Level delimiter configuration
|
||||
* @param[out] ret_delimiter Returned delimiter handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_new_rx_level_delimiter(const parlio_rx_level_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter);
|
||||
esp_err_t parlio_new_rx_level_delimiter(const parlio_rx_level_delimiter_config_t *config,
|
||||
parlio_rx_delimiter_handle_t *ret_delimiter);
|
||||
|
||||
/**
|
||||
* @brief Configuration of pulse delimiter
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t data_line_as_enable; /*!< Which IO data line will be used as data enable signal */
|
||||
uint32_t valid_sig_line_id; /*!< The data line id of valid/enable signal.
|
||||
The selected data line will be used as the valid/enable signal (i.e. pulse signal) in this delimiter.
|
||||
As the line of valid/enable signal is shared with the data line, this line_id will be conflict
|
||||
with the data line if set the id within 'data_width',
|
||||
therefore the range is (data_width, PARLIO_RX_UNIT_MAX_DATA_WIDTH]. */
|
||||
parlio_sample_edge_t sample_edge; /*!< Parallel IO sample edge */
|
||||
parlio_bit_pack_order_t bit_pack_order; /*!< Set how we pack the bits into one bytes */
|
||||
uint32_t eof_data_len; /*!< Set the data length to trigger the End Of Frame (EOF, i.e. transaction done)
|
||||
interrupt, if the data length is set to `0`, that mean the EOF will only triggers
|
||||
when the end pulse detected, please ensure there is an end pulse for a frame and
|
||||
`has_end_pulse` flag is set */
|
||||
uint32_t timeout_ticks; /*!< The number of source clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt
|
||||
The timeout counter starts when the valid/enable signal is invalid/disabled. */
|
||||
struct {
|
||||
uint32_t active_level: 1; /*!< On which level the pulse is considered active */
|
||||
uint32_t start_bit_included: 1; /*!< Whether data bit is included in the start pulse */
|
||||
uint32_t end_bit_included: 1; /*!< Whether data bit is included in the end pulse, only valid when `has_end_pulse` is true */
|
||||
uint32_t has_end_pulse: 1; /*!< Whether there's an end pulse to terminate the transaction,
|
||||
if no, the transaction will be terminated by user configured transcation length */
|
||||
uint32_t end_bit_included: 1; /*!< Whether data bit is included in the end pulse, only valid when `has_end_pulse` is true */
|
||||
uint32_t gen_eof_by_end_pulse: 1; /*!< Whether the DMA EOF event is generated by the end pulse instead of data length,
|
||||
only valid when `end_bit_included` is true */
|
||||
uint32_t pulse_invert: 1; /*!< Whether to invert the pulse */
|
||||
} flags; /*!< Extra flags */
|
||||
} parlio_rx_pulse_delimiter_config_t;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @brief Create a pulse delimiter
|
||||
*
|
||||
* @note There're at most `SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1` IO pins left for RXD
|
||||
*
|
||||
* @param config
|
||||
* @param ret_delimiter
|
||||
* @param[in] config Pulse delimiter configuration
|
||||
* @param[out] ret_delimiter Returned delimiter handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_new_rx_pulse_delimiter(const parlio_rx_pulse_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter);
|
||||
|
||||
typedef struct {
|
||||
} parlio_rx_soft_delimiter_config_t;
|
||||
|
||||
esp_err_t parlio_new_rx_soft_delimiter(const parlio_rx_soft_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter);
|
||||
esp_err_t parlio_rx_soft_delimiter_begin_end(parlio_rx_delimiter_handle_t delimiter, bool begin_end);
|
||||
esp_err_t parlio_new_rx_pulse_delimiter(const parlio_rx_pulse_delimiter_config_t *config,
|
||||
parlio_rx_delimiter_handle_t *ret_delimiter);
|
||||
|
||||
/**
|
||||
* @brief Configuration of soft delimiter
|
||||
*/
|
||||
typedef struct {
|
||||
parlio_sample_edge_t sample_edge; /*!< Parallel IO sample edge */
|
||||
parlio_bit_pack_endian_t bit_pack_endian; /*!< Set how we pack the bits into one bytes */
|
||||
parlio_rx_delimiter_handle_t delimiter; /*!< Specify the frame delimiter, either from software or external signal (level or pulse) */
|
||||
uint32_t timeout_us; /*!< Timeout in us, 0 means no timeout */
|
||||
parlio_bit_pack_order_t bit_pack_order; /*!< Set how we pack the bits into one bytes, set 1 to pack the bits into a byte from LSB,
|
||||
otherwise from MSB */
|
||||
uint32_t eof_data_len; /*!< Set the data length to trigger the End Of Frame (EOF, i.e. transaction done)
|
||||
interrupt, if the data length is set to `0`, that mean the EOF will only triggers
|
||||
when the end pulse detected, please ensure there is an end pulse for a frame and
|
||||
`has_end_pulse` flag is set */
|
||||
uint32_t timeout_ticks; /*!< The number of APB clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt */
|
||||
} parlio_rx_soft_delimiter_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a pulse delimiter
|
||||
*
|
||||
* @param[in] config Soft delimiter configuration
|
||||
* @param[out] ret_delimiter Returned delimiter handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_new_rx_soft_delimiter(const parlio_rx_soft_delimiter_config_t *config,
|
||||
parlio_rx_delimiter_handle_t *ret_delimiter);
|
||||
|
||||
/**
|
||||
* @brief Start/stop the soft delimiter
|
||||
* @note Soft delimiter need to start or stop manually because it has no validating/enabling signal to indicate the data has started or stopped
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
|
||||
* @param[in] delimiter Delimiter handle
|
||||
* @param[in] start_stop Set true to start, set false to stop
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_rx_soft_delimiter_start_stop(parlio_rx_unit_handle_t rx_unit, parlio_rx_delimiter_handle_t delimiter, bool start_stop);
|
||||
|
||||
/**
|
||||
* @brief Generic Selection of the delimiter create functions
|
||||
* @note This is a helper function for creating the delimiter generically,
|
||||
* input different type of configuration to create a corresponding delimiter
|
||||
*
|
||||
* @param[in] config The configuration of the delimiter, whose type decides the delimiter type that created,
|
||||
* Can be `parlio_rx_level_delimiter_config_t`, `parlio_rx_pulse_delimiter_config_t` or `parlio_rx_soft_delimiter_config_t`
|
||||
* @param[out] ret_delimiter Returned delimiter handle
|
||||
*/
|
||||
#define parlio_new_rx_delimiter(config, ret_delimiter) _Generic((config), \
|
||||
parlio_rx_level_delimiter_config_t* : parlio_new_rx_level_delimiter, \
|
||||
parlio_rx_pulse_delimiter_config_t* : parlio_new_rx_pulse_delimiter, \
|
||||
parlio_rx_soft_delimiter_config_t* : parlio_new_rx_soft_delimiter, \
|
||||
default: parlio_new_rx_soft_delimiter) (config, ret_delimiter)
|
||||
|
||||
/**
|
||||
* @brief Delete the delimiter
|
||||
* @note To delete the delimiter safely, please delete it after disable all the RX units
|
||||
*
|
||||
* @param[in] delimiter Delimiter handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_del_rx_delimiter(parlio_rx_delimiter_handle_t delimiter);
|
||||
|
||||
/**
|
||||
* @brief Enable the Parallel IO RX unit
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
|
||||
* @param[in] reset_queue Whether to reset the receiving queue.
|
||||
* If set to false, the legacy receive transactions in the queue are still available,
|
||||
* If set to true, the legacy receive transactions in the queue are dropped.
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queue);
|
||||
|
||||
/**
|
||||
* @brief Disable the Parallel IO TX unit
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_rx_unit_disable(parlio_rx_unit_handle_t rx_unit);
|
||||
|
||||
/**
|
||||
* @brief Configuration of a receive transaction
|
||||
*/
|
||||
typedef struct {
|
||||
parlio_rx_delimiter_handle_t delimiter; /*!< The delimiter of this receiving transaction */
|
||||
struct {
|
||||
uint32_t is_infinite: 1; /*!< Whether this is an infinite transaction that supposed to receive continuously */
|
||||
uint32_t indirect_mount: 1;/*!< This flag only take effect when `is_infinite` is enabled.
|
||||
* Enable this flag, the DMA descriptor will mount to an internal DMA buffer instead,
|
||||
* The data will be copy to the payload in every interrupt. So that to guarantee the payload buffer
|
||||
* is valid during the `on_receive_done` callback.
|
||||
* Either `is_infinite` or `indirect_mount` is disabled,
|
||||
* the finite payload will be mounted to the DMA descriptor directly.
|
||||
* By default, the receive payload will be mounted to the DMA descriptor directly,
|
||||
*/
|
||||
} flags; /*!< Extra flags */
|
||||
} parlio_receive_config_t;
|
||||
|
||||
esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, void *buffer, size_t buffer_size, const parlio_receive_config_t *config);
|
||||
/**
|
||||
* @brief Receive data by Parallel IO RX unit
|
||||
* @note The receive transaction will start immediately when there is not other transaction on receiving,
|
||||
* Otherwise it will be sent to the transaction queue to wait for the bus.
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
|
||||
* @param[in] payload The payload buffer pointer
|
||||
* @param[in] payload_size The size of the payload buffer, in bytes.
|
||||
* @param[in] recv_cfg The configuration of this receive transaction
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit,
|
||||
void *payload,
|
||||
size_t payload_size,
|
||||
const parlio_receive_config_t* recv_cfg);
|
||||
|
||||
/**
|
||||
* @brief Wait for all pending RX transactions done
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
|
||||
* @param[in] timeout_ms Timeout in milliseconds, `-1` means to wait forever (software timeout)
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_rx_unit_wait_all_done(parlio_rx_unit_handle_t rx_unit, int timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Event callback data
|
||||
*/
|
||||
typedef struct {
|
||||
parlio_rx_delimiter_handle_t delimiter; /*!< The current delimiter of this receiving event */
|
||||
void *data; /*!< The data buffer address that just finished receiving */
|
||||
size_t size; /*!< The total size of the data buffer , in byte */
|
||||
size_t recv_bytes; /*!< The number of received bytes in the data buffer */
|
||||
} parlio_rx_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief The template of the Parallel IO RX callback function
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that given from ISR
|
||||
* @param[in] edata The event data that given from ISR
|
||||
* @param[in] user_data The user specified data that given while registering the callbacks
|
||||
*
|
||||
* @return
|
||||
* - True: to awoke high priority tasks
|
||||
* - False: not to awoke high priority tasks
|
||||
*/
|
||||
typedef bool (*parlio_rx_callback_t)(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Parallel IO RX event callbacks
|
||||
*/
|
||||
typedef struct {
|
||||
parlio_rx_callback_t on_partial_receive; /*!< Callback of received partial data */
|
||||
parlio_rx_callback_t on_receive_done; /*!< Callback of receiving transaction done */
|
||||
parlio_rx_callback_t on_timeout; /*!< Callback of hardware receiving timeout */
|
||||
} parlio_rx_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Register event callbacks for Parallel IO RX unit
|
||||
*
|
||||
* @param[in] rx_unit Parallel IO RX unit handle that created by `parlio_new_rx_unit`
|
||||
* @param[in] cbs Callbacks
|
||||
* @param[in] user_data User specified data that will be transported to the callbacks
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t parlio_rx_unit_register_event_callbacks(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -89,3 +89,52 @@ void parlio_release_group_handle(parlio_group_t *group)
|
||||
ESP_LOGD(TAG, "del group(%d)", group_id);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t parlio_register_unit_to_group(parlio_unit_base_handle_t unit)
|
||||
{
|
||||
parlio_group_t *group = NULL;
|
||||
int unit_id = -1;
|
||||
for (int i = 0; i < SOC_PARLIO_GROUPS; i++) {
|
||||
group = parlio_acquire_group_handle(i);
|
||||
parlio_unit_base_handle_t *group_unit = NULL;
|
||||
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no memory for group (%d)", i);
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int j = 0; j < SOC_PARLIO_RX_UNITS_PER_GROUP; j++) {
|
||||
group_unit = (unit->dir == PARLIO_DIR_TX) ? &group->tx_units[j] : &group->rx_units[j];
|
||||
if (*group_unit == NULL) {
|
||||
*group_unit = unit;
|
||||
unit_id = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
if (unit_id < 0) {
|
||||
/* didn't find a free unit slot in the group */
|
||||
parlio_release_group_handle(group);
|
||||
group = NULL;
|
||||
} else {
|
||||
unit->unit_id = unit_id;
|
||||
unit->group = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(unit_id >= 0, ESP_ERR_NOT_FOUND, TAG,
|
||||
"no free %s unit", unit->dir == PARLIO_DIR_TX ? "tx" : "rx");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void parlio_unregister_unit_from_group(parlio_unit_base_handle_t unit)
|
||||
{
|
||||
if (unit) {
|
||||
parlio_group_t *group = unit->group;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
if (unit->dir == PARLIO_DIR_TX) {
|
||||
group->tx_units[unit->unit_id] = NULL;
|
||||
} else {
|
||||
group->rx_units[unit->unit_id] = NULL;
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
/* the rx unit has a reference of the group, release it now */
|
||||
parlio_release_group_handle(group);
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,11 @@ enum {
|
||||
PARLIO_TX_QUEUE_MAX,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PARLIO_DIR_TX,
|
||||
PARLIO_DIR_RX,
|
||||
} parlio_dir_t;
|
||||
|
||||
typedef enum {
|
||||
PARLIO_TX_FSM_INIT_WAIT,
|
||||
PARLIO_TX_FSM_INIT,
|
||||
@ -93,17 +98,34 @@ typedef enum {
|
||||
PARLIO_TX_FSM_RUN,
|
||||
} parlio_tx_fsm_t;
|
||||
|
||||
typedef struct parlio_unit_t *parlio_unit_base_handle_t;
|
||||
|
||||
typedef struct parlio_group_t {
|
||||
int group_id; // group ID, index from 0
|
||||
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
|
||||
parlio_hal_context_t hal; // hal layer for each group
|
||||
parlio_tx_unit_handle_t tx_units[SOC_PARLIO_TX_UNITS_PER_GROUP]; // tx unit handles
|
||||
parlio_unit_base_handle_t tx_units[SOC_PARLIO_TX_UNITS_PER_GROUP]; // tx unit handles
|
||||
parlio_unit_base_handle_t rx_units[SOC_PARLIO_RX_UNITS_PER_GROUP]; // rx unit handles
|
||||
} parlio_group_t;
|
||||
|
||||
/**
|
||||
* @brief The common field of rx and tx unit structure
|
||||
*
|
||||
*/
|
||||
struct parlio_unit_t {
|
||||
int unit_id; // unit id
|
||||
parlio_dir_t dir;
|
||||
parlio_group_t *group; // group handle
|
||||
};
|
||||
|
||||
parlio_group_t *parlio_acquire_group_handle(int group_id);
|
||||
|
||||
void parlio_release_group_handle(parlio_group_t *group);
|
||||
|
||||
esp_err_t parlio_register_unit_to_group(parlio_unit_base_handle_t unit);
|
||||
|
||||
void parlio_unregister_unit_from_group(parlio_unit_base_handle_t unit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
904
components/driver/parlio/parlio_rx.c
Normal file
904
components/driver/parlio/parlio_rx.c
Normal file
@ -0,0 +1,904 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/param.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
#if CONFIG_PARLIO_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "esp_pm.h"
|
||||
#include "soc/parlio_periph.h"
|
||||
#include "hal/parlio_ll.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "hal/dma_types.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/parlio_rx.h"
|
||||
#include "parlio_private.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_private/gdma.h"
|
||||
|
||||
static const char *TAG = "parlio-rx";
|
||||
|
||||
/**
|
||||
* @brief Parlio RX transaction
|
||||
*/
|
||||
typedef struct {
|
||||
parlio_rx_delimiter_handle_t delimiter; /*!< Delimiter of this transaction */
|
||||
void *payload; /*!< The payload of this transaction, will be mounted to DMA descriptor */
|
||||
size_t size; /*!< The payload size in byte */
|
||||
size_t recv_bytes; /*!< The received bytes of this transaction
|
||||
will be reset when all data filled in the infinite transaction */
|
||||
struct {
|
||||
uint32_t infinite : 1; /*!< Whether this is an infinite transaction */
|
||||
} flags;
|
||||
} parlio_rx_transaction_t;
|
||||
|
||||
/**
|
||||
* @brief Parlio RX unit resource management
|
||||
*/
|
||||
typedef struct parlio_rx_unit_t {
|
||||
/* Unit general Resources */
|
||||
int unit_id; /*!< unit id */
|
||||
parlio_dir_t dir; /*!< unit direction */
|
||||
parlio_group_t *group; /*!< group handle */
|
||||
parlio_clock_source_t clk_src; /*!< clock source of the unit */
|
||||
parlio_rx_unit_config_t cfg; /*!< basic configuration of the rx unit */
|
||||
bool is_enabled; /*!< State flag that indicates whether the unit is enabled */
|
||||
/* Mutex Lock */
|
||||
SemaphoreHandle_t mutex; /*!< Mutex lock for concurrence safety,
|
||||
* which should be acquired and released in a same function */
|
||||
|
||||
/* Power Management */
|
||||
esp_pm_lock_handle_t pm_lock; /*!< power management lock */
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[PARLIO_PM_LOCK_NAME_LEN_MAX]; /*!< pm lock name */
|
||||
#endif
|
||||
|
||||
/* Transaction Resources */
|
||||
QueueHandle_t trans_que; /*!< Static transaction queue handle */
|
||||
parlio_rx_transaction_t curr_trans; /*!< The current transaction */
|
||||
SemaphoreHandle_t trans_sem; /*!< Binary semaphore to deliver transaction done signal,
|
||||
* which can be acquired and released between different functions */
|
||||
|
||||
/* DMA Resources */
|
||||
gdma_channel_handle_t dma_chan; /*!< DMA channel */
|
||||
size_t max_recv_size; /*!< Maximum receive size for a normal transaction */
|
||||
size_t desc_num; /*!< DMA descriptor number */
|
||||
dma_descriptor_t *dma_descs; /*!< DMA descriptor array pointer */
|
||||
dma_descriptor_t *curr_desc; /*!< The pointer of the current descriptor */
|
||||
void *usr_recv_buf; /*!< The pointe to the user's receiving buffer */
|
||||
/* Infinite transaction specific */
|
||||
void *dma_buf; /*!< Additional internal DMA buffer only for infinite transactions */
|
||||
|
||||
/* Callback */
|
||||
parlio_rx_event_callbacks_t cbs; /*!< The group of callback function pointers */
|
||||
void *user_data; /*!< User data that supposed to be transported to the callback functions */
|
||||
|
||||
} parlio_rx_unit_t;
|
||||
|
||||
/**
|
||||
* @brief Delimiter mode
|
||||
*/
|
||||
typedef enum {
|
||||
PARLIO_RX_LEVEL_MODE, /*!< Delimit by the level of valid signal */
|
||||
PARLIO_RX_PULSE_MODE, /*!< Delimit by the pulse of valid signal */
|
||||
PARLIO_RX_SOFT_MODE, /*!< Delimit by the length of received data */
|
||||
} parlio_rx_delimiter_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Pralio RX delimiter management
|
||||
*/
|
||||
typedef struct parlio_rx_delimiter_t {
|
||||
parlio_rx_delimiter_mode_t mode; /*!< Delimiter mode */
|
||||
bool under_using; /*!< Whether this delimiter is under using */
|
||||
|
||||
uint32_t valid_sig;
|
||||
gpio_num_t valid_sig_line_id; /*!< The data line id for the valid signal */
|
||||
parlio_sample_edge_t sample_edge; /*!< The sampling edge of the data */
|
||||
parlio_bit_pack_order_t bit_pack_order; /*!< The order to pack the bit on the data line */
|
||||
uint32_t eof_data_len; /*!< The length of the data to trigger the eof interrupt */
|
||||
uint32_t timeout_ticks; /*!< The ticks of source clock that can trigger hardware timeout */
|
||||
struct {
|
||||
uint32_t active_level: 1; /*!< Which level indicates the validation of the transmitting data */
|
||||
uint32_t start_bit_included: 1; /*!< Whether data bit is included in the start pulse */
|
||||
uint32_t end_bit_included: 1; /*!< Whether data bit is included in the end pulse, only valid when `has_end_pulse` is true */
|
||||
uint32_t has_end_pulse: 1; /*!< Whether there's an end pulse to terminate the transaction,
|
||||
if no, the transaction will be terminated by user configured transcation length */
|
||||
uint32_t pulse_invert: 1; /*!< Whether to invert the pulse */
|
||||
} flags;
|
||||
} parlio_rx_delimiter_t;
|
||||
|
||||
|
||||
static IRAM_ATTR size_t s_parlio_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parlio_rx_transaction_t *trans)
|
||||
{
|
||||
dma_descriptor_t *p_desc = rx_unit->dma_descs;
|
||||
/* Update the current transaction to the next one, and declare the delimiter is under using of the rx unit */
|
||||
memcpy(&rx_unit->curr_trans, trans, sizeof(parlio_rx_transaction_t));
|
||||
trans->delimiter->under_using = true;
|
||||
|
||||
uint32_t desc_num = trans->size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
|
||||
uint32_t remain_num = trans->size % DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
|
||||
/* If there are still data remained, need one more descriptor */
|
||||
desc_num += remain_num ? 1 : 0;
|
||||
if (trans->flags.infinite && desc_num < 2) {
|
||||
/* At least 2 descriptors needed */
|
||||
desc_num = 2;
|
||||
}
|
||||
size_t mount_size = 0;
|
||||
size_t offset = 0;
|
||||
for (int i = 0; i < desc_num; i++) {
|
||||
size_t rest_size = trans->size - offset;
|
||||
if (rest_size >= 2 * DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) {
|
||||
mount_size = trans->size / desc_num;
|
||||
}
|
||||
else if (rest_size <= DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) {
|
||||
mount_size = (desc_num == 2) && (i == 0) ? rest_size / 2 : rest_size;
|
||||
}
|
||||
else {
|
||||
mount_size = rest_size / 2;
|
||||
}
|
||||
p_desc[i].buffer = (void *)((uint8_t *)trans->payload + offset);
|
||||
p_desc[i].dw0.size = mount_size;
|
||||
p_desc[i].dw0.length = mount_size;
|
||||
p_desc[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
|
||||
// Link the descriptor
|
||||
if (i > 0) {
|
||||
p_desc[i - 1].next = &p_desc[i];
|
||||
}
|
||||
offset += mount_size;
|
||||
}
|
||||
/* For infinite transaction, link the descriptor as a ring */
|
||||
p_desc[desc_num - 1].next = trans->flags.infinite ? &p_desc[0] : NULL;
|
||||
/* Reset the current DMA node */
|
||||
rx_unit->curr_desc = p_desc;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static IRAM_ATTR void s_parlio_set_delimiter_config(parlio_rx_unit_handle_t rx_unit, parlio_rx_delimiter_handle_t deli)
|
||||
{
|
||||
parlio_hal_context_t *hal = &(rx_unit->group->hal);
|
||||
|
||||
/* Set the clock sampling edge and the bit order */
|
||||
parlio_ll_rx_set_sample_clock_edge(hal->regs, deli->sample_edge);
|
||||
parlio_ll_rx_set_bit_pack_order(hal->regs, deli->bit_pack_order);
|
||||
|
||||
/* Set receive mode according to the delimiter */
|
||||
switch (deli->mode) {
|
||||
case PARLIO_RX_LEVEL_MODE:
|
||||
/* Select the level receive mode */
|
||||
parlio_ll_rx_set_level_recv_mode(hal->regs, deli->flags.active_level);
|
||||
parlio_ll_rx_treat_data_line_as_en(hal->regs, deli->valid_sig_line_id);
|
||||
break;
|
||||
case PARLIO_RX_PULSE_MODE:
|
||||
/* Select the pulse receive mode */
|
||||
parlio_ll_rx_set_pulse_recv_mode(hal->regs, deli->flags.start_bit_included,
|
||||
deli->flags.end_bit_included,
|
||||
!deli->flags.has_end_pulse,
|
||||
deli->flags.pulse_invert);
|
||||
parlio_ll_rx_treat_data_line_as_en(hal->regs, deli->valid_sig_line_id);
|
||||
break;
|
||||
default:
|
||||
/* Select the soft receive mode */
|
||||
parlio_ll_rx_set_soft_recv_mode(hal->regs);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set EOF configuration */
|
||||
if (deli->eof_data_len) {
|
||||
/* If EOF data length specified, set the eof condition to data length and set data bytes */
|
||||
parlio_ll_rx_set_recv_bit_len(hal->regs, deli->eof_data_len * 8);
|
||||
parlio_ll_rx_set_eof_condition(hal->regs, PARLIO_LL_RX_EOF_COND_RX_FULL);
|
||||
} else {
|
||||
/* If EOF data length not specified, set the eof condition to the external enable signal */
|
||||
parlio_ll_rx_set_eof_condition(hal->regs, PARLIO_LL_RX_EOF_COND_EN_INACTIVE);
|
||||
}
|
||||
|
||||
/* Set timeout configuration */
|
||||
if (deli->timeout_ticks) {
|
||||
parlio_ll_rx_enable_timeout(hal->regs, true);
|
||||
parlio_ll_rx_set_timeout_thres(hal->regs, deli->timeout_ticks);
|
||||
} else {
|
||||
parlio_ll_rx_enable_timeout(hal->regs, false);
|
||||
}
|
||||
|
||||
/* Set the validation signal if the validation signal number is set for level or pulse delimiter */
|
||||
if (deli->mode != PARLIO_RX_SOFT_MODE) {
|
||||
esp_rom_gpio_connect_in_signal(rx_unit->cfg.valid_gpio_num, deli->valid_sig, false);
|
||||
/* Update the valid_sig_line_num */
|
||||
parlio_ll_rx_treat_data_line_as_en(hal->regs, deli->valid_sig_line_id);
|
||||
}
|
||||
|
||||
/* Update/synchronize the new configurations */
|
||||
parlio_ll_rx_update_config(hal->regs);
|
||||
}
|
||||
|
||||
static esp_err_t s_parlio_rx_unit_set_gpio(parlio_rx_unit_handle_t rx_unit, const parlio_rx_unit_config_t *config)
|
||||
{
|
||||
int group_id = rx_unit->group->group_id;
|
||||
int unit_id = rx_unit->unit_id;
|
||||
|
||||
gpio_config_t gpio_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.pull_down_en = false,
|
||||
.pull_up_en = true,
|
||||
};
|
||||
|
||||
if (config->clk_src == PARLIO_CLK_SRC_EXTERNAL) {
|
||||
ESP_RETURN_ON_FALSE(config->clk_gpio_num >= 0, ESP_ERR_INVALID_ARG, TAG, "clk_gpio_num must be set while the clock input from external");
|
||||
/* Connect the clock in signal to the GPIO matrix if it is set */
|
||||
if (!config->flags.io_no_init) {
|
||||
gpio_conf.mode = config->flags.io_loop_back ? GPIO_MODE_INPUT_OUTPUT : GPIO_MODE_INPUT;
|
||||
gpio_conf.pin_bit_mask = BIT64(config->clk_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk in GPIO failed");
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_gpio_num], PIN_FUNC_GPIO);
|
||||
}
|
||||
esp_rom_gpio_connect_in_signal(config->clk_gpio_num,
|
||||
parlio_periph_signals.groups[group_id].rx_units[unit_id].clk_in_sig, false);
|
||||
}
|
||||
else if (config->clk_gpio_num >= 0) {
|
||||
#if SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
|
||||
gpio_conf.mode = GPIO_MODE_OUTPUT;
|
||||
gpio_conf.pin_bit_mask = BIT64(config->clk_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk in GPIO failed");
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_gpio_num], PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_out_signal(config->clk_gpio_num,
|
||||
parlio_periph_signals.groups[group_id].rx_units[unit_id].clk_out_sig, false, false);
|
||||
#else
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "this target not support to output the clock");
|
||||
#endif // SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
|
||||
}
|
||||
|
||||
gpio_conf.mode = GPIO_MODE_INPUT;
|
||||
if (config->valid_gpio_num >= 0) {
|
||||
if (!config->flags.io_no_init) {
|
||||
gpio_conf.pin_bit_mask = BIT64(config->valid_gpio_num);
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->valid_gpio_num], PIN_FUNC_GPIO);
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config data GPIO failed");
|
||||
/* Not connect the signal here, the signal is lazy connected until the delimiter takes effect */
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->data_width; i++) {
|
||||
/* Loop the data_gpio_nums to connect data and valid signals via GPIO matrix */
|
||||
if (config->data_gpio_nums[i] >= 0) {
|
||||
if (!config->flags.io_no_init) {
|
||||
gpio_conf.pin_bit_mask = BIT64(config->data_gpio_nums[i]);
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config data GPIO failed");
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->data_gpio_nums[i]], PIN_FUNC_GPIO);
|
||||
}
|
||||
esp_rom_gpio_connect_in_signal(config->data_gpio_nums[i],
|
||||
parlio_periph_signals.groups[group_id].rx_units[unit_id].data_sigs[i], false);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "data line %d not assigned", i);
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool s_parlio_rx_default_trans_done_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
|
||||
BaseType_t high_task_woken = pdFALSE;
|
||||
bool need_yield = false;
|
||||
|
||||
/* If configured on_receive_done callback and transaction just done */
|
||||
if (rx_unit->cbs.on_receive_done) {
|
||||
parlio_rx_event_data_t evt_data = {
|
||||
.delimiter = rx_unit->curr_trans.delimiter,
|
||||
.data = rx_unit->usr_recv_buf,
|
||||
.size = rx_unit->curr_trans.size,
|
||||
.recv_bytes = rx_unit->curr_trans.recv_bytes,
|
||||
};
|
||||
need_yield |= rx_unit->cbs.on_receive_done(rx_unit, &evt_data, rx_unit->user_data);
|
||||
}
|
||||
|
||||
if (rx_unit->curr_trans.flags.infinite) {
|
||||
/* For infinite transactions, reset the receiving bytes when the transaction is done */
|
||||
rx_unit->curr_trans.recv_bytes = 0;
|
||||
} else {
|
||||
parlio_rx_transaction_t next_trans;
|
||||
/* The current transaction finished, try to get the next transaction from the transaction queue */
|
||||
if (xQueueReceiveFromISR(rx_unit->trans_que, &next_trans, &high_task_woken) == pdTRUE) {
|
||||
if (rx_unit->cfg.flags.free_clk) {
|
||||
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, false);
|
||||
}
|
||||
/* If the delimiter of the next transaction is not same as the current one, need to re-config the hardware */
|
||||
if (next_trans.delimiter != rx_unit->curr_trans.delimiter) {
|
||||
s_parlio_set_delimiter_config(rx_unit, next_trans.delimiter);
|
||||
}
|
||||
/* Mount the new transaction buffer and start the new transaction */
|
||||
s_parlio_mount_transaction_buffer(rx_unit, &rx_unit->curr_trans);
|
||||
gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->dma_descs);
|
||||
if (rx_unit->cfg.flags.free_clk) {
|
||||
parlio_ll_rx_start(rx_unit->group->hal.regs, true);
|
||||
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, true);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* No more transaction pending to receive, clear the current transaction */
|
||||
rx_unit->curr_trans.delimiter->under_using = false;
|
||||
memset(&rx_unit->curr_trans, 0, sizeof(parlio_rx_transaction_t));
|
||||
need_yield |= high_task_woken == pdTRUE;
|
||||
xSemaphoreGiveFromISR(rx_unit->trans_sem, &high_task_woken);
|
||||
}
|
||||
}
|
||||
|
||||
need_yield |= high_task_woken == pdTRUE;
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool s_parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
|
||||
bool need_yield = false;
|
||||
/* Get the finished descriptor from the current descriptor */
|
||||
dma_descriptor_t *finished_desc = rx_unit->curr_desc;
|
||||
parlio_rx_event_data_t evt_data = {
|
||||
.delimiter = rx_unit->curr_trans.delimiter,
|
||||
.data = finished_desc->buffer,
|
||||
.size = finished_desc->dw0.size,
|
||||
.recv_bytes = finished_desc->dw0.length,
|
||||
};
|
||||
if (rx_unit->cbs.on_partial_receive) {
|
||||
need_yield |= rx_unit->cbs.on_partial_receive(rx_unit, &evt_data, rx_unit->user_data);
|
||||
}
|
||||
/* For the infinite transaction, need to copy the data in DMA buffer to the user receiving buffer */
|
||||
if (rx_unit->curr_trans.flags.infinite) {
|
||||
memcpy(rx_unit->usr_recv_buf + rx_unit->curr_trans.recv_bytes, evt_data.data, evt_data.recv_bytes);
|
||||
} else {
|
||||
rx_unit->curr_trans.delimiter->under_using = false;
|
||||
}
|
||||
/* Update received bytes */
|
||||
if (rx_unit->curr_trans.recv_bytes >= rx_unit->curr_trans.size) {
|
||||
rx_unit->curr_trans.recv_bytes = 0;
|
||||
}
|
||||
rx_unit->curr_trans.recv_bytes += evt_data.recv_bytes;
|
||||
/* Move to the next DMA descriptor */
|
||||
rx_unit->curr_desc = rx_unit->curr_desc->next;
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool s_parlio_rx_default_timeout_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
|
||||
bool need_yield = false;
|
||||
parlio_rx_delimiter_handle_t deli = rx_unit->curr_trans.delimiter;
|
||||
if (rx_unit->cbs.on_timeout && deli) {
|
||||
parlio_rx_event_data_t evt_data = {
|
||||
.delimiter = deli,
|
||||
};
|
||||
need_yield |= rx_unit->cbs.on_timeout(rx_unit, &evt_data, rx_unit->user_data);
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static esp_err_t s_parlio_rx_create_dma_descriptors(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid param");
|
||||
|
||||
uint32_t desc_num =max_recv_size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED + 1;
|
||||
/* set at least 2 descriptors */
|
||||
if (desc_num < 2) {
|
||||
desc_num = 4;
|
||||
}
|
||||
rx_unit->desc_num = desc_num;
|
||||
|
||||
/* Allocated and link the descriptor nodes */
|
||||
rx_unit->dma_descs = (dma_descriptor_t *)heap_caps_calloc(desc_num, sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
ESP_RETURN_ON_FALSE(rx_unit->dma_descs, ESP_ERR_NO_MEM, TAG, "no memory for DMA descriptors");
|
||||
|
||||
rx_unit->max_recv_size = max_recv_size;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit)
|
||||
{
|
||||
/* Allocate and connect the GDMA channel */
|
||||
gdma_channel_alloc_config_t dma_chan_config = {
|
||||
.direction = GDMA_CHANNEL_DIRECTION_RX,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_chan_config, &rx_unit->dma_chan), TAG, "allocate RX DMA channel failed");
|
||||
gdma_connect(rx_unit->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_PARLIO, 0));
|
||||
|
||||
/* Set GDMA strategy */
|
||||
gdma_strategy_config_t gdma_strategy_conf = {
|
||||
.auto_update_desc = true,
|
||||
.owner_check = false, // no need to check owner
|
||||
};
|
||||
gdma_apply_strategy(rx_unit->dma_chan, &gdma_strategy_conf);
|
||||
|
||||
/* Register callbacks */
|
||||
gdma_rx_event_callbacks_t cbs = {
|
||||
.on_recv_eof = s_parlio_rx_default_trans_done_callback,
|
||||
.on_recv_done = s_parlio_rx_default_desc_done_callback,
|
||||
// TODO: not on_err_desc, wait for GDMA supports on_err_eof
|
||||
// .on_err_eof = s_parlio_rx_default_timeout_callback,
|
||||
.on_descr_err = s_parlio_rx_default_timeout_callback,
|
||||
};
|
||||
gdma_register_rx_event_callbacks(rx_unit->dma_chan, &cbs, rx_unit);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_parlio_select_periph_clock(parlio_rx_unit_handle_t rx_unit, const parlio_rx_unit_config_t *config)
|
||||
{
|
||||
parlio_hal_context_t *hal = &rx_unit->group->hal;
|
||||
parlio_clock_source_t clk_src = config->clk_src;
|
||||
uint32_t periph_src_clk_hz = 0;
|
||||
uint32_t div = 1;
|
||||
/* if the source clock is input from the GPIO, then we're in the slave mode */
|
||||
if (clk_src != PARLIO_CLK_SRC_EXTERNAL) {
|
||||
ESP_RETURN_ON_FALSE(config->clk_freq_hz, ESP_ERR_INVALID_ARG, TAG, "clock frequency not set");
|
||||
/* get the internal clock source frequency */
|
||||
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz);
|
||||
/* set clock division, round up */
|
||||
div = (periph_src_clk_hz + config->clk_freq_hz - 1) / config->clk_freq_hz;
|
||||
} else {
|
||||
periph_src_clk_hz = config->clk_freq_hz;
|
||||
}
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
if (clk_src != PARLIO_CLK_SRC_EXTERNAL) {
|
||||
/* XTAL and PLL clock source will be turned off in light sleep, so we need to create a NO_LIGHT_SLEEP lock */
|
||||
sprintf(rx_unit->pm_lock_name, "parlio_rx_%d_%d", rx_unit->group->group_id, rx_unit->unit_id); // e.g. parlio_rx_0_0
|
||||
esp_err_t ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, rx_unit->pm_lock_name, &rx_unit->pm_lock);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "create NO_LIGHT_SLEEP lock failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set clock configuration */
|
||||
parlio_ll_rx_set_clock_source(hal->regs, clk_src);
|
||||
parlio_ll_rx_set_clock_div(hal->regs, div);
|
||||
|
||||
rx_unit->clk_src = clk_src;
|
||||
rx_unit->cfg.clk_freq_hz = periph_src_clk_hz / div;
|
||||
/* warning if precision lost due to division */
|
||||
if ((clk_src != PARLIO_CLK_SRC_EXTERNAL) &&
|
||||
(config->clk_freq_hz != rx_unit->cfg.clk_freq_hz )) {
|
||||
ESP_LOGW(TAG, "precision loss, real output frequency: %"PRIu32, rx_unit->cfg.clk_freq_hz );
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_parlio_destroy_rx_unit(parlio_rx_unit_handle_t rx_unit)
|
||||
{
|
||||
/* Free the transaction queue */
|
||||
if (rx_unit->trans_que) {
|
||||
vQueueDeleteWithCaps(rx_unit->trans_que);
|
||||
}
|
||||
/* Free the mutex lock */
|
||||
if (rx_unit->mutex) {
|
||||
vSemaphoreDeleteWithCaps(rx_unit->mutex);
|
||||
}
|
||||
/* Free the transaction semaphore */
|
||||
if (rx_unit->trans_sem) {
|
||||
vSemaphoreDeleteWithCaps(rx_unit->trans_sem);
|
||||
}
|
||||
/* Free the power management lock */
|
||||
if (rx_unit->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(rx_unit->pm_lock), TAG, "delete pm lock failed");
|
||||
}
|
||||
/* Delete the GDMA channel */
|
||||
if (rx_unit->dma_chan) {
|
||||
ESP_RETURN_ON_ERROR(gdma_disconnect(rx_unit->dma_chan), TAG, "disconnect dma channel failed");
|
||||
ESP_RETURN_ON_ERROR(gdma_del_channel(rx_unit->dma_chan), TAG, "delete dma channel failed");
|
||||
}
|
||||
/* Free the DMA descriptors */
|
||||
if (rx_unit->dma_descs) {
|
||||
free(rx_unit->dma_descs);
|
||||
}
|
||||
/* Free the internal DMA buffer */
|
||||
if (rx_unit->dma_buf) {
|
||||
free(rx_unit->dma_buf);
|
||||
}
|
||||
/* Unregister the RX unit from the PARLIO group */
|
||||
if (rx_unit->group) {
|
||||
parlio_unregister_unit_from_group((parlio_unit_base_handle_t)rx_unit);
|
||||
}
|
||||
/* Free the RX unit */
|
||||
free(rx_unit);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_unit_handle_t *ret_unit)
|
||||
{
|
||||
#if CONFIG_PARLIO_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
ESP_RETURN_ON_FALSE(config && ret_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
/* Check the data width to be the the power of 2 */
|
||||
ESP_RETURN_ON_FALSE(__builtin_popcount(config->data_width) == 1, ESP_ERR_INVALID_ARG, TAG,
|
||||
"data line number should be the power of 2 without counting valid signal");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
parlio_rx_unit_handle_t unit = NULL;
|
||||
|
||||
/* Allocate unit memory */
|
||||
unit = heap_caps_calloc(1, sizeof(parlio_rx_unit_t), PARLIO_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no memory for rx unit");
|
||||
unit->dir = PARLIO_DIR_RX;
|
||||
unit->is_enabled = false;
|
||||
|
||||
/* Initialize mutex lock */
|
||||
unit->mutex = xSemaphoreCreateMutexWithCaps(PARLIO_MEM_ALLOC_CAPS);
|
||||
/* Create transaction binary semaphore */
|
||||
unit->trans_sem = xSemaphoreCreateBinaryWithCaps(PARLIO_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(unit->trans_sem, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction semaphore");
|
||||
xSemaphoreGive(unit->trans_sem);
|
||||
|
||||
/* Create the transaction queue. Choose `parlio_rx_transaction_t` as the queue element instead of its pointer
|
||||
* Because the queue will do the copy to the element, no need to worry about the item in the queue will expire,
|
||||
* so that we don't have to allocate additional memory to store the transaction. */
|
||||
unit->trans_que = xQueueCreateWithCaps(config->trans_queue_depth, sizeof(parlio_rx_transaction_t), PARLIO_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(unit->trans_que, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction queue");
|
||||
|
||||
ESP_GOTO_ON_ERROR(s_parlio_rx_create_dma_descriptors(unit, config->max_recv_size), err, TAG, "create dma descriptor failed");
|
||||
/* Register and attach the rx unit to the group */
|
||||
ESP_GOTO_ON_ERROR(parlio_register_unit_to_group((parlio_unit_base_handle_t)unit), err, TAG, "failed to register the rx unit to the group");
|
||||
memcpy(&unit->cfg, config, sizeof(parlio_rx_unit_config_t));
|
||||
/* If not using external clock source, the internal clock is always a free running clock */
|
||||
if (config->clk_src != PARLIO_CLK_SRC_EXTERNAL) {
|
||||
unit->cfg.flags.free_clk = 1;
|
||||
}
|
||||
|
||||
parlio_group_t *group = unit->group;
|
||||
parlio_hal_context_t *hal = &group->hal;
|
||||
/* Initialize GPIO */
|
||||
ESP_GOTO_ON_ERROR(s_parlio_rx_unit_set_gpio(unit, config), err, TAG, "failed to set GPIO");
|
||||
/* Install DMA service */
|
||||
ESP_GOTO_ON_ERROR(s_parlio_rx_unit_init_dma(unit), err, TAG, "install rx DMA failed");
|
||||
/* Reset RX module */
|
||||
parlio_ll_rx_reset_clock(hal->regs);
|
||||
parlio_ll_rx_reset_fifo(hal->regs);
|
||||
parlio_ll_rx_enable_clock(hal->regs, false);
|
||||
parlio_ll_rx_start(hal->regs, false);
|
||||
/* parlio_ll_clock_source_t and parlio_clock_source_t are binary compatible if the clock source is from internal */
|
||||
ESP_GOTO_ON_ERROR(s_parlio_select_periph_clock(unit, config), err, TAG, "set clock source failed");
|
||||
/* Set the data width */
|
||||
parlio_ll_rx_set_bus_width(hal->regs, config->data_width);
|
||||
#if SOC_PARLIO_RX_CLK_SUPPORT_GATING
|
||||
parlio_ll_rx_enable_clock_gating(hal->regs, config->flags.clk_gate_en);
|
||||
#endif // SOC_PARLIO_RX_CLK_SUPPORT_GATING
|
||||
|
||||
/* return RX unit handle */
|
||||
*ret_unit = unit;
|
||||
|
||||
ESP_LOGD(TAG, "new rx unit(%d,%d) at %p, trans_queue_depth=%zu",
|
||||
group->group_id, unit->unit_id, (void *)unit, unit->cfg.trans_queue_depth);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (unit) {
|
||||
s_parlio_destroy_rx_unit(unit);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t parlio_del_rx_unit(parlio_rx_unit_handle_t rx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(!rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has not disabled");
|
||||
|
||||
ESP_LOGD(TAG, "del rx unit (%d, %d)", rx_unit->group->group_id, rx_unit->unit_id);
|
||||
return s_parlio_destroy_rx_unit(rx_unit);
|
||||
}
|
||||
|
||||
esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queue)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(!rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has enabled or running");
|
||||
rx_unit->is_enabled = true;
|
||||
|
||||
parlio_hal_context_t *hal = &rx_unit->group->hal;
|
||||
|
||||
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
|
||||
/* Acquire the power management lock incase */
|
||||
if (rx_unit->pm_lock) {
|
||||
esp_pm_lock_acquire(rx_unit->pm_lock);
|
||||
}
|
||||
|
||||
/* For non-free running clock, the unit can't stop once enabled, otherwise the data alignment will go wrong */
|
||||
if (!rx_unit->cfg.flags.free_clk) {
|
||||
parlio_ll_rx_reset_fifo(hal->regs);
|
||||
parlio_ll_rx_start(hal->regs, true);
|
||||
parlio_ll_rx_enable_clock(hal->regs, true);
|
||||
}
|
||||
|
||||
/* Check if we need to start a pending transaction */
|
||||
parlio_rx_transaction_t trans;
|
||||
if (reset_queue) {
|
||||
xQueueReset(rx_unit->trans_que);
|
||||
xSemaphoreGive(rx_unit->trans_sem);
|
||||
} else if (xQueueReceive(rx_unit->trans_que, &trans, 0) == pdTRUE) {
|
||||
xSemaphoreTake(rx_unit->trans_sem, 0);
|
||||
if (rx_unit->cfg.flags.free_clk) {
|
||||
parlio_ll_rx_enable_clock(hal->regs, false);
|
||||
}
|
||||
s_parlio_set_delimiter_config(rx_unit, trans.delimiter);
|
||||
s_parlio_mount_transaction_buffer(rx_unit, &rx_unit->curr_trans);
|
||||
gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc);
|
||||
if (rx_unit->cfg.flags.free_clk) {
|
||||
parlio_ll_rx_start(hal->regs, true);
|
||||
parlio_ll_rx_enable_clock(hal->regs, true);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_rx_unit_disable(parlio_rx_unit_handle_t rx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has disabled");
|
||||
rx_unit->is_enabled = false;
|
||||
|
||||
/* stop the RX engine */
|
||||
parlio_hal_context_t *hal = &rx_unit->group->hal;
|
||||
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
|
||||
gdma_stop(rx_unit->dma_chan);
|
||||
parlio_ll_rx_enable_clock(hal->regs, false);
|
||||
parlio_ll_rx_start(hal->regs, false);
|
||||
if (rx_unit->curr_trans.delimiter) {
|
||||
rx_unit->curr_trans.delimiter->under_using = false;
|
||||
}
|
||||
|
||||
/* For continuous receiving, free the temporary buffer and stop the DMA */
|
||||
if (rx_unit->dma_buf) {
|
||||
free(rx_unit->dma_buf);
|
||||
rx_unit->dma_buf = NULL;
|
||||
}
|
||||
/* release power management lock */
|
||||
if (rx_unit->pm_lock) {
|
||||
esp_pm_lock_release(rx_unit->pm_lock);
|
||||
}
|
||||
/* Erase the current transaction */
|
||||
memset(&rx_unit->curr_trans, 0, sizeof(parlio_rx_transaction_t));
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_new_rx_level_delimiter(const parlio_rx_level_delimiter_config_t *config,
|
||||
parlio_rx_delimiter_handle_t *ret_delimiter)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(config && ret_delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
/* Validation signal line must specified for level delimiter */
|
||||
ESP_RETURN_ON_FALSE(config->valid_sig_line_id < PARLIO_RX_UNIT_MAX_DATA_WIDTH,
|
||||
ESP_ERR_INVALID_ARG, TAG, "no valid signal line specified");
|
||||
|
||||
parlio_rx_delimiter_handle_t delimiter = NULL;
|
||||
|
||||
/* Allocate memory for the delimiter */
|
||||
delimiter = (parlio_rx_delimiter_handle_t)heap_caps_calloc(1, sizeof(parlio_rx_delimiter_t), PARLIO_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_NO_MEM, TAG, "no memory for rx delimiter");
|
||||
|
||||
/* Assign configuration for the level delimiter */
|
||||
delimiter->mode = PARLIO_RX_LEVEL_MODE;
|
||||
delimiter->valid_sig_line_id = config->valid_sig_line_id;
|
||||
delimiter->sample_edge = config->sample_edge;
|
||||
delimiter->bit_pack_order = config->bit_pack_order;
|
||||
delimiter->eof_data_len = config->eof_data_len;
|
||||
delimiter->timeout_ticks = config->timeout_ticks;
|
||||
delimiter->flags.active_level = config->flags.active_level;
|
||||
|
||||
*ret_delimiter = delimiter;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_new_rx_pulse_delimiter(const parlio_rx_pulse_delimiter_config_t *config,
|
||||
parlio_rx_delimiter_handle_t *ret_delimiter)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(config && ret_delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
/* Validation signal line must specified for pulse delimiter */
|
||||
ESP_RETURN_ON_FALSE(config->valid_sig_line_id < PARLIO_RX_UNIT_MAX_DATA_WIDTH,
|
||||
ESP_ERR_INVALID_ARG, TAG, "no valid signal line specified");
|
||||
/* Guarantee there is an end symbol, end by length or pulse */
|
||||
ESP_RETURN_ON_FALSE(config->eof_data_len || config->flags.has_end_pulse,
|
||||
ESP_ERR_INVALID_ARG, TAG, "Either eof_data_len or has_end_pulse should be set");
|
||||
/* If end by length, the maximum length is limited */
|
||||
ESP_RETURN_ON_FALSE(config->eof_data_len <= PARLIO_LL_RX_MAX_BYTES_PER_FRAME, ESP_ERR_INVALID_ARG,
|
||||
TAG, "EOF data length exceed the max value %d", PARLIO_LL_RX_MAX_BYTES_PER_FRAME);
|
||||
|
||||
parlio_rx_delimiter_handle_t delimiter = NULL;
|
||||
|
||||
/* Allocate memory for the delimiter */
|
||||
delimiter = (parlio_rx_delimiter_handle_t)heap_caps_calloc(1, sizeof(parlio_rx_delimiter_t), PARLIO_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_NO_MEM, TAG, "no memory for rx delimiter");
|
||||
|
||||
/* Assign configuration for the pulse delimiter */
|
||||
delimiter->mode = PARLIO_RX_PULSE_MODE;
|
||||
delimiter->valid_sig_line_id = config->valid_sig_line_id;
|
||||
delimiter->sample_edge = config->sample_edge;
|
||||
delimiter->bit_pack_order = config->bit_pack_order;
|
||||
delimiter->eof_data_len = config->eof_data_len;
|
||||
delimiter->timeout_ticks = config->timeout_ticks;
|
||||
delimiter->flags.start_bit_included = config->flags.start_bit_included;
|
||||
delimiter->flags.has_end_pulse = config->flags.has_end_pulse;
|
||||
delimiter->flags.end_bit_included = config->flags.end_bit_included;
|
||||
delimiter->flags.pulse_invert = config->flags.pulse_invert;
|
||||
|
||||
*ret_delimiter = delimiter;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_new_rx_soft_delimiter(const parlio_rx_soft_delimiter_config_t *config,
|
||||
parlio_rx_delimiter_handle_t *ret_delimiter)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(config && ret_delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
/* The soft delimiter can only end by length, EOF length should be within range (0, PARLIO_LL_RX_MAX_BYTES_PER_FRAME] */
|
||||
ESP_RETURN_ON_FALSE(config->eof_data_len > 0 && config->eof_data_len <= PARLIO_LL_RX_MAX_BYTES_PER_FRAME, ESP_ERR_INVALID_ARG,
|
||||
TAG, "EOF data length is 0 or exceed the max value %d", PARLIO_LL_RX_MAX_BYTES_PER_FRAME);
|
||||
|
||||
parlio_rx_delimiter_handle_t delimiter = NULL;
|
||||
|
||||
/* Allocate memory for the delimiter */
|
||||
delimiter = (parlio_rx_delimiter_handle_t)heap_caps_calloc(1, sizeof(parlio_rx_delimiter_t), PARLIO_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_NO_MEM, TAG, "no memory for rx delimiter");
|
||||
|
||||
/* Assign configuration for the soft delimiter */
|
||||
delimiter->mode = PARLIO_RX_SOFT_MODE;
|
||||
delimiter->under_using = false;
|
||||
delimiter->sample_edge = config->sample_edge;
|
||||
delimiter->bit_pack_order = config->bit_pack_order;
|
||||
delimiter->eof_data_len = config->eof_data_len;
|
||||
delimiter->timeout_ticks = config->timeout_ticks;
|
||||
|
||||
*ret_delimiter = delimiter;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_rx_soft_delimiter_start_stop(parlio_rx_unit_handle_t rx_unit, parlio_rx_delimiter_handle_t delimiter, bool start_stop)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit && delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(delimiter->mode == PARLIO_RX_SOFT_MODE, ESP_ERR_INVALID_ARG, TAG, "The delimiter is not soft delimiter");
|
||||
ESP_RETURN_ON_FALSE(rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has not enabled");
|
||||
|
||||
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
|
||||
parlio_hal_context_t *hal = &(rx_unit->group->hal);
|
||||
parlio_ll_rx_start_soft_recv(hal->regs, start_stop);
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_del_rx_delimiter(parlio_rx_delimiter_handle_t delimiter)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(delimiter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(!delimiter->under_using, ESP_ERR_INVALID_STATE, TAG, "the delimiter is under using");
|
||||
free(delimiter);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_parlio_rx_unit_do_transaction(parlio_rx_unit_handle_t rx_unit, parlio_rx_transaction_t *trans)
|
||||
{
|
||||
if (!rx_unit->curr_trans.delimiter) {
|
||||
xSemaphoreTake(rx_unit->trans_sem, 0);
|
||||
if (rx_unit->cfg.flags.free_clk) {
|
||||
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, false);
|
||||
}
|
||||
if (trans->delimiter != rx_unit->curr_trans.delimiter) {
|
||||
s_parlio_set_delimiter_config(rx_unit, trans->delimiter);
|
||||
}
|
||||
s_parlio_mount_transaction_buffer(rx_unit, trans);
|
||||
gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc);
|
||||
if (rx_unit->cfg.flags.free_clk) {
|
||||
parlio_ll_rx_start(rx_unit->group->hal.regs, true);
|
||||
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, true);
|
||||
}
|
||||
} else { // Otherwise send to the queue
|
||||
/* Send the transaction to the queue */
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(rx_unit->trans_que, trans, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "transaction queue is full, failed to send transaction to the queue");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit,
|
||||
void *payload,
|
||||
size_t payload_size,
|
||||
const parlio_receive_config_t* recv_cfg)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit && payload && recv_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(recv_cfg->delimiter, ESP_ERR_INVALID_ARG, TAG, "no delimiter specified");
|
||||
ESP_RETURN_ON_FALSE(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG, TAG, "trans length too large");
|
||||
#if CONFIG_GDMA_ISR_IRAM_SAFE
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM");
|
||||
#endif
|
||||
if (recv_cfg->delimiter->eof_data_len) {
|
||||
ESP_RETURN_ON_FALSE(payload_size >= recv_cfg->delimiter->eof_data_len, ESP_ERR_INVALID_ARG,
|
||||
TAG, "payload size should be greater than eof_data_len");
|
||||
}
|
||||
if (recv_cfg->delimiter->mode != PARLIO_RX_SOFT_MODE) {
|
||||
ESP_RETURN_ON_FALSE(rx_unit->cfg.valid_gpio_num >= 0, ESP_ERR_INVALID_ARG, TAG, "The validate gpio of this unit is not set");
|
||||
/* Check if the valid_sig_line_id is equal or greater than data width, otherwise valid_sig_line_id is conflict with data signal.
|
||||
* Specifically, level or pulse delimiter requires one data line as valid signal, so these two delimiters can't support PARLIO_RX_UNIT_MAX_DATA_WIDTH */
|
||||
ESP_RETURN_ON_FALSE(recv_cfg->delimiter->valid_sig_line_id >= rx_unit->cfg.data_width,
|
||||
ESP_ERR_INVALID_ARG, TAG, "the valid_sig_line_id of this delimiter is conflict with rx unit data width");
|
||||
/* Assign the signal here to ensure iram safe */
|
||||
recv_cfg->delimiter->valid_sig = parlio_periph_signals.groups[rx_unit->group->group_id].
|
||||
rx_units[rx_unit->unit_id].
|
||||
data_sigs[recv_cfg->delimiter->valid_sig_line_id];
|
||||
}
|
||||
void *p_buffer = payload;
|
||||
|
||||
/* Create the internal DMA buffer for the infinite transaction if indirect_mount is set */
|
||||
if (recv_cfg->flags.is_infinite && recv_cfg->flags.indirect_mount) {
|
||||
/* Allocate the internal DMA buffer to store the data temporary */
|
||||
rx_unit->dma_buf = heap_caps_calloc(1, payload_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
ESP_RETURN_ON_FALSE(rx_unit->dma_buf, ESP_ERR_NO_MEM, TAG, "No memory for the internal DMA buffer");
|
||||
/* Use the internal DMA buffer so that the user buffer can always be available */
|
||||
p_buffer = rx_unit->dma_buf;
|
||||
}
|
||||
|
||||
/* Create the transaction */
|
||||
parlio_rx_transaction_t transaction = {
|
||||
.delimiter = recv_cfg->delimiter,
|
||||
.payload = p_buffer,
|
||||
.size = payload_size,
|
||||
.recv_bytes = 0,
|
||||
.flags.infinite = recv_cfg->flags.is_infinite,
|
||||
};
|
||||
rx_unit->usr_recv_buf = payload;
|
||||
|
||||
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
|
||||
esp_err_t ret = s_parlio_rx_unit_do_transaction(rx_unit, &transaction);
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t parlio_rx_unit_wait_all_done(parlio_rx_unit_handle_t rx_unit, int timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
|
||||
TickType_t ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
/* Waiting for the all transaction done signal */
|
||||
if (xSemaphoreTake(rx_unit->trans_sem, ticks) == pdFALSE) {
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
/* Put back the signal */
|
||||
xSemaphoreGive(rx_unit->trans_sem);
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_rx_unit_register_event_callbacks(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(!rx_unit->is_enabled, ESP_ERR_INVALID_STATE, TAG, "the unit has enabled or running");
|
||||
|
||||
xSemaphoreTake(rx_unit->mutex, portMAX_DELAY);
|
||||
memcpy(&rx_unit->cbs, cbs, sizeof(parlio_rx_event_callbacks_t));
|
||||
rx_unit->user_data = user_data;
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
@ -46,8 +46,9 @@ typedef struct {
|
||||
|
||||
typedef struct parlio_tx_unit_t {
|
||||
int unit_id; // unit id
|
||||
size_t data_width; // data width
|
||||
parlio_dir_t dir;
|
||||
parlio_group_t *group; // group handle
|
||||
size_t data_width; // data width
|
||||
intr_handle_t intr; // allocated interrupt handle
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
gdma_channel_handle_t dma_chan; // DMA channel
|
||||
@ -71,44 +72,6 @@ typedef struct parlio_tx_unit_t {
|
||||
|
||||
static void parlio_tx_default_isr(void *args);
|
||||
|
||||
static esp_err_t parlio_tx_register_to_group(parlio_tx_unit_t *unit)
|
||||
{
|
||||
parlio_group_t *group = NULL;
|
||||
int unit_id = -1;
|
||||
for (int i = 0; i < SOC_PARLIO_GROUPS; i++) {
|
||||
group = parlio_acquire_group_handle(i);
|
||||
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no memory for group (%d)", i);
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
for (int j = 0; j < SOC_PARLIO_TX_UNITS_PER_GROUP; j++) {
|
||||
if (group->tx_units[j] == NULL) {
|
||||
group->tx_units[j] = unit;
|
||||
unit_id = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
if (unit_id < 0) {
|
||||
// didn't find a free unit slot in the group
|
||||
parlio_release_group_handle(group);
|
||||
} else {
|
||||
unit->unit_id = unit_id;
|
||||
unit->group = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(unit_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free tx unit");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void parlio_tx_unregister_to_group(parlio_tx_unit_t *unit, parlio_group_t *group)
|
||||
{
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->tx_units[unit->unit_id] = NULL;
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
// the tx unit has a reference of the group, release it now
|
||||
parlio_release_group_handle(group);
|
||||
}
|
||||
|
||||
static esp_err_t parlio_tx_create_trans_queue(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
|
||||
{
|
||||
esp_err_t ret;
|
||||
@ -159,7 +122,7 @@ static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
|
||||
}
|
||||
if (tx_unit->group) {
|
||||
// de-register from group
|
||||
parlio_tx_unregister_to_group(tx_unit, tx_unit->group);
|
||||
parlio_unregister_unit_from_group((parlio_unit_base_handle_t)tx_unit);
|
||||
}
|
||||
free(tx_unit->dma_nodes);
|
||||
free(tx_unit);
|
||||
@ -185,6 +148,7 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
|
||||
esp_rom_gpio_connect_out_signal(config->data_gpio_nums[i],
|
||||
parlio_periph_signals.groups[group_id].tx_units[unit_id].data_sigs[i], false, false);
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->data_gpio_nums[i]], PIN_FUNC_GPIO);
|
||||
|
||||
}
|
||||
}
|
||||
// Note: the valid signal will override TXD[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG]
|
||||
@ -232,12 +196,10 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit)
|
||||
static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
|
||||
{
|
||||
parlio_hal_context_t *hal = &tx_unit->group->hal;
|
||||
// parlio_ll_clock_source_t and parlio_clock_source_t are binary compatible if the clock source is from internal
|
||||
parlio_ll_clock_source_t clk_src = (parlio_ll_clock_source_t)(config->clk_src);
|
||||
parlio_clock_source_t clk_src = config->clk_in_gpio_num >= 0 ? PARLIO_CLK_SRC_EXTERNAL : config->clk_src;
|
||||
uint32_t periph_src_clk_hz = 0;
|
||||
// if the source clock is input from the GPIO, then we're in the slave mode
|
||||
if (config->clk_in_gpio_num >= 0) {
|
||||
clk_src = PARLIO_LL_CLK_SRC_PAD;
|
||||
if (clk_src == PARLIO_CLK_SRC_EXTERNAL) {
|
||||
periph_src_clk_hz = config->input_clk_src_freq_hz;
|
||||
} else {
|
||||
// get the internal clock source frequency
|
||||
@ -246,7 +208,7 @@ static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const par
|
||||
ESP_RETURN_ON_FALSE(periph_src_clk_hz, ESP_ERR_INVALID_ARG, TAG, "invalid clock source frequency");
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
if (clk_src != PARLIO_LL_CLK_SRC_PAD) {
|
||||
if (clk_src != PARLIO_CLK_SRC_EXTERNAL) {
|
||||
// XTAL and PLL clock source will be turned off in light sleep, so we need to create a NO_LIGHT_SLEEP lock
|
||||
sprintf(tx_unit->pm_lock_name, "parlio_tx_%d_%d", tx_unit->group->group_id, tx_unit->unit_id); // e.g. parlio_tx_0_0
|
||||
esp_err_t ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, tx_unit->pm_lock_name, &tx_unit->pm_lock);
|
||||
@ -318,13 +280,13 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
|
||||
unit->dma_nodes[i].next = (i == dma_nodes_num - 1) ? NULL : &(unit->dma_nodes[i+1]);
|
||||
}
|
||||
unit->max_transfer_bits = config->max_transfer_size * 8;
|
||||
|
||||
unit->dir = PARLIO_DIR_TX;
|
||||
unit->data_width = data_width;
|
||||
//create transaction queue
|
||||
ESP_GOTO_ON_ERROR(parlio_tx_create_trans_queue(unit, config), err, TAG, "create transaction queue failed");
|
||||
|
||||
// register the unit to a group
|
||||
ESP_GOTO_ON_ERROR(parlio_tx_register_to_group(unit), err, TAG, "register unit to group failed");
|
||||
ESP_GOTO_ON_ERROR(parlio_register_unit_to_group((parlio_unit_base_handle_t)unit), err, TAG, "register unit to group failed");
|
||||
parlio_group_t *group = unit->group;
|
||||
parlio_hal_context_t *hal = &group->hal;
|
||||
// select the clock source
|
||||
|
@ -1,2 +1,2 @@
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 | ESP32-P4 |
|
||||
| ----------------- | -------- | -------- | -------- |
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
@ -39,12 +39,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
PARLIO_LL_CLK_SRC_XTAL = PARLIO_CLK_SRC_XTAL,
|
||||
PARLIO_LL_CLK_SRC_PLL_F240M = PARLIO_CLK_SRC_PLL_F240M,
|
||||
PARLIO_LL_CLK_SRC_PAD, // clock source from GPIO pad
|
||||
} parlio_ll_clock_source_t;
|
||||
|
||||
typedef enum {
|
||||
PARLIO_LL_RX_EOF_COND_RX_FULL, /*!< RX unit generates EOF event when it receives enough data */
|
||||
PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */
|
||||
@ -82,18 +76,21 @@ static inline void parlio_ll_reset_register(int group_id)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param src Clock source
|
||||
*/
|
||||
static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src)
|
||||
static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = 0;
|
||||
switch (src) {
|
||||
case PARLIO_LL_CLK_SRC_XTAL:
|
||||
case PARLIO_CLK_SRC_XTAL:
|
||||
clk_sel = 0;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PLL_F240M:
|
||||
case PARLIO_CLK_SRC_PLL_F240M:
|
||||
clk_sel = 1;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PAD:
|
||||
case PARLIO_CLK_SRC_RC_FAST:
|
||||
clk_sel = 2;
|
||||
break;
|
||||
case PARLIO_CLK_SRC_EXTERNAL:
|
||||
clk_sel = 3;
|
||||
break;
|
||||
|
||||
@ -104,6 +101,33 @@ static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
|
||||
PCR.parl_clk_rx_conf.parl_clk_rx_sel = clk_sel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the clock source for the RX unit
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
* @return
|
||||
* parlio_clock_source_t RX core clock source
|
||||
*/
|
||||
static inline parlio_clock_source_t parlio_ll_rx_get_clock_source(parl_io_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = PCR.parl_clk_rx_conf.parl_clk_rx_sel;
|
||||
switch (clk_sel) {
|
||||
case 0:
|
||||
return PARLIO_CLK_SRC_XTAL;
|
||||
case 1:
|
||||
return PARLIO_CLK_SRC_PLL_F240M;
|
||||
case 2:
|
||||
return PARLIO_CLK_SRC_RC_FAST;
|
||||
case 3:
|
||||
return PARLIO_CLK_SRC_EXTERNAL;
|
||||
default: // unsupported clock source
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
return PARLIO_CLK_SRC_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the clock divider for the RX unit
|
||||
*
|
||||
@ -149,6 +173,7 @@ static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param cond RX EOF condition
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_rx_eof_cond_t cond)
|
||||
{
|
||||
dev->rx_cfg0.rx_eof_gen_sel = cond;
|
||||
@ -160,6 +185,7 @@ static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_
|
||||
* @param dev Parallel IO register base address
|
||||
* @param en True to start, False to stop
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en)
|
||||
{
|
||||
dev->rx_cfg0.rx_start = en;
|
||||
@ -173,6 +199,7 @@ static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param bitlen Number of bits to receive in the next transaction, bitlen must be a multiple of 8
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bitlen)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_cfg0, rx_data_bytelen, bitlen / 8);
|
||||
@ -184,6 +211,7 @@ static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bi
|
||||
* @param dev Parallel IO register base address
|
||||
* @param active_level Level of the external enable signal, true for active high, false for active low
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool active_level)
|
||||
{
|
||||
dev->rx_cfg0.rx_smp_mode_sel = 0;
|
||||
@ -199,6 +227,7 @@ static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool act
|
||||
* @param end_by_len Whether to use the frame length to determine the end of the frame
|
||||
* @param pulse_inv Whether the pulse is inverted
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool start_inc, bool end_inc, bool end_by_len, bool pulse_inv)
|
||||
{
|
||||
uint32_t submode = 0;
|
||||
@ -226,6 +255,7 @@ static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool sta
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_soft_recv_mode(parl_io_dev_t *dev)
|
||||
{
|
||||
dev->rx_cfg0.rx_smp_mode_sel = 2;
|
||||
@ -248,6 +278,7 @@ static inline void parlio_ll_rx_start_soft_recv(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param edge Sample clock edge
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge)
|
||||
{
|
||||
dev->rx_cfg0.rx_clk_edge_sel = edge;
|
||||
@ -259,6 +290,7 @@ static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio
|
||||
* @param dev Parallel IO register base address
|
||||
* @param order Packing order
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order)
|
||||
{
|
||||
dev->rx_cfg0.rx_bit_pack_order = order;
|
||||
@ -318,6 +350,7 @@ static inline void parlio_ll_rx_reset_fifo(parl_io_dev_t *dev)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param line_num Data line number (0-15)
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_treat_data_line_as_en(parl_io_dev_t *dev, uint32_t line_num)
|
||||
{
|
||||
dev->rx_cfg1.rx_ext_en_sel = line_num;
|
||||
@ -329,6 +362,7 @@ static inline void parlio_ll_rx_treat_data_line_as_en(parl_io_dev_t *dev, uint32
|
||||
* @param dev Parallel IO register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en)
|
||||
{
|
||||
dev->rx_cfg1.rx_timeout_en = en;
|
||||
@ -340,6 +374,7 @@ static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param thres Threshold of RX timeout
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t thres)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_cfg1, rx_timeout_threshold, thres);
|
||||
@ -350,6 +385,7 @@ static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t t
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
|
||||
{
|
||||
dev->rx_cfg1.rx_reg_update = 1;
|
||||
@ -364,18 +400,21 @@ static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param src Clock source
|
||||
*/
|
||||
static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src)
|
||||
static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = 0;
|
||||
switch (src) {
|
||||
case PARLIO_LL_CLK_SRC_XTAL:
|
||||
case PARLIO_CLK_SRC_XTAL:
|
||||
clk_sel = 0;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PLL_F240M:
|
||||
case PARLIO_CLK_SRC_PLL_F240M:
|
||||
clk_sel = 1;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PAD:
|
||||
case PARLIO_CLK_SRC_RC_FAST:
|
||||
clk_sel = 2;
|
||||
break;
|
||||
case PARLIO_CLK_SRC_EXTERNAL:
|
||||
clk_sel = 3;
|
||||
break;
|
||||
|
||||
@ -386,6 +425,33 @@ static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
|
||||
PCR.parl_clk_tx_conf.parl_clk_tx_sel = clk_sel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the clock source for the TX unit
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
* @return
|
||||
* parlio_clock_source_t TX core clock source
|
||||
*/
|
||||
static inline parlio_clock_source_t parlio_ll_tx_get_clock_source(parl_io_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = PCR.parl_clk_tx_conf.parl_clk_tx_sel;
|
||||
switch (clk_sel) {
|
||||
case 0:
|
||||
return PARLIO_CLK_SRC_XTAL;
|
||||
case 1:
|
||||
return PARLIO_CLK_SRC_PLL_F240M;
|
||||
case 2:
|
||||
return PARLIO_CLK_SRC_RC_FAST;
|
||||
case 3:
|
||||
return PARLIO_CLK_SRC_EXTERNAL;
|
||||
default: // unsupported clock source
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
return PARLIO_CLK_SRC_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the clock divider for the TX unit
|
||||
*
|
||||
|
@ -41,12 +41,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
PARLIO_LL_CLK_SRC_XTAL = PARLIO_CLK_SRC_XTAL,
|
||||
PARLIO_LL_CLK_SRC_PLL_F96M = PARLIO_CLK_SRC_PLL_F96M,
|
||||
PARLIO_LL_CLK_SRC_PAD, // clock source from GPIO pad
|
||||
} parlio_ll_clock_source_t;
|
||||
|
||||
typedef enum {
|
||||
PARLIO_LL_RX_EOF_COND_RX_FULL, /*!< RX unit generates EOF event when it receives enough data */
|
||||
PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */
|
||||
@ -84,18 +78,21 @@ static inline void parlio_ll_reset_register(int group_id)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param src Clock source
|
||||
*/
|
||||
static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src)
|
||||
static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = 0;
|
||||
switch (src) {
|
||||
case PARLIO_LL_CLK_SRC_XTAL:
|
||||
case PARLIO_CLK_SRC_XTAL:
|
||||
clk_sel = 0;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PLL_F96M:
|
||||
case PARLIO_CLK_SRC_PLL_F96M:
|
||||
clk_sel = 1;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PAD:
|
||||
case PARLIO_CLK_SRC_RC_FAST:
|
||||
clk_sel = 2;
|
||||
break;
|
||||
case PARLIO_CLK_SRC_EXTERNAL:
|
||||
clk_sel = 3;
|
||||
break;
|
||||
|
||||
@ -106,6 +103,33 @@ static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
|
||||
PCR.parl_clk_rx_conf.parl_clk_rx_sel = clk_sel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the clock source for the RX unit
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
* @return
|
||||
* parlio_clock_source_t RX core clock source
|
||||
*/
|
||||
static inline parlio_clock_source_t parlio_ll_rx_get_clock_source(parl_io_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = PCR.parl_clk_rx_conf.parl_clk_rx_sel;
|
||||
switch (clk_sel) {
|
||||
case 0:
|
||||
return PARLIO_CLK_SRC_XTAL;
|
||||
case 1:
|
||||
return PARLIO_CLK_SRC_PLL_F96M;
|
||||
case 2:
|
||||
return PARLIO_CLK_SRC_RC_FAST;
|
||||
case 3:
|
||||
return PARLIO_CLK_SRC_EXTERNAL;
|
||||
default: // unsupported clock source
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
return PARLIO_CLK_SRC_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the clock divider for the RX unit
|
||||
*
|
||||
@ -137,6 +161,7 @@ static inline void parlio_ll_rx_reset_clock(parl_io_dev_t *dev)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en)
|
||||
{
|
||||
(void)dev;
|
||||
@ -149,6 +174,7 @@ static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param cond RX EOF condition
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_rx_eof_cond_t cond)
|
||||
{
|
||||
dev->rx_genrl_cfg.rx_eof_gen_sel = cond;
|
||||
@ -160,6 +186,7 @@ static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_
|
||||
* @param dev Parallel IO register base address
|
||||
* @param en True to start, False to stop
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en)
|
||||
{
|
||||
dev->rx_start_cfg.rx_start = en;
|
||||
@ -173,6 +200,7 @@ static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param bitlen Number of bits to receive in the next transaction, bitlen must be a multiple of 8
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bitlen)
|
||||
{
|
||||
dev->rx_data_cfg.rx_bitlen = bitlen;
|
||||
@ -184,6 +212,7 @@ static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bi
|
||||
* @param dev Parallel IO register base address
|
||||
* @param active_level Level of the external enable signal, true for active high, false for active low
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool active_level)
|
||||
{
|
||||
dev->rx_mode_cfg.rx_smp_mode_sel = 0;
|
||||
@ -199,6 +228,7 @@ static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool act
|
||||
* @param end_by_len Whether to use the frame length to determine the end of the frame
|
||||
* @param pulse_inv Whether the pulse is inverted
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool start_inc, bool end_inc, bool end_by_len, bool pulse_inv)
|
||||
{
|
||||
uint32_t submode = 0;
|
||||
@ -224,6 +254,7 @@ static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool sta
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_soft_recv_mode(parl_io_dev_t *dev)
|
||||
{
|
||||
dev->rx_mode_cfg.rx_smp_mode_sel = 2;
|
||||
@ -246,6 +277,7 @@ static inline void parlio_ll_rx_start_soft_recv(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param edge Sample clock edge
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge)
|
||||
{
|
||||
dev->rx_clk_cfg.rx_clk_i_inv = edge;
|
||||
@ -258,6 +290,7 @@ static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio
|
||||
* @param dev Parallel IO register base address
|
||||
* @param order Packing order
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order)
|
||||
{
|
||||
dev->rx_data_cfg.rx_data_order_inv = order;
|
||||
@ -313,6 +346,7 @@ static inline void parlio_ll_rx_reset_fifo(parl_io_dev_t *dev)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param line_num Data line number (0-15)
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_treat_data_line_as_en(parl_io_dev_t *dev, uint32_t line_num)
|
||||
{
|
||||
dev->rx_mode_cfg.rx_ext_en_sel = line_num;
|
||||
@ -335,6 +369,7 @@ static inline void parlio_ll_rx_enable_clock_gating(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en)
|
||||
{
|
||||
dev->rx_genrl_cfg.rx_timeout_en = en;
|
||||
@ -346,6 +381,7 @@ static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param thres Threshold of RX timeout
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t thres)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_genrl_cfg, rx_timeout_thres, thres);
|
||||
@ -356,6 +392,7 @@ static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t t
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
|
||||
{
|
||||
dev->reg_update.rx_reg_update = 1;
|
||||
@ -370,18 +407,21 @@ static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param src Clock source
|
||||
*/
|
||||
static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src)
|
||||
static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = 0;
|
||||
switch (src) {
|
||||
case PARLIO_LL_CLK_SRC_XTAL:
|
||||
case PARLIO_CLK_SRC_XTAL:
|
||||
clk_sel = 0;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PLL_F96M:
|
||||
case PARLIO_CLK_SRC_PLL_F96M:
|
||||
clk_sel = 1;
|
||||
break;
|
||||
case PARLIO_LL_CLK_SRC_PAD:
|
||||
case PARLIO_CLK_SRC_RC_FAST:
|
||||
clk_sel = 2;
|
||||
break;
|
||||
case PARLIO_CLK_SRC_EXTERNAL:
|
||||
clk_sel = 3;
|
||||
break;
|
||||
|
||||
@ -392,6 +432,33 @@ static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_c
|
||||
PCR.parl_clk_tx_conf.parl_clk_tx_sel = clk_sel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the clock source for the TX unit
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
* @return
|
||||
* parlio_clock_source_t TX core clock source
|
||||
*/
|
||||
static inline parlio_clock_source_t parlio_ll_tx_get_clock_source(parl_io_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
uint32_t clk_sel = PCR.parl_clk_tx_conf.parl_clk_tx_sel;
|
||||
switch (clk_sel) {
|
||||
case 0:
|
||||
return PARLIO_CLK_SRC_XTAL;
|
||||
case 1:
|
||||
return PARLIO_CLK_SRC_PLL_F96M;
|
||||
case 2:
|
||||
return PARLIO_CLK_SRC_RC_FAST;
|
||||
case 3:
|
||||
return PARLIO_CLK_SRC_EXTERNAL;
|
||||
default: // unsupported clock source
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
return PARLIO_CLK_SRC_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the clock divider for the TX unit
|
||||
*
|
||||
|
@ -199,6 +199,7 @@ static inline void parlio_ll_rx_reset_clock(parl_io_dev_t *dev)
|
||||
* @param dev Parallel IO register base address
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en)
|
||||
{
|
||||
(void)dev;
|
||||
@ -227,6 +228,7 @@ static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_
|
||||
* @param dev Parallel IO register base address
|
||||
* @param en True to start, False to stop
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en)
|
||||
{
|
||||
dev->rx_start_cfg.rx_start = en;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hal/parlio_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -49,6 +49,11 @@ typedef enum {
|
||||
PARLIO_BIT_PACK_ORDER_MSB, /*!< Bit pack order: MSB */
|
||||
} parlio_bit_pack_order_t;
|
||||
|
||||
typedef enum {
|
||||
PARIO_CLK_TYPE_FREE,
|
||||
PARIO_CLK_TYPE_NON_FREE,
|
||||
} parlio_clock_type_t;
|
||||
|
||||
#if SOC_PARLIO_SUPPORTED
|
||||
/**
|
||||
* @brief Parallel IO clock source
|
||||
|
@ -460,6 +460,8 @@ typedef enum {
|
||||
typedef enum {
|
||||
PARLIO_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
|
||||
PARLIO_CLK_SRC_PLL_F240M = SOC_MOD_CLK_PLL_F240M, /*!< Select PLL_F240M as the source clock */
|
||||
PARLIO_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */
|
||||
PARLIO_CLK_SRC_EXTERNAL = -1, /*!< Select EXTERNAL clock as the source clock */
|
||||
PARLIO_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F240M, /*!< Select PLL_F240M as the default clock choice */
|
||||
} soc_periph_parlio_clk_src_t;
|
||||
|
||||
|
@ -815,6 +815,14 @@ config SOC_PARLIO_TX_CLK_SUPPORT_GATING
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_RX_CLK_SUPPORT_GATING
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_TRANS_BIT_ALIGN
|
||||
bool
|
||||
default y
|
||||
|
@ -451,7 +451,7 @@ typedef enum {
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of PARLIO
|
||||
*/
|
||||
#define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F96M}
|
||||
#define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F96M, SOC_MOD_CLK_RC_FAST}
|
||||
|
||||
/**
|
||||
* @brief PARLIO clock source
|
||||
@ -459,6 +459,8 @@ typedef enum {
|
||||
typedef enum {
|
||||
PARLIO_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
|
||||
PARLIO_CLK_SRC_PLL_F96M = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the source clock */
|
||||
PARLIO_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */
|
||||
PARLIO_CLK_SRC_EXTERNAL = -1, /*!< Select EXTERNAL clock as the source clock */
|
||||
PARLIO_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the default clock choice */
|
||||
} soc_periph_parlio_clk_src_t;
|
||||
|
||||
|
@ -323,6 +323,8 @@
|
||||
#define SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH 8 /*!< Number of data lines of the TX unit */
|
||||
#define SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH 8 /*!< Number of data lines of the RX unit */
|
||||
#define SOC_PARLIO_TX_CLK_SUPPORT_GATING 1 /*!< Support gating TX clock */
|
||||
#define SOC_PARLIO_RX_CLK_SUPPORT_GATING 1 /*!< Support gating RX clock */
|
||||
#define SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT 1 /*!< Support output RX clock to a GPIO */
|
||||
#define SOC_PARLIO_TRANS_BIT_ALIGN 1 /*!< Support bit alignment in transaction */
|
||||
|
||||
/*--------------------------- MPI CAPS ---------------------------------------*/
|
||||
|
@ -47,10 +47,6 @@ config SOC_ETM_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_ASYNC_MEMCPY_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
@ -32,7 +32,7 @@
|
||||
#define SOC_MCPWM_SUPPORTED 1
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
#define SOC_ETM_SUPPORTED 1
|
||||
#define SOC_PARLIO_SUPPORTED 1 //TODO: IDF-7471
|
||||
// #define SOC_PARLIO_SUPPORTED 1 //TODO: IDF-7471
|
||||
#define SOC_ASYNC_MEMCPY_SUPPORTED 1
|
||||
// disable usb serial jtag for esp32p4, current image does not support
|
||||
// #define SOC_USB_SERIAL_JTAG_SUPPORTED 1 //TODO: IDF-7496
|
||||
|
Loading…
Reference in New Issue
Block a user