Getting Started with Micrium OS on SiLabs EFM32GG11

Created by Ben Roloff, last modified on Mar 11, 2019

Introduction

Most embedded applications work on a foreground and background type configuration. The background is a simple loop that runs through the application. The foreground is made up of interrupts that indicate a change in states. This can be made to work for any application, but as the application gets more complex the implementation becomes more complicated. It becomes harder for the application to be quickly responsive. It is also hard to update the application or make changes to it. An RTOS looks to overcome these problems. So, what is an RTOS? Well RTOS stands for Real Time Operating System. It is an OS that is designed to deal with and react in real time. On an embedded level, a big difference is that an RTOS splits the application into tasks instead of having one large loop run through the parts of an application. The RTOS handles the schedule of when what task should happen at what time. This allows it to responsive to inputs. It also makes updating and changing the application easier since it is split up into tasks that aren’t all dependent on the others. An RTOS is designed to help develop an application that is able to work and react in real time.

Micrium OS

Now there are few RTOS choices out there. For this project we will be using Micrium OS. It was recently bought by Silicon Labs. This is what allows the base kernel to be readily available in Simplicity Studio. This is nice because you can start messing around with the RTOS without having to get a license first. Now if you are planning on making a commercial product you will need to get a license. You will also notice that there are some peripheral modules with the base Micrium OS, USB, net, CAN, and file system. These are not including with the base kernel. If you want to use any of these you will need to get a specific license for them. But with the base kernel built into the SDK you can easily start messing around with the RTOS.

Building the Code

For our Micrium OS project we will be using Simplicity Studio v4 and the SLSTK3701A development board. You will want to make sure you have the latest updates downloaded and the latest SDK. We will start by making a new project.

  • In Simplicity Studio just go to File > New > Project.
  • Choose Silicon Labs MCU Project
  • Click Next
  • Choose your board (EFM32GG11 Giant Gecko Starter Kit board) And the Gecko SDK
  • Click Next
  • Choose an Empty C program
  • Click Next
  • Name it whatever you want
  • Click Next
  • Make sure GNU ARM is chosen
  • Click Finish

Now we will need to add the necessary files to make it a Micrium OS project

  • Right click on your project and go to Properties
  • Go to C/C++ Builder > Project Modules > EFM32 > Middleware > Select Micrium OS
  • Click Apply
  • Click Ok
  • Right click on your project and go to New > folder
  • Name it cfg
  • Right click on the cfg folder go to import > MCU project > more import options
  • Choose file systems
  • Click Next
  • Click Browse
  • Under C drive find Silicon Labs > Simplicity Studio > v4 > developer > sdks > gecko_sdk_suite > v2.5 > platform > Micrium OS > cfg
  • Click Ok
  • Click to add
    • common_cfg.h
    • cpu_cfg.h
    • os_cfg.h
    • rtos_cfg.h
    • rtos_description.h
    • rtos_err_cfg.h
  • Click Apply
  • Click Ok
  • Right click on cfg folder and go to New > header file
  • Name it bsp_cfg.h
  • Copy this code into it

bsp_cfg.h

#ifndef CFG_BSP_CFG_H_
#define CFG_BSP_CFG_H_
 
/*
*********************************************************************************************************
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*********************************************************************************************************
*/
 
#include  <bsp_opt_def.h>
 
 
/*
*********************************************************************************************************
*********************************************************************************************************
*                                     CLOCK CONFIGURATION
*********************************************************************************************************
*********************************************************************************************************
*/
 
 
#define  BSP_HF_CLK_SEL                                     BSP_HF_CLK_HFRCO
 
#define  BSP_LF_CLK_SEL                                     BSP_LF_CLK_LFRCO
 
#endif /* CFG_BSP_CFG_H_ */
  • Open up rtos_description.h
  • Comment out or delete the module defines for everything but kernel.

rtos_description.h

/********************************************************************************************************
 ********************************************************************************************************
 *                                       RTOS MODULES DESCRIPTION
 ********************************************************************************************************
 *******************************************************************************************************/
 
//                                                                 ---------------------- KERNEL ----------------------
#define  RTOS_MODULE_KERNEL_AVAIL
 
//                                                                 ---------------------- COMMON ----------------------
/*
#define  RTOS_MODULE_COMMON_CLK_AVAIL
#define  RTOS_MODULE_COMMON_SHELL_AVAIL
 
//                                                                 ------------------------ IO ------------------------
#define  RTOS_MODULE_IO_AVAIL
 
#define  RTOS_MODULE_IO_SERIAL_AVAIL
#define  RTOS_MODULE_IO_SERIAL_SPI_AVAIL
 
#define  RTOS_MODULE_IO_SD_AVAIL
 
//                                                                 ------------------- FILE SYSTEM --------------------
 
#define  RTOS_MODULE_FS_AVAIL
 
#define  RTOS_MODULE_FS_STORAGE_NAND_AVAIL
#define  RTOS_MODULE_FS_STORAGE_NOR_AVAIL
#define  RTOS_MODULE_FS_STORAGE_RAM_DISK_AVAIL
#define  RTOS_MODULE_FS_STORAGE_SCSI_AVAIL
#define  RTOS_MODULE_FS_STORAGE_SD_CARD_AVAIL
#define  RTOS_MODULE_FS_STORAGE_SD_SPI_AVAIL
 
//                                                                 --------------------- NETWORK ----------------------
#define  RTOS_MODULE_NET_AVAIL
 
#define  RTOS_MODULE_NET_IF_ETHER_AVAIL
#define  RTOS_MODULE_NET_IF_WIFI_AVAIL
#define  RTOS_MODULE_NET_SSL_TLS_AVAIL
#define  RTOS_MODULE_NET_SSL_TLS_MOCANA_NANOSSL_AVAIL
#define  RTOS_MODULE_NET_SSL_TLS_MBEDTLS_AVAIL
#define  RTOS_MODULE_NET_HTTP_CLIENT_AVAIL
#define  RTOS_MODULE_NET_HTTP_SERVER_AVAIL
#define  RTOS_MODULE_NET_TELNET_SERVER_AVAIL
#define  RTOS_MODULE_NET_MQTT_CLIENT_AVAIL
#define  RTOS_MODULE_NET_SMTP_CLIENT_AVAIL
#define  RTOS_MODULE_NET_SNTP_CLIENT_AVAIL
#define  RTOS_MODULE_NET_IPERF_AVAIL
#define  RTOS_MODULE_NET_FTP_CLIENT_AVAIL
#define  RTOS_MODULE_NET_TFTP_CLIENT_AVAIL
#define  RTOS_MODULE_NET_TFTP_SERVER_AVAIL
 
//                                                                 -------------------- USB DEVICE --------------------
#define  RTOS_MODULE_USB_DEV_AVAIL
 
#define  RTOS_MODULE_USB_DEV_CDC_AVAIL
#define  RTOS_MODULE_USB_DEV_ACM_AVAIL
#define  RTOS_MODULE_USB_DEV_EEM_AVAIL
#define  RTOS_MODULE_USB_DEV_HID_AVAIL
#define  RTOS_MODULE_USB_DEV_MSC_AVAIL
#define  RTOS_MODULE_USB_DEV_VENDOR_AVAIL
 
//                                                                 --------------------- USB HOST ---------------------
#define  RTOS_MODULE_USB_HOST_AVAIL
 
#define  RTOS_MODULE_USB_HOST_PBHCI_AVAIL
#define  RTOS_MODULE_USB_HOST_CDC_AVAIL
#define  RTOS_MODULE_USB_HOST_ACM_AVAIL
#define  RTOS_MODULE_USB_HOST_AOAP_AVAIL
#define  RTOS_MODULE_USB_HOST_USB2SER_AVAIL
#define  RTOS_MODULE_USB_HOST_USB2SER_FTDI_AVAIL
#define  RTOS_MODULE_USB_HOST_USB2SER_SILABS_AVAIL
#define  RTOS_MODULE_USB_HOST_USB2SER_PROLIFIC_AVAIL
#define  RTOS_MODULE_USB_HOST_MSC_AVAIL
#define  RTOS_MODULE_USB_HOST_HID_AVAIL
 
//                                                                 --------------------- CAN BUS ----------------------
#define  RTOS_MODULE_CAN_BUS_AVAIL
 
//                                                                 --------------------- CANOPEN ----------------------
#define  RTOS_MODULE_CANOPEN_AVAIL
 
 */
  • Right click emlib folder go to import > more import options > file system
  • Browse for the the emlib source files (gecko_sdk_suite > v2.5 > emlib > src)
  • Add the following files
    • em_assert.c
    • em_cmu.c
    • em_core.c
    • em_emu.c
    • em_gpio.c
    • em_usart.c
  • In the BSP folder under the source folder delete all files except bsp_cpu.c and bsp_os.c
  • Right click the BSP folder go to import > more import options > file system
  • Browse for the common hardware bsp files (gecko_sdk_suite > v2.5 > hardware > kits > common)
  • Add the following files
    • bsp_bcc.c
    • bsp_stk_leds.c
    • bsp_stk.c
    • bsp_trace.c

