forked from espressif/esp-idf
feat(parlio_tx): support to mount bitscrambler
This commit is contained in:
@ -8,10 +8,14 @@ if(CONFIG_SOC_PARLIO_SUPPORTED)
|
||||
"src/parlio_rx.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
list(APPEND srcs "src/parlio_bitscrambler.c")
|
||||
endif()
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
set(priv_requires "")
|
||||
else()
|
||||
set(priv_requires esp_pm esp_driver_gpio esp_mm)
|
||||
set(priv_requires esp_pm esp_driver_gpio esp_mm esp_driver_bitscrambler)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/parlio_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Decorate Parlio TX units with BitScrambler
|
||||
*
|
||||
* @note This function creates a BitScrambler instance and associates it with the Parlio TX unit.
|
||||
* The decorated TX unit will be able to do some pre-process to the user data with the help of BitScrambler
|
||||
* Only can be called before enable the TX unit
|
||||
*
|
||||
* @param[in] tx_unit Parlio TX unit handle
|
||||
* @return
|
||||
* - ESP_OK: Decorate Parlio TX units with BitScrambler success
|
||||
* - ESP_ERR_INVALID_ARG: Failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Failed because the TX unit is already decorated with BitScrambler
|
||||
* - ESP_FAIL: Failed because of other error
|
||||
*/
|
||||
esp_err_t parlio_tx_unit_decorate_bitscrambler(parlio_tx_unit_handle_t tx_unit);
|
||||
|
||||
/**
|
||||
* @brief Remove the BitScrambler decoration from the Parlio TX unit
|
||||
*
|
||||
* @note This function removes the BitScrambler decoration from the Parlio TX unit, restoring the original functionality.
|
||||
* Only can be called before enable the TX unit
|
||||
*
|
||||
* @param[in] tx_unit Parlio TX unit handle
|
||||
* @return
|
||||
* - ESP_OK: Remove the BitScrambler decoration from the Parlio TX unit success
|
||||
* - ESP_ERR_INVALID_ARG: Failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Failed because the TX unit is not decorated with BitScrambler
|
||||
* - ESP_FAIL: Failed because of other error
|
||||
*/
|
||||
esp_err_t parlio_tx_unit_undecorate_bitscrambler(parlio_tx_unit_handle_t tx_unit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -108,23 +108,6 @@ esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t unit);
|
||||
*/
|
||||
esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t unit);
|
||||
|
||||
/**
|
||||
* @brief Type of Parallel IO TX done event data
|
||||
*/
|
||||
typedef struct {
|
||||
} parlio_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Prototype of parlio tx event callback
|
||||
* @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit`
|
||||
* @param[in] edata Point to Parallel IO TX event data. The lifecycle of this pointer memory is inside this function,
|
||||
* user should copy it into static memory if used outside this function.
|
||||
* @param[in] user_ctx User registered context, passed from `parlio_tx_unit_register_event_callbacks`
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*parlio_tx_done_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief Group of Parallel IO TX callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
@ -157,6 +140,7 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t idle_value; /*!< The value on the data line when the parallel IO is in idle state */
|
||||
const void *bitscrambler_program; /*!< BitScrambler program binary, NULL if not use BitScrambler */
|
||||
struct {
|
||||
uint32_t queue_nonblocking : 1; /*!< If set, when the transaction queue is full, driver will not block the thread but return directly */
|
||||
uint32_t loop_transmission : 1; /*!< If set, the transmission will be repeated continuously, until the tx_unit is disabled by `parlio_tx_unit_disable` */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -31,6 +31,23 @@ typedef struct parlio_rx_unit_t *parlio_rx_unit_handle_t;
|
||||
*/
|
||||
typedef struct parlio_rx_delimiter_t *parlio_rx_delimiter_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of Parallel IO TX done event data
|
||||
*/
|
||||
typedef struct {
|
||||
} parlio_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Prototype of parlio tx event callback
|
||||
* @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit`
|
||||
* @param[in] edata Point to Parallel IO TX event data. The lifecycle of this pointer memory is inside this function,
|
||||
* user should copy it into static memory if used outside this function.
|
||||
* @param[in] user_ctx User registered context, passed from `parlio_tx_unit_register_event_callbacks`
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*parlio_tx_done_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
83
components/esp_driver_parlio/src/parlio_bitscrambler.c
Normal file
83
components/esp_driver_parlio/src/parlio_bitscrambler.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "parlio_priv.h"
|
||||
|
||||
static esp_err_t parlio_tx_unit_bs_enable(parlio_tx_unit_handle_t tx_unit, parlio_tx_trans_desc_t *t)
|
||||
{
|
||||
if (t->bitscrambler_program) {
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_enable(tx_unit->bs_handle), TAG, "enable bitscrambler failed");
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_load_program(tx_unit->bs_handle, t->bitscrambler_program), TAG, "load bitscrambler program failed");
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_reset(tx_unit->bs_handle), TAG, "reset bitscrambler failed");
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_start(tx_unit->bs_handle), TAG, "start bitscrambler failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t parlio_tx_unit_bs_disable(parlio_tx_unit_handle_t tx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_disable(tx_unit->bs_handle), TAG, "disable bitscrambler failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_tx_unit_decorate_bitscrambler(parlio_tx_unit_handle_t tx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid TX unit");
|
||||
|
||||
// bitscrambler function is used under Parlio TX ISR context
|
||||
// if the cache is disabled, all functions called by ISR must be in IRAM
|
||||
#if CONFIG_PARLIO_TX_ISR_CACHE_SAFE && !CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM must be enabled");
|
||||
#endif
|
||||
|
||||
// check if already decorated
|
||||
if (tx_unit->bs_handle) {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit already decorated with BitScrambler");
|
||||
}
|
||||
|
||||
// create BitScrambler instance
|
||||
bitscrambler_config_t bs_config = {
|
||||
.dir = BITSCRAMBLER_DIR_TX,
|
||||
.attach_to = SOC_BITSCRAMBLER_ATTACH_PARL_IO,
|
||||
};
|
||||
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_INIT;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_new(&bs_config, &tx_unit->bs_handle), TAG, "create bitscrambler failed");
|
||||
tx_unit->bs_enable_fn = parlio_tx_unit_bs_enable;
|
||||
tx_unit->bs_disable_fn = parlio_tx_unit_bs_disable;
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_INIT);
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit is not in init state");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_tx_unit_undecorate_bitscrambler(parlio_tx_unit_handle_t tx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(tx_unit != NULL, ESP_ERR_INVALID_ARG, TAG, "invalid TX unit");
|
||||
|
||||
// check if already decorated
|
||||
if (!tx_unit->bs_handle) {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit not decorated with BitScrambler yet");
|
||||
}
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_INIT;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
bitscrambler_free(tx_unit->bs_handle);
|
||||
tx_unit->bs_handle = NULL;
|
||||
tx_unit->bs_enable_fn = NULL;
|
||||
tx_unit->bs_disable_fn = NULL;
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_INIT);
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit is not in init state");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
#include "rom/cache.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "driver/parlio_types.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "esp_pm.h"
|
||||
@ -122,12 +123,10 @@ typedef enum {
|
||||
} parlio_dir_t;
|
||||
|
||||
typedef enum {
|
||||
PARLIO_TX_FSM_INIT_WAIT,
|
||||
PARLIO_TX_FSM_INIT,
|
||||
PARLIO_TX_FSM_ENABLE_WAIT,
|
||||
PARLIO_TX_FSM_ENABLE,
|
||||
PARLIO_TX_FSM_RUN_WAIT,
|
||||
PARLIO_TX_FSM_RUN,
|
||||
PARLIO_TX_FSM_WAIT,
|
||||
} parlio_tx_fsm_t;
|
||||
|
||||
typedef struct parlio_unit_t *parlio_unit_base_handle_t;
|
||||
@ -150,6 +149,52 @@ struct parlio_unit_t {
|
||||
parlio_group_t *group; // group handle
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t idle_value; // Parallel IO bus idle value
|
||||
const void *payload; // payload to be transmitted
|
||||
size_t payload_bits; // payload size in bits
|
||||
int dma_link_idx; // index of DMA link list
|
||||
const void *bitscrambler_program; // bitscrambler program binary
|
||||
struct {
|
||||
uint32_t loop_transmission : 1; // whether the transmission is in loop mode
|
||||
} flags; // Extra configuration flags
|
||||
} parlio_tx_trans_desc_t;
|
||||
|
||||
// original function pointer type definition
|
||||
|
||||
typedef esp_err_t (*parlio_tx_bs_enable_fn_t)(parlio_tx_unit_handle_t tx_unit, parlio_tx_trans_desc_t *t);
|
||||
typedef esp_err_t (*parlio_tx_bs_disable_fn_t)(parlio_tx_unit_handle_t tx_unit);
|
||||
|
||||
typedef struct parlio_tx_unit_t {
|
||||
struct parlio_unit_t base; // base unit
|
||||
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
|
||||
gdma_link_list_handle_t dma_link[PARLIO_DMA_LINK_NUM]; // DMA link list handle
|
||||
size_t int_mem_align; // Alignment for internal memory
|
||||
size_t ext_mem_align; // Alignment for external memory
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[PARLIO_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
#endif
|
||||
portMUX_TYPE spinlock; // prevent resource accessing by user and interrupt concurrently
|
||||
uint32_t out_clk_freq_hz; // output clock frequency
|
||||
parlio_clock_source_t clk_src; // Parallel IO internal clock source
|
||||
size_t max_transfer_bits; // maximum transfer size in bits
|
||||
size_t queue_depth; // size of transaction queue
|
||||
size_t num_trans_inflight; // indicates the number of transactions that are undergoing but not recycled to ready_queue
|
||||
QueueHandle_t trans_queues[PARLIO_TX_QUEUE_MAX]; // transaction queues
|
||||
parlio_tx_trans_desc_t *cur_trans; // points to current transaction
|
||||
uint32_t idle_value_mask; // mask of idle value
|
||||
_Atomic parlio_tx_fsm_t fsm; // Driver FSM state
|
||||
parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done
|
||||
void *user_data; // user data passed to the callback function
|
||||
bitscrambler_handle_t bs_handle; // bitscrambler handle
|
||||
parlio_tx_bs_enable_fn_t bs_enable_fn; // bitscrambler enable function
|
||||
parlio_tx_bs_disable_fn_t bs_disable_fn; // bitscrambler disable function
|
||||
parlio_tx_trans_desc_t trans_desc_pool[]; // transaction descriptor pool
|
||||
} parlio_tx_unit_t;
|
||||
|
||||
/**
|
||||
* @brief Register the rx or tx unit to the parlio group
|
||||
*
|
||||
|
@ -10,43 +10,6 @@
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "parlio_priv.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t idle_value; // Parallel IO bus idle value
|
||||
const void *payload; // payload to be transmitted
|
||||
size_t payload_bits; // payload size in bits
|
||||
int dma_link_idx; // index of DMA link list
|
||||
struct {
|
||||
uint32_t loop_transmission : 1; // whether the transmission is in loop mode
|
||||
} flags; // Extra configuration flags
|
||||
} parlio_tx_trans_desc_t;
|
||||
|
||||
typedef struct parlio_tx_unit_t {
|
||||
struct parlio_unit_t base; // base unit
|
||||
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
|
||||
gdma_link_list_handle_t dma_link[PARLIO_DMA_LINK_NUM]; // DMA link list handle
|
||||
size_t int_mem_align; // Alignment for internal memory
|
||||
size_t ext_mem_align; // Alignment for external memory
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[PARLIO_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
#endif
|
||||
portMUX_TYPE spinlock; // prevent resource accessing by user and interrupt concurrently
|
||||
uint32_t out_clk_freq_hz; // output clock frequency
|
||||
parlio_clock_source_t clk_src; // Parallel IO internal clock source
|
||||
size_t max_transfer_bits; // maximum transfer size in bits
|
||||
size_t queue_depth; // size of transaction queue
|
||||
size_t num_trans_inflight; // indicates the number of transactions that are undergoing but not recycled to ready_queue
|
||||
QueueHandle_t trans_queues[PARLIO_TX_QUEUE_MAX]; // transaction queues
|
||||
parlio_tx_trans_desc_t *cur_trans; // points to current transaction
|
||||
uint32_t idle_value_mask; // mask of idle value
|
||||
_Atomic parlio_tx_fsm_t fsm; // Driver FSM state
|
||||
parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done
|
||||
void *user_data; // user data passed to the callback function
|
||||
parlio_tx_trans_desc_t trans_desc_pool[]; // transaction descriptor pool
|
||||
} parlio_tx_unit_t;
|
||||
|
||||
static void parlio_tx_default_isr(void *args);
|
||||
|
||||
static esp_err_t parlio_tx_create_trans_queue(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
|
||||
@ -82,6 +45,9 @@ exit:
|
||||
|
||||
static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
|
||||
{
|
||||
if (tx_unit->bs_handle) {
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "please call parlio_tx_unit_undecorate_bitscrambler() before delete the tx unit");
|
||||
}
|
||||
if (tx_unit->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(tx_unit->intr), TAG, "delete interrupt service failed");
|
||||
}
|
||||
@ -526,6 +492,11 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
|
||||
parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
|
||||
|
||||
if (tx_unit->bs_handle) {
|
||||
// load the bitscrambler program and start it
|
||||
tx_unit->bs_enable_fn(tx_unit, t);
|
||||
}
|
||||
|
||||
gdma_start(tx_unit->dma_chan, gdma_link_get_head_addr(tx_unit->dma_link[t->dma_link_idx]));
|
||||
// wait until the data goes from the DMA to TX unit's FIFO
|
||||
while (parlio_ll_tx_is_ready(hal->regs) == false);
|
||||
@ -541,7 +512,7 @@ esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit)
|
||||
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
|
||||
ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_INIT;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_ENABLE_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
// acquire power management lock
|
||||
if (tx_unit->pm_lock) {
|
||||
esp_pm_lock_acquire(tx_unit->pm_lock);
|
||||
@ -560,7 +531,7 @@ esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit)
|
||||
// check if we need to start one pending transaction
|
||||
parlio_tx_trans_desc_t *t = NULL;
|
||||
expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
// check if we need to start one transaction
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||
// sanity check
|
||||
@ -581,11 +552,11 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t tx_unit)
|
||||
bool valid_state = false;
|
||||
// check the supported states, and switch to intermediate state: INIT_WAIT
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_INIT_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
valid_state = true;
|
||||
}
|
||||
expected_fsm = PARLIO_TX_FSM_RUN;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_INIT_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
valid_state = true;
|
||||
assert(tx_unit->cur_trans);
|
||||
// recycle the interrupted transaction
|
||||
@ -693,6 +664,12 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
t->idle_value = config->idle_value & tx_unit->idle_value_mask;
|
||||
t->flags.loop_transmission = config->flags.loop_transmission;
|
||||
|
||||
if (tx_unit->bs_handle) {
|
||||
t->bitscrambler_program = config->bitscrambler_program;
|
||||
} else if (config->bitscrambler_program) {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit is not decorated with bitscrambler");
|
||||
}
|
||||
|
||||
// send the transaction descriptor to progress queue
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "failed to send transaction descriptor to progress queue");
|
||||
@ -700,7 +677,7 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
|
||||
// check if we need to start one pending transaction
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
// check if we need to start one transaction
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN);
|
||||
@ -733,6 +710,10 @@ static void parlio_tx_default_isr(void *args)
|
||||
parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_EOF);
|
||||
parlio_ll_tx_start(hal->regs, false);
|
||||
|
||||
if (tx_unit->bs_handle && tx_unit->cur_trans->bitscrambler_program) {
|
||||
tx_unit->bs_disable_fn(tx_unit);
|
||||
}
|
||||
|
||||
// invoke callback before sending the transaction to complete queue
|
||||
parlio_tx_done_callback_t done_cb = tx_unit->on_trans_done;
|
||||
if (done_cb) {
|
||||
@ -743,7 +724,7 @@ static void parlio_tx_default_isr(void *args)
|
||||
|
||||
parlio_tx_trans_desc_t *trans_desc = NULL;
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_RUN;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_ENABLE_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
trans_desc = tx_unit->cur_trans;
|
||||
// move current finished transaction to the complete queue
|
||||
xQueueSendFromISR(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &trans_desc, &high_task_woken);
|
||||
@ -756,7 +737,7 @@ static void parlio_tx_default_isr(void *args)
|
||||
|
||||
// if the tx unit is till in enable state (i.e. not disabled by user), let's try start the next pending transaction
|
||||
expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
if (xQueueReceiveFromISR(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &trans_desc, &high_task_woken) == pdTRUE) {
|
||||
// sanity check
|
||||
assert(trans_desc);
|
||||
|
@ -6,9 +6,19 @@ if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE)
|
||||
list(APPEND srcs "test_parlio_sleep.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
list(APPEND srcs "test_parlio_bitscrambler.c")
|
||||
endif()
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES unity esp_driver_parlio esp_driver_gpio
|
||||
esp_driver_i2s esp_driver_spi esp_psram
|
||||
esp_driver_bitscrambler
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
target_bitscrambler_add_src("test_parlio_tx_LSB_to_MSB.bsasm")
|
||||
target_bitscrambler_add_src("test_parlio_tx_multiply.bsasm")
|
||||
endif()
|
||||
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "driver/parlio_rx.h"
|
||||
#include "driver/parlio_bitscrambler.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/parlio_ll.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_attr.h"
|
||||
#include "test_board.h"
|
||||
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_test_tx_LSB_to_MSB, "test_parlio_tx_LSB_to_MSB");
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_test_tx_multiply, "test_parlio_tx_multiply");
|
||||
|
||||
TEST_PARLIO_CALLBACK_ATTR
|
||||
static bool test_parlio_rx_done_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
TaskHandle_t task = (TaskHandle_t)user_ctx;
|
||||
vTaskNotifyGiveFromISR(task, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
#define TEST_PAYLOAD_SIZE 64
|
||||
static void test_parlio_bitscrambler(void)
|
||||
{
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
parlio_tx_unit_config_t tx_config = {
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT,
|
||||
.data_width = 4,
|
||||
.clk_in_gpio_num = -1, // use internal clock source
|
||||
.valid_gpio_num = TEST_VALID_GPIO,
|
||||
.clk_out_gpio_num = TEST_CLK_GPIO,
|
||||
.data_gpio_nums = {
|
||||
TEST_DATA0_GPIO,
|
||||
TEST_DATA1_GPIO,
|
||||
TEST_DATA2_GPIO,
|
||||
TEST_DATA3_GPIO,
|
||||
},
|
||||
.output_clk_freq_hz = 1 * 1000 * 1000,
|
||||
.trans_queue_depth = 8,
|
||||
.max_transfer_size = 128,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
};
|
||||
|
||||
parlio_rx_unit_handle_t rx_unit = NULL;
|
||||
parlio_rx_unit_config_t rx_config = {
|
||||
.trans_queue_depth = 10,
|
||||
.max_recv_size = 1024,
|
||||
.data_width = 4,
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT,
|
||||
.ext_clk_freq_hz = 0,
|
||||
.clk_in_gpio_num = -1,
|
||||
.exp_clk_freq_hz = 1 * 1000 * 1000,
|
||||
.clk_out_gpio_num = -1,
|
||||
.valid_gpio_num = TEST_VALID_GPIO,
|
||||
.data_gpio_nums = {
|
||||
TEST_DATA0_GPIO,
|
||||
TEST_DATA1_GPIO,
|
||||
TEST_DATA2_GPIO,
|
||||
TEST_DATA3_GPIO,
|
||||
},
|
||||
.flags = {
|
||||
.clk_gate_en = false,
|
||||
}
|
||||
};
|
||||
|
||||
printf("install parlio unit\r\n");
|
||||
TEST_ESP_OK(parlio_new_tx_unit(&tx_config, &tx_unit));
|
||||
TEST_ESP_OK(parlio_new_rx_unit(&rx_config, &rx_unit));
|
||||
|
||||
printf("decorate tx unit with bitscrambler\r\n");
|
||||
TEST_ESP_OK(parlio_tx_unit_decorate_bitscrambler(tx_unit));
|
||||
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00,
|
||||
.bitscrambler_program = bitscrambler_program_test_tx_LSB_to_MSB,
|
||||
};
|
||||
uint8_t tx_payload[TEST_PAYLOAD_SIZE] = {0};
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
tx_payload[i] = i;
|
||||
}
|
||||
|
||||
parlio_rx_delimiter_handle_t deli = NULL;
|
||||
parlio_rx_level_delimiter_config_t lvl_deli_cfg = {
|
||||
.valid_sig_line_id = PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, // data in LSB mode is adjusted by bitscrambler, so we use MSB mode for receiving
|
||||
.eof_data_len = TEST_PAYLOAD_SIZE,
|
||||
.timeout_ticks = 0,
|
||||
.flags = {
|
||||
.active_low_en = 0,
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli));
|
||||
printf("register receive_done event callback\r\n");
|
||||
parlio_rx_event_callbacks_t rx_cbs = {
|
||||
.on_receive_done = test_parlio_rx_done_callback,
|
||||
};
|
||||
TEST_ESP_OK(parlio_rx_unit_register_event_callbacks(rx_unit, &rx_cbs, xTaskGetCurrentTaskHandle()));
|
||||
|
||||
parlio_receive_config_t recv_config = {
|
||||
.delimiter = deli,
|
||||
.flags.partial_rx_en = false,
|
||||
};
|
||||
__attribute__((aligned(TEST_PAYLOAD_SIZE))) uint8_t rx_payload[TEST_PAYLOAD_SIZE] = {0};
|
||||
|
||||
// Tx in LSB mode
|
||||
// Tx Bitscrambler turn LSB to MSB
|
||||
// Rx in MSB mode
|
||||
printf("enable parlio and transmit\r\n");
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, 1));
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, rx_payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, tx_payload, TEST_PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
TEST_ASSERT_EQUAL(tx_payload[i], rx_payload[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
|
||||
// we don't use bitscrambler to adjust the bit order in the following tests, so we keep LSB order to receive the original data
|
||||
lvl_deli_cfg.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB;
|
||||
TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli));
|
||||
|
||||
// not use bitscrambler
|
||||
transmit_config.bitscrambler_program = NULL;
|
||||
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, rx_payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, tx_payload, TEST_PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
TEST_ASSERT_EQUAL(tx_payload[i], rx_payload[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// use another bitscrambler program
|
||||
// Tx Bitscrambler multiply the tx payload by 2
|
||||
transmit_config.bitscrambler_program = bitscrambler_program_test_tx_multiply;
|
||||
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, rx_payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, tx_payload, TEST_PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
TEST_ASSERT_EQUAL(tx_payload[i] * 2, rx_payload[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, parlio_del_tx_unit(tx_unit));
|
||||
TEST_ESP_OK(parlio_tx_unit_undecorate_bitscrambler(tx_unit));
|
||||
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
|
||||
TEST_ESP_OK(parlio_rx_unit_disable(rx_unit));
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
|
||||
TEST_ESP_OK(parlio_del_rx_unit(rx_unit));
|
||||
}
|
||||
|
||||
TEST_CASE("parlio_tx_bitscrambler_test", "[parlio_bitscrambler]")
|
||||
{
|
||||
test_parlio_bitscrambler();
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
|
||||
cfg prefetch true # enable data prefetch
|
||||
cfg eof_on upstream # set EOF on upstream
|
||||
cfg trailing_bytes 9 # due to prefetch is enable, upstream is 8 bytes ahead of downstream
|
||||
|
||||
loop:
|
||||
set 0..3 4..7,
|
||||
set 4..7 0..3,
|
||||
write 8,
|
||||
read 8,
|
||||
jmp loop
|
@ -0,0 +1,14 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
|
||||
cfg prefetch true # enable data prefetch
|
||||
cfg eof_on upstream # set EOF on upstream
|
||||
cfg trailing_bytes 9 # due to prefetch is enable, upstream is 8 bytes ahead of downstream
|
||||
|
||||
loop:
|
||||
set 1..7 0..6,
|
||||
set 0 L,
|
||||
write 8,
|
||||
read 8,
|
||||
jmp loop
|
@ -2,6 +2,7 @@ CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||
CONFIG_PARLIO_TX_ISR_CACHE_SAFE=y
|
||||
CONFIG_PARLIO_RX_ISR_CACHE_SAFE=y
|
||||
CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM=y
|
||||
# place non-ISR FreeRTOS functions in Flash
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
|
@ -106,6 +106,7 @@ static size_t rmt_encode_bs(rmt_encoder_t *encoder, rmt_channel_handle_t channel
|
||||
static esp_err_t rmt_del_bs_encoder(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_disable(bs_encoder->bs), TAG, "disable bitscrambler failed");
|
||||
bitscrambler_free(bs_encoder->bs);
|
||||
free(bs_encoder);
|
||||
return ESP_OK;
|
||||
@ -129,7 +130,7 @@ esp_err_t rmt_new_bitscrambler_encoder(const rmt_bs_encoder_config_t *config, rm
|
||||
.attach_to = SOC_BITSCRAMBLER_ATTACH_RMT,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(bitscrambler_new(&bs_config, &encoder->bs), err, TAG, "create bitscrambler failed");
|
||||
|
||||
ESP_GOTO_ON_ERROR(bitscrambler_enable(encoder->bs), err, TAG, "enable bitscrambler failed");
|
||||
// load the bitscrambler program
|
||||
ESP_GOTO_ON_ERROR(bitscrambler_load_program(encoder->bs, config->program_bin), err, TAG, "load bitscrambler program failed");
|
||||
|
||||
|
@ -129,6 +129,7 @@ The following are the configuration parameters of the :cpp:type:`parlio_transmit
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`parlio_transmit_config_t::idle_value` Sets the value on the data lines when the TX unit is idle after transmission. This value will remain even after calling :cpp:func:`parlio_tx_unit_disable` to disable the TX unit.
|
||||
:SOC_BITSCRAMBLER_SUPPORTED: - :cpp:member:`parlio_transmit_config_t::bitscrambler_program` The pointer to the bitscrambler program binary file. Set to ``NULL`` if the bitscrambler is not used in this transmission.
|
||||
- :cpp:member:`parlio_transmit_config_t::flags` Usually used to fine-tune some behaviors of the transmission, including the following options
|
||||
- :cpp:member:`parlio_transmit_config_t::flags::queue_nonblocking` Sets whether the function needs to wait when the transmission queue is full. If this value is set to ``true``, the function will immediately return the error code :c:macro:`ESP_ERR_INVALID_STATE` when the queue is full. Otherwise, the function will block the current thread until there is space in the transmission queue.
|
||||
:SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION: - :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` Setting this to ``true`` enables infinite loop transmission. In this case, the transmission will not stop unless manually calling :cpp:func:`parlio_tx_unit_disable`, and no "trans_done" event will be generated. Since the loop is controlled by DMA, the TX unit can generate periodic sequences with minimal CPU intervention.
|
||||
@ -308,6 +309,17 @@ The waveform of the external clock input is shown below:
|
||||
|
||||
If you need to modify the transmission payload after enabling infinite loop transmission, you can configure :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` and call :cpp:func:`parlio_tx_unit_transmit` again with a new payload buffer. The driver will switch to the new buffer after the old buffer is completely transmitted. Therefore, users need to maintain two buffers to avoid data inconsistency caused by premature modification or recycling of the old buffer.
|
||||
|
||||
.. only:: SOC_BITSCRAMBLER_SUPPORTED
|
||||
|
||||
Custom Bitstream Generation with BitScrambler
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
We can use the :doc:`BitScrambler </api-reference/peripherals/bitscrambler>` assembly code to control the data on the DMA path, thereby implementing some simple encoding work. Compared to using the CPU for encoding, the BitScrambler has higher performance and does not consume CPU resources, but is limited by the limited instruction memory of the BitScrambler, so it cannot implement complex encoding work.
|
||||
|
||||
After writing the BitScrambler program, we can enable it by calling :cpp:func:`parlio_tx_unit_decorate_bitscrambler`. And configure the :cpp:member:`parlio_transmit_config_t::bitscrambler_program` to point to the binary file of the BitScrambler program. Different transmission transactions can use different BitScrambler programs. The binary file must conform to the BitScrambler assembly language specification, and will be loaded into the BitScrambler's instruction memory at runtime. For details on how to write and compile the BitScrambler program, please refer to :doc:`BitScrambler Programming Guide </api-reference/peripherals/bitscrambler>`.
|
||||
|
||||
:cpp:func:`parlio_tx_unit_decorate_bitscrambler` and :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` need to be used in pairs. When deleting the TX unit, you need to call :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` first to remove the BitScrambler.
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -129,6 +129,7 @@ TX 单元以比特为单位进行传输,且传输的比特长度必须配置
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`parlio_transmit_config_t::idle_value` 设置 TX 单元发送完毕后空闲状态时数据线上的值。该值在调用 :cpp:func:`parlio_tx_unit_disable` 禁用 TX 单元后依然会保持。
|
||||
:SOC_BITSCRAMBLER_SUPPORTED: - :cpp:member:`parlio_transmit_config_t::bitscrambler_program` 指向比特调节器程序的二进制文件的指针。若此次传输不使用比特调节器,则设置为 ``NULL``。
|
||||
- :cpp:member:`parlio_transmit_config_t::flags` 通常用来微调传输的一些行为,包括以下选项
|
||||
- :cpp:member:`parlio_transmit_config_t::flags::queue_nonblocking` 设置当传输队列满的时候该函数是否需要等待。如果该值设置为 ``true`` 那么当遇到队列满的时候,该函数会立即返回错误代码 :c:macro:`ESP_ERR_INVALID_STATE`。否则,函数会阻塞当前线程,直到传输队列有空档。
|
||||
:SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION: - :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` 设置为 ``true``,会启用无限循环发送机制。此时,除非手动调用 :cpp:func:`parlio_tx_unit_disable`,否则发送不会停止,也不会生成“完成发送”事件。由于循环由 DMA 控制, TX 单元可以在几乎不需要 CPU 干预的情况下,生成周期性序列。
|
||||
@ -308,6 +309,17 @@ TX 单元可以选择各种不同的时钟源,其中外部时钟源较为特
|
||||
|
||||
如果启用无限循环发送后需要修改发送内容,可以配置 :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` 并再次调用 :cpp:func:`parlio_tx_unit_transmit` 传入新的 payload buffer,驱动会在旧 buffer 完整发送后,切换到新传入的 buffer。因此需要用户自行维护好两块buffer,避免旧 buffer 被提早修改或者回收导致产生数据不连贯的现象。
|
||||
|
||||
.. only:: SOC_BITSCRAMBLER_SUPPORTED
|
||||
|
||||
配合比特调节器 (BitScrambler) 产生自定义的比特流
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
我们可以通过编写 :doc:`比特调节器 </api-reference/peripherals/bitscrambler>` 汇编代码来控制 DMA 通路上的数据,进而实现一些简单的编码工作。相较于使用 CPU 做编码工作,比特调节器的性能更高,且不会占用 CPU 资源,但是受限于 BitScrambler 有限的指令存储器空间,它无法实现复杂的编码工作。
|
||||
|
||||
编写好比特调节器程序后,通过调用 :cpp:func:`parlio_tx_unit_decorate_bitscrambler` 启用比特调节器。并在 :cpp:member:`parlio_transmit_config_t::bitscrambler_program` 配置本次传输使用比特调节器程序的二进制文件。不同的传输事务可以使用不同的比特调节器程序。该二进制文件必须符合比特调节器的汇编语言规范,并且在运行时会被加载到比特调节器的指令存储器中。如何编写并编译比特调节器程序请参考 :doc:`比特调节器编程指南 </api-reference/peripherals/bitscrambler>`。
|
||||
|
||||
:cpp:func:`parlio_tx_unit_decorate_bitscrambler` 和 :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 需要成对使用。在删除 TX 单元时,需要先调用 :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 移除比特调节器。
|
||||
|
||||
电源管理
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
|
Reference in New Issue
Block a user