3#if SOC_GDMA_SUPPORTED && SOC_UHCI_SUPPORTED
10#include "esp_heap_caps.h"
11#include "esp_memory_utils.h"
12#include "esp_private/periph_ctrl.h"
13#include "hal/uhci_ll.h"
14#include "soc/ext_mem_defs.h"
22constexpr uint32_t DMA_RX_NODE_COUNT = 8;
26constexpr size_t DMA_MAX_BUFFER_SIZE_PER_LINK_ITEM = 4095U;
36 uint32_t reserved24 : 4;
38 uint32_t reserved29 : 1;
46constexpr uint32_t GDMA_OWNER_CPU = 0U;
47constexpr uint32_t GDMA_OWNER_DMA = 1U;
51size_t AlignUp(
size_t value,
size_t align)
57 return ((value + align - 1) / align) * align;
64uintptr_t CacheAddrToNonCache(uintptr_t addr)
66#if SOC_NON_CACHEABLE_OFFSET
67 return addr + SOC_NON_CACHEABLE_OFFSET;
75GdmaLinkItem* LinkItemFromHeadAddr(uintptr_t head_addr)
77 return reinterpret_cast<GdmaLinkItem*
>(CacheAddrToNonCache(head_addr));
80#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
81extern "C" esp_err_t esp_cache_msync(
void* addr,
size_t size,
int flags);
83constexpr int CACHE_SYNC_FLAG_UNALIGNED = (1 << 1);
84constexpr int CACHE_SYNC_FLAG_DIR_C2M = (1 << 2);
85constexpr int CACHE_SYNC_FLAG_DIR_M2C = (1 << 3);
89bool CacheSyncDmaBuffer(
const void* addr,
size_t size,
bool cache_to_mem)
91 if ((addr ==
nullptr) || (size == 0U))
96#if SOC_PSRAM_DMA_CAPABLE && !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
97 if (!esp_ptr_external_ram(addr))
103 int flags = cache_to_mem ? CACHE_SYNC_FLAG_DIR_C2M : CACHE_SYNC_FLAG_DIR_M2C;
104 flags |= CACHE_SYNC_FLAG_UNALIGNED;
106 const esp_err_t ret = esp_cache_msync(
const_cast<void*
>(addr), size, flags);
108 return (ret == ESP_OK) || (ret == ESP_ERR_INVALID_ARG);
118bool IRAM_ATTR ESP32UART::DmaTxEofCallback(gdma_channel_handle_t, gdma_event_data_t*,
121 auto* uart =
static_cast<ESP32UART*
>(user_data);
131bool IRAM_ATTR ESP32UART::DmaTxDescrErrCallback(gdma_channel_handle_t, gdma_event_data_t*,
134 auto* uart =
static_cast<ESP32UART*
>(user_data);
137 uart->HandleDmaTxError();
144bool IRAM_ATTR ESP32UART::DmaRxDoneCallback(gdma_channel_handle_t,
145 gdma_event_data_t* event_data,
148 auto* uart =
static_cast<ESP32UART*
>(user_data);
151 uart->HandleDmaRxDone(event_data);
158bool IRAM_ATTR ESP32UART::DmaRxDescrErrCallback(gdma_channel_handle_t, gdma_event_data_t*,
161 auto* uart =
static_cast<ESP32UART*
>(user_data);
164 uart->HandleDmaRxError();
179 if (dma_backend_enabled_)
184 periph_module_enable(PERIPH_UHCI0_MODULE);
185 periph_module_reset(PERIPH_UHCI0_MODULE);
187 uhci_hal_init(&uhci_hal_, 0);
188 uhci_ll_attach_uart_port(uhci_hal_.dev,
uart_num_);
190 uhci_seper_chr_t sep_chr = {};
191 sep_chr.sub_chr_en = 0;
192 uhci_ll_set_seper_chr(uhci_hal_.dev, &sep_chr);
193 uhci_ll_rx_set_eof_mode(uhci_hal_.dev, UHCI_RX_IDLE_EOF);
195 gdma_channel_alloc_config_t tx_cfg = {
196 .sibling_chan =
nullptr,
197 .direction = GDMA_CHANNEL_DIRECTION_TX,
200 if (gdma_new_ahb_channel(&tx_cfg, &tx_dma_channel_) != ESP_OK)
205 if (gdma_connect(tx_dma_channel_, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)) !=
211 gdma_transfer_config_t transfer_cfg = {
212 .max_data_burst_size = 0,
213 .access_ext_mem =
true,
215 if (gdma_config_transfer(tx_dma_channel_, &transfer_cfg) != ESP_OK)
220 size_t tx_int_alignment = 1;
221 size_t tx_ext_alignment = 1;
222 if (gdma_get_alignment_constraints(tx_dma_channel_, &tx_int_alignment,
223 &tx_ext_alignment) != ESP_OK)
227 const size_t tx_dma_alignment =
228 std::max<size_t>(1, std::max(tx_int_alignment, tx_ext_alignment));
230 gdma_strategy_config_t tx_strategy = {
232 .auto_update_desc =
true,
233 .eof_till_data_popped =
true,
235 if (gdma_apply_strategy(tx_dma_channel_, &tx_strategy) != ESP_OK)
240 gdma_link_list_config_t tx_link_cfg = {
245 gdma_link_list_handle_t tx_dma_links[2] = {
nullptr,
nullptr};
247 for (
int i = 0; i < 2; ++i)
249 if (gdma_new_link_list(&tx_link_cfg, &tx_dma_links[i]) != ESP_OK)
254 gdma_buffer_mount_config_t tx_mount = {
256 .buffer_alignment = tx_dma_alignment,
262 .bypass_buffer_align_check = 0,
266 if (gdma_link_mount_buffers(tx_dma_links[i], 0, &tx_mount, 1,
nullptr) != ESP_OK)
271 tx_dma_head_addr_[i] = gdma_link_get_head_addr(tx_dma_links[i]);
272 if (tx_dma_head_addr_[i] == 0U)
278 gdma_tx_event_callbacks_t tx_callbacks = {
279 .on_trans_eof = DmaTxEofCallback,
280 .on_descr_err = DmaTxDescrErrCallback,
282 if (gdma_register_tx_event_callbacks(tx_dma_channel_, &tx_callbacks,
this) != ESP_OK)
287 gdma_channel_alloc_config_t rx_cfg = {
288 .sibling_chan =
nullptr,
289 .direction = GDMA_CHANNEL_DIRECTION_RX,
292 if (gdma_new_ahb_channel(&rx_cfg, &rx_dma_channel_) != ESP_OK)
297 if (gdma_connect(rx_dma_channel_, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)) !=
303 if (gdma_config_transfer(rx_dma_channel_, &transfer_cfg) != ESP_OK)
308 size_t rx_int_alignment = 1;
309 size_t rx_ext_alignment = 1;
310 if (gdma_get_alignment_constraints(rx_dma_channel_, &rx_int_alignment,
311 &rx_ext_alignment) != ESP_OK)
315 const size_t rx_dma_alignment =
316 std::max<size_t>(1, std::max(rx_int_alignment, rx_ext_alignment));
318 gdma_link_list_config_t rx_link_cfg = {
319 .num_items = DMA_RX_NODE_COUNT,
323 if (gdma_new_link_list(&rx_link_cfg, &rx_dma_link_) != ESP_OK)
330 const size_t rx_chunk_target = std::min<size_t>(
332 rx_dma_chunk_size_ = std::max<size_t>(AlignUp(rx_chunk_target, 4), 32);
333 const size_t rx_storage_alignment = std::max<size_t>(4, rx_dma_alignment);
334 const size_t rx_storage_bytes =
335 AlignUp(rx_dma_chunk_size_ * DMA_RX_NODE_COUNT, rx_storage_alignment);
337 rx_dma_storage_ =
static_cast<uint8_t*
>(
338 heap_caps_aligned_alloc(rx_storage_alignment, rx_storage_bytes,
339 MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT));
340 if (rx_dma_storage_ ==
nullptr)
345 std::array<gdma_buffer_mount_config_t, DMA_RX_NODE_COUNT> rx_mount = {};
346 for (uint32_t i = 0; i < DMA_RX_NODE_COUNT; ++i)
348 rx_mount[i] = gdma_buffer_mount_config_t{
349 .buffer = rx_dma_storage_ + (
static_cast<size_t>(i) * rx_dma_chunk_size_),
350 .buffer_alignment = rx_dma_alignment,
351 .length = rx_dma_chunk_size_,
356 .bypass_buffer_align_check = 0,
361 if (gdma_link_mount_buffers(rx_dma_link_, 0, rx_mount.data(), DMA_RX_NODE_COUNT,
367 gdma_rx_event_callbacks_t rx_callbacks = {
368 .on_recv_eof =
nullptr,
369 .on_descr_err = DmaRxDescrErrCallback,
370 .on_recv_done = DmaRxDoneCallback,
372 if (gdma_register_rx_event_callbacks(rx_dma_channel_, &rx_callbacks,
this) != ESP_OK)
377 if (gdma_reset(rx_dma_channel_) != ESP_OK)
382 if (gdma_start(rx_dma_channel_, gdma_link_get_head_addr(rx_dma_link_)) != ESP_OK)
387 rx_dma_node_index_ = 0;
388 dma_backend_enabled_ =
true;
395bool IRAM_ATTR ESP32UART::StartDmaTx()
404 if ((active_buffer ==
nullptr) || (active_len == 0) ||
405 (active_len > DMA_MAX_BUFFER_SIZE_PER_LINK_ITEM))
411 if ((link_index != 0) && (link_index != 1))
416 if (tx_dma_head_addr_[link_index] == 0U)
421 auto* desc = LinkItemFromHeadAddr(tx_dma_head_addr_[link_index]);
429 desc->buffer = active_buffer;
430 desc->dw0.size =
static_cast<uint32_t
>(active_len);
431 desc->dw0.length =
static_cast<uint32_t
>(active_len);
432 desc->dw0.err_eof = 0U;
433 desc->dw0.suc_eof = 1U;
434 desc->dw0.owner = GDMA_OWNER_DMA;
435 desc->next =
nullptr;
436 std::atomic_thread_fence(std::memory_order_release);
438#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
439 if (!CacheSyncDmaBuffer(active_buffer, active_len,
true))
445 return gdma_start(tx_dma_channel_, tx_dma_head_addr_[link_index]) == ESP_OK;
452void IRAM_ATTR ESP32UART::PushDmaRxData(
size_t recv_size,
bool in_isr)
454 if ((rx_dma_storage_ ==
nullptr) || (rx_dma_chunk_size_ == 0))
459 const size_t max_window = rx_dma_chunk_size_ * DMA_RX_NODE_COUNT;
460 size_t remaining = std::min(recv_size, max_window);
462 while (remaining > 0)
464 const size_t offset =
static_cast<size_t>(rx_dma_node_index_) * rx_dma_chunk_size_;
465 const size_t chunk = std::min(remaining, rx_dma_chunk_size_);
466 auto* chunk_ptr = rx_dma_storage_ + offset;
468#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
469 if (!CacheSyncDmaBuffer(chunk_ptr, chunk,
false))
477 rx_dma_node_index_ = (rx_dma_node_index_ + 1U) % DMA_RX_NODE_COUNT;
483void IRAM_ATTR ESP32UART::HandleDmaRxDone(gdma_event_data_t* event_data)
485 if ((rx_dma_storage_ ==
nullptr) || (rx_dma_chunk_size_ == 0))
490 if ((event_data !=
nullptr) && event_data->flags.abnormal_eof)
496 size_t recv_size = rx_dma_chunk_size_;
497 if ((event_data !=
nullptr) && event_data->flags.normal_eof)
499 const size_t eof_size = gdma_link_count_buffer_size_till_eof(
500 rx_dma_link_,
static_cast<int>(rx_dma_node_index_));
503 recv_size = eof_size;
507 PushDmaRxData(recv_size,
true);
512void IRAM_ATTR ESP32UART::HandleDmaRxError()
514 if ((rx_dma_channel_ ==
nullptr) || (rx_dma_link_ ==
nullptr))
519 gdma_stop(rx_dma_channel_);
520 gdma_reset(rx_dma_channel_);
521 rx_dma_node_index_ = 0;
522 (void)gdma_start(rx_dma_channel_, gdma_link_get_head_addr(rx_dma_link_));
528void IRAM_ATTR ESP32UART::HandleDmaTxError()
530 if (tx_dma_channel_ !=
nullptr)
532 gdma_stop(tx_dma_channel_);
533 gdma_reset(tx_dma_channel_);
int ActiveBlock() const
获取当前活动缓冲区编号 Returns the current active block index
uint8_t * Buffer(int block) const
获取指定编号缓冲区的指针 Returns the pointer of the specified block
uint8_t * ActiveBuffer() const
获取当前正在使用的缓冲区指针 Returns the currently active buffer
size_t rx_isr_buffer_size_
Size of rx_isr_buffer_.
ESP32UART(uart_port_t uart_num, int tx_pin, int rx_pin, int rts_pin=PIN_NO_CHANGE, int cts_pin=PIN_NO_CHANGE, size_t rx_buffer_size=1024, size_t tx_buffer_size=512, uint32_t tx_queue_size=5, UART::Configuration config={115200, UART::Parity::NO_PARITY, 8, 1}, bool enable_dma=true)
Create and initialize one ESP32 UART instance.
bool tx_active_valid_
Whether the active TX metadata is valid.
void OnTxTransferDone(bool in_isr, ErrorCode result)
Finalize one TX transfer result.
DoubleBuffer tx_dma_buffer_
TX double-buffer view for the DMA path.
uart_port_t uart_num_
Selected UART peripheral index.
void PushRxBytes(const uint8_t *data, size_t size, bool in_isr)
Push RX bytes into the software queue.
size_t tx_active_length_
Active TX payload length in bytes.
@ INIT_ERR
初始化错误 | Initialization error
@ NO_MEM
内存不足 | Insufficient memory
@ FAILED
操作失败 | Operation failed
@ OK
操作成功 | Operation successful