Now you are ready to start writing your main code.

Main.c

Now your code still starts in main.c like a normal embedded application. Its structure is the same. It differs in that it is not the source of the main application loop anymore. The main function is to start the RTOS and begin your first task. After that the RTOS will focus on the tasks it won’t go back to the main function. So it will look similar, but have some stark differences as well.

Includes, Defines, and Prototypes

The includes, defines, global variables, and prototypes are the same as any normal application. You will just have ones that are for an RTOS. The includes will have ones for the kernel, OS, and BSP. The BSP libraries are for interfacing the RTOS to the specific board. For your defines and global variables you will need to set up your task TCBs and stacks. You will also need to define any semaphores or mutexes. You can also set up your task priorities with a define so that they are easier to change later. Your prototypes will include your task prototypes along with any functions you have.

Includes/Defines/Prototypes

/*
*********************************************************************************************************
*********************************************************************************************************
*                                            INCLUDE FILES
*********************************************************************************************************
*********************************************************************************************************
*/
 
#include  <bsp_os.h>
#include  "bsp.h"
#include  "bspconfig.h"
#include  "em_gpio.h"
 
#include  <cpu/include/cpu.h>
#include  <kernel/include/os.h>
#include  <kernel/include/os_trace.h>
#include  <common/include/common.h>
#include  <common/include/lib_def.h>
#include  <common/include/rtos_utils.h>
#include  <common/include/toolchains.h>
 
/*
*********************************************************************************************************
*********************************************************************************************************
*                                             LOCAL DEFINES
*********************************************************************************************************
*********************************************************************************************************
*/
 
#define  MAIN_START_TASK_PRIO              0u
#define  MAIN_START_TASK_STK_SIZE         512u
 
#define  BUTTON_TASK_PRIO                    0u
#define  BUTTON_TASK_STK_SIZE               512u
 
 
/*
*********************************************************************************************************
*********************************************************************************************************
*                                        LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*********************************************************************************************************
*/
 
                                                                /* Start Task Stack.                                    */
static  CPU_STK  MainStartTaskStk[MAIN_START_TASK_STK_SIZE];
                                                                /* Start Task TCB.                                      */
static  OS_TCB   MainStartTaskTCB;
 
static  CPU_STK  ButtonTaskStk[BUTTON_TASK_STK_SIZE];
                                                                /* Button Task TCB.                                      */
static  OS_TCB   ButtonTaskTCB;
 
static  OS_SEM   ButtonSem;
 
/*
********************************************************************************************************
*********************************************************************************************************
*                                       LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*********************************************************************************************************
*/
 
static  void  MainStartTask (void  *p_arg);
 
