FreeRTOS using Kinetis SDK 1.3 and KDS

Well those are a lot of K’s on a title.
First I’d like to say that this article is inspired by this post made by well known Prof. Erich Styger. Last month I found myself working on a project on which Processor Expert was just not an option for me, and after all the struggle to make FreeRTOS work, I decided that maybe I should share my experience with the embedded community out there.

For this setup I will be using the FRDM-KL43Z board, since is the one I have closer, but it should work with any Kinetis MCU; at least with the K and L series which are the ones I have tried it on yet. To illustrate the functionalitie of the different tasks we will also make use of the GPIO library.
Alors, c’est parti..!
First we need to make sure that the correct library has been built. Freescale (now NXP) offers a different library for each platform, including FreeRTOS. So the first thing be should do is add the project found on {Kinetis_SDK}/lib/ksdk_freertos_lib/kds to KDS.
Capture_1
Capture_2
And compile it…
Capture_3
No error should rase at this point and we should end up with a binary in either the debug or release folder, depending on the selected building configuration. Although, later we will need to make sur that we are using the correct file for our project.
The next step is then to create the project on which we will be working. Mine will be called FreeRTOS_NoPE.
Capture_4.JPG
Be sure to select the right target…
Capture_5
And the right SDK without selecting Processor Expert
Capture_6
After all these steps a project should have been created. All projects created using the Kinetis SDK with no PE have this initial (mostly self-explanatory) structure:
Capture_7
And the code inside main.c should look something like this:
[code language=”cpp”]
#include "fsl_device_registers.h"
static int i = 0;
int main(void)
{
/* Write your code here */
/* This for loop should be replaced. By default this loop allows a single stepping. */
for (;;) {
i++;
}
/* Never leave main */
return 0;
}
[/code]
The first thing that we are going to add is the headers of the libraries taht we will be using.
[code language=”cpp”]
#include "fsl_device_registers.h"
#include "fsl_gpio_driver.h"
#include "fsl_os_abstraction.h"
[/code]
And with them, we should also configure the include paths for the headers of the OSA (Operating system abstraction, yep, it is important), drivers, the HAL, the system and the utilities libraries.
Capture_8
 
Capture_9
 
Capture_10
 
Capture_11
 
Capture_11_b
 
Capture_11_c
Before continuing with the configuration of the project, let us first explain the source code we will be using so we can take that out of the way.
A quick note here. On this oportunity I won’t take too much time to explain the sample code, since the objective of this post is to show the configuration of the project and the build tools; however, if you are interested, this code has been taken straight from the examples of the Kinetis SDK.
The first thing we are going to do is define a couple of constants important for the configuration of the TASKS of our operating system. Remember, FreeRTOS considers the 1 as the highest priority.
[code language=”cpp”]
#define TASK_PRIO_GREEN 2U
#define TASK_PRIO_RED 3U
#define TASK_STACK_SIZE_GREEN 512U
#define TASK_STACK_SIZE_RED 256U
[/code]
The next step is to define our Tasks. In this case I am defining one tasks that will make the green led on the board blink and a second task that will make the red one blink. The reason why I define the second task between a pre-processor condition as a security measure; we will see that if the project is not configured properly, the USE_RTOS constant will not be defined.
[code language=”cpp”]
void blinky_green (task_param_t param)
{
for(;;){
LED1_TOGGLE;
OSA_TimeDelay(100);
}
}
void blinky_red (task_param_t param)
{
for(;;){
LED2_TOGGLE;
OSA_TimeDelay(100);
}
}
OSA_TASK_DEFINE(blinky_green, TASK_STACK_SIZE_GREEN);
#if USE_RTOS
OSA_TASK_DEFINE(blinky_res, TASK_STACK_SIZE_RED);
#endif
[/code]
 
Next, the configuration of the GPIO needs to be done. First we define the configuration structures
[code language=”cpp”]
/* Write your code here */
// Configure LED1 as digital output and enable the high drive strength.
gpio_output_pin_t outputPin = {
.outputLogic = 0,
.slewRate = kPortFastSlewRate,
.driveStrength = kPortHighDriveStrength,
};
gpio_output_pin_user_config_t outputUserConfig[] = {
{
.pinName = kGpioLED1,
.config = outputPin,
},
{
.pinName = kGpioLED2,
.config = outputPin,
},
{
.pinName = GPIO_PINS_OUT_OF_RANGE,
.config = outputPin,
},
};
[/code]
Then we perform the hardware initialization (we will talk about this later) and the initialization of the OSA and the GPIO driver.
[code language=”cpp”]
// Enable clock for PORTs, setup board clock source
hardware_init();
OSA_Init();
// Initializes GPIO pins.
GPIO_DRV_Init(NULL, outputUserConfig);
[/code]
Creation of the tasks….
[code language=”cpp”]
/* Create Tasks */
osa_status_t result = kStatus_OSA_Error;
result = OSA_TaskCreate(blinky_green,
(uint8_t *) "blinky_green",
TASK_STACK_SIZE_GREEN,
blinky_green_stack,
TASK_PRIO_GREEN,
(task_param_t)0,
false,
blinky_green_task_handler);
if(result != kStatus_OSA_Success){for(;;);}
#if USE_RTOS
result = OSA_TaskCreate(blinky_red,
(uint8_t *) "blinky_red",
TASK_STACK_SIZE_RED,
blinky_red_stack,
TASK_PRIO_RED,
(task_param_t)0,
false,
blinky_red_task_handler);
if(result != kStatus_OSA_Success){for(;;);}
#endif
[/code]
 
