forked from espressif/esp-idf
Merge branch 'feat/uart_dma_support' into 'master'
feat(uhci): Add uhci(uart-dma) support on ESP32C3, ESP32P4, ESP32C6, ESP32S3🚴🏼♂️ See merge request espressif/esp-idf!37494
This commit is contained in:
@@ -119,7 +119,7 @@ STAILQ_HEAD(g_hci_rxinfo_list, hci_message);
|
||||
|
||||
DRAM_ATTR struct g_hci_rxinfo_list g_hci_rxinfo_head;
|
||||
static DRAM_ATTR struct uart_env_tag uart_env;
|
||||
static volatile uhci_dev_t *s_uhci_hw = &UHCI0;
|
||||
static uhci_dev_t *s_uhci_hw = &UHCI0;
|
||||
static DRAM_ATTR gdma_channel_handle_t s_rx_channel;
|
||||
static DRAM_ATTR gdma_channel_handle_t s_tx_channel;
|
||||
|
||||
@@ -470,8 +470,8 @@ static void hci_driver_uart_dma_install(void)
|
||||
gdma_register_tx_event_callbacks(s_tx_channel, &tx_cbs, NULL);
|
||||
// configure UHCI
|
||||
uhci_ll_init((uhci_dev_t *)s_uhci_hw);
|
||||
// uhci_ll_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_LEN_EOF);
|
||||
uhci_ll_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_IDLE_EOF);
|
||||
// uhci_ll_rx_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_LEN_EOF);
|
||||
uhci_ll_rx_set_eof_mode((uhci_dev_t *)s_uhci_hw, UHCI_RX_IDLE_EOF);
|
||||
// disable software flow control
|
||||
s_uhci_hw->escape_conf.val = 0;
|
||||
uhci_ll_attach_uart_port((uhci_dev_t *)s_uhci_hw, s_hci_driver_uart_dma_env.hci_uart_params->hci_uart_port);
|
||||
|
@@ -4,12 +4,16 @@ set(srcs)
|
||||
set(public_include "include")
|
||||
if(CONFIG_SOC_UART_SUPPORTED)
|
||||
list(APPEND srcs "src/uart.c" "src/uart_wakeup.c")
|
||||
if(CONFIG_SOC_UHCI_SUPPORTED)
|
||||
list(APPEND srcs "src/uhci.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
set(priv_requires esp_ringbuf)
|
||||
else()
|
||||
set(priv_requires esp_pm esp_driver_gpio esp_ringbuf)
|
||||
set(priv_requires esp_pm esp_driver_gpio esp_ringbuf esp_mm)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
|
@@ -10,3 +10,31 @@ menu "ESP-Driver:UART Configurations"
|
||||
may cause data lost when doing spi flash operation.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "ESP-Driver:UHCI Configurations"
|
||||
|
||||
config UHCI_ISR_HANDLER_IN_IRAM
|
||||
bool "Place UHCI ISR function into IRAM"
|
||||
default n
|
||||
help
|
||||
If this option is not selected, UHCI interrupt will be disabled for a long time and
|
||||
may cause data lost when doing spi flash operation.
|
||||
|
||||
config UHCI_ISR_CACHE_SAFE
|
||||
bool "Allow UHCI ISR to execute when cache is disabled" if !SPI_FLASH_AUTO_SUSPEND
|
||||
select UHCI_ISR_HANDLER_IN_IRAM
|
||||
select GDMA_ISR_HANDLER_IN_IRAM if SOC_GDMA_SUPPORTED
|
||||
default n
|
||||
help
|
||||
Enable this option to allow the ISR for UHCI to execute even when the cache is disabled.
|
||||
This can be useful in scenarios where the cache might be turned off, but the UHCI
|
||||
functionality is still required to operate correctly.
|
||||
|
||||
config UHCI_ENABLE_DEBUG_LOG
|
||||
bool "Enable debug log"
|
||||
default n
|
||||
help
|
||||
whether to enable the debug log message for UHCI driver.
|
||||
Note that, this option only controls the UHCI driver log, won't affect other drivers.
|
||||
|
||||
endmenu
|
||||
|
161
components/esp_driver_uart/include/driver/uhci.h
Normal file
161
components/esp_driver_uart/include/driver/uhci.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/uhci_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UHCI controller specific configurations
|
||||
*/
|
||||
typedef struct {
|
||||
uart_port_t uart_port; /*!< UART port that connect to UHCI controller */
|
||||
size_t tx_trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background */
|
||||
size_t max_transmit_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
|
||||
size_t max_receive_internal_mem; /*!< Maximum transfer size in one transaction, in bytes. Each DMA node can point to a maximum of 4096 bytes. This value determines the number of DMA nodes used for each transaction. When your transfer size is large enough, it is recommended to set this value greater than 4096 to facilitate efficient ping-pong operations, such as 10 * 1024. */
|
||||
size_t dma_burst_size; /*!< DMA burst size, in bytes */
|
||||
|
||||
struct {
|
||||
uint16_t rx_brk_eof: 1; /*!< UHCI will end payload receive process when NULL frame is received by UART. */
|
||||
uint16_t idle_eof: 1; /*!< UHCI will end payload receive process when UART has been in idle state. */
|
||||
uint16_t length_eof: 1; /*!< UHCI will end payload receive process when the receiving byte count has reached the specific value. */
|
||||
} rx_eof_flags; /*!< UHCI eof flags */
|
||||
} uhci_controller_config_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for defining callback functions for UHCI events.
|
||||
*/
|
||||
typedef struct {
|
||||
uhci_rx_event_callback_t on_rx_trans_event; /*!< Callback function for handling the completion of a reception. */
|
||||
uhci_tx_done_callback_t on_tx_trans_done; /*!< Callback function for handling the completion of a transmission. */
|
||||
} uhci_event_callbacks_t;
|
||||
|
||||
/**
|
||||
* @brief Create and initialize a new UHCI controller.
|
||||
*
|
||||
* This function initializes a new UHCI controller instance based on the provided configuration.
|
||||
* It allocates and configures resources required for the UHCI controller, such as DMA and
|
||||
* communication settings. The created controller handle is returned through the output parameter.
|
||||
*
|
||||
* @param[in] config Pointer to a `uhci_controller_config_t` structure containing the
|
||||
* configuration parameters for the UHCI controller.
|
||||
* @param[out] ret_uhci_ctrl Pointer to a variable where the handle to the newly created UHCI controller
|
||||
* will be stored. This handle is used in subsequent operations involving the
|
||||
* controller.
|
||||
*
|
||||
* @return
|
||||
* - `ESP_OK`: Controller successfully created and initialized.
|
||||
* - `ESP_ERR_INVALID_ARG`: One or more arguments are invalid (e.g., null pointers or invalid config).
|
||||
* - `ESP_ERR_NO_MEM`: Memory allocation for the controller failed.
|
||||
* - Other error codes: Indicate failure in the underlying hardware or driver initialization.
|
||||
*/
|
||||
esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_controller_handle_t *ret_uhci_ctrl);
|
||||
|
||||
/**
|
||||
* @brief Receive data from the UHCI controller.
|
||||
*
|
||||
* This function retrieves data from the UHCI controller into the provided buffer. It is typically
|
||||
* used for receiving data that was transmitted via UART and processed by the UHCI DMA controller.
|
||||
*
|
||||
* @param[in] uhci_ctrl Handle to the UHCI controller, which was previously created using
|
||||
* `uhci_new_controller()`.
|
||||
* @param[out] read_buffer Pointer to the buffer where the received data will be stored.
|
||||
* The buffer must be pre-allocated by the caller.
|
||||
* @param[in] buffer_size The size of read buffer.
|
||||
*
|
||||
* @note @note The function is non-blocking, it just mounts the user buffer to the DMA.
|
||||
* The return from the function doesn't mean a finished receive. You need to register corresponding
|
||||
* callback function to get notification.
|
||||
*
|
||||
* @return
|
||||
* - `ESP_OK`: Data successfully received and written to the buffer.
|
||||
* - `ESP_ERR_INVALID_ARG`: Invalid arguments (e.g., null buffer or invalid controller handle).
|
||||
*/
|
||||
esp_err_t uhci_receive(uhci_controller_handle_t uhci_ctrl, uint8_t *read_buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Transmit data using the UHCI controller.
|
||||
*
|
||||
* This function sends data from the provided buffer through the UHCI controller. It uses the DMA
|
||||
* capabilities of UHCI to efficiently handle data transmission via UART.
|
||||
*
|
||||
* @param[in] uhci_ctrl Handle to the UHCI controller, which was previously created using
|
||||
* `uhci_new_controller()`.
|
||||
* @param[in] write_buffer Pointer to the buffer containing the data to be transmitted.
|
||||
* The buffer must remain valid until the transmission is complete.
|
||||
* @param[in] write_size The number of bytes to transmit from the buffer.
|
||||
*
|
||||
* @note The function is an non-blocking api, which means this function will return immediately. You can
|
||||
* get corresponding event from callbacks.
|
||||
*
|
||||
* @return
|
||||
* - `ESP_OK`: Data successfully queued for transmission.
|
||||
* - `ESP_ERR_INVALID_ARG`: Invalid arguments (e.g., null buffer, invalid handle, or zero `write_size`).
|
||||
*/
|
||||
esp_err_t uhci_transmit(uhci_controller_handle_t uhci_ctrl, uint8_t *write_buffer, size_t write_size);
|
||||
|
||||
/**
|
||||
* @brief Uninstall the UHCI (UART Host Controller Interface) driver and release resources.
|
||||
*
|
||||
* This function deinitializes the UHCI controller and frees any resources allocated during its
|
||||
* initialization. It ensures proper cleanup and prevents resource leaks when the UHCI controller
|
||||
* is no longer needed.
|
||||
*
|
||||
* @param[in] uhci_ctrl Handle to the UHCI controller, which was previously created using
|
||||
* `uhci_new_controller()`. Passing an invalid or uninitialized handle
|
||||
* may result in undefined behavior.
|
||||
*
|
||||
* @return
|
||||
* - `ESP_OK`: The UHCI driver was successfully uninstalled, and resources were released.
|
||||
* - `ESP_ERR_INVALID_ARG`: The provided `uhci_ctrl` handle is invalid or null.
|
||||
*/
|
||||
esp_err_t uhci_del_controller(uhci_controller_handle_t uhci_ctrl);
|
||||
|
||||
/**
|
||||
* @brief Register event callback functions for a UHCI controller.
|
||||
*
|
||||
* This function allows the user to register callback functions to handle specific UHCI events, such as
|
||||
* transmission or reception completion. The callbacks provide a mechanism to handle asynchronous events
|
||||
* generated by the UHCI controller.
|
||||
*
|
||||
* @param[in] uhci_ctrl Handle to the UHCI controller, which was previously created using
|
||||
* `uhci_new_controller()`.
|
||||
* @param[in] cbs Pointer to a `uhci_event_callbacks_t` structure that defines the callback
|
||||
* functions to be registered. This structure includes pointers to the callback
|
||||
* functions for handling UHCI events.
|
||||
* @param[in] user_data Pointer to user-defined data that will be passed to the callback functions
|
||||
* when they are invoked. This can be used to provide context or state information
|
||||
* specific to the application.
|
||||
*
|
||||
* @return
|
||||
* - `ESP_OK`: Event callbacks were successfully registered.
|
||||
* - `ESP_ERR_INVALID_ARG`: Invalid arguments (e.g., null `uhci_ctrl` handle or `cbs` pointer).
|
||||
*/
|
||||
esp_err_t uhci_register_event_callbacks(uhci_controller_handle_t uhci_ctrl, const uhci_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Wait for all pending TX transactions done
|
||||
*
|
||||
* @param[in] uhci_ctrl UHCI controller that created by `uhci_new_controller`
|
||||
* @param[in] timeout_ms Timeout in milliseconds, `-1` means to wait forever
|
||||
* @return
|
||||
* - ESP_OK: All pending TX transactions is finished and recycled
|
||||
* - ESP_ERR_INVALID_ARG: Wait for all pending TX transactions done failed because of invalid argument
|
||||
* - ESP_ERR_TIMEOUT: Wait for all pending TX transactions done timeout
|
||||
* - ESP_FAIL: Wait for all pending TX transactions done failed because of other error
|
||||
*/
|
||||
esp_err_t uhci_wait_all_tx_transaction_done(uhci_controller_handle_t uhci_ctrl, int timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
73
components/esp_driver_uart/include/driver/uhci_types.h
Normal file
73
components/esp_driver_uart/include/driver/uhci_types.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include "hal/uart_types.h"
|
||||
#include "hal/uhci_types.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UHCI Controller Handle Type
|
||||
*/
|
||||
typedef struct uhci_controller_t *uhci_controller_handle_t;
|
||||
|
||||
/**
|
||||
* @brief UHCI TX Done Event Data
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *buffer; /**< Pointer to the which data buffer has been finished the transaction */
|
||||
size_t sent_size; /**< Size has been sent out */
|
||||
} uhci_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief UHCI TX Done Callback Function Type
|
||||
* @param uhci_ctrl Handle to the UHCI controller that initiated the transmission.
|
||||
* @param edata Pointer to a structure containing event data related to the completed transmission.
|
||||
* This structure provides details such as the number of bytes transmitted and any
|
||||
* status information relevant to the operation.
|
||||
* @param user_ctx User-defined context passed during the callback registration.
|
||||
* It can be used to maintain application-specific state or data.
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*uhci_tx_done_callback_t)(uhci_controller_handle_t uhci_ctrl, const uhci_tx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief UHCI RX Done Event Data Structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *data; /*!< Pointer to the received data buffer */
|
||||
size_t recv_size; /*!< Number of bytes received */
|
||||
struct {
|
||||
uint32_t totally_received: 1; /*!< When callback is invoked, while this bit is not set, means the current event gives partial of whole data, the transaction has not been finished. If set, means the current event gives whole data, the transaction finished. */
|
||||
} flags; /*!< I2C master config flags */
|
||||
} uhci_rx_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief UHCI RX Done Callback Function Type
|
||||
* @param uhci_ctrl Handle to the UHCI controller that initiated the transmission.
|
||||
* @param edata Pointer to a structure containing event data related to receive event.
|
||||
* This structure provides details such as the number of bytes received and any
|
||||
* status information relevant to the operation.
|
||||
* @param user_ctx User-defined context passed during the callback registration.
|
||||
* It can be used to maintain application-specific state or data.
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*uhci_rx_event_callback_t)(uhci_controller_handle_t uhci_ctrl, const uhci_rx_event_data_t *edata, void *user_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -9,3 +9,20 @@ archive: libhal.a
|
||||
entries:
|
||||
if UART_ISR_IN_IRAM = y:
|
||||
uart_hal_iram (noflash)
|
||||
|
||||
[mapping:uhci]
|
||||
archive: libesp_driver_uart.a
|
||||
entries:
|
||||
if UHCI_ISR_HANDLER_IN_IRAM = y:
|
||||
uhci: uhci_gdma_rx_callback_done (noflash)
|
||||
uhci: uhci_gdma_tx_callback_eof (noflash)
|
||||
uhci: uhci_do_transmit (noflash)
|
||||
|
||||
[mapping:uhci_driver_gdma]
|
||||
archive: libesp_hw_support.a
|
||||
entries:
|
||||
if UHCI_ISR_HANDLER_IN_IRAM = y:
|
||||
gdma_link: gdma_link_count_buffer_size_till_eof (noflash)
|
||||
gdma_link: gdma_link_mount_buffers (noflash)
|
||||
gdma_link: gdma_link_get_head_addr (noflash)
|
||||
gdma: gdma_start (noflash)
|
||||
|
574
components/esp_driver_uart/src/uhci.c
Normal file
574
components/esp_driver_uart/src/uhci.c
Normal file
@@ -0,0 +1,574 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_intr_alloc.h"
|
||||
#if CONFIG_UHCI_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 "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/uart.h"
|
||||
#include "driver/uhci.h"
|
||||
#include "driver/uhci_types.h"
|
||||
#include "soc/uhci_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/uhci_hal.h"
|
||||
#include "hal/uhci_ll.h"
|
||||
#include "hal/dma_types.h"
|
||||
#include "hal/cache_hal.h"
|
||||
#include "hal/cache_ll.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/gdma.h"
|
||||
#include "esp_private/esp_dma_utils.h"
|
||||
#include "esp_private/gdma_link.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "uhci_private.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_cache.h"
|
||||
|
||||
static const char* TAG = "uhci";
|
||||
|
||||
typedef struct uhci_platform_t {
|
||||
_lock_t mutex; // platform level mutex lock.
|
||||
uhci_controller_handle_t controller[SOC_UHCI_NUM]; // array of UHCI instances.
|
||||
} uhci_platform_t;
|
||||
|
||||
static uhci_platform_t s_uhci_platform = {}; // singleton platform
|
||||
|
||||
static bool uhci_ctrl_occupied(int port_num)
|
||||
{
|
||||
return s_uhci_platform.controller[port_num] != NULL;
|
||||
}
|
||||
|
||||
static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transaction_desc_t *trans);
|
||||
|
||||
static bool uhci_gdma_tx_callback_eof(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
BaseType_t do_yield = pdFALSE;
|
||||
uhci_controller_handle_t uhci_ctrl = (uhci_controller_handle_t) user_data;
|
||||
uhci_transaction_desc_t *trans_desc = NULL;
|
||||
bool need_yield = false;
|
||||
|
||||
uhci_tx_fsm_t expected_fsm = UHCI_TX_FSM_RUN;
|
||||
if (atomic_compare_exchange_strong(&uhci_ctrl->tx_dir.tx_fsm, &expected_fsm, UHCI_TX_FSM_ENABLE_WAIT)) {
|
||||
trans_desc = uhci_ctrl->tx_dir.cur_trans;
|
||||
xQueueSendFromISR(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_COMPLETE], &trans_desc, &do_yield);
|
||||
if (do_yield) {
|
||||
need_yield = true;
|
||||
}
|
||||
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
|
||||
}
|
||||
|
||||
if (uhci_ctrl->pm_lock) {
|
||||
esp_pm_lock_release(uhci_ctrl->pm_lock);
|
||||
}
|
||||
|
||||
if (uhci_ctrl->tx_dir.on_tx_trans_done) {
|
||||
uhci_tx_done_event_data_t evt_data = {
|
||||
.buffer = uhci_ctrl->tx_dir.cur_trans->buffer,
|
||||
.sent_size = uhci_ctrl->tx_dir.cur_trans->buffer_size,
|
||||
};
|
||||
if (uhci_ctrl->tx_dir.on_tx_trans_done(uhci_ctrl, &evt_data, uhci_ctrl->user_data)) {
|
||||
need_yield |= true;
|
||||
}
|
||||
}
|
||||
|
||||
expected_fsm = UHCI_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&uhci_ctrl->tx_dir.tx_fsm, &expected_fsm, UHCI_TX_FSM_RUN_WAIT)) {
|
||||
if (xQueueReceiveFromISR(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_PROGRESS], &trans_desc, &do_yield) == pdTRUE) {
|
||||
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_RUN);
|
||||
uhci_do_transmit(uhci_ctrl, trans_desc);
|
||||
if (do_yield) {
|
||||
need_yield |= true;
|
||||
}
|
||||
} else {
|
||||
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static bool uhci_gdma_rx_callback_done(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
bool need_yield = false;
|
||||
uhci_controller_handle_t uhci_ctrl = (uhci_controller_handle_t) user_data;
|
||||
// If the data is not all received, handle it in not normal_eof block. Otherwise, in eof block.
|
||||
if (!event_data->flags.normal_eof) {
|
||||
size_t rx_size = uhci_ctrl->rx_dir.buffer_size_per_desc_node[uhci_ctrl->rx_dir.node_index];
|
||||
uhci_rx_event_data_t evt_data = {
|
||||
.data = uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index],
|
||||
.recv_size = rx_size,
|
||||
.flags.totally_received = false,
|
||||
};
|
||||
|
||||
bool need_cache_sync = esp_ptr_internal(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index]) ? (uhci_ctrl->int_mem_cache_line_size > 0) : (uhci_ctrl->ext_mem_cache_line_size > 0);
|
||||
if (need_cache_sync) {
|
||||
esp_cache_msync(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index], rx_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
}
|
||||
if (uhci_ctrl->rx_dir.on_rx_trans_event) {
|
||||
need_yield |= uhci_ctrl->rx_dir.on_rx_trans_event(uhci_ctrl, &evt_data, uhci_ctrl->user_data);
|
||||
}
|
||||
uhci_ctrl->rx_dir.node_index++;
|
||||
// Go back to 0 as its a circle descriptor link
|
||||
if (uhci_ctrl->rx_dir.node_index >= uhci_ctrl->rx_dir.rx_num_dma_nodes) {
|
||||
uhci_ctrl->rx_dir.node_index = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
// eof event
|
||||
size_t rx_size = gdma_link_count_buffer_size_till_eof(uhci_ctrl->rx_dir.dma_link, uhci_ctrl->rx_dir.node_index);
|
||||
uhci_rx_event_data_t evt_data = {
|
||||
.data = uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index],
|
||||
.recv_size = rx_size,
|
||||
.flags.totally_received = true,
|
||||
};
|
||||
|
||||
bool need_cache_sync = esp_ptr_internal(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index]) ? (uhci_ctrl->int_mem_cache_line_size > 0) : (uhci_ctrl->ext_mem_cache_line_size > 0);
|
||||
size_t m2c_size = UHCI_ALIGN_UP(rx_size, uhci_ctrl->rx_dir.cache_line);
|
||||
if (need_cache_sync) {
|
||||
esp_cache_msync(uhci_ctrl->rx_dir.buffer_pointers[uhci_ctrl->rx_dir.node_index], m2c_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
}
|
||||
// release power manager lock
|
||||
if (uhci_ctrl->pm_lock) {
|
||||
esp_pm_lock_release(uhci_ctrl->pm_lock);
|
||||
}
|
||||
if (uhci_ctrl->rx_dir.on_rx_trans_event) {
|
||||
need_yield |= uhci_ctrl->rx_dir.on_rx_trans_event(uhci_ctrl, &evt_data, uhci_ctrl->user_data);
|
||||
}
|
||||
|
||||
uhci_ctrl->rx_dir.rx_fsm = UHCI_RX_FSM_ENABLE;
|
||||
}
|
||||
|
||||
if (event_data->flags.abnormal_eof) {
|
||||
esp_rom_printf(DRAM_STR("An abnormal eof on uhci detected\n"));
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static esp_err_t uhci_gdma_initialize(uhci_controller_handle_t uhci_ctrl, const uhci_controller_config_t *config)
|
||||
{
|
||||
// Initialize DMA TX channel
|
||||
gdma_channel_alloc_config_t tx_alloc_config = {
|
||||
.direction = GDMA_CHANNEL_DIRECTION_TX,
|
||||
#if CONFIG_UHCI_ISR_CACHE_SAFE
|
||||
.flags.isr_cache_safe = true,
|
||||
#endif
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&tx_alloc_config, &uhci_ctrl->tx_dir.dma_chan), TAG, "DMA tx channel alloc failed");
|
||||
gdma_connect(uhci_ctrl->tx_dir.dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
|
||||
|
||||
gdma_transfer_config_t transfer_cfg = {
|
||||
.access_ext_mem = true,
|
||||
.max_data_burst_size = config->dma_burst_size,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_config_transfer(uhci_ctrl->tx_dir.dma_chan, &transfer_cfg), TAG, "Config DMA tx channel transfer failed");
|
||||
|
||||
gdma_strategy_config_t strategy_config = {
|
||||
.auto_update_desc = true,
|
||||
.owner_check = true,
|
||||
.eof_till_data_popped = true,
|
||||
};
|
||||
gdma_apply_strategy(uhci_ctrl->tx_dir.dma_chan, &strategy_config);
|
||||
|
||||
// create DMA link list
|
||||
gdma_get_alignment_constraints(uhci_ctrl->tx_dir.dma_chan, &uhci_ctrl->tx_dir.int_mem_align, &uhci_ctrl->tx_dir.ext_mem_align);
|
||||
size_t buffer_alignment = UHCI_MAX(uhci_ctrl->tx_dir.int_mem_align, uhci_ctrl->tx_dir.ext_mem_align);
|
||||
size_t num_dma_nodes = esp_dma_calculate_node_count(config->max_transmit_size, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
||||
gdma_link_list_config_t dma_link_config = {
|
||||
.buffer_alignment = buffer_alignment,
|
||||
.item_alignment = 4,
|
||||
.num_items = num_dma_nodes,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &uhci_ctrl->tx_dir.dma_link), TAG, "DMA tx link list alloc failed");
|
||||
ESP_LOGD(TAG, "tx_dma node number is %d", num_dma_nodes);
|
||||
|
||||
// Initialize DMA RX channel
|
||||
gdma_channel_alloc_config_t rx_alloc_config = {
|
||||
.direction = GDMA_CHANNEL_DIRECTION_RX,
|
||||
.sibling_chan = uhci_ctrl->tx_dir.dma_chan,
|
||||
#if CONFIG_UHCI_ISR_CACHE_SAFE
|
||||
.flags.isr_cache_safe = true,
|
||||
#endif
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&rx_alloc_config, &uhci_ctrl->rx_dir.dma_chan), TAG, "DMA rx channel alloc failed");
|
||||
gdma_connect(uhci_ctrl->rx_dir.dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
|
||||
ESP_RETURN_ON_ERROR(gdma_config_transfer(uhci_ctrl->rx_dir.dma_chan, &transfer_cfg), TAG, "Config DMA rx channel transfer failed");
|
||||
|
||||
gdma_get_alignment_constraints(uhci_ctrl->rx_dir.dma_chan, &uhci_ctrl->rx_dir.int_mem_align, &uhci_ctrl->rx_dir.ext_mem_align);
|
||||
buffer_alignment = UHCI_MAX(uhci_ctrl->rx_dir.int_mem_align, uhci_ctrl->rx_dir.ext_mem_align);
|
||||
uhci_ctrl->rx_dir.rx_num_dma_nodes = esp_dma_calculate_node_count(config->max_receive_internal_mem, buffer_alignment, DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
||||
dma_link_config.buffer_alignment = buffer_alignment;
|
||||
dma_link_config.num_items = uhci_ctrl->rx_dir.rx_num_dma_nodes;
|
||||
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &uhci_ctrl->rx_dir.dma_link), TAG, "DMA rx link list alloc failed");
|
||||
ESP_LOGD(TAG, "rx_dma node number is %d", uhci_ctrl->rx_dir.rx_num_dma_nodes);
|
||||
|
||||
uhci_ctrl->rx_dir.buffer_size_per_desc_node = heap_caps_calloc(uhci_ctrl->rx_dir.rx_num_dma_nodes, sizeof(uhci_ctrl->rx_dir.buffer_size_per_desc_node), UHCI_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl->rx_dir.buffer_size_per_desc_node, ESP_ERR_NO_MEM, TAG, "no memory for recording buffer size for desc node");
|
||||
uhci_ctrl->rx_dir.buffer_pointers = heap_caps_calloc(uhci_ctrl->rx_dir.rx_num_dma_nodes, sizeof(*uhci_ctrl->rx_dir.buffer_pointers), UHCI_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl->rx_dir.buffer_pointers, ESP_ERR_NO_MEM, TAG, "no memory for recording buffer pointers for desc node");
|
||||
|
||||
// Register callbacks
|
||||
gdma_tx_event_callbacks_t tx_cbk = {
|
||||
.on_trans_eof = uhci_gdma_tx_callback_eof,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(uhci_ctrl->tx_dir.dma_chan, &tx_cbk, uhci_ctrl), TAG, "register DMA callbacks failed");
|
||||
|
||||
gdma_rx_event_callbacks_t rx_cbk = {
|
||||
.on_recv_done = uhci_gdma_rx_callback_done,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_register_rx_event_callbacks(uhci_ctrl->rx_dir.dma_chan, &rx_cbk, uhci_ctrl), TAG, "register DMA callbacks failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t uhci_gdma_deinitialize(uhci_controller_handle_t uhci_ctrl)
|
||||
{
|
||||
if (uhci_ctrl->tx_dir.dma_chan) {
|
||||
ESP_RETURN_ON_ERROR(gdma_disconnect(uhci_ctrl->tx_dir.dma_chan), TAG, "disconnect tx dma channel failed");
|
||||
ESP_RETURN_ON_ERROR(gdma_del_channel(uhci_ctrl->tx_dir.dma_chan), TAG, "delete tx dma channel failed");
|
||||
}
|
||||
|
||||
if (uhci_ctrl->rx_dir.dma_chan) {
|
||||
ESP_RETURN_ON_ERROR(gdma_disconnect(uhci_ctrl->rx_dir.dma_chan), TAG, "disconnect rx dma channel failed");
|
||||
ESP_RETURN_ON_ERROR(gdma_del_channel(uhci_ctrl->rx_dir.dma_chan), TAG, "delete rx dma channel failed");
|
||||
}
|
||||
if (uhci_ctrl->tx_dir.dma_link) {
|
||||
ESP_RETURN_ON_ERROR(gdma_del_link_list(uhci_ctrl->tx_dir.dma_link), TAG, "dma delete link list failed");
|
||||
}
|
||||
if (uhci_ctrl->rx_dir.dma_link) {
|
||||
ESP_RETURN_ON_ERROR(gdma_del_link_list(uhci_ctrl->rx_dir.dma_link), TAG, "dma delete link list failed");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void uhci_do_transmit(uhci_controller_handle_t uhci_ctrl, uhci_transaction_desc_t *trans)
|
||||
{
|
||||
uhci_ctrl->tx_dir.cur_trans = trans;
|
||||
gdma_buffer_mount_config_t mount_config = {
|
||||
.buffer = trans->buffer,
|
||||
.length = trans->buffer_size,
|
||||
.flags = {
|
||||
.mark_eof = true,
|
||||
.mark_final = true,
|
||||
}
|
||||
};
|
||||
|
||||
// acquire power manager lock
|
||||
if (uhci_ctrl->pm_lock) {
|
||||
esp_pm_lock_acquire(uhci_ctrl->pm_lock);
|
||||
}
|
||||
|
||||
gdma_link_mount_buffers(uhci_ctrl->tx_dir.dma_link, 0, &mount_config, 1, NULL);
|
||||
gdma_start(uhci_ctrl->tx_dir.dma_chan, gdma_link_get_head_addr(uhci_ctrl->tx_dir.dma_link));
|
||||
}
|
||||
|
||||
esp_err_t uhci_receive(uhci_controller_handle_t uhci_ctrl, uint8_t *read_buffer, size_t buffer_size)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE((read_buffer != NULL), ESP_ERR_INVALID_ARG, TAG, "read buffer null");
|
||||
|
||||
uint32_t mem_cache_line_size = esp_ptr_external_ram(read_buffer) ? uhci_ctrl->ext_mem_cache_line_size : uhci_ctrl->int_mem_cache_line_size;
|
||||
|
||||
// Must take cache line into consideration for C2M operation.
|
||||
uint32_t max_alignment_needed = UHCI_MAX(UHCI_MAX(uhci_ctrl->rx_dir.int_mem_align, uhci_ctrl->rx_dir.ext_mem_align), mem_cache_line_size);
|
||||
|
||||
// Align the read_buffer pointer to mem_cache_line_size
|
||||
if (max_alignment_needed > 0 && (((uintptr_t)read_buffer) & (max_alignment_needed - 1)) != 0) {
|
||||
uintptr_t aligned_address = ((uintptr_t)read_buffer + max_alignment_needed - 1) & ~(max_alignment_needed - 1);
|
||||
size_t offset = aligned_address - (uintptr_t)read_buffer;
|
||||
|
||||
ESP_RETURN_ON_FALSE(buffer_size > offset, ESP_ERR_INVALID_ARG, TAG, "buffer size too small to align");
|
||||
|
||||
read_buffer = (uint8_t *)aligned_address;
|
||||
buffer_size -= offset;
|
||||
}
|
||||
|
||||
uhci_ctrl->rx_dir.cache_line = mem_cache_line_size;
|
||||
uhci_rx_fsm_t expected_fsm = UHCI_RX_FSM_ENABLE;
|
||||
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&uhci_ctrl->rx_dir.rx_fsm, &expected_fsm, UHCI_RX_FSM_RUN_WAIT), ESP_ERR_INVALID_STATE, TAG, "controller not in enable state");
|
||||
|
||||
size_t node_count = uhci_ctrl->rx_dir.rx_num_dma_nodes;
|
||||
|
||||
// Initialize the mount configurations for each DMA node, making sure every no
|
||||
size_t usable_size = (max_alignment_needed == 0) ? buffer_size : (buffer_size / max_alignment_needed) * max_alignment_needed;
|
||||
size_t base_size = (max_alignment_needed == 0) ? usable_size / node_count : (usable_size / node_count / max_alignment_needed) * max_alignment_needed;
|
||||
size_t remaining_size = usable_size - (base_size * node_count);
|
||||
|
||||
gdma_buffer_mount_config_t mount_configs[node_count];
|
||||
memset(mount_configs, 0, node_count);
|
||||
|
||||
for (size_t i = 0; i < node_count; i++) {
|
||||
uhci_ctrl->rx_dir.buffer_size_per_desc_node[i] = base_size;
|
||||
uhci_ctrl->rx_dir.buffer_pointers[i] = read_buffer;
|
||||
|
||||
// Distribute the remaining size to the first few nodes
|
||||
if (remaining_size >= max_alignment_needed) {
|
||||
uhci_ctrl->rx_dir.buffer_size_per_desc_node[i] += max_alignment_needed;
|
||||
remaining_size -= max_alignment_needed;
|
||||
}
|
||||
|
||||
mount_configs[i] = (gdma_buffer_mount_config_t) {
|
||||
.buffer = read_buffer,
|
||||
.length = uhci_ctrl->rx_dir.buffer_size_per_desc_node[i],
|
||||
.flags = {
|
||||
.mark_final = false,
|
||||
}
|
||||
};
|
||||
ESP_LOGD(TAG, "The DMA node %d has %d byte", i, uhci_ctrl->rx_dir.buffer_size_per_desc_node[i]);
|
||||
read_buffer += uhci_ctrl->rx_dir.buffer_size_per_desc_node[i];
|
||||
}
|
||||
|
||||
// acquire power manager lock
|
||||
if (uhci_ctrl->pm_lock) {
|
||||
esp_pm_lock_acquire(uhci_ctrl->pm_lock);
|
||||
}
|
||||
|
||||
gdma_link_mount_buffers(uhci_ctrl->rx_dir.dma_link, 0, mount_configs, node_count, NULL);
|
||||
|
||||
gdma_reset(uhci_ctrl->rx_dir.dma_chan);
|
||||
gdma_start(uhci_ctrl->rx_dir.dma_chan, gdma_link_get_head_addr(uhci_ctrl->rx_dir.dma_link));
|
||||
|
||||
atomic_store(&uhci_ctrl->rx_dir.rx_fsm, UHCI_RX_FSM_RUN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t uhci_transmit(uhci_controller_handle_t uhci_ctrl, uint8_t *write_buffer, size_t write_size)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE((write_buffer != NULL), ESP_ERR_INVALID_ARG, TAG, "write buffer null");
|
||||
|
||||
size_t alignment = 0;
|
||||
size_t cache_line_size = 0;
|
||||
esp_ptr_external_ram(write_buffer) ? (alignment = uhci_ctrl->tx_dir.ext_mem_align, cache_line_size = uhci_ctrl->ext_mem_cache_line_size) : (alignment = uhci_ctrl->tx_dir.int_mem_align, cache_line_size = uhci_ctrl->int_mem_cache_line_size);
|
||||
|
||||
ESP_RETURN_ON_FALSE(((((uintptr_t)write_buffer) & (alignment - 1)) == 0) && (((write_size) & (alignment - 1)) == 0), ESP_ERR_INVALID_ARG,
|
||||
TAG, "buffer address or size are not %d bytes aligned", alignment);
|
||||
|
||||
if (cache_line_size > 0) {
|
||||
// Write back to cache to synchronize the cache before DMA start
|
||||
ESP_RETURN_ON_ERROR(esp_cache_msync((void *)write_buffer, write_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "cache sync failed");
|
||||
}
|
||||
|
||||
uhci_transaction_desc_t *t = NULL;
|
||||
|
||||
if (xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_READY], &t, 0) != pdTRUE) {
|
||||
if (xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_COMPLETE], &t, 0) == pdTRUE) {
|
||||
atomic_fetch_sub(&uhci_ctrl->tx_dir.num_trans_inflight, 1);
|
||||
}
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth");
|
||||
|
||||
memset(t, 0, sizeof(uhci_transaction_desc_t));
|
||||
t->buffer = write_buffer;
|
||||
t->buffer_size = write_size;
|
||||
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_PROGRESS], &t, 0) == pdTRUE, ESP_ERR_NO_MEM, TAG, "uhci tx transaction queue full");
|
||||
atomic_fetch_add(&uhci_ctrl->tx_dir.num_trans_inflight, 1);
|
||||
|
||||
uhci_tx_fsm_t expected_fsm = UHCI_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&uhci_ctrl->tx_dir.tx_fsm, &expected_fsm, UHCI_TX_FSM_RUN_WAIT)) {
|
||||
if (xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_PROGRESS], &t, 0)) {
|
||||
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_RUN);
|
||||
uhci_do_transmit(uhci_ctrl, t);
|
||||
} else {
|
||||
atomic_store(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t uhci_del_controller(uhci_controller_handle_t uhci_ctrl)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
if (uhci_ctrl->rx_dir.rx_fsm != UHCI_RX_FSM_ENABLE) {
|
||||
ESP_LOGE(TAG, "RX transaction is not finished, delete controller failed");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (uhci_ctrl->tx_dir.tx_fsm != UHCI_TX_FSM_ENABLE) {
|
||||
ESP_LOGE(TAG, "TX transaction is not finished, delete controller failed");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
UHCI_RCC_ATOMIC() {
|
||||
uhci_ll_enable_bus_clock(uhci_ctrl->uhci_num, false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < UHCI_TRANS_QUEUE_MAX; i++) {
|
||||
if (uhci_ctrl->tx_dir.trans_queues[i]) {
|
||||
vQueueDeleteWithCaps(uhci_ctrl->tx_dir.trans_queues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (uhci_ctrl->tx_dir.trans_desc_pool) {
|
||||
heap_caps_free(uhci_ctrl->tx_dir.trans_desc_pool);
|
||||
}
|
||||
|
||||
if (uhci_ctrl->rx_dir.buffer_size_per_desc_node) {
|
||||
free(uhci_ctrl->rx_dir.buffer_size_per_desc_node);
|
||||
}
|
||||
if (uhci_ctrl->rx_dir.buffer_pointers) {
|
||||
free(uhci_ctrl->rx_dir.buffer_pointers);
|
||||
}
|
||||
|
||||
if (uhci_ctrl->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(uhci_ctrl->pm_lock), TAG, "delete rx pm_lock failed");
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(uhci_gdma_deinitialize(uhci_ctrl), TAG, "deinitialize uhci dam channel failed");
|
||||
|
||||
uhci_hal_deinit(&uhci_ctrl->hal);
|
||||
|
||||
s_uhci_platform.controller[uhci_ctrl->uhci_num] = NULL;
|
||||
|
||||
heap_caps_free(uhci_ctrl);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t uhci_new_controller(const uhci_controller_config_t *config, uhci_controller_handle_t *ret_uhci_ctrl)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE((config && ret_uhci_ctrl), ESP_ERR_INVALID_ARG, TAG, "UHCI invalid argument");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
uhci_controller_handle_t uhci_ctrl = (uhci_controller_handle_t)heap_caps_calloc(1, sizeof(uhci_controller_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_NO_MEM, TAG, "no mem for uhci controller handle");
|
||||
|
||||
atomic_init(&uhci_ctrl->tx_dir.tx_fsm, UHCI_TX_FSM_ENABLE);
|
||||
atomic_init(&uhci_ctrl->rx_dir.rx_fsm, UHCI_TX_FSM_ENABLE);
|
||||
|
||||
// Auto search a free controller
|
||||
bool ctrl_found = false;
|
||||
_lock_acquire(&s_uhci_platform.mutex);
|
||||
for (int i = 0; i < SOC_UHCI_NUM; i++) {
|
||||
if (uhci_ctrl_occupied(i) == false) {
|
||||
s_uhci_platform.controller[i] = uhci_ctrl;
|
||||
ctrl_found = true;
|
||||
uhci_ctrl->uhci_num = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_lock_release(&s_uhci_platform.mutex);
|
||||
ESP_GOTO_ON_FALSE(ctrl_found, ESP_ERR_NOT_FOUND, err, TAG, "no free controller");
|
||||
|
||||
for (int i = 0; i < UHCI_TRANS_QUEUE_MAX; i++) {
|
||||
uhci_ctrl->tx_dir.trans_queues[i] = xQueueCreateWithCaps(config->tx_trans_queue_depth, sizeof(uhci_transaction_desc_t *), UHCI_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(uhci_ctrl->tx_dir.trans_queues[i], ESP_ERR_NO_MEM, err, TAG, "no mem for transaction queue");
|
||||
}
|
||||
|
||||
uhci_ctrl->tx_dir.trans_desc_pool = heap_caps_calloc(config->tx_trans_queue_depth, sizeof(uhci_transaction_desc_t), UHCI_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(uhci_ctrl->tx_dir.trans_desc_pool, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction desc pool");
|
||||
uhci_transaction_desc_t *p_trans_desc = NULL;
|
||||
|
||||
for (int i = 0; i < config->tx_trans_queue_depth; i++) {
|
||||
p_trans_desc = &uhci_ctrl->tx_dir.trans_desc_pool[i];
|
||||
xQueueSend(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_READY], &p_trans_desc, 0);
|
||||
}
|
||||
|
||||
UHCI_RCC_ATOMIC() {
|
||||
uhci_ll_enable_bus_clock(uhci_ctrl->uhci_num, true);
|
||||
uhci_ll_reset_register(uhci_ctrl->uhci_num);
|
||||
}
|
||||
|
||||
uhci_hal_init(&uhci_ctrl->hal, uhci_ctrl->uhci_num);
|
||||
uhci_ll_attach_uart_port(uhci_ctrl->hal.dev, config->uart_port);
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
esp_pm_lock_type_t pm_lock_type = ESP_PM_NO_LIGHT_SLEEP;
|
||||
|
||||
sprintf(uhci_ctrl->pm_lock_name, "uhci_%d", uhci_ctrl->uhci_num); // e.g. uhci_0
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, uhci_ctrl->pm_lock_name, &uhci_ctrl->pm_lock), err, TAG, "create pm lock failed");
|
||||
#endif
|
||||
|
||||
// Must set separate character as 0, otherwise uhci will lose data.
|
||||
uhci_seper_chr_t seper_chr = {
|
||||
.sub_chr_en = 0,
|
||||
};
|
||||
uhci_ll_set_seper_chr(uhci_ctrl->hal.dev, &seper_chr);
|
||||
|
||||
if (config->rx_eof_flags.idle_eof) {
|
||||
uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_IDLE_EOF);
|
||||
}
|
||||
if (config->rx_eof_flags.length_eof) {
|
||||
uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_LEN_EOF);
|
||||
}
|
||||
if (config->rx_eof_flags.rx_brk_eof) {
|
||||
uhci_ll_rx_set_eof_mode(uhci_ctrl->hal.dev, UHCI_RX_BREAK_CHR_EOF);
|
||||
}
|
||||
|
||||
esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &uhci_ctrl->ext_mem_cache_line_size);
|
||||
esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &uhci_ctrl->int_mem_cache_line_size);
|
||||
|
||||
ESP_GOTO_ON_ERROR(uhci_gdma_initialize(uhci_ctrl, config), err, TAG, "uhci gdma initialize failed");
|
||||
|
||||
*ret_uhci_ctrl = uhci_ctrl;
|
||||
return ESP_OK;
|
||||
err:
|
||||
uhci_del_controller(uhci_ctrl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t uhci_register_event_callbacks(uhci_controller_handle_t uhci_ctrl, const uhci_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
#if CONFIG_UHCI_ISR_CACHE_SAFE
|
||||
if (cbs->on_rx_trans_event) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_rx_trans_event), ESP_ERR_INVALID_ARG, TAG, "on_rx_trans_event callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_tx_trans_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_tx_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_tx_trans_done callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
uhci_ctrl->rx_dir.on_rx_trans_event = cbs->on_rx_trans_event;
|
||||
uhci_ctrl->tx_dir.on_tx_trans_done = cbs->on_tx_trans_done;
|
||||
uhci_ctrl->user_data = user_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t uhci_wait_all_tx_transaction_done(uhci_controller_handle_t uhci_ctrl, int timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(uhci_ctrl, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
TickType_t wait_ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
uhci_transaction_desc_t *t = NULL;
|
||||
|
||||
size_t cnt = uhci_ctrl->tx_dir.num_trans_inflight;
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
ESP_RETURN_ON_FALSE(xQueueReceive(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_COMPLETE], &t, wait_ticks) == pdTRUE,
|
||||
ESP_ERR_TIMEOUT, TAG, "flush timeout");
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(uhci_ctrl->tx_dir.trans_queues[UHCI_TRANS_QUEUE_READY], &t, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
|
||||
atomic_fetch_sub(&uhci_ctrl->tx_dir.num_trans_inflight, 1);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_UHCI_ENABLE_DEBUG_LOG
|
||||
__attribute__((constructor))
|
||||
static void uhci_override_default_log_level(void)
|
||||
{
|
||||
esp_log_level_set(TAG, ESP_LOG_VERBOSE);
|
||||
}
|
||||
#endif
|
114
components/esp_driver_uart/src/uhci_private.h
Normal file
114
components/esp_driver_uart/src/uhci_private.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <sys/queue.h>
|
||||
#include "esp_dma_utils.h"
|
||||
#include "esp_private/gdma.h"
|
||||
#include "esp_private/gdma_link.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_pm.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uhci_controller_t uhci_controller_t;
|
||||
|
||||
#define UHCI_ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#define UHCI_MAX(a, b) (((a)>(b))?(a):(b))
|
||||
|
||||
#define UHCI_PM_LOCK_NAME_LEN_MAX 16
|
||||
|
||||
#if CONFIG_UHCI_ISR_HANDLER_IN_IRAM
|
||||
#define UHCI_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define UHCI_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
|
||||
#endif
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define UHCI_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define UHCI_CLOCK_SRC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if !SOC_RCC_IS_INDEPENDENT
|
||||
#define UHCI_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define UHCI_RCC_ATOMIC()
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
void *buffer; // buffer for saving the received symbols
|
||||
size_t buffer_size; // size of the buffer, in bytes
|
||||
} uhci_transaction_desc_t;
|
||||
|
||||
typedef enum {
|
||||
UHCI_TX_FSM_ENABLE_WAIT, /**< FSM is waiting to enable the UHCI system. */
|
||||
UHCI_TX_FSM_ENABLE, /**< FSM is enabling the UHCI system. */
|
||||
UHCI_TX_FSM_RUN_WAIT, /**< FSM is waiting to transition to the running state. */
|
||||
UHCI_TX_FSM_RUN, /**< FSM is in the running state, actively handling UHCI operations. */
|
||||
} uhci_tx_fsm_t;
|
||||
|
||||
typedef enum {
|
||||
UHCI_TRANS_QUEUE_READY, /**< The transaction queue is ready to accept new transactions. */
|
||||
UHCI_TRANS_QUEUE_PROGRESS, /**< A transaction is currently in progress. */
|
||||
UHCI_TRANS_QUEUE_COMPLETE, /**< All transactions in the queue are completed. */
|
||||
UHCI_TRANS_QUEUE_MAX, /**< Placeholder for the maximum number of states (not a valid state). */
|
||||
} uhci_trans_queue_enum_t;
|
||||
|
||||
typedef enum {
|
||||
UHCI_RX_FSM_ENABLE_WAIT, /**< FSM is waiting to enable the UHCI system. */
|
||||
UHCI_RX_FSM_ENABLE, /**< FSM is enabling the UHCI system. */
|
||||
UHCI_RX_FSM_RUN_WAIT, /**< FSM is waiting to transition to the running state. */
|
||||
UHCI_RX_FSM_RUN, /**< FSM is in the running state, actively handling UHCI operations. */
|
||||
} uhci_rx_fsm_t;
|
||||
|
||||
typedef struct {
|
||||
gdma_channel_handle_t dma_chan; // GDMA channel
|
||||
uhci_tx_done_callback_t on_tx_trans_done; // tx transaction done callback function
|
||||
gdma_link_list_handle_t dma_link; // GDMA link handle
|
||||
uhci_transaction_desc_t *trans_desc_pool; // transaction descriptor pool
|
||||
uhci_transaction_desc_t *cur_trans; // pointer to current transaction
|
||||
QueueHandle_t trans_queues[UHCI_TRANS_QUEUE_MAX]; // transaction queue
|
||||
_Atomic uhci_tx_fsm_t tx_fsm; // channel life cycle specific FSM
|
||||
size_t int_mem_align; // Alignment for internal memory
|
||||
size_t ext_mem_align; // Alignment for external memory
|
||||
atomic_int num_trans_inflight; // Indicates the number of transactions that are undergoing but not recycled to ready_queue
|
||||
} uhci_tx_dir;
|
||||
|
||||
typedef struct {
|
||||
gdma_channel_handle_t dma_chan; // GDMA channel
|
||||
uhci_rx_event_callback_t on_rx_trans_event; // rx transaction done callback function
|
||||
gdma_link_list_handle_t dma_link; // GDMA link handle
|
||||
size_t *buffer_size_per_desc_node; // Pointer to buffer size per dma descriptor node
|
||||
size_t node_index; // node index in receive
|
||||
uint8_t **buffer_pointers; // Pointer for saving buffer pointer
|
||||
_Atomic uhci_rx_fsm_t rx_fsm; // channel life cycle specific FSM
|
||||
size_t cache_line; // cache line size need to be aligned up.
|
||||
size_t int_mem_align; // Alignment for internal memory
|
||||
size_t ext_mem_align; // Alignment for external memory
|
||||
size_t rx_num_dma_nodes; // rx dma number nodes
|
||||
} uhci_rx_dir;
|
||||
|
||||
struct uhci_controller_t {
|
||||
int uhci_num; // UHCI port number
|
||||
uhci_hal_context_t hal; // uhci hal layer context
|
||||
uhci_tx_dir tx_dir; // tx direction structure
|
||||
uhci_rx_dir rx_dir; // rx direction structure
|
||||
void *user_data; // user data
|
||||
size_t int_mem_cache_line_size; // internal memory cache line size
|
||||
size_t ext_mem_cache_line_size; // external memory cache line size
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
char pm_lock_name[UHCI_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -28,3 +28,10 @@ components/esp_driver_uart/test_apps/uart_vfs:
|
||||
depends_components:
|
||||
- esp_driver_uart
|
||||
- vfs
|
||||
|
||||
components/esp_driver_uart/test_apps/uhci:
|
||||
disable:
|
||||
- if: SOC_UHCI_SUPPORTED != 1
|
||||
- if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1
|
||||
depends_components:
|
||||
- esp_driver_uart
|
||||
|
28
components/esp_driver_uart/test_apps/uhci/CMakeLists.txt
Normal file
28
components/esp_driver_uart/test_apps/uhci/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(uhci_test)
|
||||
|
||||
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||
add_custom_target(check_test_app_sections ALL
|
||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||
--rtl-dirs ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_uart/,${CMAKE_BINARY_DIR}/esp-idf/hal/
|
||||
--elf-file ${CMAKE_BINARY_DIR}/uhci_test.elf
|
||||
find-refs
|
||||
--from-sections=.iram0.text
|
||||
--to-sections=.flash.text,.flash.rodata
|
||||
--exit-code
|
||||
DEPENDS ${elf}
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "Checking uhci registers are not read-write by half-word")
|
||||
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
|
||||
check_register_rw_half_word(SOC_MODULES "uhci" "pcr" "hp_sys_clkrst" "lpperi" "lp_clkrst"
|
||||
HAL_MODULES "uhci")
|
2
components/esp_driver_uart/test_apps/uhci/README.md
Normal file
2
components/esp_driver_uart/test_apps/uhci/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-P4 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- | -------- | -------- |
|
@@ -0,0 +1,8 @@
|
||||
# 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 "test_app_main.c"
|
||||
"test_uhci.c"
|
||||
REQUIRES esp_driver_uart unity test_utils esp_psram
|
||||
WHOLE_ARCHIVE
|
||||
)
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_newlib.h"
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (500)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
|
||||
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf(" __ __ __ __ ______ __ \n");
|
||||
printf("| | | | | | | | / || |\n");
|
||||
printf("| | | | | |__| | | ,----'| |\n");
|
||||
printf("| | | | | __ | | | | |\n");
|
||||
printf("| `--' | | | | | | `----.| |\n");
|
||||
printf(" \\______/ |__| |__| \\______||__|\n");
|
||||
|
||||
unity_run_menu();
|
||||
}
|
294
components/esp_driver_uart/test_apps/uhci/main/test_uhci.c
Normal file
294
components/esp_driver_uart/test_apps/uhci/main/test_uhci.c
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "driver/uart.h"
|
||||
#include "driver/uhci.h"
|
||||
|
||||
#define DATA_LENGTH 1024
|
||||
#define EX_UART_NUM 1
|
||||
#define UART_TX_IO 2
|
||||
#define UART_RX_IO 3
|
||||
|
||||
TEST_CASE("UHCI driver memory leaking check", "[uhci]")
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 1 * 1000 * 1000,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
|
||||
//UART parameter config
|
||||
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
|
||||
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_RX_IO, -1, -1));
|
||||
|
||||
uhci_controller_config_t uhci_cfg = {
|
||||
.uart_port = EX_UART_NUM,
|
||||
.tx_trans_queue_depth = 30,
|
||||
.max_receive_internal_mem = 10 * 1024,
|
||||
.max_transmit_size = 10 * 1024,
|
||||
.dma_burst_size = 32,
|
||||
.rx_eof_flags.idle_eof = 1,
|
||||
};
|
||||
|
||||
uhci_controller_handle_t uhci_ctrl;
|
||||
|
||||
int size = esp_get_free_heap_size();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
|
||||
}
|
||||
|
||||
TEST_ASSERT_INT_WITHIN(300, size, esp_get_free_heap_size());
|
||||
}
|
||||
|
||||
TEST_CASE("UHCI controller install-uninstall test", "[i2c]")
|
||||
{
|
||||
uhci_controller_config_t uhci_cfg = {
|
||||
.uart_port = EX_UART_NUM,
|
||||
.tx_trans_queue_depth = 30,
|
||||
.max_receive_internal_mem = 10 * 1024,
|
||||
.max_transmit_size = 10 * 1024,
|
||||
.dma_burst_size = 32,
|
||||
.rx_eof_flags.idle_eof = 1,
|
||||
};
|
||||
|
||||
uhci_controller_handle_t uhci_ctrl;
|
||||
uhci_controller_handle_t uhci_ctrl2;
|
||||
|
||||
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, uhci_new_controller(&uhci_cfg, &uhci_ctrl2));
|
||||
|
||||
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
UHCI_EVT_PARTIAL_DATA,
|
||||
UHCI_EVT_EOF,
|
||||
} uhci_event_t;
|
||||
|
||||
typedef struct {
|
||||
QueueHandle_t uhci_queue;
|
||||
size_t receive_size;
|
||||
uint8_t *p_receive_data;
|
||||
} uhci_context_t;
|
||||
|
||||
static void disp_buf(uint8_t *buf, int len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
printf("%02x ", buf[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
IRAM_ATTR static bool s_uhci_rx_event_cbs(uhci_controller_handle_t uhci_ctrl, const uhci_rx_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
uhci_context_t *ctx = (uhci_context_t *)user_ctx;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
uhci_event_t evt = 0;
|
||||
if (edata->flags.totally_received) {
|
||||
evt = UHCI_EVT_EOF;
|
||||
ctx->receive_size += edata->recv_size;
|
||||
memcpy(ctx->p_receive_data, edata->data, edata->recv_size);
|
||||
} else {
|
||||
evt = UHCI_EVT_PARTIAL_DATA;
|
||||
ctx->receive_size += edata->recv_size;
|
||||
memcpy(ctx->p_receive_data, edata->data, edata->recv_size);
|
||||
ctx->p_receive_data += edata->recv_size;
|
||||
}
|
||||
|
||||
xQueueSendFromISR(ctx->uhci_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
static void uhci_receive_test(void *arg)
|
||||
{
|
||||
uhci_controller_handle_t uhci_ctrl = ((uhci_controller_handle_t *)arg)[0];
|
||||
SemaphoreHandle_t exit_sema = ((SemaphoreHandle_t *)arg)[1];
|
||||
|
||||
uhci_context_t *ctx = calloc(1, sizeof(uhci_context_t));
|
||||
assert(ctx);
|
||||
ctx->uhci_queue = xQueueCreate(15, sizeof(uhci_event_t));
|
||||
assert(ctx->uhci_queue);
|
||||
|
||||
uint8_t *receive_data = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
assert(receive_data);
|
||||
uint8_t *pdata = heap_caps_calloc(1, DATA_LENGTH / 4, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
assert(pdata);
|
||||
ctx->p_receive_data = receive_data;
|
||||
|
||||
uhci_event_callbacks_t uhci_cbs = {
|
||||
.on_rx_trans_event = s_uhci_rx_event_cbs,
|
||||
};
|
||||
TEST_ESP_OK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx));
|
||||
TEST_ESP_OK(uhci_receive(uhci_ctrl, pdata, DATA_LENGTH / 4));
|
||||
|
||||
uhci_event_t evt;
|
||||
while (1) {
|
||||
if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) {
|
||||
if (evt == UHCI_EVT_EOF) {
|
||||
disp_buf(receive_data, ctx->receive_size);
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
TEST_ASSERT(receive_data[i] == (uint8_t)i);
|
||||
}
|
||||
printf("Received size: %d\n", ctx->receive_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vQueueDelete(ctx->uhci_queue);
|
||||
free(pdata);
|
||||
free(receive_data);
|
||||
free(ctx);
|
||||
xSemaphoreGive(exit_sema);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("UHCI write and receive", "[uhci]")
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 5 * 1000 * 1000,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_XTAL,
|
||||
};
|
||||
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
|
||||
// Connect TX and RX together for testing self send-receive
|
||||
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_TX_IO, -1, -1));
|
||||
|
||||
uhci_controller_config_t uhci_cfg = {
|
||||
.uart_port = EX_UART_NUM,
|
||||
.tx_trans_queue_depth = 30,
|
||||
.max_receive_internal_mem = 10 * 1024,
|
||||
.max_transmit_size = 10 * 1024,
|
||||
.dma_burst_size = 32,
|
||||
.rx_eof_flags.idle_eof = 1,
|
||||
};
|
||||
|
||||
uhci_controller_handle_t uhci_ctrl;
|
||||
SemaphoreHandle_t exit_sema = xSemaphoreCreateBinary();
|
||||
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
|
||||
|
||||
void *args[] = { uhci_ctrl, exit_sema };
|
||||
xTaskCreate(uhci_receive_test, "uhci_receive_test", 4096 * 2, args, 5, NULL);
|
||||
|
||||
uint8_t data_wr[DATA_LENGTH];
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
TEST_ESP_OK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH));
|
||||
uhci_wait_all_tx_transaction_done(uhci_ctrl, portMAX_DELAY);
|
||||
xSemaphoreTake(exit_sema, portMAX_DELAY);
|
||||
vTaskDelay(2);
|
||||
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
|
||||
vSemaphoreDelete(exit_sema);
|
||||
}
|
||||
|
||||
#if CONFIG_SPIRAM
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
static void uhci_receive_test_in_psram(void *arg)
|
||||
{
|
||||
uhci_controller_handle_t uhci_ctrl = ((uhci_controller_handle_t *)arg)[0];
|
||||
SemaphoreHandle_t exit_sema = ((SemaphoreHandle_t *)arg)[1];
|
||||
|
||||
uhci_context_t *ctx = calloc(1, sizeof(uhci_context_t));
|
||||
assert(ctx);
|
||||
ctx->uhci_queue = xQueueCreate(15, sizeof(uhci_event_t));
|
||||
assert(ctx->uhci_queue);
|
||||
|
||||
uint8_t *receive_data = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
assert(receive_data);
|
||||
uint8_t *pdata = heap_caps_calloc(1, DATA_LENGTH / 4, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
assert(pdata);
|
||||
ctx->p_receive_data = receive_data;
|
||||
|
||||
uhci_event_callbacks_t uhci_cbs = {
|
||||
.on_rx_trans_event = s_uhci_rx_event_cbs,
|
||||
};
|
||||
TEST_ESP_OK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx));
|
||||
TEST_ESP_OK(uhci_receive(uhci_ctrl, pdata, DATA_LENGTH / 4));
|
||||
|
||||
uhci_event_t evt;
|
||||
while (1) {
|
||||
if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) {
|
||||
if (evt == UHCI_EVT_EOF) {
|
||||
disp_buf(receive_data, ctx->receive_size);
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
TEST_ASSERT(receive_data[i] == (uint8_t)i);
|
||||
}
|
||||
printf("Received size: %d\n", ctx->receive_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vQueueDelete(ctx->uhci_queue);
|
||||
free(pdata);
|
||||
free(receive_data);
|
||||
free(ctx);
|
||||
xSemaphoreGive(exit_sema);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("UHCI write and receive in psram", "[uhci]")
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 5 * 1000 * 1000,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_XTAL,
|
||||
};
|
||||
TEST_ESP_OK(uart_param_config(EX_UART_NUM, &uart_config));
|
||||
// Connect TX and RX together for testing self send-receive
|
||||
TEST_ESP_OK(uart_set_pin(EX_UART_NUM, UART_TX_IO, UART_TX_IO, -1, -1));
|
||||
|
||||
uhci_controller_config_t uhci_cfg = {
|
||||
.uart_port = EX_UART_NUM,
|
||||
.tx_trans_queue_depth = 30,
|
||||
.max_receive_internal_mem = 10 * 1024,
|
||||
.max_transmit_size = 10 * 1024,
|
||||
.dma_burst_size = 32,
|
||||
.rx_eof_flags.idle_eof = 1,
|
||||
};
|
||||
|
||||
uhci_controller_handle_t uhci_ctrl;
|
||||
SemaphoreHandle_t exit_sema = xSemaphoreCreateBinary();
|
||||
TEST_ESP_OK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
|
||||
|
||||
void *args[] = { uhci_ctrl, exit_sema };
|
||||
xTaskCreate(uhci_receive_test_in_psram, "uhci_receive_test_in_psram", 4096 * 2, args, 5, NULL);
|
||||
|
||||
uint8_t *data_wr = heap_caps_calloc(1, DATA_LENGTH, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
for (int i = 0; i < DATA_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
TEST_ESP_OK(uhci_transmit(uhci_ctrl, data_wr, DATA_LENGTH));
|
||||
uhci_wait_all_tx_transaction_done(uhci_ctrl, portMAX_DELAY);
|
||||
xSemaphoreTake(exit_sema, portMAX_DELAY);
|
||||
vTaskDelay(2);
|
||||
free(data_wr);
|
||||
TEST_ESP_OK(uhci_del_controller(uhci_ctrl));
|
||||
vSemaphoreDelete(exit_sema);
|
||||
}
|
||||
#endif
|
||||
#endif // CONFIG_SPIRAM
|
20
components/esp_driver_uart/test_apps/uhci/pytest_uhci.py
Normal file
20
components/esp_driver_uart/test_apps/uhci/pytest_uhci.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
from pytest_embedded_idf.utils import soc_filtered_targets
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
'cache_safe',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', soc_filtered_targets('SOC_UHCI_SUPPORTED == 1'), indirect=['target'])
|
||||
def test_uhci(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
@@ -0,0 +1,11 @@
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_UHCI_ISR_CACHE_SAFE=y
|
||||
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_NONE=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
|
||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_HAL_ASSERTION_SILENT=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
|
@@ -0,0 +1 @@
|
||||
CONFIG_SPIRAM=y
|
@@ -0,0 +1,6 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_DFS_INIT_AUTO=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
@@ -126,6 +126,10 @@ elseif(NOT BOOTLOADER_BUILD)
|
||||
list(APPEND srcs "mcpwm_hal.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_UHCI_SUPPORTED)
|
||||
list(APPEND srcs "uhci_hal.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_TWAI_SUPPORTED)
|
||||
list(APPEND srcs "twai_hal.c" "twai_hal_iram.c")
|
||||
endif()
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "hal/uhci_types.h"
|
||||
#include "soc/uhci_struct.h"
|
||||
#include "soc/system_struct.h"
|
||||
#include "hal/misc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -77,27 +78,22 @@ static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
|
||||
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
|
||||
{
|
||||
if (seper_char->sub_chr_en) {
|
||||
hw->conf0.seper_en = 1;
|
||||
typeof(hw->esc_conf0) esc_conf0_reg;
|
||||
esc_conf0_reg.val = hw->esc_conf0.val;
|
||||
|
||||
esc_conf0_reg.seper_char = seper_char->seper_chr;
|
||||
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
|
||||
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
|
||||
hw->esc_conf0.val = esc_conf0_reg.val;
|
||||
hw->escape_conf.tx_c0_esc_en = 1;
|
||||
hw->escape_conf.rx_c0_esc_en = 1;
|
||||
} else {
|
||||
hw->escape_conf.tx_c0_esc_en = 0;
|
||||
hw->escape_conf.rx_c0_esc_en = 0;
|
||||
hw->conf0.seper_en = 0;
|
||||
hw->escape_conf.val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
|
||||
{
|
||||
(void)hw;
|
||||
(void)seper_chr;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
|
||||
{
|
||||
typeof(hw->escape_conf) escape_conf_reg;
|
||||
@@ -109,12 +105,12 @@ static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_c
|
||||
typeof(hw->esc_conf3) esc_conf3_reg;
|
||||
esc_conf3_reg.val = hw->esc_conf3.val;
|
||||
|
||||
esc_conf2_reg.seq1 = sub_ctr->xon_chr;
|
||||
esc_conf2_reg.seq1_char0 = sub_ctr->xon_sub1;
|
||||
esc_conf2_reg.seq1_char1 = sub_ctr->xon_sub2;
|
||||
esc_conf3_reg.seq2 = sub_ctr->xoff_chr;
|
||||
esc_conf3_reg.seq2_char0 = sub_ctr->xoff_sub1;
|
||||
esc_conf3_reg.seq2_char1 = sub_ctr->xoff_sub2;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1, sub_ctr->xon_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char0, sub_ctr->xon_sub1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char1, sub_ctr->xon_sub2);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2, sub_ctr->xoff_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char0, sub_ctr->xoff_sub1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char1, sub_ctr->xoff_sub2);
|
||||
escape_conf_reg.tx_11_esc_en = 1;
|
||||
escape_conf_reg.tx_13_esc_en = 1;
|
||||
escape_conf_reg.rx_11_esc_en = 1;
|
||||
@@ -150,7 +146,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
|
||||
return hw->int_st.val;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
{
|
||||
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
|
||||
hw->conf0.uart_rx_brk_eof_en = 1;
|
||||
|
@@ -1,13 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// The LL layer for UHCI register operations.
|
||||
// Note that most of the register operations in this layer are non-atomic operations.
|
||||
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include "hal/uhci_types.h"
|
||||
#include "soc/uhci_struct.h"
|
||||
#include "soc/pcr_struct.h"
|
||||
#include "hal/misc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -22,6 +27,30 @@ typedef enum {
|
||||
UHCI_RX_EOF_MAX = 0x7,
|
||||
} uhci_rxeof_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Enable the bus clock for UHCI module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
* @param enable true to enable, false to disable
|
||||
*/
|
||||
static inline void uhci_ll_enable_bus_clock(int group_id, bool enable)
|
||||
{
|
||||
(void)group_id;
|
||||
PCR.uhci_conf.uhci_clk_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the UHCI module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
*/
|
||||
static inline void uhci_ll_reset_register(int group_id)
|
||||
{
|
||||
(void)group_id;
|
||||
PCR.uhci_conf.uhci_rst_en = 1;
|
||||
PCR.uhci_conf.uhci_rst_en = 0;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_init(uhci_dev_t *hw)
|
||||
{
|
||||
typeof(hw->conf0) conf0_reg;
|
||||
@@ -34,34 +63,29 @@ static inline void uhci_ll_init(uhci_dev_t *hw)
|
||||
|
||||
static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
|
||||
{
|
||||
hw->conf0.uart0_ce = (uart_num == 0)? 1: 0;
|
||||
hw->conf0.uart1_ce = (uart_num == 1)? 1: 0;
|
||||
hw->conf0.uart0_ce = (uart_num == 0) ? 1 : 0;
|
||||
hw->conf0.uart1_ce = (uart_num == 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
|
||||
{
|
||||
if (seper_char->sub_chr_en) {
|
||||
hw->conf0.seper_en = 1;
|
||||
typeof(hw->esc_conf0) esc_conf0_reg;
|
||||
esc_conf0_reg.val = hw->esc_conf0.val;
|
||||
|
||||
esc_conf0_reg.seper_char = seper_char->seper_chr;
|
||||
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
|
||||
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
|
||||
hw->esc_conf0.val = esc_conf0_reg.val;
|
||||
hw->escape_conf.tx_c0_esc_en = 1;
|
||||
hw->escape_conf.rx_c0_esc_en = 1;
|
||||
} else {
|
||||
hw->escape_conf.tx_c0_esc_en = 0;
|
||||
hw->escape_conf.rx_c0_esc_en = 0;
|
||||
hw->conf0.seper_en = 0;
|
||||
hw->escape_conf.val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
|
||||
{
|
||||
(void)hw;
|
||||
(void)seper_chr;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
|
||||
{
|
||||
typeof(hw->escape_conf) escape_conf_reg;
|
||||
@@ -73,12 +97,12 @@ static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_c
|
||||
typeof(hw->esc_conf3) esc_conf3_reg;
|
||||
esc_conf3_reg.val = hw->esc_conf3.val;
|
||||
|
||||
esc_conf2_reg.esc_seq1 = sub_ctr->xon_chr;
|
||||
esc_conf2_reg.esc_seq1_char0 = sub_ctr->xon_sub1;
|
||||
esc_conf2_reg.esc_seq1_char1 = sub_ctr->xon_sub2;
|
||||
esc_conf3_reg.esc_seq2 = sub_ctr->xoff_chr;
|
||||
esc_conf3_reg.esc_seq2_char0 = sub_ctr->xoff_sub1;
|
||||
esc_conf3_reg.esc_seq2_char1 = sub_ctr->xoff_sub2;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1, sub_ctr->xon_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1_char0, sub_ctr->xon_sub1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, esc_seq1_char1, sub_ctr->xon_sub2);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2, sub_ctr->xoff_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2_char0, sub_ctr->xoff_sub1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, esc_seq2_char1, sub_ctr->xoff_sub2);
|
||||
escape_conf_reg.tx_11_esc_en = 1;
|
||||
escape_conf_reg.tx_13_esc_en = 1;
|
||||
escape_conf_reg.rx_11_esc_en = 1;
|
||||
@@ -114,8 +138,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
|
||||
return hw->int_st.val;
|
||||
}
|
||||
|
||||
|
||||
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
{
|
||||
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
|
||||
hw->conf0.uart_rx_brk_eof_en = 1;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stdio.h>
|
||||
#include "hal/uhci_types.h"
|
||||
#include "soc/uhci_struct.h"
|
||||
#include "hal/misc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -41,27 +42,22 @@ static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
|
||||
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
|
||||
{
|
||||
if (seper_char->sub_chr_en) {
|
||||
hw->conf0.seper_en = 1;
|
||||
typeof(hw->esc_conf0) esc_conf0_reg;
|
||||
esc_conf0_reg.val = hw->esc_conf0.val;
|
||||
|
||||
esc_conf0_reg.seper_char = seper_char->seper_chr;
|
||||
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
|
||||
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
|
||||
hw->esc_conf0.val = esc_conf0_reg.val;
|
||||
hw->escape_conf.tx_c0_esc_en = 1;
|
||||
hw->escape_conf.rx_c0_esc_en = 1;
|
||||
} else {
|
||||
hw->escape_conf.tx_c0_esc_en = 0;
|
||||
hw->escape_conf.rx_c0_esc_en = 0;
|
||||
hw->conf0.seper_en = 0;
|
||||
hw->escape_conf.val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
|
||||
{
|
||||
(void)hw;
|
||||
(void)seper_chr;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
|
||||
{
|
||||
typeof(hw->escape_conf) escape_conf_reg;
|
||||
@@ -115,7 +111,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
|
||||
}
|
||||
|
||||
|
||||
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
{
|
||||
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
|
||||
hw->conf0.uart_rx_brk_eof_en = 1;
|
||||
|
112
components/hal/esp32p4/include/hal/uhci_ll.h
Normal file
112
components/hal/esp32p4/include/hal/uhci_ll.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// The LL layer for UHCI register operations.
|
||||
// Note that most of the register operations in this layer are non-atomic operations.
|
||||
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include "hal/uhci_types.h"
|
||||
#include "soc/uhci_struct.h"
|
||||
#include "soc/hp_sys_clkrst_struct.h"
|
||||
#include "hal/misc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define UHCI_LL_GET_HW(num) (((num) == 0) ? (&UHCI0) : (NULL))
|
||||
|
||||
typedef enum {
|
||||
UHCI_RX_BREAK_CHR_EOF = 0x1,
|
||||
UHCI_RX_IDLE_EOF = 0x2,
|
||||
UHCI_RX_LEN_EOF = 0x4,
|
||||
UHCI_RX_EOF_MAX = 0x7,
|
||||
} uhci_rxeof_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Enable the bus clock for UHCI module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
* @param enable true to enable, false to disable
|
||||
*/
|
||||
static inline void _uhci_ll_enable_bus_clock(int group_id, bool enable)
|
||||
{
|
||||
(void)group_id;
|
||||
HP_SYS_CLKRST.soc_clk_ctrl2.reg_uhci_apb_clk_en = enable;
|
||||
HP_SYS_CLKRST.soc_clk_ctrl1.reg_uhci_sys_clk_en = enable;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define uhci_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; _uhci_ll_enable_bus_clock(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Reset the UHCI module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
*/
|
||||
static inline void _uhci_ll_reset_register(int group_id)
|
||||
{
|
||||
(void)group_id;
|
||||
HP_SYS_CLKRST.hp_rst_en1.reg_rst_en_uhci = 1;
|
||||
HP_SYS_CLKRST.hp_rst_en1.reg_rst_en_uhci = 0;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define uhci_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; _uhci_ll_reset_register(__VA_ARGS__)
|
||||
|
||||
static inline void uhci_ll_init(uhci_dev_t *hw)
|
||||
{
|
||||
typeof(hw->conf0) conf0_reg;
|
||||
hw->conf0.clk_en = 1;
|
||||
conf0_reg.val = 0;
|
||||
conf0_reg.clk_en = 1;
|
||||
hw->conf0.val = conf0_reg.val;
|
||||
hw->conf1.val = 0;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
|
||||
{
|
||||
hw->conf0.uart_sel = uart_num;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
|
||||
{
|
||||
if (seper_char->sub_chr_en) {
|
||||
hw->conf0.seper_en = 1;
|
||||
typeof(hw->esc_conf0) esc_conf0_reg;
|
||||
esc_conf0_reg.val = hw->esc_conf0.val;
|
||||
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
|
||||
hw->esc_conf0.val = esc_conf0_reg.val;
|
||||
hw->escape_conf.tx_c0_esc_en = 1;
|
||||
hw->escape_conf.rx_c0_esc_en = 1;
|
||||
} else {
|
||||
hw->conf0.seper_en = 0;
|
||||
hw->escape_conf.val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
{
|
||||
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
|
||||
hw->conf0.uart_rx_brk_eof_en = 1;
|
||||
}
|
||||
if (eof_mode & UHCI_RX_IDLE_EOF) {
|
||||
hw->conf0.uart_idle_eof_en = 1;
|
||||
}
|
||||
if (eof_mode & UHCI_RX_LEN_EOF) {
|
||||
hw->conf0.len_eof_en = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "hal/uhci_types.h"
|
||||
#include "soc/uhci_struct.h"
|
||||
#include "soc/system_struct.h"
|
||||
#include "hal/misc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -78,27 +79,22 @@ static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num)
|
||||
static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char)
|
||||
{
|
||||
if (seper_char->sub_chr_en) {
|
||||
hw->conf0.seper_en = 1;
|
||||
typeof(hw->esc_conf0) esc_conf0_reg;
|
||||
esc_conf0_reg.val = hw->esc_conf0.val;
|
||||
|
||||
esc_conf0_reg.seper_char = seper_char->seper_chr;
|
||||
esc_conf0_reg.seper_esc_char0 = seper_char->sub_chr1;
|
||||
esc_conf0_reg.seper_esc_char1 = seper_char->sub_chr2;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_char, seper_char->seper_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char0, seper_char->sub_chr1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf0_reg, seper_esc_char1, seper_char->sub_chr2);
|
||||
hw->esc_conf0.val = esc_conf0_reg.val;
|
||||
hw->escape_conf.tx_c0_esc_en = 1;
|
||||
hw->escape_conf.rx_c0_esc_en = 1;
|
||||
} else {
|
||||
hw->escape_conf.tx_c0_esc_en = 0;
|
||||
hw->escape_conf.rx_c0_esc_en = 0;
|
||||
hw->conf0.seper_en = 0;
|
||||
hw->escape_conf.val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void uhci_ll_get_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_chr)
|
||||
{
|
||||
(void)hw;
|
||||
(void)seper_chr;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_ctrl_sub_chr_t *sub_ctr)
|
||||
{
|
||||
typeof(hw->escape_conf) escape_conf_reg;
|
||||
@@ -110,12 +106,12 @@ static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_c
|
||||
typeof(hw->esc_conf3) esc_conf3_reg;
|
||||
esc_conf3_reg.val = hw->esc_conf3.val;
|
||||
|
||||
esc_conf2_reg.seq1 = sub_ctr->xon_chr;
|
||||
esc_conf2_reg.seq1_char0 = sub_ctr->xon_sub1;
|
||||
esc_conf2_reg.seq1_char1 = sub_ctr->xon_sub2;
|
||||
esc_conf3_reg.seq2 = sub_ctr->xoff_chr;
|
||||
esc_conf3_reg.seq2_char0 = sub_ctr->xoff_sub1;
|
||||
esc_conf3_reg.seq2_char1 = sub_ctr->xoff_sub2;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1, sub_ctr->xon_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char0, sub_ctr->xon_sub1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf2_reg, seq1_char1, sub_ctr->xon_sub2);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2, sub_ctr->xoff_chr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char0, sub_ctr->xoff_sub1);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(esc_conf3_reg, seq2_char1, sub_ctr->xoff_sub2);
|
||||
escape_conf_reg.tx_11_esc_en = 1;
|
||||
escape_conf_reg.tx_13_esc_en = 1;
|
||||
escape_conf_reg.rx_11_esc_en = 1;
|
||||
@@ -151,7 +147,7 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw)
|
||||
return hw->int_st.val;
|
||||
}
|
||||
|
||||
static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
static inline void uhci_ll_rx_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode)
|
||||
{
|
||||
if (eof_mode & UHCI_RX_BREAK_CHR_EOF) {
|
||||
hw->conf0.uart_rx_brk_eof_en = 1;
|
||||
|
59
components/hal/include/hal/uhci_hal.h
Normal file
59
components/hal/include/hal/uhci_hal.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
* The hal is not public api, don't use in application code.
|
||||
* See readme.md in hal/readme.md
|
||||
******************************************************************************/
|
||||
|
||||
// The HAL layer for UHCI.
|
||||
// There is no parameter check in the hal layer, so the caller must ensure the correctness of the parameters.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/uhci_types.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if SOC_UHCI_SUPPORTED
|
||||
typedef struct uhci_dev_t *uhci_soc_handle_t; // UHCI SOC layer handle
|
||||
|
||||
/**
|
||||
* Context that should be maintained by both the driver and the HAL
|
||||
*/
|
||||
typedef struct {
|
||||
uhci_soc_handle_t dev;
|
||||
} uhci_hal_context_t;
|
||||
|
||||
/**
|
||||
* @brief Init UHCI hardware instance.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param uhci_num UHCI number
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void uhci_hal_init(uhci_hal_context_t *hal, int uhci_num);
|
||||
|
||||
/**
|
||||
* @brief Deinit UHCI hardware instance.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param uhci_num UHCI number
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void uhci_hal_deinit(uhci_hal_context_t *hal);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,14 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Though the UHCI driver hasn't been published, some types are defined here
|
||||
// for users to develop over the HAL. See example: controller_hci_uart_esp32c3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
20
components/hal/uhci_hal.c
Normal file
20
components/hal/uhci_hal.c
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// The HAL layer for UHCI (common part)
|
||||
#include "hal/uhci_hal.h"
|
||||
#include "hal/uhci_ll.h"
|
||||
|
||||
void uhci_hal_init(uhci_hal_context_t *hal, int uhci_num)
|
||||
{
|
||||
hal->dev = UHCI_LL_GET_HW(uhci_num);
|
||||
uhci_ll_init(hal->dev);
|
||||
}
|
||||
|
||||
void uhci_hal_deinit(uhci_hal_context_t *hal)
|
||||
{
|
||||
hal->dev = NULL;
|
||||
}
|
@@ -19,6 +19,10 @@ config SOC_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_AHB_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
@@ -1015,6 +1019,10 @@ config SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_NUM
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_COEX_HW_PTI
|
||||
bool
|
||||
default y
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#define SOC_DEDICATED_GPIO_SUPPORTED 1
|
||||
#define SOC_UART_SUPPORTED 1
|
||||
#define SOC_GDMA_SUPPORTED 1
|
||||
#define SOC_UHCI_SUPPORTED 1
|
||||
#define SOC_AHB_GDMA_SUPPORTED 1
|
||||
#define SOC_GPTIMER_SUPPORTED 1
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
@@ -424,6 +425,9 @@
|
||||
|
||||
#define SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE (1)
|
||||
|
||||
/*--------------------------- UHCI CAPS -------------------------------------*/
|
||||
#define SOC_UHCI_NUM (1UL)
|
||||
|
||||
/*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
|
||||
#define SOC_COEX_HW_PTI (1)
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -10,8 +10,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef volatile struct uhci_dev_s {
|
||||
union {
|
||||
typedef struct uhci_dev_t {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t tx_rst: 1; /*Write 1 then write 0 to this bit to reset decode state machine.*/
|
||||
uint32_t rx_rst: 1; /*Write 1 then write 0 to this bit to reset encode state machine.*/
|
||||
@@ -30,7 +30,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} conf0;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start: 1; /*a*/
|
||||
uint32_t tx_start: 1; /*a*/
|
||||
@@ -45,7 +45,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_raw;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start: 1; /*a*/
|
||||
uint32_t tx_start: 1; /*a*/
|
||||
@@ -60,7 +60,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_st;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start: 1; /*a*/
|
||||
uint32_t tx_start: 1; /*a*/
|
||||
@@ -75,7 +75,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_ena;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start: 1; /*a*/
|
||||
uint32_t tx_start: 1; /*a*/
|
||||
@@ -90,7 +90,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_clr;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t check_sum_en: 1; /*a*/
|
||||
uint32_t check_seq_en: 1; /*a*/
|
||||
@@ -106,7 +106,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} conf1;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_err_cause: 3; /*a*/
|
||||
uint32_t decode_state: 3; /*a*/
|
||||
@@ -114,14 +114,14 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} state0;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t encode_state: 3; /*a*/
|
||||
uint32_t reserved3: 29;
|
||||
};
|
||||
uint32_t val;
|
||||
} state1;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t tx_c0_esc_en: 1; /*a*/
|
||||
uint32_t tx_db_esc_en: 1; /*a*/
|
||||
@@ -135,7 +135,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} escape_conf;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t txfifo_timeout: 8; /*a*/
|
||||
uint32_t txfifo_timeout_shift: 3; /*a*/
|
||||
@@ -147,7 +147,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} hung_conf;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t ack_num: 3;
|
||||
uint32_t ack_num_load: 1; /*a*/
|
||||
@@ -156,7 +156,7 @@ typedef volatile struct uhci_dev_s {
|
||||
uint32_t val;
|
||||
} ack_num;
|
||||
uint32_t rx_head; /*a*/
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t single_send_num: 3; /*a*/
|
||||
uint32_t single_send_en: 1; /*a*/
|
||||
@@ -166,10 +166,10 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} quick_sent;
|
||||
struct {
|
||||
volatile struct {
|
||||
uint32_t w_data[2]; /*a*/
|
||||
} q_data[7];
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seper_char: 8; /*a*/
|
||||
uint32_t seper_esc_char0: 8; /*a*/
|
||||
@@ -178,7 +178,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf0;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seq0: 8; /*a*/
|
||||
uint32_t seq0_char0: 8; /*a*/
|
||||
@@ -187,7 +187,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf1;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seq1: 8; /*a*/
|
||||
uint32_t seq1_char0: 8; /*a*/
|
||||
@@ -196,7 +196,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf2;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seq2: 8; /*a*/
|
||||
uint32_t seq2_char0: 8; /*a*/
|
||||
@@ -205,14 +205,14 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf3;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t thrs: 13; /*a*/
|
||||
uint32_t reserved13:19;
|
||||
};
|
||||
uint32_t val;
|
||||
} pkt_thres;
|
||||
uint32_t date; /*a*/
|
||||
volatile uint32_t date; /*a*/
|
||||
} uhci_dev_t;
|
||||
extern uhci_dev_t UHCI0;
|
||||
#ifdef __cplusplus
|
||||
|
@@ -15,6 +15,10 @@ config SOC_UART_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
@@ -1367,6 +1371,10 @@ config SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_NUM
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_COEX_HW_PTI
|
||||
bool
|
||||
default y
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#define SOC_ADC_SUPPORTED 1
|
||||
#define SOC_DEDICATED_GPIO_SUPPORTED 1
|
||||
#define SOC_UART_SUPPORTED 1
|
||||
#define SOC_UHCI_SUPPORTED 1
|
||||
#define SOC_GDMA_SUPPORTED 1
|
||||
#define SOC_AHB_GDMA_SUPPORTED 1
|
||||
#define SOC_GPTIMER_SUPPORTED 1
|
||||
@@ -526,6 +527,9 @@
|
||||
#define SOC_UART_WAKEUP_SUPPORT_START_BIT_MODE (1)
|
||||
#define SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE (1)
|
||||
|
||||
/*--------------------------- UHCI CAPS -------------------------------------*/
|
||||
#define SOC_UHCI_NUM (1UL)
|
||||
|
||||
// TODO: IDF-5679 (Copy from esp32c3, need check)
|
||||
|
||||
/*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
|
||||
|
@@ -618,7 +618,7 @@ typedef union {
|
||||
} uhci_date_reg_t;
|
||||
|
||||
|
||||
typedef struct uhci_dev_s {
|
||||
typedef struct uhci_dev_t {
|
||||
volatile uhci_conf0_reg_t conf0;
|
||||
volatile uhci_int_raw_reg_t int_raw;
|
||||
volatile uhci_int_st_reg_t int_st;
|
||||
|
@@ -618,7 +618,7 @@ typedef union {
|
||||
} uhci_date_reg_t;
|
||||
|
||||
|
||||
typedef struct uhci_dev_s {
|
||||
typedef struct uhci_dev_t {
|
||||
volatile uhci_conf0_reg_t conf0;
|
||||
volatile uhci_int_raw_reg_t int_raw;
|
||||
volatile uhci_int_st_reg_t int_st;
|
||||
|
@@ -23,6 +23,10 @@ config SOC_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_AHB_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
@@ -1943,6 +1947,10 @@ config SOC_LP_I2S_SUPPORT_VAD
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_NUM
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_COEX_HW_PTI
|
||||
bool
|
||||
default y
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#define SOC_DEDICATED_GPIO_SUPPORTED 1
|
||||
#define SOC_UART_SUPPORTED 1
|
||||
#define SOC_GDMA_SUPPORTED 1
|
||||
#define SOC_UHCI_SUPPORTED 1
|
||||
#define SOC_AHB_GDMA_SUPPORTED 1
|
||||
#define SOC_AXI_GDMA_SUPPORTED 1
|
||||
#define SOC_DW_GDMA_SUPPORTED 1
|
||||
@@ -712,6 +713,9 @@
|
||||
/*-------------------------- LP_VAD CAPS -------------------------------------*/
|
||||
#define SOC_LP_I2S_SUPPORT_VAD (1)
|
||||
|
||||
/*--------------------------- UHCI CAPS -------------------------------------*/
|
||||
#define SOC_UHCI_NUM (1UL)
|
||||
|
||||
// TODO: IDF-5679 (Copy from esp32c3, need check)
|
||||
/*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
|
||||
#define SOC_COEX_HW_PTI (1)
|
||||
|
@@ -797,7 +797,7 @@ typedef union {
|
||||
} uhci_date_reg_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct uhci_dev_t{
|
||||
volatile uhci_conf0_reg_t conf0;
|
||||
volatile uhci_int_raw_reg_t int_raw;
|
||||
volatile uhci_int_st_reg_t int_st;
|
||||
@@ -833,6 +833,7 @@ typedef struct {
|
||||
volatile uhci_date_reg_t date;
|
||||
} uhci_dev_t;
|
||||
|
||||
extern uhci_dev_t UHCI0;
|
||||
|
||||
#ifndef __cplusplus
|
||||
_Static_assert(sizeof(uhci_dev_t) == 0x84, "Invalid size of uhci_dev_t structure");
|
||||
|
@@ -31,6 +31,10 @@ config SOC_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_AHB_GDMA_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
@@ -1127,6 +1131,10 @@ config SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_UHCI_NUM
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_USB_OTG_PERIPH_NUM
|
||||
int
|
||||
default 1
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#define SOC_WIFI_SUPPORTED 1
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
#define SOC_GDMA_SUPPORTED 1
|
||||
#define SOC_UHCI_SUPPORTED 1
|
||||
#define SOC_AHB_GDMA_SUPPORTED 1
|
||||
#define SOC_GPTIMER_SUPPORTED 1
|
||||
#define SOC_LCDCAM_SUPPORTED 1
|
||||
@@ -433,6 +434,9 @@
|
||||
|
||||
#define SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE (1)
|
||||
|
||||
/*--------------------------- UHCI CAPS -------------------------------------*/
|
||||
#define SOC_UHCI_NUM (1UL)
|
||||
|
||||
/*-------------------------- USB CAPS ----------------------------------------*/
|
||||
#define SOC_USB_OTG_PERIPH_NUM (1U)
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,8 +12,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef volatile struct uhci_dev_s {
|
||||
union {
|
||||
typedef struct uhci_dev_t {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t tx_rst : 1;
|
||||
uint32_t rx_rst : 1;
|
||||
@@ -32,7 +32,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} conf0;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start : 1;
|
||||
uint32_t tx_start : 1;
|
||||
@@ -47,7 +47,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_raw;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start : 1;
|
||||
uint32_t tx_start : 1;
|
||||
@@ -62,7 +62,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_st;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start : 1;
|
||||
uint32_t tx_start : 1;
|
||||
@@ -77,7 +77,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_ena;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_start : 1;
|
||||
uint32_t tx_start : 1;
|
||||
@@ -92,7 +92,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} int_clr;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t app_ctrl0_int_set : 1;
|
||||
uint32_t app_ctrl1_int_set : 1;
|
||||
@@ -100,7 +100,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} app_int_set;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t check_sum_en : 1;
|
||||
uint32_t check_seq_en : 1;
|
||||
@@ -116,7 +116,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} conf1;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t rx_err_cause : 3;
|
||||
uint32_t decode_state : 3;
|
||||
@@ -124,14 +124,14 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} state0;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t encode_state : 3;
|
||||
uint32_t reserved3 : 29;
|
||||
};
|
||||
uint32_t val;
|
||||
} state1;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t tx_c0_esc_en : 1;
|
||||
uint32_t tx_db_esc_en : 1;
|
||||
@@ -145,7 +145,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} escape_conf;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t txfifo_timeout : 8;
|
||||
uint32_t txfifo_timeout_shift : 3;
|
||||
@@ -157,7 +157,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} hung_conf;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t ack_num : 3;
|
||||
uint32_t ack_num_load : 1;
|
||||
@@ -166,7 +166,7 @@ typedef volatile struct uhci_dev_s {
|
||||
uint32_t val;
|
||||
} ack_num;
|
||||
uint32_t rx_head;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t single_send_num : 3;
|
||||
uint32_t single_send_en : 1;
|
||||
@@ -176,10 +176,10 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} quick_sent;
|
||||
struct {
|
||||
volatile struct {
|
||||
uint32_t word[2];
|
||||
} q_data[7];
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seper_char : 8;
|
||||
uint32_t seper_esc_char0 : 8;
|
||||
@@ -188,7 +188,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf0;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seq0 : 8;
|
||||
uint32_t seq0_char0 : 8;
|
||||
@@ -197,7 +197,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf1;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seq1 : 8;
|
||||
uint32_t seq1_char0 : 8;
|
||||
@@ -206,7 +206,7 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf2;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t seq2 : 8;
|
||||
uint32_t seq2_char0 : 8;
|
||||
@@ -215,14 +215,14 @@ typedef volatile struct uhci_dev_s {
|
||||
};
|
||||
uint32_t val;
|
||||
} esc_conf3;
|
||||
union {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t thrs : 13;
|
||||
uint32_t reserved13 : 19;
|
||||
};
|
||||
uint32_t val;
|
||||
} pkt_thres;
|
||||
uint32_t date;
|
||||
volatile uint32_t date;
|
||||
} uhci_dev_t;
|
||||
extern uhci_dev_t UHCI0;
|
||||
#ifdef __cplusplus
|
||||
|
@@ -51,7 +51,7 @@ struct uart_env_tag {
|
||||
|
||||
struct uart_env_tag uart_env;
|
||||
|
||||
static volatile uhci_dev_t *s_uhci_hw = &UHCI0;
|
||||
static uhci_dev_t *s_uhci_hw = &UHCI0;
|
||||
static gdma_channel_handle_t s_rx_channel;
|
||||
static gdma_channel_handle_t s_tx_channel;
|
||||
|
||||
@@ -247,7 +247,7 @@ void uhci_uart_install(void)
|
||||
|
||||
// configure UHCI
|
||||
uhci_ll_init(s_uhci_hw);
|
||||
uhci_ll_set_eof_mode(s_uhci_hw, UHCI_RX_LEN_EOF);
|
||||
uhci_ll_rx_set_eof_mode(s_uhci_hw, UHCI_RX_LEN_EOF);
|
||||
// disable software flow control
|
||||
s_uhci_hw->escape_conf.val = 0;
|
||||
uhci_ll_attach_uart_port(s_uhci_hw, 1);
|
||||
|
@@ -512,6 +512,14 @@ examples/peripherals/twai/twai_self_test:
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
|
||||
examples/peripherals/uart/uart_dma_ota:
|
||||
disable:
|
||||
- if: SOC_UHCI_SUPPORTED != 1
|
||||
disable_test:
|
||||
- if: IDF_TARGET in ["esp32p4"]
|
||||
temporary: true
|
||||
reason: Lack runners
|
||||
|
||||
examples/peripherals/uart/uart_echo_rs485:
|
||||
enable:
|
||||
- if: INCLUDE_DEFAULT == 1
|
||||
|
6
examples/peripherals/uart/uart_dma_ota/CMakeLists.txt
Normal file
6
examples/peripherals/uart/uart_dma_ota/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(uart_dma_ota)
|
113
examples/peripherals/uart/uart_dma_ota/README.md
Normal file
113
examples/peripherals/uart/uart_dma_ota/README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-P4 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# UART OTA Example
|
||||
|
||||
This example demonstrates how to use UART to perform an Over-the-Air (OTA) firmware update on an ESP32-based device. It transfers firmware data via UART using DMA and applies the update seamlessly.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
The example can be run on any development board, that is based on the Espressif SoC. The board shall be connected to a computer with a single USB cable for flashing and monitoring. The external interface should have 3.3V outputs. You may use e.g. 3.3V compatible USB-to-Serial dongle.
|
||||
|
||||
### Software Required
|
||||
|
||||
1. Partition Table:
|
||||
|
||||
The example requires a valid partition table with an OTA partition. Update your partitions.csv or equivalent partition table to include an OTA data partition. Below is an example of a partition table with OTA support:
|
||||
|
||||
```
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
ota_0, app, ota_0, 0x110000, 1M,
|
||||
ota_1, app, ota_1, 0x210000, 1M,
|
||||
```
|
||||
|
||||
You can also use `CONFIG_PARTITION_TABLE_TWO_OTA_LARGE` if you don't want to define a partition table by yourself.
|
||||
|
||||
2. Firmware Binary:
|
||||
|
||||
Prepare the firmware binary (.bin file) you want to flash via OTA. This binary should be compatible with the ESP32 platform and must match the partition table configuration.
|
||||
Example:
|
||||
|
||||
*1.* Build the firmware binary via:
|
||||
|
||||
```
|
||||
idf.py build
|
||||
```
|
||||
|
||||
*2.* Locate the binary file in the build/ directory (e.g., build/your_project.bin)
|
||||
|
||||
### Setup the Hardware
|
||||
|
||||
Connect the external serial interface to the board as follows.
|
||||
|
||||
```
|
||||
|
||||
+-------------------+ +-------------------+
|
||||
| ESP Chip | | External UART Pin |
|
||||
| Interface | | |
|
||||
+-------------------+ +-------------------+
|
||||
| |
|
||||
| |
|
||||
Receive Data (RxD): + GPIO9 <------------------------------> TxD +
|
||||
| |
|
||||
Ground: + GND <--------------------------------> GND +
|
||||
| |
|
||||
|
||||
```
|
||||
|
||||
### Configure the project
|
||||
|
||||
Use the command below to configure project using Kconfig menu as showed in the table above.
|
||||
The default Kconfig values can be changed such as: UART_BAUD_RATE, UART_PORT_NUM, UART_RX_IO (Refer to Kconfig file).
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
Then transmit the prepared bin to ESP-chips. Please note that the bin should be in one packet transaction which means there should not have any interval during the transaction in this example because it uses idle to judge whether the packet finishes or not.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (277) main_task: Started on CPU0
|
||||
I (287) main_task: Calling app_main()
|
||||
I (287) uhci-example: UHCI initialized
|
||||
I (447) uhci-example: OTA process started
|
||||
I (18047) uhci-example: Received size: 150032
|
||||
I (18047) uhci-example: Total received size: 150032
|
||||
I (18047) esp_image: segment 0: paddr=001d0020 vaddr=3c020020 size=06748h ( 26440) map
|
||||
I (18057) esp_image: segment 1: paddr=001d6770 vaddr=3fc8b400 size=0111ch ( 4380)
|
||||
I (18067) esp_image: segment 2: paddr=001d7894 vaddr=40380000 size=08784h ( 34692)
|
||||
I (18077) esp_image: segment 3: paddr=001e0020 vaddr=42000020 size=11d98h ( 73112) map
|
||||
....
|
||||
I (18147) uhci-example: OTA update successful. Rebooting...
|
||||
.... (the following part depends on your bin, this output depends on example/get-started/hello_world)
|
||||
I (268) sleep_gpio: Enable automatic switching of GPIO sleep configuration
|
||||
I (275) main_task: Started on CPU0
|
||||
I (275) main_task: Calling app_main()
|
||||
Hello world!
|
||||
This is esp32c3 chip with 1 CPU core(s), WiFi/BLE, silicon revision v0.4, 4MB external flash
|
||||
Minimum free heap size: 333524 bytes
|
||||
Restarting in 10 seconds...
|
||||
Restarting in 9 seconds...
|
||||
Restarting in 8 seconds...
|
||||
```
|
||||
|
||||
|
||||
|
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "uart_dma_ota_example_main.c"
|
||||
REQUIRES esp_driver_uart app_update
|
||||
INCLUDE_DIRS ".")
|
@@ -0,0 +1,21 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config UART_RX_IO
|
||||
int "UART RX GPIO Num"
|
||||
default 2
|
||||
help
|
||||
GPIO number for UART RX line.
|
||||
|
||||
config UART_PORT_NUM
|
||||
int "UART port number"
|
||||
default 1
|
||||
help
|
||||
The UART device attached to DMA.
|
||||
|
||||
config UART_BAUD_RATE
|
||||
int "uart baud rate"
|
||||
default 1000000
|
||||
help
|
||||
Uart baud rate.
|
||||
|
||||
endmenu
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/uart.h"
|
||||
#include "driver/uhci.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
static const char *TAG = "uhci-example";
|
||||
|
||||
#define EXAMPLE_UART_NUM CONFIG_UART_PORT_NUM
|
||||
#define EXAMPLE_UART_BAUD_RATE CONFIG_UART_BAUD_RATE
|
||||
#define EXAMPLE_UART_RX_IO CONFIG_UART_RX_IO
|
||||
#define UART_DMA_OTA_BUFFER_SIZE (10 * 1024)
|
||||
|
||||
typedef enum {
|
||||
UHCI_EVT_PARTIAL_DATA,
|
||||
UHCI_EVT_EOF,
|
||||
} uhci_event_t;
|
||||
|
||||
typedef struct {
|
||||
QueueHandle_t uhci_queue;
|
||||
size_t receive_size;
|
||||
uint8_t *ota_data1;
|
||||
uint8_t *ota_data2;
|
||||
bool use_ota_data1;
|
||||
} ota_example_context_t;
|
||||
|
||||
static bool s_uhci_rx_event_cbs(uhci_controller_handle_t uhci_ctrl, const uhci_rx_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
ota_example_context_t *ctx = (ota_example_context_t *)user_ctx;
|
||||
BaseType_t xTaskWoken = 0;
|
||||
uhci_event_t evt = 0;
|
||||
|
||||
if (edata->flags.totally_received) {
|
||||
evt = UHCI_EVT_EOF;
|
||||
} else {
|
||||
evt = UHCI_EVT_PARTIAL_DATA;
|
||||
}
|
||||
|
||||
// Choose the buffer to store received data
|
||||
ctx->receive_size = edata->recv_size;
|
||||
if (ctx->use_ota_data1) {
|
||||
ctx->ota_data1 = edata->data;
|
||||
} else {
|
||||
ctx->ota_data2 = edata->data;
|
||||
}
|
||||
|
||||
// Toggle the buffer for the next receive
|
||||
ctx->use_ota_data1 = !ctx->use_ota_data1;
|
||||
|
||||
xQueueSendFromISR(ctx->uhci_queue, &evt, &xTaskWoken);
|
||||
return xTaskWoken;
|
||||
}
|
||||
|
||||
static void perform_ota_update(uhci_controller_handle_t uhci_ctrl, ota_example_context_t *ctx)
|
||||
{
|
||||
const esp_partition_t *ota_partition = esp_ota_get_next_update_partition(NULL);
|
||||
if (!ota_partition) {
|
||||
ESP_LOGE(TAG, "No OTA partition found");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_ota_handle_t ota_handle;
|
||||
ESP_ERROR_CHECK(esp_ota_begin(ota_partition, OTA_SIZE_UNKNOWN, &ota_handle));
|
||||
ESP_LOGI(TAG, "OTA process started");
|
||||
|
||||
uhci_event_t evt;
|
||||
uint32_t received_size = 0;
|
||||
uint8_t *pdata = heap_caps_calloc(1, UART_DMA_OTA_BUFFER_SIZE, MALLOC_CAP_DEFAULT);
|
||||
assert(pdata);
|
||||
ESP_ERROR_CHECK(uhci_receive(uhci_ctrl, pdata, UART_DMA_OTA_BUFFER_SIZE));
|
||||
while (1) {
|
||||
if (xQueueReceive(ctx->uhci_queue, &evt, portMAX_DELAY) == pdTRUE) {
|
||||
uint8_t *data_to_write = ctx->use_ota_data1 ? ctx->ota_data2 : ctx->ota_data1;
|
||||
ESP_ERROR_CHECK(esp_ota_write(ota_handle, data_to_write, ctx->receive_size));
|
||||
received_size += ctx->receive_size;
|
||||
if (evt == UHCI_EVT_EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
free(pdata);
|
||||
|
||||
ESP_LOGI(TAG, "Total received size: %ld", received_size);
|
||||
ESP_ERROR_CHECK(esp_ota_end(ota_handle));
|
||||
ESP_ERROR_CHECK(esp_ota_set_boot_partition(ota_partition));
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = EXAMPLE_UART_BAUD_RATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(uart_param_config(EXAMPLE_UART_NUM, &uart_config));
|
||||
ESP_ERROR_CHECK(uart_set_pin(EXAMPLE_UART_NUM, -1, EXAMPLE_UART_RX_IO, -1, -1));
|
||||
|
||||
uhci_controller_config_t uhci_cfg = {
|
||||
.uart_port = EXAMPLE_UART_NUM,
|
||||
.tx_trans_queue_depth = 1,
|
||||
.max_receive_internal_mem = UART_DMA_OTA_BUFFER_SIZE,
|
||||
.max_transmit_size = UART_DMA_OTA_BUFFER_SIZE,
|
||||
.dma_burst_size = 32,
|
||||
.rx_eof_flags.idle_eof = 1, // receive finishes when rx line turns idle.
|
||||
};
|
||||
|
||||
uhci_controller_handle_t uhci_ctrl;
|
||||
ESP_ERROR_CHECK(uhci_new_controller(&uhci_cfg, &uhci_ctrl));
|
||||
|
||||
ESP_LOGI(TAG, "UHCI initialized, baud rate is %d, rx pin is %d", uart_config.baud_rate, EXAMPLE_UART_RX_IO);
|
||||
|
||||
ota_example_context_t *ctx = calloc(1, sizeof(ota_example_context_t));
|
||||
assert(ctx);
|
||||
|
||||
ctx->uhci_queue = xQueueCreate(2, sizeof(uhci_event_t));
|
||||
assert(ctx->uhci_queue);
|
||||
|
||||
ctx->use_ota_data1 = true; // Start with ota_data1
|
||||
|
||||
uhci_event_callbacks_t uhci_cbs = {
|
||||
.on_rx_trans_event = s_uhci_rx_event_cbs,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(uhci_register_event_callbacks(uhci_ctrl, &uhci_cbs, ctx));
|
||||
|
||||
perform_ota_update(uhci_ctrl, ctx);
|
||||
|
||||
ESP_ERROR_CHECK(uhci_del_controller(uhci_ctrl));
|
||||
|
||||
free(ctx);
|
||||
ESP_LOGI(TAG, "OTA update successful. Rebooting...");
|
||||
esp_restart();
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import os
|
||||
|
||||
import pytest
|
||||
import serial
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
PACKET_SIZE = 10 * 1024 # 10 KB for upper computer
|
||||
FLASH_PORT = '/dev/serial_ports/ttyUSB-esp32'
|
||||
|
||||
|
||||
def send_file_via_uart(port: str, baud_rate: int, file_path: str, packet_size: int) -> None:
|
||||
try:
|
||||
with serial.Serial(port, baud_rate, timeout=1) as ser:
|
||||
print(f'Opened {port} at {baud_rate} baud')
|
||||
|
||||
file_size = os.path.getsize(file_path)
|
||||
print(f'File size: {file_size} bytes')
|
||||
|
||||
with open(file_path, 'rb') as file:
|
||||
bytes_sent = 0
|
||||
while bytes_sent < file_size:
|
||||
chunk = file.read(packet_size)
|
||||
if not chunk:
|
||||
break
|
||||
ser.write(chunk)
|
||||
bytes_sent += len(chunk)
|
||||
|
||||
print('File sent successfully!')
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error: {e}')
|
||||
|
||||
|
||||
@pytest.mark.usb_serial_jtag
|
||||
@pytest.mark.parametrize(
|
||||
'port, flash_port, config',
|
||||
[
|
||||
pytest.param('/dev/serial_ports/ttyACM-esp32', FLASH_PORT, 'defaults'),
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32c6', 'esp32c3', 'esp32s3'], indirect=['target'])
|
||||
def test_uart_dma_ota(dut: Dut) -> None:
|
||||
dut.expect_exact('uhci-example: OTA process started')
|
||||
# We OTA the same binary to another partition and switch to there.
|
||||
binary_path = os.path.join(dut.app.binary_path, 'uart_dma_ota.bin')
|
||||
assert os.path.exists(binary_path), f'OTA binary not found at {binary_path}'
|
||||
|
||||
buad_rate = dut.app.sdkconfig.get('UART_BAUD_RATE')
|
||||
send_file_via_uart(FLASH_PORT, buad_rate, binary_path, PACKET_SIZE)
|
||||
|
||||
dut.expect('OTA update successful. Rebooting', timeout=10)
|
||||
dut.expect('ESP-ROM:', timeout=10)
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
|
||||
CONFIG_UART_PORT_NUM=0
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c3"
|
||||
CONFIG_UART_RX_IO=20
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c6"
|
||||
CONFIG_UART_RX_IO=17
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_UART_RX_IO=44
|
@@ -0,0 +1,3 @@
|
||||
# partition table layout, with a 4MB flash size
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_TWO_OTA_LARGE=y
|
Reference in New Issue
Block a user