Step 4: Add Application Code to the Project

Last modified by Microchip on 2026/06/29 11:13

Application Code for the Project

Once the project is generated, open the main.c file and declare all the macros, variables and structure variables outside the main() function.

Code:

#define ADC_VREF (3.3f)
#define TOTAL_TICK 400
/*---------------- Task IDs ----------------*/
typedef enum
{   
    TASK_LED = 0,
    TASK_ADC,
    TASK_UART,
    TASK_IDLE,        
} TASK_ID;

/*---------------- Globals ----------------*/
volatile uint32_t ticks = 0;

volatile uint8_t taskDelay[3] = {100, 200, 100};
volatile uint8_t idx = 0;
volatile uint8_t taskState;

static uint16_t adcCount = 0;
static float adcResult = 0.0f;

Lab code

.

Once the required variables and macros are declared, add the functions required as shown in the accompanying image.

Code:

/*---------------- SysTick Callback ----------------*/
void SYSTICK_Callback(uintptr_t context)
{       
   if(ticks == taskDelay[idx])
    {
        taskState = idx;
    }
   
   if(ticks > TOTAL_TICK)
    {
        ticks = 0;
    }
   
   else
    {
        ticks++;   
    }
}

In this function, the SYSTICK_Callback function is called on every SysTick interrupt and acts as a simple time base for task scheduling. It compares the global ticks counter with the delay value of the current task index (taskDelay[idx]), and when they match, it updates taskState to signal that the task is ready to run. After that, the function either resets ticks back to zero when it exceeds TOTAL_TICK to maintain a periodic timing window or increments ticks otherwise. This ensures a continuous cyclic tick count while keeping the interrupt routine short and suitable for a cooperative multitasking design.

Code:

/*---------------- Tasks ----------------*/
static void Task_LED(void)
{
    LED_Toggle();
}

static void Task_ADC(void)
{
    adcCount = ADC_ConversionResultGet();
}

static void Task_UART(void)
{
    adcResult = (float)adcCount * ADC_VREF / 4095U;
    printf("ADC = %d.%02d V\r\n",
       (int)adcResult,
       (int)((adcResult - (int)adcResult) * 100));
}
Information

Note: These three task functions each perform a single, well‑defined operation in the system.

Task 1 - Task_LED()

Task 2 - Task_ADC()

Task 3 - Task_UART()

Task_LED simply toggles the LED state, typically used as a status indicator. Task_ADC reads the latest raw Analog-to-Digital Converter (ADC) conversion value and stores it in adcCount. Task_UART then converts this raw value to a voltage using the ADC reference voltage (ADC_VREF) and 12‑bit resolution (4095), formats the result and transmits it over Universal Asynchronous Receiver Transmitter (UART) using printf, displaying the voltage with two decimal places.

Lab code

 

Once the three tasks have been added, incorporate the following function.

Code:

static void Scheduler_Run(void)
{
   switch (taskState)
    {
       case TASK_LED:
            Task_LED();
            idx = 1;
            taskState = TASK_IDLE;
           break;

       case TASK_ADC:
            Task_ADC();
            idx = 2;
            taskState = TASK_IDLE;
           break;

       case TASK_UART:
            Task_UART();
            idx = 0;
            taskState = TASK_IDLE;
           break;

       case TASK_IDLE:
           break;
           
       default:
           break;
    }
}

This Scheduler_Run function implements a simple cooperative scheduler by examining taskState and executing exactly one task when it becomes ready. If the state is TASK_LED, TASK_ADC, or TASK_UART, the function calls the corresponding task, updates idx to select the next task in round‑robin order, and then resets taskState to TASK_IDLE so no further task runs until the SysTick callback schedules the next one. If the state is TASK_IDLE or an unexpected value, the function performs no action and returns.

Lab code

 

Add the following inside the main() function, but outside the while loop.

Code:

    ADC_Enable();
   
    SYSTICK_TimerCallbackSet(SYSTICK_Callback, 0);
    SYSTICK_TimerStart();

Call the Scheduler_Run inside the while loop.

Code:

    Scheduler_Run();

Lab code

Now that the ADC is enabled, the SysTick timer is configured and starts with SYSTICK_Callback to generate periodic timing events inside the endless while loop. Scheduler_Run is then repeatedly called to execute any task that has been marked ready by the SysTick interrupt. SYS_Tasks will service all polled MPLAB® Harmony v3 framework modules, allowing the system to operate continuously without blocking or executing application logic inside interrupts.

Back to Top