static  void  ButtonTask (void  *p_arg);

Int main (void)

The main function is where an RTOS will most differ from a standard embedded application. You will be initializing your OS, first task, and any semaphores or mutexes. The big difference is that you will not have a loop inside. You will instead start your OS and it will move onto the tasks. You will also notice that after any OS calls we check for errors. This is a good practice to make sure everything is running correctly. The standard process is first you initialize the BSP system, then the CPU, and any other OS initializations. Then you will call OSInit. After that you will create your semaphores and mutexes, and your main task. Then you will call OSStart. Then the RTOS starts and the main function is done.

int main (void)

/*
*********************************************************************************************************
*                                                main()
*
* Description : This is the standard entry point for C applications. It initializes the first task and
*                 the OS.
*
* Argument(s) : None.
*
* Return(s)   : None.
*
* Note(s)     : None.
*********************************************************************************************************
*/
int  main (void)
{
    RTOS_ERR  err;
 
    BSP_SystemInit();                                           /* Initialize System.                                   */
    CPU_Init();                                                 /* Initialize CPU.                                      */
 
    OS_TRACE_INIT();                                            /* Initialize trace if enabled                          */
    OSInit(&err);                                               /* Initialize the Kernel.                               */
                                                                /*   Check error code.                                  */
    APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), 1);
 
    OSSemCreate(&ButtonSem,
                        "Button Semaphore",
                        0,
                        &err);
 
    APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), 1);
 
    OSTaskCreate(&MainStartTaskTCB,                          /* Create the Start Task.                               */
                 "Main Start Task",
                  MainStartTask,
                  DEF_NULL,
                  MAIN_START_TASK_PRIO,
                 &MainStartTaskStk[0],
                 (MAIN_START_TASK_STK_SIZE / 10u),
                  MAIN_START_TASK_STK_SIZE,
                  0u,
                  0u,
                  DEF_NULL,
                 (OS_OPT_TASK_STK_CLR),
                 &err);
                                                                /*   Check error code.                                  */
    APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), 1);
 
 
    OSStart(&err);                                              /* Start the kernel.                                    */
                                                                /*   Check error code.                                  */
    APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), 1);
 
 
    return (1);
}

Tasks

After this you will have your tasks. Tasks in an RTOS are almost a smaller application themselves. They have their own memory stack and each usually contains a continuous loop. For this one we only have two tasks. Our first one is our main task. It initializes a few things, mainly the BSP tick and CPU. It also creates our other task. For its loop it waits for the semaphore to be ready, once ready it decrements the semaphore and toggles the LED. The other task is for reading a button press. It first initializes the button. It then goes into a loop, polling the button pin till it is pressed and then released. It then increments the semaphore. This signals to the other task that the semaphore is ready and to go forward and toggle the LED. The RTOS goes back and forth between these two tasks based upon which one is ready and which one is waiting.

Tasks

