Step 4: Add Application Code to the Project
This example application on the PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit board demonstrates two separate projects, one for CPU-driven transfer and another for Direct Memory Access (DMA)-driven transfer. Add the appropriate application code for each project accordingly.
Application Code for CPU-Driven Transfer
Add the following application code to the generated project for CPU-driven transfer.
Open the main.c source file in the non-secure project. Include the <stdio.h> and <string.h> header file, and then define TRANSFER_SIZE as 1024. Next, create two arrays named srcBuffer and dstBuffer and declare timeStamp as an external volatile variable.
Code:
#include <string.h>
#define TRANSFER_SIZE 1024
char srcBuffer[TRANSFER_SIZE] = {};
char dstBuffer[TRANSFER_SIZE] = {};
volatile uint32_t timeStamp = 0;

TRANSFER_SIZE specifies the number of bytes to transfer (1024B). srcBuffer and dstBuffer are the source and destination arrays for data transfer. timeStamp is a volatile variable used to record timing information, such as the number of cycles taken for the transfer.
After calling SYS_Initialize(NULL); in the main() function, the srcBuffer[] (source buffer) is initialized with known 8-bit values to ensure transfer accuracy.
Code:
{
srcBuffer[i] = (uint8_t)i;
}
![srcBuffer[] initialized](/xwiki/bin/download/products/mcu-mpu/32bit-mcu/ap-exercises/lab12/step4/WebHome/1771837719341-821.png?rev=1.1)
After initializing the source buffer, a message is printed using printf to indicate the start of the CPU memory transfer demonstration. The SysTick timer is started with the SYSTICK_TimerStart(); function, and the initial timestamp is captured using SYSTICK_TimerCounterGet(); to measure execution time. The CPU then copies data from srcBuffer to dstBuffer byte by byte using a manual for loop. After the transfer is complete, the SysTick timer is read again, and the elapsed time is calculated to determine the total duration of the CPU-based memory transfer.
Code:
SYSTICK_TimerStart();
timeStamp = SYSTICK_TimerCounterGet();
for(uint32_t i = 0; i < TRANSFER_SIZE; i++)
{
dstBuffer[i] = srcBuffer[i];
}
timeStamp = timeStamp - SYSTICK_TimerCounterGet();

After completing the CPU-based memory copy, the strncmp() function is used to compare srcBuffer and dstBuffer over TRANSFER_SIZE bytes to verify data integrity. If the buffers match, a success message is printed; otherwise, a data mismatch message is displayed. The total number of SysTick cycles, stored in timeStamp, is then printed to indicate the execution time of the CPU transfer.
Code:
{
printf("\n\r CPU Memory Transfer Successful with Data Match\n\r");
}
else
{
printf("\n\r CPU Memory Transfer Data Mismatch !!!\n\r");
}
printf("\n\r No of Transfer Cycles for Last Transfer --> %lu", timeStamp);

Application Code for DMA-Driven Transfer
Add the following application code to the generated project for DMA-driven transfer.
For the DMA-driven transfer project, after including the header files, define TRANSFER_SIZE as 1024. Then, declare two arrays, srcBuffer and dstBuffer, each with a size of TRANSFER_SIZE. Next, declare a volatile bool variable named completeStatus and a volatile uint32_t variable named timeStamp to track the completion status and timing information, respectively.
Code:
char srcBuffer[TRANSFER_SIZE] = {};
char dstBuffer[TRANSFER_SIZE] = {};
volatile bool completeStatus = false;
volatile uint32_t timeStamp = 0;

In the main.c file, inside the int main() function and after the SYS_Initialize() call, add the DMAC_ChannelCallbackRegister() function to register APP_Callback for DMA Channel 0. This allows the CPU to be automatically notified when the DMA transfer is complete, eliminating the need for polling. Then, use the SYSTICK_TimerStart() function to start the SysTick timer and measure the execution time of the DMA transfer.
Code:
SYSTICK_TimerStart();

Add the APP_Callback function after the extern variable declarations. The APP_Callback() function is automatically executed when a DMA transfer event occurs, as registered with the DMA controller. It calculates the total DMA transfer time using the SysTick timer and sets the completeStatus flag upon successful completion.
Code:
{
timeStamp = timeStamp - SYSTICK_TimerCounterGet();
if(status == DMAC_TRANSFER_EVENT_COMPLETE)
{
completeStatus = true;
}
}

After starting the SysTick timer, the srcBuffer is initialized with test data using a for loop, and a message is printed with printf to indicate the start of the DMA transfer demonstration. The current timer value is stored in timeStamp, and the DMAC_ChannelTransfer() function is called to initiate the memory transfer from srcBuffer to dstBuffer using DMA Channel 0.
Code:
{
srcBuffer[i] = (uint8_t)i;
}
printf("\n\r\t\t DMAC Memory Transfer DEMO\t\t");
timeStamp = SYSTICK_TimerCounterGet();
DMAC_ChannelTransfer(DMAC_CHANNEL_0, &srcBuffer, &dstBuffer, TRANSFER_SIZE);

Inside the while loop, the code checks the completeStatus flag to determine if the DMA transfer has finished. Once the transfer is complete, it verifies the data by comparing srcBuffer and dstBuffer using strncmp, prints the transfer result, and displays the total transfer cycles measured using the SysTick timer.
Code:
{
completeStatus = false;
if(strncmp(srcBuffer, dstBuffer, TRANSFER_SIZE) == 0)
{
printf("\n\r DMAC Memory Transfer Successful with Data Match\n\r");
}
else
{
printf("\n\r DMAC Memory Transfer Data Mismatch !!!\n\r");
}
printf("\n\r No of Transfer for Last Transfer --> %lu", timeStamp);
}
