FreeRTOS with Atmega168PA

FreeRTOS

The real time operating system chosen for experiments was FreeRTOS because of many advantages such as open source, robust task management and portability to different platforms.This Real time operating system is a real-time kernel written in C and is designed to be simple, portable and concise. It is ideal to be portable to microcontroller systems in which many applications can be organized in a collection of independent threads. Its kernel is responsible for deciding which thread should execute by examining the priorities assigned by the designer. In simple words, the designer can assign higher priorities to threads which implement hard requirements, and lower priorities to threads that implement soft requirements.

In FreeRTOS each thread of execution is called a task. The important point of this free operating system is its kernel. It is responsible for executing times and provides a time management for tasks based on priorities. It is possible to build an event-driven system and no processing time is wasted by event that has not occurred. 

It includes full memory protection unit and its whole code can be portable to another microcontroller. In this case an Atmega168PA portable C code was included which all the development could be done to implement real time tasks. This code contains all the definitions for registers, memory management functions, special case registers and correct addresses for all needed applications of a specific microcontroller and some assembly code.

More features of this operating system are: options for scheduling policy such as preemptive, priority driven, cooperative mode when context switches only if a task blocks, message queues, semaphores, and mutex.

After all the definition of the project is done, the implementation can start and the designer has to implement prototypes for his tasks, set priorities, synchronization methods if needed and start the scheduler. With simple steps like above it is possible to have a free, stable RTOS running on some chip. The only point the designer has to be careful is the amount of memory available in the chip, and the values have to be set in FreeRTOSConfig.h file. This file has some operating system configurations and the designer has to check depending on which features he is interested in.

 Real time problem

Thinking about a real time solution, we have found a useful application for implementing a real time operating system. In our quadcopter project, we have certain functions such as sampling sensors, calculating the output values, testing values to guarantee a safe range of motor speed and, effectively, updating the motors speed. Our first approximation was to create two tasks, the first, with higher priority, is responsible for updating the motor speed, since it is a critical activity, and another with idle priority to execute the sampling and stabilization code (which involves all the calculations and safe code to keep the speed in the correct range).

In FreeRTOS system, an idle task is automatically created when the scheduler is activated and it is preempted whenever a higher priority task occurs in the scheduler and remains in this state until the higher priority task finishes. In order to guarantee that the first task to execute is the idle, because we want that the output executes only after sampling the values from sensors, we have used a synchronization method between tasks provided by queue. It works as follows, first all the tasks are created at the beginning and when the scheduler starts it will allocate the task with highest priority first. So, higher priority task will execute and check if there is data inside queue (that means it is time to execute), case not, this task will be preempted indefinitely until some data is saved inside a queue (system queue in this case).

Now, the idle task is able to execute with no interruption. It will sample sensors, calculate values, do some testing and after it is done, it will send a data to queue, “authorizing” the execution of another task. At this point, the higher priority task will find something inside a queue and then the scheduler will recognize that this task is able to execute and then change the processor slot to it, consequently, the idle task will be preempted and wait until next execution time, when the highest priority finishes again. Thus, we guarantee that each task will be in a ready state while another is executing and vice-versa.

A future idea would be sending the output values for all 4 motors through this queue, however, because of execution and time constraints, and not enough familiarity with the operating system, the queue was implemented with space for 1 data for sync purposes. A critical situation could be that our motors have a maximum sampling time and because of the time to take data from the queue, the deadline could be missed causing a wrong output response to motors.

The example below shows how to create two tasks and use a queue to synchronize them. The objective is to send the data through a queue from one task to another with a value to send to a digital output pin in order to blink a LED.

int main( void )
{
    /* Setup the microcontroller hardware for the demo. */
    DDRB  = 0xff;
    PORTB = 0x00;	
    // parameter: max of elements, size of each element 
    // is 1 byte (char) provided by portBASE_TYPE.
    queue = xQueueCreate(1, sizeof(portBASE_TYPE));

    if (queue != NULL) {
      //the task which is waiting data to be ready in the
      // queue has higher priority, 
      // because it is waiting indefinitely and is blocked.
      xTaskCreate( task1, ( signed char * ) "Receiver",
          configMINIMAL_STACK_SIZE, NULL, 1, NULL );

      //the task which is sending data to queue has lower priority
      xTaskCreate( task2, ( signed char * ) "Sender", 
          configMINIMAL_STACK_SIZE, NULL, 0, NULL );	//

      //Start the scheduler and tasks start executing.
      vTaskStartScheduler();			
    }
    else {
       // the queue could not be created.
    }

    // Leave this function.
    vStartFlashCoRoutines(8);

    // Start the RTOS scheduler.
    vTaskStartScheduler();

    // Should never get here!
    return 0;    
}
// lower priority, it sends data to the queue
void task2 (void * pvParameters) {
    portBASE_TYPE xStatus;
    // demonstration's day!! hahahahahah
    volatile portBASE_TYPE valueToSend = 0x00;
    for ( ;; )
    {
       /*
	this code here executes all the time, when it wants 
        to change to another task
	we have just to send a "message" using the next line,
        which means that this task
	will block because the task 1 will perceive data from 
        queue and task 1 has higher priority.
       */
       // invert values
       valueToSend ^= (unsigned char)1;
       xStatus = xQueueSendToBack(queue, &valueToSend, 0);
       if (xStatus != pdPASS)
       {
	  // operation could not be completed 
          // because the queue was full.
       }
       else {
 	// just debugging...
       }
    }
}
// higher priority, receiver. Waits for data in the queue
void task1 (void * pvParameters) 
{
    portBASE_TYPE xStatus;
    volatile portBASE_TYPE receivedValue = 0;

    for( ;; ) {	
        // portMAX_DELAY means that this task will 
        // wait until queue receives data
	xStatus = xQueueReceive(queue, &receivedValue, portMAX_DELAY);	
	if (xStatus = pdPASS) {
            // data was received successfully
	    PORTB = receivedValue;
        }
        else {
	   // error, data was not received correctly
	}
    }
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *