Using FreeRTOS kernel in AVR projects

FreeRTOS is known as Real Time Operating System. Probably it would be too dare call it real-time-os, rather a real time scheduler where applications can be split in to independent tasks that share full processor resources by switching them rapidly it looks like all tasks are executed in parallel. This feature is called multitasking.

There are lots of debates on using RTOS on AVR microcontrollers as they are arguable too small for running scheduler. The main limitation is small amount of ram and increased power usage. If you are gonna use lots tasks in application, probably you will run out of ram that is used for saving context when switching between tasks. Consider FreeRTOS only if you use larger scale AVRs like Atmega128 or Atmega256. Surely you can find smaller schedulers that are specially designed for smaller microcontrollers even tiny series. In other hand if you master FreeRTOS it can be used with multiple types of microcontrollers like ARM, Cortex, PIC and multiple compilers including IAR, GCC, Keil, Rowley, Attolic. And main reason to keep eye on it – its free.

Probably it would take lots of time and space to go through RTOS theory. Some great information can be found on FreeRTOS website itself. In this series of posts we are going to focus on practical side of using RTOS on AVR microcontroller. We will go through several steps from single task application to more complex solutions.

Things needed to start with FreeRTOS

To start using FreeRTOS we need to download it from http://www.freertos.org/. While writing this post latest version was FreeRTOSV7.0.1. In downloaded package you’ll find freertos source code files, ports to specific microcontrollers and lots of example programs for various microcontrollers and compilers. We are going to use old good Piconomic Atmega128L development board with external memory expansion board that adds additional 8K of SRAM. You can chose any Atmega128 development board as far as it can blink LEDs, read buttons and use USART. Programs will work fine on any of them. As programming environment we are going to use AVRStudio5 which is still quite new and capricious. If you want to learn how to start working with AVRStudio5, check out this short tutorial.

Preparing AVRStudio5 project

First of all we create new project in AVRStudio5. For this we simply File->New Project and select AVR GCC C Executable Project. Enter proper location where your project will be stored.

Click OK. Then select DeviceAtmega128” from device list:

Click OK and yo are set up with basic project with main program file that contains some initial code. As FreeRTOS package contains lots of files that we don’t need for our project we are going to copy necessary files to our folder. To make it easier to update FreeRTOS files with upcoming releases we are going to maintain folder structure close to original. To do so in out project tree we are going to create Source folder. To do so just click right mouse button on project folder and select Add->New Folder and type in Source. Now import following files to Source folder from downloaded FreeRTOS package Source folder:

add all files that are in Source directory (including readme.txt). Adding files is simple – just click right mouse button of Source folder and select Add->Existing Item and in file browser select necessary files (multiple select is possible!).

We have added only kernel C files. Now we have to take care of headers. For this we need to create include folder inside Source folder. Then add all files to it from FreeRTOS package include folder.

Now that we’ve taken care of FreeRTOS kernel we need port files. As you may know, port files are to support specific microcontroller hardware. Port files contain information on how to run SysTick timer and how to save and restore context to and from task. Porting is essential part if wee need to support one or another microcontroller. FreeRTOS package already has lots of port options and mos likely you may find one that will support your selected MCU. In other case you’ll have to write port by your own. As we are using Atmega128L microcontroller seems that included Atmega323 port works fine so we are going to use it for now. To include port files properly lest create folder named portable inside Source folder. And then in portable folder we create GCC folder. And in GCC we create folder named ATMega323. Then import porting files port.c and portmacro.h to this folder from FreeRTOS package. Still this isn’t finished with files. We also need memory management file heap_1.c, which takes care of allocating and freeing memory for tasks and queues. To add this file to project create MemMang folder in portable folder and add file from same folder in downloaded package. And lastly FreeRTOS needs FreeRTOSConfig.h configuration file that keeps all freeRTOS related settings. Just import it from FreeRTOS\Demo\AVR_ATMega323_WinAVR.

To make things neat lets create another folder Drivers in project root directory. This will be used to store microcontroller peripheral drivers like USART, I2C, ADC, button, LED and so on.

Before we are going to write some code, project has to be configured. To start configuration just go to menu Project->Properties. First go to Build tab.

This time we are going to configure project as release. Select Configuration as Release. So wee need to generate .hex file (.map, .lss and .eep). These can be selected in Build Artifact group.

In Toolchain tab and Optimization select -Os optimization. And in Directories you will need to include all directories containing .h files in your project(I only managed to get working only with absolute paths). Add GCC_MEGA_AVR in Defined Symbols to tell core that we are going to use GCC for AVR microcontroller.

