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);
82#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
83extern "C" esp_err_t esp_cache_get_alignment(uint32_t heap_caps,
size_t* out_alignment);
86constexpr int CACHE_SYNC_FLAG_UNALIGNED = (1 << 1);
87constexpr int CACHE_SYNC_FLAG_DIR_C2M = (1 << 2);
88constexpr int CACHE_SYNC_FLAG_DIR_M2C = (1 << 3);
92bool CacheSyncDmaBuffer(
const void* addr,
size_t size,
bool cache_to_mem)
94 if ((addr ==
nullptr) || (size == 0U))
99#if SOC_PSRAM_DMA_CAPABLE && !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
100 if (!esp_ptr_external_ram(addr))
109 int flags = cache_to_mem ? (CACHE_SYNC_FLAG_DIR_C2M | CACHE_SYNC_FLAG_UNALIGNED)
110 : CACHE_SYNC_FLAG_DIR_M2C;
112 const esp_err_t ret = esp_cache_msync(
const_cast<void*
>(addr), size, flags);
114 return (ret == ESP_OK) || (ret == ESP_ERR_INVALID_ARG);
124bool IRAM_ATTR ESP32UART::DmaTxEofCallback(gdma_channel_handle_t, gdma_event_data_t*,
127 auto* uart =
static_cast<ESP32UART*
>(user_data);
130 uart->service_.Invoke(ESP32UART::EVENT_TX_DONE,
true,
131 [uart](uint32_t events,
bool in_isr)
132 { uart->ServiceEvents(events, in_isr); });
139bool IRAM_ATTR ESP32UART::DmaTxDescrErrCallback(gdma_channel_handle_t, gdma_event_data_t*,
142 auto* uart =
static_cast<ESP32UART*
>(user_data);
145 uart->service_.Invoke(ESP32UART::EVENT_TX_ERROR,
true,
146 [uart](uint32_t events,
bool in_isr)
147 { uart->ServiceEvents(events, in_isr); });
154bool IRAM_ATTR ESP32UART::DmaRxDoneCallback(gdma_channel_handle_t,
155 gdma_event_data_t* event_data,
158 auto* uart =
static_cast<ESP32UART*
>(user_data);
161 const uint32_t
event = (event_data !=
nullptr && event_data->flags.abnormal_eof)
162 ? ESP32UART::EVENT_RX_ERROR
164 uart->service_.Invoke(event,
true, [uart](uint32_t events,
bool in_isr)
165 { uart->ServiceEvents(events, in_isr); });
172bool IRAM_ATTR ESP32UART::DmaRxDescrErrCallback(gdma_channel_handle_t, gdma_event_data_t*,
175 auto* uart =
static_cast<ESP32UART*
>(user_data);
178 uart->service_.Invoke(ESP32UART::EVENT_RX_ERROR,
true,
179 [uart](uint32_t events,
bool in_isr)
180 { uart->ServiceEvents(events, in_isr); });
195 if (dma_backend_enabled_)
200 periph_module_enable(PERIPH_UHCI0_MODULE);
201 periph_module_reset(PERIPH_UHCI0_MODULE);
203 uhci_hal_init(&uhci_hal_, 0);
204 uhci_ll_attach_uart_port(uhci_hal_.dev, uart_num_);
206 uhci_seper_chr_t sep_chr = {};
207 sep_chr.sub_chr_en = 0;
208 uhci_ll_set_seper_chr(uhci_hal_.dev, &sep_chr);
209 uhci_ll_rx_set_eof_mode(uhci_hal_.dev, UHCI_RX_IDLE_EOF);
211 gdma_channel_alloc_config_t tx_cfg = {
212 .sibling_chan =
nullptr,
213 .direction = GDMA_CHANNEL_DIRECTION_TX,
216 if (gdma_new_ahb_channel(&tx_cfg, &tx_dma_channel_) != ESP_OK)
221 if (gdma_connect(tx_dma_channel_, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)) !=
227 gdma_transfer_config_t transfer_cfg = {
228 .max_data_burst_size = 0,
229 .access_ext_mem =
true,
231 if (gdma_config_transfer(tx_dma_channel_, &transfer_cfg) != ESP_OK)
236 size_t tx_int_alignment = 1;
237 size_t tx_ext_alignment = 1;
238 if (gdma_get_alignment_constraints(tx_dma_channel_, &tx_int_alignment,
239 &tx_ext_alignment) != ESP_OK)
243 const size_t tx_dma_alignment =
244 std::max<size_t>(1, std::max(tx_int_alignment, tx_ext_alignment));
246 gdma_strategy_config_t tx_strategy = {
248 .auto_update_desc =
true,
249 .eof_till_data_popped =
true,
251 if (gdma_apply_strategy(tx_dma_channel_, &tx_strategy) != ESP_OK)
256 gdma_link_list_config_t tx_link_cfg = {
261 gdma_link_list_handle_t tx_dma_links[2] = {
nullptr,
nullptr};
263 for (
int i = 0; i < 2; ++i)
265 if (gdma_new_link_list(&tx_link_cfg, &tx_dma_links[i]) != ESP_OK)
270 gdma_buffer_mount_config_t tx_mount = {
271 .buffer = tx_dma_buffer_.
Buffer(i),
272 .buffer_alignment = tx_dma_alignment,
278 .bypass_buffer_align_check = 0,
282 if (gdma_link_mount_buffers(tx_dma_links[i], 0, &tx_mount, 1,
nullptr) != ESP_OK)
287 tx_dma_head_addr_[i] = gdma_link_get_head_addr(tx_dma_links[i]);
288 if (tx_dma_head_addr_[i] == 0U)
294 gdma_tx_event_callbacks_t tx_callbacks = {
295 .on_trans_eof = DmaTxEofCallback,
296 .on_descr_err = DmaTxDescrErrCallback,
298 if (gdma_register_tx_event_callbacks(tx_dma_channel_, &tx_callbacks,
this) != ESP_OK)
303 gdma_channel_alloc_config_t rx_cfg = {
304 .sibling_chan =
nullptr,
305 .direction = GDMA_CHANNEL_DIRECTION_RX,
308 if (gdma_new_ahb_channel(&rx_cfg, &rx_dma_channel_) != ESP_OK)
313 if (gdma_connect(rx_dma_channel_, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0)) !=
319 if (gdma_config_transfer(rx_dma_channel_, &transfer_cfg) != ESP_OK)
324 gdma_strategy_config_t rx_strategy = {
326 .auto_update_desc =
false,
327 .eof_till_data_popped =
false,
329 if (gdma_apply_strategy(rx_dma_channel_, &rx_strategy) != ESP_OK)
334 size_t rx_int_alignment = 1;
335 size_t rx_ext_alignment = 1;
336 if (gdma_get_alignment_constraints(rx_dma_channel_, &rx_int_alignment,
337 &rx_ext_alignment) != ESP_OK)
341 const size_t rx_dma_alignment =
342 std::max<size_t>(1, std::max(rx_int_alignment, rx_ext_alignment));
344 size_t rx_cache_alignment = 1U;
345#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
346 if ((esp_cache_get_alignment(MALLOC_CAP_INTERNAL, &rx_cache_alignment) != ESP_OK) ||
347 (rx_cache_alignment == 0U))
353 gdma_link_list_config_t rx_link_cfg = {
354 .num_items = DMA_RX_NODE_COUNT,
358 if (gdma_new_link_list(&rx_link_cfg, &rx_dma_link_) != ESP_OK)
365 const size_t rx_chunk_target = std::min<size_t>(
366 std::max<size_t>(32, rx_isr_buffer_size_ / DMA_RX_NODE_COUNT), 512);
367 const size_t rx_storage_alignment =
368 std::max<size_t>(4, std::max(rx_dma_alignment, rx_cache_alignment));
370 std::max<size_t>(AlignUp(rx_chunk_target, rx_storage_alignment), 32);
371 const size_t rx_storage_bytes =
372 AlignUp(rx_dma_chunk_size_ * DMA_RX_NODE_COUNT, rx_storage_alignment);
374 rx_dma_storage_ =
static_cast<uint8_t*
>(
375 heap_caps_aligned_alloc(rx_storage_alignment, rx_storage_bytes,
376 MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT));
377 if (rx_dma_storage_ ==
nullptr)
382 std::array<gdma_buffer_mount_config_t, DMA_RX_NODE_COUNT> rx_mount = {};
383 for (uint32_t i = 0; i < DMA_RX_NODE_COUNT; ++i)
385 rx_mount[i] = gdma_buffer_mount_config_t{
386 .buffer = rx_dma_storage_ + (
static_cast<size_t>(i) * rx_dma_chunk_size_),
387 .buffer_alignment = rx_dma_alignment,
388 .length = rx_dma_chunk_size_,
393 .bypass_buffer_align_check = 0,
398 if (gdma_link_mount_buffers(rx_dma_link_, 0, rx_mount.data(), DMA_RX_NODE_COUNT,
404 gdma_rx_event_callbacks_t rx_callbacks = {
405 .on_recv_eof =
nullptr,
406 .on_descr_err = DmaRxDescrErrCallback,
407 .on_recv_done = DmaRxDoneCallback,
409 if (gdma_register_rx_event_callbacks(rx_dma_channel_, &rx_callbacks,
this) != ESP_OK)
414 if (gdma_reset(rx_dma_channel_) != ESP_OK)
419 rx_dma_node_index_ = 0;
420 dma_backend_enabled_ =
true;
421 if (gdma_start(rx_dma_channel_, gdma_link_get_head_addr(rx_dma_link_)) != ESP_OK)
431bool IRAM_ATTR ESP32UART::StartDmaTx()
433 if ((tx_dma_channel_ ==
nullptr) || !tx_active_valid_)
438 uint8_t*
const active_buffer = tx_dma_buffer_.
ActiveBuffer();
439 const size_t active_len = tx_active_length_;
440 if ((active_buffer ==
nullptr) || (active_len == 0) ||
441 (active_len > DMA_MAX_BUFFER_SIZE_PER_LINK_ITEM))
446 const int link_index = tx_dma_buffer_.
ActiveBlock();
447 if ((link_index != 0) && (link_index != 1))
452 if (tx_dma_head_addr_[link_index] == 0U)
457 auto* desc = LinkItemFromHeadAddr(tx_dma_head_addr_[link_index]);
465 desc->buffer = active_buffer;
466 desc->dw0.size =
static_cast<uint32_t
>(active_len);
467 desc->dw0.length =
static_cast<uint32_t
>(active_len);
468 desc->dw0.err_eof = 0U;
469 desc->dw0.suc_eof = 1U;
470 desc->dw0.owner = GDMA_OWNER_DMA;
471 desc->next =
nullptr;
472 std::atomic_thread_fence(std::memory_order_release);
474#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
475 if (!CacheSyncDmaBuffer(active_buffer, active_len,
true))
481 return gdma_start(tx_dma_channel_, tx_dma_head_addr_[link_index]) == ESP_OK;
489void IRAM_ATTR ESP32UART::HandleDmaRxDone(gdma_event_data_t* event_data,
bool in_isr)
492 if ((rx_dma_storage_ ==
nullptr) || (rx_dma_chunk_size_ == 0))
497 auto* first = LinkItemFromHeadAddr(gdma_link_get_head_addr(rx_dma_link_));
498 DEV_ASSERT_FROM_CALLBACK(first !=
nullptr, in_isr);
500 for (uint32_t i = 0U; i < rx_dma_node_index_; ++i)
502 DEV_ASSERT_FROM_CALLBACK(item !=
nullptr, in_isr);
507 for (uint32_t processed = 0U; processed < DMA_RX_NODE_COUNT; ++processed)
509 DEV_ASSERT_FROM_CALLBACK(item !=
nullptr, in_isr);
510 if (item->dw0.owner != GDMA_OWNER_CPU)
515 std::atomic_thread_fence(std::memory_order_acquire);
516 const size_t received = std::min<size_t>(item->dw0.length, rx_dma_chunk_size_);
517#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
518 [[maybe_unused]]
const auto cache_sync_dma_buffer_result =
519 CacheSyncDmaBuffer(item->buffer, rx_dma_chunk_size_,
false);
520 REQUIRE_FROM_CALLBACK(cache_sync_dma_buffer_result, in_isr);
522 if (received != 0U && queue.EmptySize() != 0U)
524 const size_t fitting = std::min(received, queue.EmptySize());
525 [[maybe_unused]]
const auto push_batch_result =
526 queue.PushBatch(
static_cast<const uint8_t*
>(item->buffer), fitting);
527 DEV_ASSERT_FROM_CALLBACK(push_batch_result ==
ErrorCode::OK, in_isr);
530 std::atomic_thread_fence(std::memory_order_release);
531 item->dw0.owner = GDMA_OWNER_DMA;
532 rx_dma_node_index_ = (rx_dma_node_index_ + 1U) % DMA_RX_NODE_COUNT;
538void IRAM_ATTR ESP32UART::StopDmaRx(
bool in_isr)
540 DEV_ASSERT_FROM_CALLBACK(rx_dma_channel_ !=
nullptr, in_isr);
541 [[maybe_unused]]
const auto gdma_stop_result = gdma_stop(rx_dma_channel_);
542 REQUIRE_FROM_CALLBACK(gdma_stop_result == ESP_OK, in_isr);
543 [[maybe_unused]]
const auto gdma_reset_result = gdma_reset(rx_dma_channel_);
544 REQUIRE_FROM_CALLBACK(gdma_reset_result == ESP_OK, in_isr);
547void IRAM_ATTR ESP32UART::ResetDmaRxDescriptors(
bool in_isr)
549 DEV_ASSERT_FROM_CALLBACK(rx_dma_link_ !=
nullptr, in_isr);
550 auto* item = LinkItemFromHeadAddr(gdma_link_get_head_addr(rx_dma_link_));
551 DEV_ASSERT_FROM_CALLBACK(item !=
nullptr, in_isr);
552 for (uint32_t i = 0U; i < DMA_RX_NODE_COUNT; ++i)
554 DEV_ASSERT_FROM_CALLBACK(item !=
nullptr, in_isr);
555 item->dw0.size =
static_cast<uint32_t
>(rx_dma_chunk_size_);
556 item->dw0.length =
static_cast<uint32_t
>(rx_dma_chunk_size_);
557 item->dw0.err_eof = 0U;
558 item->dw0.suc_eof = 0U;
559 std::atomic_thread_fence(std::memory_order_release);
560 item->dw0.owner = GDMA_OWNER_DMA;
564 rx_dma_node_index_ = 0;
567void IRAM_ATTR ESP32UART::StartDmaRx(
bool in_isr)
569 DEV_ASSERT_FROM_CALLBACK(rx_dma_channel_ !=
nullptr, in_isr);
570 DEV_ASSERT_FROM_CALLBACK(rx_dma_link_ !=
nullptr, in_isr);
571 [[maybe_unused]]
const auto gdma_start_result =
572 gdma_start(rx_dma_channel_, gdma_link_get_head_addr(rx_dma_link_));
573 REQUIRE_FROM_CALLBACK(gdma_start_result == ESP_OK, in_isr);
578void IRAM_ATTR ESP32UART::HandleDmaRxError(
bool in_isr)
580 if ((rx_dma_channel_ ==
nullptr) || (rx_dma_link_ ==
nullptr))
586 ResetDmaRxDescriptors(in_isr);
593void IRAM_ATTR ESP32UART::HandleDmaTxError(
bool in_isr)
595 if (tx_dma_channel_ !=
nullptr)
597 gdma_stop(tx_dma_channel_);
598 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
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)
构造并初始化 ESP UART / Construct and initialize an ESP UART.
void Publish()
结束本次写入并通知挂起读 / End production and notify a pending read.
ReadQueue GetReadQueue(bool in_isr=false)
获取后端接收入队接口 / Obtain the backend RX producer interface.
LibXR 命名空间 / LibXR namespace.
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ INIT_ERR
初始化错误 / Initialization error
@ NO_MEM
内存不足 / Insufficient memory
@ FAILED
操作失败 / Operation failed
@ OK
操作成功 / Operation successful