Memory To Memory - Single Block
This example uses the Single Block feature of DMA to implement memory-to-memory data transfer.
The maximum data size of a single block is 65535. If it exceeds that size, use Multi Block to transfer data. For more details, please refer to the Memory to Memory - Multi Block example.
Requirements
The sample supports the following development kits:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
For more requirements, please refer to Quick Start.
Building and Downloading
This sample can be found in the SDK folder:
Project file: board\evb\io_sample\GDMA\Mem2Mem_single_block\mdk
Project file: board\evb\io_sample\GDMA\Mem2Mem_single_block\gcc
Please follow these steps to build and run the example:
Open sample project file.
To build the target, follow the steps listed on the Generating App Image in Quick Start.
After a successful compilation, the app bin
app_MP_xxx.bin
will be generated in the directorymdk\bin
orgcc\bin
.To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.
Press reset button on EVB board and it will start running.
Experimental Verification
After the EVB resets, DMA begins to transfer data. Once the data transfer is complete, the Debug Analyzer will display a transfer completion message.
[io_gdma] io_handle_gdma_msg: GDMA transfer data completion!
Note
If an error is detected in the transferred data, observe the error information in the Debug Analyzer.
Code Overview
This chapter will be introduced according to the following several parts:
Peripheral initialization will be introduced in chapter Initialization.
Functional implementation after initialization will be introduced in chapter Function Implementation.
Source Code Directory
Project directory:
sdk\board\evb\io_sample\GDMA\Mem2Mem_single_block
Source code directory:
sdk\src\sample\io_sample\GDMA\Mem2Mem_single_block
Source files are currently categorized into several groups as below.
└── Project: adc_continuous_gdma
└── secure_only_app
└── include
├── app_define.h
└── rom_uuid.h
├── cmsis includes CMSIS header files and startup files
├── overlay_mgr.c
├── system_rtl876x.c
└── startup_rtl876x.s
├── lib includes all binary symbol files that user application is built on
├── rtl8752h_sdk.lib
├── gap_utils.lib
└── ROM.lib
├── peripheral includes all peripheral drivers and module code used by the application
├── rtl876x_rcc.c
├── rtl876x_nvic.c
└── rtl876x_gdma.c
├── profile
└── app includes the ble_peripheral user application implementation
├── main.c
├── ancs.c
├── app.c
├── app_task.c
└── io_gdma.c
Initialization
After the EVB resets, the main()
function is called, and the following process will be executed:
int main(void)
{
extern uint32_t random_seed_value;
srand(random_seed_value);
board_init();
le_gap_init(APP_MAX_LINKS);
gap_lib_init();
app_le_gap_init();
app_le_profile_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
Note
le_gap_init()
, gap_lib_init()
, app_le_gap_init
, and app_le_profile_init
are related to the initialization of the privacy management module. Refer to the initialization process description in LE Peripheral Privacy.
The specific initialization process related to peripherals is as follows:
After executing
os_sched_start()
to start task scheduling, in theapp_main_task
main task, executedriver_init
to initialize and configure the peripheral drivers.when
driver_gdma_init
is called indriver_init
function to initialize the DMA peripheral, the typical process includes:Initialize Send Buffer
GDMA_Send_Buf
and Receive BufferGDMA_Recv_Buf
.Enable RCC clock.
Set transmission direction from memory to memory.
Set each DMA transmission to transfer 200 bytes.
Set source address increase and destination address increase.
Set source and destination data width and burst size.
Set source address to
GDMA_Send_Buf
and destination address toGDMA_Recv_Buf
.Enable transmission completion interrupt
GDMA_INT_Transfer
.
void driver_gdma_init(void) { uint32_t i = 0; /* Test data buffer */ for (i = 0; i < GDMA_TRANSFER_SIZE; i++) { GDMA_Send_Buf[i] = i + 1; GDMA_Recv_Buf[i] = 0; } /* Turn on gdma clock */ RCC_PeriphClockCmd(APBPeriph_GDMA, APBPeriph_GDMA_CLOCK, ENABLE); GDMA_InitTypeDef GDMA_InitStruct; GDMA_StructInit(&GDMA_InitStruct); /* GDMA initial*/ GDMA_InitStruct.GDMA_ChannelNum = GDMA_CHANNEL_NUM; GDMA_InitStruct.GDMA_DIR = GDMA_DIR_MemoryToMemory; GDMA_InitStruct.GDMA_BufferSize = GDMA_TRANSFER_SIZE; GDMA_InitStruct.GDMA_SourceInc = DMA_SourceInc_Inc; GDMA_InitStruct.GDMA_DestinationInc = DMA_DestinationInc_Inc; GDMA_InitStruct.GDMA_SourceDataSize = GDMA_DataSize_Word; GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Word; GDMA_InitStruct.GDMA_SourceMsize = GDMA_Msize_8; GDMA_InitStruct.GDMA_DestinationMsize = GDMA_Msize_8; GDMA_InitStruct.GDMA_SourceAddr = (uint32_t)GDMA_Send_Buf; GDMA_InitStruct.GDMA_DestinationAddr = (uint32_t)GDMA_Recv_Buf; GDMA_Init(GDMA_Channel, &GDMA_InitStruct); GDMA_INTConfig(GDMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE); /* GDMA irq init */ NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = GDMA_Channel_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd = (FunctionalState)ENABLE; NVIC_InitStruct.NVIC_IRQChannelPriority = 3; NVIC_Init(&NVIC_InitStruct); }
Functional Implementation
Execute
os_sched_start()
to start task scheduling. When the stack is ready, executeGDMA_Cmd()
to start DMA transmission inapp_handle_dev_state_evt
function.void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause) { ... if (gap_dev_state.gap_init_state != new_state.gap_init_state) { if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY) { APP_PRINT_INFO0("GAP stack ready"); /*stack ready*/ GDMA_Cmd(GDMA_CHANNEL_NUM, ENABLE); } } ... }
After the DMA data transfer is completed, the
GDMA_INT_Transfer
interrupt is triggered, entering the DMA interrupt handler function.Disable DMA channel 0
GDMA_INT_Transfer
interrupt.Define message type
IO_MSG_TYPE_GDMA
and send a message to the task. In the main task, process the sent message data.Clear DMA channel 0
GDMA_INT_Transfer
interrupt flag.Execute
io_handle_gdma_msg
, print data transfer completion information, and check ifGDMA_Recv_Buf
is the same asGDMA_Send_Buf
. If not, print error data.
void GDMA_Channel_Handler(void) { GDMA_INTConfig(GDMA_CHANNEL_NUM, GDMA_INT_Transfer, DISABLE); T_IO_MSG int_gdma_msg; int_gdma_msg.type = IO_MSG_TYPE_GDMA; int_gdma_msg.subtype = 0; if (false == app_send_msg_to_apptask(&int_gdma_msg)) { APP_PRINT_ERROR0("[io_gdma] GDMA_Channel_Handler: Send int_gdma_msg failed!"); //Add user code here! GDMA_ClearINTPendingBit(GDMA_CHANNEL_NUM, GDMA_INT_Transfer); return; } GDMA_ClearINTPendingBit(GDMA_CHANNEL_NUM, GDMA_INT_Transfer); } void io_handle_gdma_msg(T_IO_MSG *io_gdma_msg) { APP_PRINT_INFO0("[io_gdma] io_handle_gdma_msg: GDMA transfer data completion!"); for (uint32_t i = 0; i < GDMA_TRANSFER_SIZE; i++) { if (GDMA_Send_Buf[i] != GDMA_Recv_Buf[i]) { APP_PRINT_INFO2("[io_gdma] io_handle_gdma_msg: Data transmission error! GDMA_Send_Buf = %d, GDMA_Recv_Buf = %d", GDMA_Send_Buf[i], GDMA_Recv_Buf[i]); } } }