Fixed Length Transmit and Receive - DMA
This example uses UART and DMA for data communication with a PC terminal.
The SoC uses DMA to send data to the PC terminal program (such as PUTTY or UartAssist), with DMA transferring data from Memory to the UART TX FIFO peripheral.
The SoC uses DMA to receive data input from the PC terminal, with DMA transferring data from the UART RX FIFO peripheral to Memory.
Once the DMA transfer is complete, the SoC sends the buffered data back to the PC terminal.
In this example, the PC terminal program must send data greater than or equal to UART_RX_GDMA_BUFFER_SIZE
bytes to trigger the DMA interrupt.
Requirements
The sample supports the following development kits:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
For more requirements, please refer to Quick Start.
Wiring
Connect P3_0 (UART TX) to the RX pin of the FT232 and P3_1 (UART RX) to the TX pin of the FT232.
Building and Downloading
This sample can be found in the SDK folder:
Project file: board\evb\io_sample\UART\GDMA\mdk
Project file: board\evb\io_sample\UART\GDMA\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
Preparation Phase
Launch PuTTY or UartAssist or other PC terminals, connect to the used COM port, and configure the following UART settings:
Baud rate: 115200
8 data bits
1 stop bit
No parity
No hardware flow control
Testing Phase
After EVB reset, the SoC first sends 100 bytes of data to the PC, with the last 10 bytes being FF. Then the SoC sends 4095 bytes of data via DMA (in this example, RX Buffer Size
UART_RX_GDMA_BUFFER_SIZE
is set to 4095, data cycles from 0x00 to 0xFF), observing the data information appearing on the PC terminal, and printing DMA TX interrupt information in the Debug Analyzer.TX DMA HANDLER
Enter more than 4095 bytes of data on the PC interrupt (in this example, RX Buffer Size
UART_RX_GDMA_BUFFER_SIZE
is set to 4095), which will trigger a DMA receive interrupt. The SoC will send the same data back to the PC, observing the data information appearing on the PC terminal, and printing DMA RX interrupt information in the Debug Analyzer.RX DMA HANDLER
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\UART\GDMA
Source code directory:
sdk\src\sample\io_sample\UART\GDMA
Source files are currently categorized into several groups as below.
└── Project: 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_pinmux.c
├── rtl876x_nvic.c
├── rtl876x_gdma.c
└── rtl876x_uart.c
├── profile
└── app includes the ble_peripheral user application implementation
├── main.c
├── ancs.c
├── app.c
├── app_task.c
└── io_uart.c
Initialization
When the EVB reset is initiated, 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);
global_data_init();
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:
In
board_init
, executeboard_uart_init
, which is responsible for PAD/PINMUX settings and includes the following process:Configure PAD: Set the pins, PINMUX mode, PowerOn, internal pull-up, and disable output.
Configure PINMUX: Set the pins for UART0_TX and UART0_RX functions.
After executing
os_sched_start()
to start task scheduling, in theapp_main_task
main task, executedriver_init
to initialize and configure the peripheral drivers.In
driver_init
, executedriver_uart_init
, which is the initialization function for the UART peripheral, including the following process:Enable RCC clock.
Configure UART baud rate to 115200.
Enable UART TX/RX DMA transfer and configure the corresponding water level.
void driver_uart_init(void) { UART_DeInit(UART0); RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE); /* uart init */ UART_InitTypeDef UART_InitStruct; UART_StructInit(&UART_InitStruct); UART_InitStruct.UART_Div = BaudRate_Table[BAUD_RATE_115200].div; UART_InitStruct.UART_Ovsr = BaudRate_Table[BAUD_RATE_115200].ovsr; UART_InitStruct.UART_OvsrAdj = BaudRate_Table[BAUD_RATE_115200].ovsr_adj; UART_InitStruct.UART_RxThdLevel = 16; //1~29 UART_InitStruct.UART_IdleTime = UART_RX_IDLE_2BYTE; //idle interrupt wait time UART_InitStruct.UART_DmaEn = UART_DMA_ENABLE; UART_InitStruct.UART_TxWaterLevel = 15; //Better to equal TX_FIFO_SIZE(16)- GDMA_MSize UART_InitStruct.UART_RxWaterLevel = 1; //Better to equal GDMA_MSize #if (UART_GDMA_TX_ENABLE == EVB_ENABLE) UART_InitStruct.UART_TxDmaEn = ENABLE; #endif #if (UART_GDMA_RX_ENABLE == EVB_ENABLE) UART_InitStruct.UART_RxDmaEn = ENABLE; #endif UART_Init(UART0, &UART_InitStruct); }
Execute
os_sched_start()
in the main function to start task scheduling. When the stack is ready, executeapp_handle_dev_state_evt
andio_uart_demo
.In
io_uart_demo
, executeUART_SendDataByGDMA
andUART_ReceiveDataByGDMA
to initialize the DMA TX/RX peripherals.In
UART_SendDataByGDMA
, initialize the DMA TX peripheral by performing the following steps:Initialize the DMA send array.
Set the data transfer direction to memory-to-peripheral.
Set the source address to increment and the destination address to be fixed.
Set the data width to 8-bit, transferring one data per burst.
Set the source address to
GDMA_SendData_Buffer
and the destination address to(&(UART0->RB_THR))
.Configure the DMA transfer complete interrupt
GDMA_INT_Transfer
.Enable the DMA TX and start sending data.
void UART_SendDataByGDMA(void) { /*--------------initialize test buffer which for sending data to UART---------------------*/ for (uint32_t i = 0; i < UART_TX_GDMA_BUFFER_SIZE; i++) { GDMA_SendData_Buffer[i] = 0x10 + i; } /*--------------GDMA init-----------------------------*/ GDMA_InitTypeDef GDMA_InitStruct; GDMA_StructInit(&GDMA_InitStruct); GDMA_InitStruct.GDMA_ChannelNum = UART_TX_GDMA_CHANNEL_NUM; GDMA_InitStruct.GDMA_DIR = GDMA_DIR_MemoryToPeripheral; GDMA_InitStruct.GDMA_BufferSize = UART_TX_GDMA_BUFFER_SIZE;//determine total transfer size GDMA_InitStruct.GDMA_SourceInc = DMA_SourceInc_Inc; GDMA_InitStruct.GDMA_DestinationInc = DMA_DestinationInc_Fix; GDMA_InitStruct.GDMA_SourceDataSize = GDMA_DataSize_Byte; GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Byte; GDMA_InitStruct.GDMA_SourceMsize = GDMA_Msize_1; GDMA_InitStruct.GDMA_DestinationMsize = GDMA_Msize_1; GDMA_InitStruct.GDMA_SourceAddr = (uint32_t)GDMA_SendData_Buffer; GDMA_InitStruct.GDMA_DestinationAddr = (uint32_t)(&(UART0->RB_THR)); GDMA_InitStruct.GDMA_DestHandshake = GDMA_Handshake_UART0_TX; GDMA_InitStruct.GDMA_ChannelPriority = 2;//channel prority between 0 to 5 GDMA_Init(UART_TX_GDMA_CHANNEL, &GDMA_InitStruct); GDMA_INTConfig(UART_TX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE); /*-----------------GDMA IRQ init-------------------*/ NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = UART_TX_GDMA_CHANNEL_IRQN; NVIC_InitStruct.NVIC_IRQChannelPriority = 3; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); /*-----------------start to send data-----------*/ GDMA_Cmd(UART_TX_GDMA_CHANNEL_NUM, ENABLE); }
In
UART_ReceiveDataByGDMA
, initialize the DMA RX peripheral by performing the following steps:Initialize the DMA receive array.
Set the data transfer direction from peripheral to memory.
Set the source address to be fixed and the destination address to increment.
Set the data width to 8 bits and transfer one data per burst.
Set the source address to
(&(UART0->RB_THR))
and the destination address toGDMA_ReceiveData_Buffer
.Configure the DMA total transfer completion interrupt
GDMA_INT_Transfer
.Enable DMA RX and start receiving data.
void UART_ReceiveDataByGDMA(void) { /*--------------initialize test buffer which for sending data to UART---------------------*/ for (uint32_t i = 0; i < UART_RX_GDMA_BUFFER_SIZE; i++) { GDMA_ReceiveData_Buffer[i] = 0; } /*--------------GDMA init-----------------------------*/ GDMA_InitTypeDef GDMA_InitStruct; GDMA_StructInit(&GDMA_InitStruct); GDMA_InitStruct.GDMA_ChannelNum = UART_RX_GDMA_CHANNEL_NUM; GDMA_InitStruct.GDMA_DIR = GDMA_DIR_PeripheralToMemory; GDMA_InitStruct.GDMA_BufferSize = UART_RX_GDMA_BUFFER_SIZE;//determine total transfer size GDMA_InitStruct.GDMA_SourceInc = DMA_SourceInc_Fix; GDMA_InitStruct.GDMA_DestinationInc = DMA_DestinationInc_Inc; GDMA_InitStruct.GDMA_SourceDataSize = GDMA_DataSize_Byte; GDMA_InitStruct.GDMA_DestinationDataSize = GDMA_DataSize_Byte; GDMA_InitStruct.GDMA_SourceMsize = GDMA_Msize_1; GDMA_InitStruct.GDMA_DestinationMsize = GDMA_Msize_1; GDMA_InitStruct.GDMA_SourceAddr = (uint32_t)(&(UART0->RB_THR)); GDMA_InitStruct.GDMA_SourceHandshake = GDMA_Handshake_UART0_RX; GDMA_InitStruct.GDMA_DestinationAddr = (uint32_t)GDMA_ReceiveData_Buffer; GDMA_InitStruct.GDMA_ChannelPriority = 2;//channel prority between 0 to 5 GDMA_Init(UART_RX_GDMA_CHANNEL, &GDMA_InitStruct); GDMA_INTConfig(UART_RX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE); /*-----------------GDMA IRQ init-------------------*/ NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = UART_RX_GDMA_CHANNEL_IRQN; NVIC_InitStruct.NVIC_IRQChannelPriority = 3; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); /*-----------------start to send data-----------*/ GDMA_Cmd(UART_RX_GDMA_CHANNEL_NUM, ENABLE); }
Functional Implementation
In
io_uart_demo
, first executeUART_SendData_Continuous
to send 100 bytes of data to the PC. See the sent data in the serial port assistant on the PC.Execute
UART_SendDataByGDMA
, the DMA TX channel will send the preset 4095 bytes of data to the PC. See the sent data in the serial port assistant on the PC.Execute
UART_ReceiveDataByGDMA
to open the DMA RX channel for data reception. When the data sent from the PC is greater than or equal to 4095 bytes, a DMA interrupt is triggered, and it enters the interrupt handler functionUART_RX_GDMA_Handler
.Clear the interrupt flag.
Define the message type as
IO_MSG_TYPE_GDMA
, the message subtype as 1, and send the message to the app task. When the main task detects the message, it executesio_handle_gdma_msg
to process the message.If the detected sub-message type is 1, indicating that it is a DMA RX interrupt message, execute
UART_SendData_Continuous
to send the data received by DMA back to the PC.
void UART_RX_GDMA_Handler(void) { GDMA_ClearINTPendingBit(UART_RX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer); DBG_DIRECT("RX DMA HANDLER"); T_IO_MSG int_uart_msg; int_uart_msg.type = IO_MSG_TYPE_GDMA; int_uart_msg.subtype = 1; int_uart_msg.u.buf = (void *)(GDMA_SendData_Buffer); if (false == app_send_msg_to_apptask(&int_uart_msg)) { APP_PRINT_ERROR0("[io_adc]UART_GDMA_Channel_Handler: Send int_uart_msg failed!"); //Add user code here! GDMA_ClearINTPendingBit(UART_TX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer); return; } } void io_handle_gdma_msg(T_IO_MSG *io_gdma_msg) { uint16_t subtype = io_gdma_msg->subtype; if (subtype == 0) ... else if (subtype == 1) { APP_PRINT_INFO0("io_handle_gdma_msg: uart rx done "); UART_SendData_Continuous(UART0, GDMA_ReceiveData_Buffer, UART_RX_GDMA_BUFFER_SIZE); } }