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);
124 uart->tx_dma_model_.OnTransferDone(
true);
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);
149 if ((uart !=
nullptr) && uart->rx_config_gate_.TryEnterRx())
151 uart->HandleDmaRxDone(event_data);
152 if (uart->rx_config_gate_.LeaveRx())
154 uart->tx_dma_model_.RequestConfig(
true);
162bool IRAM_ATTR ESP32UART::DmaRxDescrErrCallback(gdma_channel_handle_t, gdma_event_data_t*,
165 auto* uart =
static_cast<ESP32UART*
>(user_data);
166 if ((uart !=
nullptr) && uart->rx_config_gate_.TryEnterRx())
168 uart->HandleDmaRxError();
169 if (uart->rx_config_gate_.LeaveRx())
171 uart->tx_dma_model_.RequestConfig(
true);
187 if (dma_backend_enabled_)
192 periph_module_enable(PERIPH_UHCI0_MODULE);
193 periph_module_reset(PERIPH_UHCI0_MODULE);
195 uhci_hal_init(&uhci_hal_, 0);
196 uhci_ll_attach_uart_port(uhci_hal_.dev,
uart_num_);
198 uhci_seper_chr_t sep_chr = {};
199 sep_chr.sub_chr_en = 0;
200 uhci_ll_set_seper_chr(uhci_hal_.dev, &sep_chr);
201 uhci_ll_rx_set_eof_mode(uhci_hal_.dev, UHCI_RX_IDLE_EOF);
203 gdma_channel_alloc_config_t tx_cfg = {
204 .sibling_chan =
nullptr,
205 .direction = GDMA_CHANNEL_DIRECTION_TX,
208 if (gdma_new_ahb_channel(&tx_cfg, &tx_dma_channel_) != ESP_OK)
213 if (gdma_connect(tx_dma_channel_, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)) !=
219 gdma_transfer_config_t transfer_cfg = {
220 .max_data_burst_size = 0,
221 .access_ext_mem =
true,
223 if (gdma_config_transfer(tx_dma_channel_, &transfer_cfg) != ESP_OK)
228 size_t tx_int_alignment = 1;
229 size_t tx_ext_alignment = 1;
230 if (gdma_get_alignment_constraints(tx_dma_channel_, &tx_int_alignment,
231 &tx_ext_alignment) != ESP_OK)
235 const size_t tx_dma_alignment =
236 std::max<size_t>(1, std::max(tx_int_alignment, tx_ext_alignment));
238 gdma_strategy_config_t tx_strategy = {
240 .auto_update_desc =
true,
241 .eof_till_data_popped =
true,
243 if (gdma_apply_strategy(tx_dma_channel_, &tx_strategy) != ESP_OK)
248 gdma_link_list_config_t tx_link_cfg = {
253 gdma_link_list_handle_t tx_dma_links[2] = {
nullptr,
nullptr};
255 for (
int i = 0; i < 2; ++i)
257 if (gdma_new_link_list(&tx_link_cfg, &tx_dma_links[i]) != ESP_OK)
262 gdma_buffer_mount_config_t tx_mount = {
264 .buffer_alignment = tx_dma_alignment,
270 .bypass_buffer_align_check = 0,
274 if (gdma_link_mount_buffers(tx_dma_links[i], 0, &tx_mount, 1,
nullptr) != ESP_OK)
279 tx_dma_head_addr_[i] = gdma_link_get_head_addr(tx_dma_links[i]);
280 if (tx_dma_head_addr_[i] == 0U)
286 gdma_tx_event_callbacks_t tx_callbacks = {
287 .on_trans_eof = DmaTxEofCallback,
288 .on_descr_err = DmaTxDescrErrCallback,
290 if (gdma_register_tx_event_callbacks(tx_dma_channel_, &tx_callbacks,
this) != ESP_OK)
295 gdma_channel_alloc_config_t rx_cfg = {
296 .sibling_chan =
nullptr,
297 .direction = GDMA_CHANNEL_DIRECTION_RX,
300 if (gdma_new_ahb_channel(&rx_cfg, &rx_dma_channel_) != ESP_OK)
305 if (gdma_connect(rx_dma_channel_, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)) !=
311 if (gdma_config_transfer(rx_dma_channel_, &transfer_cfg) != ESP_OK)
316 size_t rx_int_alignment = 1;
317 size_t rx_ext_alignment = 1;
318 if (gdma_get_alignment_constraints(rx_dma_channel_, &rx_int_alignment,
319 &rx_ext_alignment) != ESP_OK)
323 const size_t rx_dma_alignment =
324 std::max<size_t>(1, std::max(rx_int_alignment, rx_ext_alignment));
326 gdma_link_list_config_t rx_link_cfg = {
327 .num_items = DMA_RX_NODE_COUNT,
331 if (gdma_new_link_list(&rx_link_cfg, &rx_dma_link_) != ESP_OK)
338 const size_t rx_chunk_target = std::min<size_t>(
340 rx_dma_chunk_size_ = std::max<size_t>(AlignUp(rx_chunk_target, 4), 32);
341 const size_t rx_storage_alignment = std::max<size_t>(4, rx_dma_alignment);
342 const size_t rx_storage_bytes =
343 AlignUp(rx_dma_chunk_size_ * DMA_RX_NODE_COUNT, rx_storage_alignment);
345 rx_dma_storage_ =
static_cast<uint8_t*
>(
346 heap_caps_aligned_alloc(rx_storage_alignment, rx_storage_bytes,
347 MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT));
348 if (rx_dma_storage_ ==
nullptr)
353 std::array<gdma_buffer_mount_config_t, DMA_RX_NODE_COUNT> rx_mount = {};
354 for (uint32_t i = 0; i < DMA_RX_NODE_COUNT; ++i)
356 rx_mount[i] = gdma_buffer_mount_config_t{
357 .buffer = rx_dma_storage_ + (
static_cast<size_t>(i) * rx_dma_chunk_size_),
358 .buffer_alignment = rx_dma_alignment,
359 .length = rx_dma_chunk_size_,
364 .bypass_buffer_align_check = 0,
369 if (gdma_link_mount_buffers(rx_dma_link_, 0, rx_mount.data(), DMA_RX_NODE_COUNT,
375 gdma_rx_event_callbacks_t rx_callbacks = {
376 .on_recv_eof =
nullptr,
377 .on_descr_err = DmaRxDescrErrCallback,
378 .on_recv_done = DmaRxDoneCallback,
380 if (gdma_register_rx_event_callbacks(rx_dma_channel_, &rx_callbacks,
this) != ESP_OK)
385 if (gdma_reset(rx_dma_channel_) != ESP_OK)
390 if (gdma_start(rx_dma_channel_, gdma_link_get_head_addr(rx_dma_link_)) != ESP_OK)
395 rx_dma_node_index_ = 0;
396 dma_backend_enabled_ =
true;
403bool IRAM_ATTR ESP32UART::StartDmaTx(uint8_t* data,
size_t size,
int block)
405 if ((tx_dma_channel_ ==
nullptr) || (data ==
nullptr) || (size == 0U) ||
406 (size > DMA_MAX_BUFFER_SIZE_PER_LINK_ITEM) || ((block != 0) && (block != 1)) ||
407 (tx_dma_head_addr_[block] == 0U))
412 auto* desc = LinkItemFromHeadAddr(tx_dma_head_addr_[block]);
419 desc->dw0.size =
static_cast<uint32_t
>(size);
420 desc->dw0.length =
static_cast<uint32_t
>(size);
421 desc->dw0.err_eof = 0U;
422 desc->dw0.suc_eof = 1U;
423 desc->dw0.owner = GDMA_OWNER_DMA;
424 desc->next =
nullptr;
425 std::atomic_thread_fence(std::memory_order_release);
427#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
428 if (!CacheSyncDmaBuffer(data, size,
true))
434 return gdma_start(tx_dma_channel_, tx_dma_head_addr_[block]) == ESP_OK;
441void IRAM_ATTR ESP32UART::PushDmaRxData(
size_t recv_size,
bool in_isr)
443 if ((rx_dma_storage_ ==
nullptr) || (rx_dma_chunk_size_ == 0))
448 const size_t max_window = rx_dma_chunk_size_ * DMA_RX_NODE_COUNT;
449 size_t remaining = std::min(recv_size, max_window);
451 while (remaining > 0)
453 const size_t offset =
static_cast<size_t>(rx_dma_node_index_) * rx_dma_chunk_size_;
454 const size_t chunk = std::min(remaining, rx_dma_chunk_size_);
455 auto* chunk_ptr = rx_dma_storage_ + offset;
457#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
458 if (!CacheSyncDmaBuffer(chunk_ptr, chunk,
false))
466 rx_dma_node_index_ = (rx_dma_node_index_ + 1U) % DMA_RX_NODE_COUNT;
472void IRAM_ATTR ESP32UART::HandleDmaRxDone(gdma_event_data_t* event_data)
474 if ((rx_dma_storage_ ==
nullptr) || (rx_dma_chunk_size_ == 0))
479 if ((event_data !=
nullptr) && event_data->flags.abnormal_eof)
485 size_t recv_size = rx_dma_chunk_size_;
486 if ((event_data !=
nullptr) && event_data->flags.normal_eof)
488 const size_t eof_size = gdma_link_count_buffer_size_till_eof(
489 rx_dma_link_,
static_cast<int>(rx_dma_node_index_));
492 recv_size = eof_size;
496 PushDmaRxData(recv_size,
true);
501void IRAM_ATTR ESP32UART::HandleDmaRxError()
503 if ((rx_dma_channel_ ==
nullptr) || (rx_dma_link_ ==
nullptr))
508 gdma_stop(rx_dma_channel_);
509 gdma_reset(rx_dma_channel_);
510 rx_dma_node_index_ = 0;
511 (void)gdma_start(rx_dma_channel_, gdma_link_get_head_addr(rx_dma_link_));
517void IRAM_ATTR ESP32UART::HandleDmaTxError()
519 if (tx_dma_channel_ !=
nullptr)
521 gdma_stop(tx_dma_channel_);
522 gdma_reset(tx_dma_channel_);
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.
UartDmaTxModel< ESP32UART > tx_dma_model_
One-shot DMA TX execution model.
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.
@ INIT_ERR
初始化错误 | Initialization error
@ NO_MEM
内存不足 | Insufficient memory
@ OK
操作成功 | Operation successful