And finally we start running the tasks
[code language=”cpp”]
OSA_Start();
[/code]
 
If we try to build the project now, the compiler is going to complain, big time. The thing is that we have been using Board constants, which are defined by the SDK specially for each board, so we need to include these.
Capture_12
[code language=”cpp”]
#include "board.h"
#include "fsl_clock_manager.h"
[/code]
And add them to the include directories…
Since there is not a single location for these files and we will be modifying them, the best is to get them from the examples folder and copy them inside our project. For this I normally create a folder called board and include it into the pre-processor include paths.
Capture_13
Capture_14
Here I also create a file were I define the hardware_init function where I perform the clock initializations and the multiplex configuration.
[code language=”cpp”]
void hardware_init(void) {
/* enable clock for PORTs */
CLOCK_SYS_EnablePortClock(PORTA_IDX);
CLOCK_SYS_EnablePortClock(PORTB_IDX);
CLOCK_SYS_EnablePortClock(PORTC_IDX);
CLOCK_SYS_EnablePortClock(PORTD_IDX);
CLOCK_SYS_EnablePortClock(PORTE_IDX);
/* Init board clock */
BOARD_ClockInit();
configure_gpio_pins(PORTD_IDX);
}
[/code]
 
Once again, if we try to build the project, we will find out that since we only added the headers, the compiler is not able to to find the definition.
Capture_15
The definitions of the sdk are included in the platform tools that we built at the beginning. For this we need to include the binary of the library into the linker configuration, so the definitions can be taken into account during the linking process.
Capture_16
 
Capture_17
The final step is to add the headers of the FreeRTOS. Here is where is gets tricky. I initially tried to add the headers the same way that I did for the rest of the sdk, however, an additional step was necessary in order to make the OSA recognize the RTOS; adding the headers and sources as a virtual folder solved the problem.
Capture_18
 
Capture_19
 
Capture_20
If KDS asks if we wish to add the paths to the pre-compiler, we say yes
Capture_21
Capture_22
And finally, we add the configuration symbol to the Compiler indicating that we are going to use FreeRTOS as the Operating System.
Capture_23
 
After finishing this configuration, we should be able to build and run the software on our board.
Capture_24
 
 


The whole used code:
[code language=”cpp”]
#include "board.h"
#include "fsl_clock_manager.h"
#include "fsl_device_registers.h"
#include "fsl_gpio_driver.h"
#include "fsl_os_abstraction.h"
static int i = 0;
#define TASK_PRIO_GREEN 2U
#define TASK_PRIO_RED 3U
#define TASK_STACK_SIZE_GREEN 512U
#define TASK_STACK_SIZE_RED 256U
void hardware_init(void) {
/* enable clock for PORTs */
CLOCK_SYS_EnablePortClock(PORTA_IDX);
CLOCK_SYS_EnablePortClock(PORTB_IDX);
CLOCK_SYS_EnablePortClock(PORTC_IDX);
CLOCK_SYS_EnablePortClock(PORTD_IDX);
CLOCK_SYS_EnablePortClock(PORTE_IDX);
/* Init board clock */
BOARD_ClockInit();
configure_gpio_pins(PORTD_IDX);
}
void blinky_green (task_param_t param)
{
for(;;){
LED1_TOGGLE;
OSA_TimeDelay(100);
}
}
void blinky_red (task_param_t param)
{
for(;;){
LED2_TOGGLE;
OSA_TimeDelay(100);
}
}
OSA_TASK_DEFINE(blinky_green, TASK_STACK_SIZE_GREEN);
#if USE_RTOS
OSA_TASK_DEFINE(blinky_red, TASK_STACK_SIZE_RED);
#endif
int main(void)
{
/* Write your code here */
// Configure LED1 as digital output and enable the high drive strength.
gpio_output_pin_t outputPin = {
.outputLogic = 0,
.slewRate = kPortFastSlewRate,
.driveStrength = kPortHighDriveStrength,
};
gpio_output_pin_user_config_t outputUserConfig[] = {
{
.pinName = kGpioLED1,
.config = outputPin,
},
{
.pinName = kGpioLED2,
.config = outputPin,
},
{
.pinName = GPIO_PINS_OUT_OF_RANGE,
.config = outputPin,
},
};
// Enable clock for PORTs, setup board clock source
hardware_init();
OSA_Init();
// Initializes GPIO pins.
GPIO_DRV_Init(NULL, outputUserConfig);
/* Create Tasks */
osa_status_t result = kStatus_OSA_Error;
result = OSA_TaskCreate(blinky_green,
(uint8_t *) "blinky_green",
TASK_STACK_SIZE_GREEN,
blinky_green_stack,
TASK_PRIO_GREEN,
(task_param_t)0,
false,
blinky_green_task_handler);
if(result != kStatus_OSA_Success){for(;;);}
#if USE_RTOS
result = OSA_TaskCreate(blinky_red,
(uint8_t *) "blinky_red",
TASK_STACK_SIZE_RED,
blinky_red_stack,
TASK_PRIO_RED,
(task_param_t)0,
false,
blinky_red_task_handler);
if(result != kStatus_OSA_Success){for(;;);}
#endif
OSA_Start();
/* This for loop should be replaced. By default this loop allows a single stepping. */
for (;;) {
i++;
}
/* Never leave main */
return 0;
}
[/code]

0 Shares

2 Comments

Leave a Reply

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

0 Shares
Tweet
Share