Skip to content

Contributed by 爱笑, organized by Ai-Thinker

[Ai-WB2 Getting Started] DMA Data Transfer

DMA (Direct Memory Access) is a memory access technology that can read and write system memory independently and directly without processor intervention. Under the same level of processor load, DMA is a fast data transfer method. The DMA controller of the Ai-WB2 has 4 independent dedicated channels that manage data transfers between peripherals and memory to improve bus efficiency. There are mainly three types of transfers: memory-to-memory, memory-to-peripheral and peripheral-to-memory. It also supports the LLI (linked-list) feature. In use, the transfer data size, source address and destination address are configured by software. This article describes in detail how to use the DMA module of the Ai-WB2. 1. DMA Introduction The DMA of the Ai-WB2 has the following features:

Click to expand full code
c
**·** 4组独立专用通道
**·** 独立控制来源与目标存取宽度(单字节、双字节、四字节)
**·** 每个通道独立作为读写缓存
**·** 每个通道可被独立的外设硬件触发或是软件触发更
**·** 支持外设包括UART、I2C、SPI、ADC.
**·** 八种流程控制
**·** DMA流程控制,来源内存、目标内存
**·** DMA流程控制,来源内存、目标外设
**·** DMA流程控制,来源外设、目标内存
**·** DMA流程控制,来源外设、目标外设
**·** 目标外设流程控制,来源外设、目标外设
**·** 目标外设流程控制,来源内存、目标外设
**·** 来源外设流程控制,来源外设、目标内存
**·** 来源外设流程控制,来源外设、目标外设
**·** 支持LLI链表功能,提高DMA效率
**DMA工作原理**

When a device attempts to transfer data directly to another device through the bus, it first sends a DMA request signal to the CPU. The peripheral requests the bus control right from the CPU through the DMA. After receiving the signal, the CPU responds to the DMA request at the end of the current bus cycle according to the priority of the DMA signal and the order in which the DMA requests were made. When the CPU responds to the DMA request of a certain device interface, it gives up the bus control right. Under the management of the DMA controller, the peripheral and memory exchange data directly without CPU intervention. After the data transfer is completed, the device sends a DMA end signal to the CPU and returns the bus control right. The DMA contains one set of AHB Master interfaces and one set of AHB Slave interfaces. The AHB Master interface actively accesses memory or peripherals through the system bus according to the current configuration requirements, serving as the port for data movement. The AHB Slave interface serves as the interface for configuring the DMA and only supports 32-bit accesses. DMA Configuration The DMA supports a total of 4 channels. The channels do not interfere with each other and can run simultaneously. The configuration process for DMA channel x is as follows:       1. Set the 32-bit source address in the DMA_C0SrcAddr register       2. Set the 32-bit destination address in the DMA_C0DstAddr register       3. Address auto-increment: whether to enable address auto-increment can be set via the SI (source) and DI (destination) bits in the DMA_C0Control register; setting them to 1 enables address auto-increment       4. Set the transfer data width via the STW (source) and DTW (destination) bits in the DMA_C0Control register; the width options are single byte, double byte and four bytes       5. Set the burst type via the SBS (source) and DBS (destination) bits in the DMA_C0Control register; the options are Single, INCR4, INCR8 and INCR16       6. Note in particular that for the configured combination, a single burst must not exceed 16 bytes       7. Set the data transfer length in the range of 0-4095 Devices supported by the DMA are as follows: The peripheral that the DMA works with can be determined by configuring SrcPeripheral (source) and DstPeripheral (destination). The mapping is 0-3: UART / 6-7: I2C / 10-11: SPI / 22-23: ADC or DAC 2. DMA Driver API Introduction Since the HOSAL layer of bl_iot_sdk does not yet provide complete DMA encapsulation, the SOC layer driver API, HAL layer and HOSAL layer driver APIs of BL602 need to be used together. The following demonstrates how to perform data transfer between memories. The DMA usage of peripherals such as UART, I2C, ADC and SPI will not be introduced here; please refer to the DMA usage of the corresponding peripherals.

Click to expand full code
c
**·** DMA的HOSAL高级驱动API在文件components/platform/hosal/include/hosal_dma.h中定义;
**·** HAL层的驱动APl在 components/platform/hosal/b1602_hal/bl_dma.h 中定义;
**·** SOC层的驱动APl在 components/platform/soc/b1602/stdDriver/inc/b1602_dma.h 中定义。

The HOSAL high-level driver APIs are as follows:

Click to expand full code
c
**·** int hosal_dma_init(void:初始化DMA。I2C、UART、SPI、ADC等外设使用DMA时,都需要调用此函数。
**·** 返回值:成功时返回0,否则返回非零值。
**·** hosal_dma_chan_t hosal_dma_chan_request(int flag):请求一个可用DMA通道。参数说明如下:
**·** flag:DMA请求标志。一般情况下该值为0。
**·** 返回值:当返回值小于0时,表示请求失败;否则返回值为DMA通道值。
**·** int hosal_dma_chan_release(hosal_dma_chan_t chan):释放已经请求分配的DMA通道。参数说明如下:
**·** chan:已经请求完成的可用DMA通道
**·** 返回值:成功时返回0,否则返回非零值。
**·** int hosal_dma_chan_start(hosal_dma_chan_t chan):启动通道传输。参数说明如下:
**·** chan:已经请求完成的可用DMA通道
**·** 成功时返回0,否则返回非零值。
**·** int hosal_dma_chan_stop(hosal_dma_chan_t chan):停止DMA传输。参数说明如下:
**·** chan:已经请求完成的可用DMA通道
**·** 返回值:成功时返回0,否则返回非零值
**·** int hosal_dma_irq_callback_set(hosal_dma_chan_t chan, hosal_ dma_irq_t pfn, void *p_arg):注册 DMA中断Q回调函数。参数说明如下:
**·** chan:已经请求完成的可用DMA通道
**·** pfn:DMA中断回调函数指针。其定义如下:
  1. typedef void (*hosal_dma_irq_t)(void *p_arg, uint32_t flag);
Click to expand full code
c
**·** p_arg:回调函数参数
**·** 返回值:成功时返回0,否则返回非零值,
**·** int hosal_dma_finalize(void):释放DMA。成功时返回0,否则返回非零值。

The commonly used driver functions at the SOC layer are as follows: · void DMA_channel_Init(DMA_channel_cfg_Type *chcfg): initializes the DMA channel. Its parameters are described as follows: · chCfg: the DMA channel configuration, defined as follows:

Click to expand full code
c
/**
*  @brief DMA channel Configuration Structure type definition
*/
typedef struct {
uint32_t srcDmaAddr;                     /*!< Source address of DMA transfer */
uint32_t destDmaAddr;                    /*!< Destination address of DMA transfer */
uint32_t transfLength;                   /*!< Transfer length, 0~4095, this is burst count */
DMA_Trans_Dir_Type dir;                  /*!< Transfer dir control. 0: Memory to Memory, 1: Memory to peripheral, 2: Peripheral to memory */
DMA_Chan_Type ch;                        /*!< Channel select 0-4 */
DMA_Trans_Width_Type srcTransfWidth;     /*!< Transfer width. 0: 8  bits, 1: 16  bits, 2: 32  bits */
DMA_Trans_Width_Type dstTransfWidth;     /*!< Transfer width. 0: 8  bits, 1: 16  bits, 2: 32  bits */
DMA_Burst_Size_Type srcBurstSzie;        /*!< Number of data items for burst transaction length. Each item width is as same as tansfer width.
0: 1 item, 1: 4 items, 2: 8 items, 3: 16 items */
DMA_Burst_Size_Type dstBurstSzie;        /*!< Number of data items for burst transaction length. Each item width is as same as tansfer width.
0: 1 item, 1: 4 items, 2: 8 items, 3: 16 items */
uint8_t srcAddrInc;                      /*!< Source address increment. 0: No change, 1: Increment */
uint8_t destAddrInc;                     /*!< Destination address increment. 0: No change, 1: Increment */
DMA_Periph_Req_Type srcPeriph;           /*!< Source peripheral select */
DMA_Periph_Req_Type dstPeriph;           /*!< Destination peripheral select */
}DMA_Channel_Cfg_Type;
Click to expand full code
c
**·** 返回值:无
**·** void DMA_channel_update_srcMemcfg(uint8_t ch, uint32_t memAddr, uint32_t len):更新Source地址和长度
**·** void DMA_channel_Update_DstMemcfg(uint8_t ch, uint32_t memAddr, uint32_t len):更新目标地址和长度
**三:DMA使用示例**

The following demonstrates the memory-to-memory transfer mode of the DMA. After this mode is started, the DMA moves data from the source address to the destination address according to the configured transfer size. After the transfer is completed, the DMA controller automatically returns to the idle state and waits for the next transfer. The configuration steps are as follows:       1. Set the value of the DMA_C0SrcAddr register to the source memory address       2. Set the value of the DMA_C0DstAddr register to the destination memory address       3. Select the transfer mode: set the FLOWCTRL bit value of the DMA_C0Config register to 0 to select memory-to-memory mode       4. Set the corresponding bit values of the DMA_C0Control register : set the DI and SI bits to 1 to enable address auto-increment mode; the DTW and STW bits set the transfer width of the destination and source respectively; the DBS and SBS bits set the burst types of the destination and source respectively.       5. Select an appropriate channel, enable the DMA, and complete the data transfer The example code is as follows:

Click to expand full code
c
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <stdbool.h>
#include <hosal_dma.h>
#include <hosal_adc.h>
#include <blog.h>
#include "bl602_dma.h"
#define TAG "dma_demo"
#define DMA_DATA_SIZE 1024
uint8_t rx_data[DMA_DATA_SIZE];
uint8_t tx_data[DMA_DATA_SIZE];
DMA_Channel_Cfg_Type txchCfg = {
(uint32_t)((unsigned char*)&tx_data[0]),
(uint32_t)((unsigned char*)&rx_data[0]),
DMA_DATA_SIZE,
DMA_TRNS_M2M,
DMA_CH0,
DMA_TRNS_WIDTH_8BITS,
DMA_TRNS_WIDTH_8BITS,
DMA_BURST_SIZE_4,
DMA_BURST_SIZE_4,
DMA_MINC_ENABLE,
DMA_MINC_ENABLE,
DMA_REQ_NONE,
DMA_REQ_NONE,
};
void verify_data(void){
bool ret = true;
for(size_t i = 0;i < DMA_DATA_SIZE;i++){
if(rx_data[i] != tx_data[i]){
printf("dma transfer failed:%d,%d\r\n",rx_data[i],tx_data[i]);
ret = false;
break;
}
}
if(ret){
printf("dma transfer success\r\n");
}
}
void dma_irq_callback(void* p_arg, uint32_t flag) {
switch (flag) {
case HOSAL_DMA_INT_TRANS_COMPLETE:
printf("dma transfer done,start verify data...\r\n");
verify_data();
break;
case HOSAL_DMA_INT_TRANS_ERROR:
printf("dma transfer error\r\n");
break;
}
}
void dma_m2m_demo(void) {
// 填充数据
for (size_t i = 0; i < DMA_DATA_SIZE;i++) {
tx_data[i] = 'A';
rx_data[i] = 0;
}
hosal_dma_chan_t chan = hosal_dma_chan_request(0);
if (chan < 0) {
printf("request dma chan failed\r\n");
return;
}
txchCfg.ch = chan;
DMA_Channel_Init(&txchCfg);
hosal_dma_irq_callback_set(chan, dma_irq_callback, (void*)&chan);
hosal_dma_chan_start(chan);
}
void main(void) {
hosal_dma_init();
dma_m2m_demo();
}

Memory to Peripheral In this working mode, the DMA moves data from the source to the internal cache according to the configured transfer size (TransferSize). It pauses automatically when the cache space is insufficient, and continues when there is enough cache space, until the configured transfer count is reached. On the other hand, when the destination peripheral request is triggered, the destination is configured to burst to the destination address until the configured transfer count is reached. When the count is reached, it automatically returns to the idle state and waits for the next start. The specific configuration flow is as follows:       1. Set the value of the DMA_C0SrcAddr register to the source memory address       2. Set the value of the DMA_C0DstAddr register to the destination peripheral address       3. Select the transfer mode: set the FLOWCTRL bit value of the DMA_C0Config register to 1 to select memory-to-peripheral mode       4. Set the values of the corresponding bits of the DMA_C0Control register: set the DI and SI bits to 1 to enable address auto-increment mode; the DTW and STW bits set the transfer width of the destination and source respectively; the DBS and SBS bits set the burst types of the destination and source respectively       5. Select an appropriate channel, enable the DMA, and complete the data transfer Peripheral to Memory In this working mode, when the source peripheral request is triggered, the source is configured to burst into the cache until the configured transfer count is reached, then it stops. On the other hand, when the internal cache is sufficient for one destination burst, the DMA automatically moves the cached content to the destination address until the configured transfer count is reached, then automatically returns to the idle state and waits for the next start. The specific configuration flow is as follows:       1. Set the value of the DMA_C0SrcAddr register to the source peripheral address       2. Set the value of the DMA_C0DstAddr register to the destination memory address       3. Select the transfer mode: set the FLOWCTRL bit value of the DMA_C0Config register to 2 to select peripheral-to-memory mode       4. Set the values of the corresponding bits of the DMA_C0Control register: set the DI and SI bits to 1 to enable address auto-increment mode; the DTW and STW bits set the transfer width of the destination and source respectively; the DBS and SBS bits set the burst types of the destination and source respectively.       5. Select an appropriate channel, enable the DMA, and complete the data transfer

Released under the MIT License. Build Time 2026-09-11 14:52:23