/*
*********************************************************************************************************
*                                          MainStartTask()
*
* Description : This is the task that will be called by the Startup when all services are initialized
*               successfully.  It waits to be able to decrement the semaphore and then toggles the LED.
*
* Argument(s) : p_arg   Argument passed from task creation. Unused, in this case.
*
* Return(s)   : None.
*
* Notes       : None.
*********************************************************************************************************
*/
static  void  MainStartTask (void  *p_arg)
{
    RTOS_ERR  err;
    CPU_TS ts;
 
 
    PP_UNUSED_PARAM(p_arg);                                     /* Prevent compiler warning.                            */
 
 
    BSP_TickInit();                                             /* Initialize Kernel tick source.                       */
 
    OSStatTaskCPUUsageInit(&err);                               /* Initialize CPU Usage.                                */
                                                                /*   Check error code.                                  */
    APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), ;);
 
    Common_Init(&err);                                          /* Call common module initialization example.           */
    APP_RTOS_ASSERT_CRITICAL(err.Code == RTOS_ERR_NONE, ;);
 
    BSP_OS_Init();                                              /* Initialize the BSP. It is expected that the BSP ...  */
                                                                /* ... will register all the hardware controller to ... */
                                                                /* ... the platform manager at this moment.             */
 
    OSTaskCreate(&ButtonTaskTCB,                                /* Create the Button Task.                               */
                     "Button Task",
                      ButtonTask,
                      DEF_NULL,
                      BUTTON_TASK_PRIO,
                     &ButtonTaskStk[0],
                     (BUTTON_TASK_STK_SIZE / 10u),
                      BUTTON_TASK_STK_SIZE,
                      0u,
                      0u,
                      DEF_NULL,
                     (OS_OPT_TASK_STK_CLR),
                     &err);
                                                                    /*   Check error code.                                  */
    APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), 1);
 
    while (DEF_ON) {
 
        OSSemPend(&ButtonSem, 0, OS_OPT_PEND_BLOCKING, &ts, &err); /* Wait for the Semaphore to be open */
 
        APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), ;);
 
        BSP_LedToggle(0);
                                                                /* Delay Start Task execution for                       */
        OSTimeDly( 500,                                        /*   500 OS Ticks                                      */
                   OS_OPT_TIME_DLY,                             /*   from now.                                          */
                  &err);
                                                                /*   Check error code.                                  */
        APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), ;);
    }
}
 
/*
*********************************************************************************************************
*                                          ButtonTask()
*
* Description : This task polls push button PB0 and incrementst the semaphore when it is pressed.
*
* Argument(s) : p_arg   Argument passed from task creation. Unused, in this case.
*
* Return(s)   : None.
*
* Notes       : None.
*********************************************************************************************************
*/
static  void  ButtonTask (void  *p_arg)
{
    RTOS_ERR  err;
    unsigned int button;
 
 
    PP_UNUSED_PARAM(p_arg);                                     /* Prevent compiler warning.                            */
 
    GPIO_PinModeSet(BSP_GPIO_PB0_PORT, BSP_GPIO_PB0_PIN, gpioModeInputPull, 1);
 
    while (DEF_ON) {
        button = GPIO_PinInGet(BSP_GPIO_PB0_PORT, BSP_GPIO_PB0_PIN);
        while(button == 1)
        {
            OSTimeDly( 100,                                        /*   100 OS Ticks                                      */
                       OS_OPT_TIME_DLY,                             /*   from now.                                          */
                       &err);
            button = GPIO_PinInGet(BSP_GPIO_PB0_PORT, BSP_GPIO_PB0_PIN);
        }
 
        OSTimeDly(50, OS_OPT_TIME_DLY,&err);
        while(button == 0)
        {
            OSTimeDly( 100,                                        /*   100 OS Ticks                                      */
                       OS_OPT_TIME_DLY,                             /*   from now.                                          */
                       &err);
            button = GPIO_PinInGet(BSP_GPIO_PB0_PORT, BSP_GPIO_PB0_PIN);
        }
        OSSemPost(&ButtonSem,OS_OPT_POST_ALL,&err);                    /* increment the semaphore signaling it is ready   */
 
        APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), ;);
                                                                /* Delay Button Task execution for                       */
        OSTimeDly( 500,                                        /*   500 OS Ticks                                      */
                   OS_OPT_TIME_DLY,                             /*   from now.                                          */
                   &err);
                                                                /*   Check error code.                                  */
        APP_RTOS_ASSERT_DBG((RTOS_ERR_CODE_GET(err) == RTOS_ERR_NONE), ;);
    }
}

Conclusion

An RTOS may be very complex, but they are designed to make it easier to program a complex project. They handle the scheduling of tasks in a program so that the most important, ready one is running. They help protect resources and make sharing data easy. Now this is just a starting point, but there is wealth of resources. Silicon Labs has some great videos and other material for learning about Micrium. If you are looking for documentation and API information you can check out Micrium’s own documentation.

By Ben R

Comments

Any questions or comments please go to our TechForum.