Async Transfer Utility Library - Non-blocking Background Data Transfer for Communication Peripherals
Async Transfer Utility Library enables non-blocking data transfers on communication peripherals -- the application initiates a transfer and continues execution while data moves in the background via CPU copy or DMA.
- Both read and write transfers via CPU copy or DMA.
- One pending transfer per direction (read and write) at a time.
- Callback notification on transfer completion.
- Abort an in-progress transfer or query availability at any time.
- Multiple independent instances managing different communication interfaces simultaneously.
- DCACHE coherency handling for DMA-based transfers.
Use this library when you need to:
- Perform UART, SPI, or I2C data transfers without blocking the application.
- Overlap communication with processing by offloading transfers to DMA.
- Manage asynchronous data flow across multiple communication interfaces simultaneously.
- Build protocol stacks or communication middleware on top of PDL or HAL peripheral drivers.
This library is peripheral-agnostic. Populate mtb_async_transfer_interface_t with function
pointers and addresses matching your communication peripheral (example using a PDL UART SCB):
mtb_async_transfer_interface_t interface = {0};
interface.inst_ref = UART_HW;
interface.rx_addr = (uint32_t*)&(UART_HW->RX_FIFO_RD);
interface.tx_addr = (uint32_t*)&(UART_HW->TX_FIFO_WR);
interface.get_num_rx_fifo = (mtb_async_transfer_get_num_fifo_t)Cy_SCB_UART_GetNumInRxFifo;
interface.get_num_tx_fifo = uart_get_num_tx_fifo;
interface.set_rx_fifo_level = (mtb_async_transfer_set_fifo_level_t)Cy_SCB_SetRxFifoLevel;
interface.set_tx_fifo_level = (mtb_async_transfer_set_fifo_level_t)Cy_SCB_SetTxFifoLevel;
interface.enable_rx_event = uart_enable_rx_event;
interface.enable_tx_event = uart_enable_tx_event;
interface.get_rx_transfer_len = uart_get_rx_transfer_len;
interface.get_tx_transfer_len = uart_get_tx_transfer_len;
interface.transfer_width = uart_get_transfer_width(UART_HW);
interface.enter_critical_section = Cy_SysLib_EnterCriticalSection;
interface.exit_critical_section = Cy_SysLib_ExitCriticalSection;For DMA mode, additionally populate the DMA-specific fields:
interface.dma_rx_ref = &dma_Descriptor_0;
interface.dma_set_length = uart_dma_set_len;
interface.dma_set_src = uart_dma_set_src_addr;
interface.dma_set_dest = uart_dma_set_dest_addr;
interface.dma_enable_rx = uart_dma_enable_rx;mtb_async_transfer_context_t async_context;
cy_rslt_t result = mtb_async_transfer_init(&async_context, &interface);result = mtb_async_transfer_register_callback(&async_context,
my_transfer_callback,
callback_arg);/* Initiate a background read */
result = mtb_async_transfer_read(&async_context, rx_buffer, sizeof(rx_buffer));
/* Initiate a background write */
result = mtb_async_transfer_write(&async_context, tx_buffer, sizeof(tx_buffer));Transfer completion is driven by interrupts. Wire up the appropriate handler depending on the transfer mode:
CPU mode -- call mtb_async_transfer_process_fifo_level_event from the peripheral FIFO-level
interrupt:
void uart_interrupt_handler(void)
{
mtb_async_transfer_direction_t direction = (mtb_async_transfer_direction_t)0;
uint32_t rxMasked = Cy_SCB_GetRxInterruptStatusMasked(UART_HW);
uint32_t txMasked = Cy_SCB_GetTxInterruptStatusMasked(UART_HW);
if (0u != (CY_SCB_UART_RX_TRIGGER & rxMasked))
direction = MTB_ASYNC_TRANSFER_DIRECTION_READ;
if (0u != (CY_SCB_UART_TX_TRIGGER & txMasked))
direction |= MTB_ASYNC_TRANSFER_DIRECTION_WRITE;
if (direction)
mtb_async_transfer_process_fifo_level_event(&async_context, direction);
}DMA mode -- call mtb_async_transfer_process_dma_complete from the DMA completion interrupt:
void dma_interrupt_handler(void)
{
cy_en_dma_intr_cause_t cause = Cy_DMA_Channel_GetStatus(DMA_HW, DMA_CHANNEL);
Cy_DMA_Channel_ClearInterrupt(DMA_HW, DMA_CHANNEL);
if (cause == CY_DMA_INTR_CAUSE_COMPLETION)
mtb_async_transfer_process_dma_complete(&async_context,
MTB_ASYNC_TRANSFER_DIRECTION_READ);
}When using DMA on devices with a data cache (DCACHE), cache coherency must be maintained for the
buffers passed to mtb_async_transfer_read and mtb_async_transfer_write.
The library assumes the following cache configuration:
- Write-back with read and write allocate: WB-RWA
- Cache line size: 32 bytes
- All application memory is cacheable
Two scenarios require explicit cache maintenance by the application:
-
CPU writes, DMA reads (TX path) -- the CPU write updates only the cache, not SRAM. Clean the cache after writing the TX buffer so the DMA sees the correct data:
SCB_CleanDCache_by_Addr(tx_buffer, sizeof(tx_buffer));
-
DMA writes, CPU reads (RX path) -- the DMA writes directly to SRAM. Invalidate the cache before reading the RX buffer so the CPU fetches fresh data from SRAM:
SCB_InvalidateDCache_by_Addr(rx_buffer, sizeof(rx_buffer));
Cache operations work at 32-byte cache-line granularity. All DMA data buffers must be aligned to 32 bytes and padded to a multiple of 32 bytes. Without this, adjacent data can be corrupted. Buffer alignment and padding is the responsibility of the application.
- RELEASE.md - Detailed release notes for all versions
Licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License. A copy of the License is available at http://www.apache.org/licenses/LICENSE-2.0.
- LICENSE - Apache License 2.0
(c) (2025-2026), Infineon Technologies AG, or an affiliate of Infineon Technologies AG. All rights reserved.