FreeRTOS Configuration

FreeRTOS has several options that allow to configure applications for various needs. All predefined parameters are placed in FreeRTOSConfig.h file. Depending on what functions are you gonna use some of them may be omitted. AVR microcontroller is too small to use all features of RTOS because of limited RAM. For instance using trace facility probably would be killing. So for our basic example we are using following settings:

#define configUSE_PREEMPTION		1
#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			0
#define configCPU_CLOCK_HZ			( ( unsigned long ) 7372800 )
#define configTICK_RATE_HZ			( ( portTickType )1000 )
#define configMAX_PRIORITIES		( ( unsigned portBASE_TYPE ) 1 )
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 85 )
#define configTOTAL_HEAP_SIZE		( (size_t ) ( 3500 ) )
#define configMAX_TASK_NAME_LEN		( 8 )
#define configUSE_TRACE_FACILITY	0
#define configUSE_16_BIT_TICKS		1
#define configIDLE_SHOULD_YIELD		1
#define configQUEUE_REGISTRY_SIZE	0

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		0
#define INCLUDE_uxTaskPriorityGet		0
#define INCLUDE_vTaskDelete				0
#define INCLUDE_vTaskCleanUpResources	0
#define INCLUDE_vTaskSuspend			0
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				0

We are going to use preemptive kernel engine where kernel timer tick ISR determines which task should run next according to task priorities. Preemptive multitasking is similar to OS like Linux or Windows. Preemptive kernel uses more RAM but is more flexible. Also we aren’t going to use idle hook for now – so disable it too and make idle task yield immediately once it is run. We won’t be changing task priorities during program flow so we disable this functionality too. We are going to use task delay utilities so leave them ON.

Each functionality that isn’t uses frees some amount of memory. For low memory devices is very important to select these carefully.

After all project properties are set, we can start coding our first application. Why not to start with single task – LED blink. To make code modular we will start with writing LED library. So we need to write these routines and put in separate .c and .h files.

 

LED freeRTOS driver

We created a Drivers folder in our project rood directory. Simply create LED.c and LED.h files by pressing right mouse button on Drivers folder and selecting Add->New Item. AVRStudio5 will generate .c and .h templates where we can start writing our code.

We are going to control one LED connected to pin D6 in our test project. Writing driver is easy. First we need to initialize LED pin as output with simple function:

void vLEDInit(void)
{
// Set LED_O as output pin
    DDR_LED_O |= (1<<BIT_LED_O);
}

and then write LED toggle function that will be called by task.

void vLEDToggle(void)
{
   //Toggle LED
   PORT_LED_O ^= (1<<BIT_LED_O);
}

that’s it.

Creating task

Later we are going to add more tasks so let’s put them all in one separate source file. I created mytasks.c file along with mytasks.h. Lets create simple tasks which will toggle LED every one second. We already have LED toggling driver ready. The only thing left is managing proper delay so LED would toggle every second. FreeRTOS has handy function that allows to set tasks delays when tasks should run. This is called vTaskDelayUntil(). It takes two parameters – previous wake time and ticks to wait. Now its a tricky part of all this. If you look back in FreeRTOSConfig.c file you’ll find a line:

#define configTICK_RATE_HZ			( ( portTickType )1000 )

which means that kernel timer ticks every 1ms. In order to get 1s delay we need to wait 1000 ticks. All we need is to set this number as second parameter.

void vLEDFlashTask( void *pvParameters )
{
vLEDInit();
portTickType xLastWakeTime;
const portTickType xFrequency = 1000;
xLastWakeTime=xTaskGetTickCount();
	for( ;; )
	{
		vLEDToggle();
		vTaskDelayUntil(&xLastWakeTime,xFrequency);
	}

}

Function xTaskGetTickCount() returns current number of ticks calculated form program start. So we need to pass this number to our delay function as first parameter. We are doing LED initialize before endless for(;;) loop. And task application goes inside endless loop. Here goes our vLEDToggle() function and vTaskDelayUntil() function. vtaskDelayUntil function ensures that task remains in blocked state until delay ends. So this means that blocked task isn’t scheduled until proper event unblocks it (in our case delay exceeds). If we would use idle task hook function for setting MCU to power save mode our application would require less energy as your task is run only every 1s.

Running freeRTOS task

