FreeRTOS Lecture 31 – Exercise: Testing via debugger Part-2

 

Exercise: Testing via debugger Part-2

 

 

In this article, let’s understand the concept of FreeRTOS task schedule.

We already created two tasks. Now we have to schedule them because otherwise, the tasks will not be running on your microcontroller. The creation of a task means an allocation of some memory.

Task creation process(Figure 1) :

The task creation process  is creating a task control block for a particular task, allocating memory for it, allocating stack memory for a task, initializing the stack memory for a task, and various other things that are related to the task creation process, which will be taken care of by the task services of the FreeRTOS.

Task creation process
Figure 1. Task creation process.

 

In our project, we have created the project and allocated the memory for it. But we never scheduled it. Now you have to use FreeRTOS scheduling APIs in order to schedule these tasks. vTaskStartScheduler() API is used to schedule the tasks. This API basically means that start the scheduler.

int main(void)
{
    //1. Reset the RCC clock configuration to the default reset state.
    //HSI ON, PLL OFF, HSE OFF, system clock = 16MHz, cpu_clock = 16MHz
    RCC_DeInit();

   //2. update the SystemCoreClock variable
   SystemCoreClockUpdate();

   //3. lets create 2 tasks , task-1 and task-2
   xTaskCreate( vTask1_handler,"Task-1", configMINIMAL_STACK_SIZE,NULL,2,&xTaskHandle1 );
   xTaskCreate( vTask2_handler,"Task-2", configMINIMAL_STACK_SIZE,NULL,2,&xTaskHandle2 );

   //4. Start the scheduler.
   vTaskStartScheduler();

  for(;;);

}

Statement to start the scheduler

 

vTaskStartScheduler() API is implemented in the task.c file, as shown in Figure 2.

Figure 3. Implementation of vTaskStartScheduler().
Figure 2. Implementation of vTaskStartScheduler().

 

Once you start the scheduler, according to the scheduling policies of the scheduler, the scheduler will be executing the task functions. There are two task functions with the same priority, i.e., 2. That’s why according to the scheduling policy of the scheduler, it will be executing the task function. If it is preemptive scheduling, then the round-robin scheduling will happen since both the tasks have got equal priorities. That means task 1 handler will run for some time, and then task 2 will run for some time. A definite time quanta will be allocated to each task.

vTaskStartScheduler() function will never return because this function will give control to the task. Since the task will be running continuously, the scheduling function will never return. Therefore, the control will never come to the infinite loop shown in Figure 3 unless and until there is an error while executing the scheduler function.

schedule task
Figure 3. Infinite loop in the main function.

 

Now let’s test this code. First, let’s build this project (Figure 4). Now the code is compiled successfully without any errors. 

schedule task
Figure 4. Building the project.

 

After compilation, download the code into the target hardware. For that, first, connect your hardware. After that, just right-click on your project, go to target and click on the program chip option, as shown in Figure 6. Then a pop-up will appear, tick the reset after program checkbox, select the elf format of your project generated by building the project and then click ok (Figure 5).

schedule task
Figure 5. Option for programming the chip.

 

schedule task - Exercise: Testing via debugger Part-2
Figure 6. Choosing the reset after program option.

 

Now let’s keep a couple of breakpoints in the task 1 handler and task 2 handler (Figure 8) in order to see whether those breakpoints really hit or not. First, let’s keep a breakpoint in vTask1_handler and then keep a breakpoint in vTask2_handler, as shown in Figure 7.

schedule task. Exercise: Testing via debugger Part-2
Figure 7. Placing the breakpoints in task handlers.

 

After that, before starting the scheduler, keep another breakpoint, as shown in Figure 8.

schedule task. Exercise: Testing via debugger Part-2
Figure 8. Keeping the breakpoint before the start of the scheduler.

 

Now let’s get into the debug mode. Right-click on the project, click on debug as, and then select the Ac6 STM32 C/C++ application option (Figure 9). While entering into the debug mode, you will find a pop-up window, as shown in Figure 11. Just click yes.

Figure 10. Option for entering into debug mode. Exercise: Testing via debugger Part-2
Figure 9. Option for entering into debug mode.

 

Figure 11. Pop-up appeared while entering into the debug mode. Exercise: Testing via debugger Part-2
Figure 10. Pop-up appeared while entering into the debug mode.

 

For debug mode, we have already set the breakpoints. Now the code is not running, and it is actually halted at line 28 of the main function. Let’s hit run (Figure 11).

Figure 12. Option to run the code. Exercise: Testing via debugger Part-2
Figure 11. Option to run the code.

 

In Figure 12, you can see that we are about to start the scheduler. Tasks are created, and the task handle is populated (Figure 13) with some address. In the beginning, the task handle was NULL. Now it is populated with some address. That means tasks are created successfully.

Figure 13. Scheduler is about to start. Exercise: Testing via debugger Part-2
Figure 12. Scheduler is about to start.

 

Figure 14. List of task handles with their corresponding populated addresses. Exercise: Testing via debugger Part-2
Figure 13. List of task handles with their corresponding populated addresses.

 

Now let’s hit run again and see whether task handlers in Figure 8 executes or not. In Figure 14, you can see that the control first came into vTask1_handler. It will execute for some time depending upon the time quanta allocated for this task by the FreeRTOS kernel.

Figure 15. Control transferred to vTask1_handler. Exercise: Testing via debugger Part-2
Figure 14. Control transferred to vTask1_handler.

 

Let’s hit run once again. It is still executing the task 1 handler, and at some time, it may come to the task 2 handler. But the control is not at all coming to the task 2 handler. It looks like there is some problem. Just remove the breakpoint in task 1 handler and try to rerun the code. Now you can see that the control is transferred into the vTask2_handler (Figure 15). This is because of the debugging things because they execute very quickly. That’s why the debugger may not be tracing those events related to the task 2 handler execution.

Testing via debugger Part-2
Figure 15. Control transferred to vTask2_handler.

 

If you remove the breakpoint in task 2 handler and keep it in task 1 handler, now if you execute the code, then the task 1 handler executes, and task 2 handler also executes. That means the context switching or scheduling is taking place. But how the tasks are synchronized, what is the time quanta, who triggers all these things, and so many other things we have to understand. That we will see as we make progress. But our simple goal here is to create 2 tasks and schedule them, which we have done already.

 

FastBit Embedded Brain Academy Courses

Click here: https://fastbitlab.com/course1

FastBitLab

The FastBit Embedded Brain Academy uses the power of internet to bring the online courses related to the field of embedded system programming, Real time operating system, Embedded Linux systems, etc at your finger tip with very low cost. Backed with strong experience of industry, we have produced lots of courses with the customer enrolment over 3000+ across 100+ countries.