Once task is create consider biggest job is done. Now what is left is to create this task in main routine and start scheduler. Before we start we must decide what priority task should be assigned with. As we are going to use one task (plus idle) we can use idle priority:

#define mainLED_TASK_PRIORITY			( tskIDLE_PRIORITY )

To create task we are gonna use one handy function xTaskCreate(). It needs several parameters. First one is task function name that has to be run. Second is a task name that can be freely chosen. Third parameter is stack size. Our application is very simple so minimal stack can be used. For more complex tasks select this parameter carefully. Then follows pointer to parameters that has to be passed to tasks. If there is no parameters to pass use NULL. Following parameter is task priority. And last parameter is used to pass back a handle by reference so task could be addressed. Pass NULL as we aren’t going to use it. Our created task should look as follows:

xTaskCreate( vLEDFlashTask, ( signed char * ) "LED", configMINIMAL_STACK_SIZE, NULL, mainLED_TASK_PRIORITY, NULL );

And lastly we must start scheduler which is our rtos kernel. This is done by calling vTaskStartScheduler().

Compiled project occupies about 6K of Flash memory and uses 3,6K(88.5%) of RAM. So RAM is limiting factor on how many tasks are you gonna run and what complexity they are. If no preemption is required as alternative co-routines can be used as they can share stack.

You can download full project here: M128RTOS[~200K]

13 Comments:

  1. Pingback: Running multiple FreeRTOS tasks on AVR | Scientific, embedded, biomedical, electronics contents.

  2. Pingback: Free RTOS on AVR Atmega128 tutorial | Embedded projects from around the web

  3. Hi,
    your instructions are nearly perfect for new commers like me.
    I wanted to ask you for a little optimisation in the following points.
    1) Ad some screenshots of the folder structure when you finished adding all the headerfiles from freeRTOS, just to be on the safe side.
    2) In the last paragraph before talking about the freertosConfig.h file, you talked about “including all directories containing .h files”. From where? FreeRTOS? If so, which ones. This also could be solved if you would add a small screenshot of the files that you are talking about.
    3) Add “GCC_MEGA_AVR” into “Defined Symbols”? A little elaboration with one or 2 screenshots would be great.

    Again those are my own humble recommendations, and I would appreciate it if you can find some time

  4. Thank you for suggestions. I’ll try to make these more clear when I find time. For now if you stuck at some point don’t hesitate to ask. And of course you can always download project archive to analyze it. It’s at the end of post.

    • Thank you for your answer.
      I’am stuck at these 3 points that where mentioned in my frist post.
      Where should the freertosCONFIG.h file located, if you consider that “./ ” represents the root if the project folder.

      “GCC_MEGA_AVR” into “Defined Symbols” What is meant by that?

  5. Thank you a lot for this lesson, actually i fellow all your steps, but an error has been appeared .

    undefined reference to ‘vApplicationIdleHook’ ..???!!

    What’s that? when i put comments mark before this function in tasks.c file, this error disappeared so i think something i missed in the initialization and declaration of RTOS files.
    I’ll be grateful if u could help me to fix this error

  6. at EreeRTOSconfig.h
    change this line #define configUSE_IDLE_HOOK 1
    to #define configUSE_IDLE_HOOK 0
    (that’s a guess )

    Good luck

  7. Hello

    When am I debugging the program and it comes to this line of code:

    /* The wake time has not overflowed, so we can use the current block list. */
    vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );

    The simulation stopped

  8. How do you debugging this project to AVR Debugger?

  9. Hello,
    I’m unable to import the FreeRTOSConfig.h file, the error says, cannot read such file, can some one pl help me.
    Thanks in advance.

  10. i guess there are few things worth noting here for the newbies like me:
    first, if you are downloading the project here then put it in D:\Elektronika\Projects\M128RTOS , or you can change the path from toolchain directories otherwise you will get can’t find the directory for some files.
    Second, you will need to change some outdated symbol
    SIG_OUTPUT_COMPARE1A to TIMER1_COMPA_vect
    and it will build successfully, then you can open proteus and just get atmega128 ,adjust the freq to external medium and in advanced properties remove default to 7372800Hz, and it will work perfectly!
    also i would like to thank Scienceprog for this amazing tutorial, it’s way better than that on the official freertos site.

  11. where can I find this file ? thanks
    Error 1 FreeRTOS.h: No such file or directory C:\Users\antonius\Documents\Atmel Studio\6.1\M128RTOS\freertosm128\freertosm128.c 9 22 freertosm128

Leave a Reply

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