TI-RTOS7 Basics#

Introduction#

The SimpleLink™ Software Development Kits (SDKs) includes TI-RTOS7 support.

TI-RTOS7 accelerates development schedules by eliminating the need to create basic system software functions from scratch. TI-RTOS7 scales from a minimal footprint real-time multitasking kernel to a complete RTOS solution including protocol stacks, multi-core communications, device drivers and power management. By providing essential system software components that are pre-tested and pre-integrated, TI-RTOS7 enables developers to focus on differentiating their application. The predecessor of TI-RTOS7 is known as TI-RTOS, which was then formerly known as SYS/BIOS.

TI-RTOS7 is included in all SimpleLink SDKs.

Here’s what we’ll learn:

  • TI-RTOS7 support in SimpleLink SDK

  • Main components of TI-RTOS7

This module includes quite a bit of text before the lab. Some people find this helpful and others feel that it slows down the whole session. If you are in the latter camp, please feel free to jump directly to the lab. For more details about specific features, you can go back and read the following content as needed.

A Brief History of TI-RTOS#

First let’s look at the history of the RTOSes that have been developed and supported by Texas Instruments.

  • DSP/BIOS: 1999-current. RTOS that is available for C2xxx, C54xx, C55xx, and C6xxx devices. Currently in maintenance-mode only. Available as a stand-alone product.

  • TI-RTOS: 2008-current. Started as SYS/BIOS and was re-branded to TI-RTOS in 2014. TI-RTOS is available for C2xxx, C6xxx, CortexA and CortexM devices. Active development of new features is ongoing in TI-RTOS7. Currently running on millions of devices (e.g. IoT, automotive, industrial, etc.).

TI-RTOS7 Product#

TI-RTOS7 product is delivered within the SimpleLink SDK, so no extra install is needed to use TI-RTOS7. When migrating between SDKs it may be required to account for TI-RTOS7 changes. Each SDK will have its own migration guide included to help users migrate from the previous SDK.

TI-RTOS to TI-RTOS7 Migration#

Recently the newest form of TI-RTOS, called TI-RTOS7 has been released. This section is meant to quickly go over how to port TI-RTOS to TI-RTOS7. First is that TI-RTOS7 exists in a similar folder path to TI-RTOS, find kernel->tirtos7->packages->ti->sysbios, as an example. When importing a TI-RTOS project, the dependent kernel project is also imported; however TI-RTOS7 leverages SysConfig generational tools, allowing the dependent project to no longer be needed.

Note

TI-RTOS7 only works with SysConfig 1.10 and above. Earlier versions of SysConfig are not compatible.

TI-RTOS7 Kernel#

At the center of TI-RTOS7 is the kernel. We’ll look at the main modules of the kernel in this section. For more specific details on any of these kernel modules, please refer to the Kernel Documentation section of the SimpleLink SDK Documentation Overview: <SimpleLink_SDK_Install_Dir>\docs\Documentation_Overview.html

Note

TI-RTOS7 is compiled in flash and does not use components in ROM.

Scheduler#

The main function of the kernel is the scheduler. The scheduler is responsible for making sure the highest priority thread is running.

Let’s look at the four different types of threads in TI-RTOS7.

../../../_images/tirtos_threads.png

Let’s go into a little more detail for each one.

  • Hardware Interrupts (Hwi): Hwi run to completion. They don’t block on anything. They can get preempted by a higher priority Hwi. All Hwi share the same stack (system stack).

    Hwi can be written in ‘C’. They are managed by the TI-RTOS7 scheduler with an exception: zero-latency interrupts. Applications can designate that any interrupt be a “zero-latency” interrupt. This means the TI-RTOS7 scheduler does not interact with that interrupt. We call it a zero-latency interrupt because the TI-RTOS7 kernel adds zero latency to the execution of these interrupts. The downside to zero-latency interrupts is that they cannot call into the kernel scheduler APIs (e.g. Semphore_post(), etc.).

  • Software Interrupts (Swi): The Swi thread is similar to a Hwi except it is software initiated instead of hardware. It also runs to completion. It shares the same system stack with the Hwi threads. Since Swi run at a lower priority than Hwi, they are useful for doing deferred Hwi work to minimize interrupt latency.

  • Tasks: A Task is a common OS thread. Each task has its own stack (where it maintains its state). Since it has its own stack, a task can block. There is no maximum number of allowable tasks (system memory permitting).

    Here are the states for a Task.

../../../_images/task_states.png
  • Idle: Idle is a special task. It runs at the lowest priority (0 is the lowest priority, 1 is the next lowest, (Task_numPriorities - 1) is the highest priority). The Idle task performs background tasks like system stack checking (if enabled), CPU Load determination (if enabled), etc. It also executes functions plugged in by the application.

    Low power devices can be placed into lower power modes when the Idle thread runs.

For a preemption example, please refer to the RTOS Concepts workshop. There is an execution graph of a typical application.

Thread Communication#

TI-RTOS7 contains several thread communication mechanisms. Here are the main ones that are used.

  • Semaphores: An object used to control access to a common resource. They can be used for task synchronization and mutual exclusion.

  • Mailboxes: Message passing module

  • Queues: Doubly linked list (no synchronization though)

  • Gates: Used to protect concurrent access to critical data structures. A Gate is a reentrant mutex.

  • Events: Module which allows synchronization via multiple events.

Let’s look at a couple of these in action.

  • Semaphore Example 1: Here is an example of a semaphore being used to manage a linked list (Queue). This semaphore is a counting semaphore since it is potentially managing multiple elements on the linked list. After an element is placed on the queue, the semaphore is posted to wake up the receiver. We want a counting semaphore here (with initial value of 0) since we may put multiple elements on the linked list before the receiver gets them.

    The right side of the picture shows the IDE console output depending on the priority setting of the respective tasks. Remember that TI-RTOS7 is a preemptive scheduler, so once the highest priority thread is ready, it will run.

../../../_images/semaphorequeue_code.png

Note

The Queue_put() and Queue_get() add/remove elements from the linked list in an atomic manner. Thread-safety is guaranteed. Also note that the user’s data structure provides the prev and next pointers via the Queue_Elem field in their MyMsg structure. This avoids memory allocation in the Queue module and also allows the linked list to be infinitely long (memory permitting).

  • Semaphore Example 2: In this example, the semaphore is being treated as a mutex (MUTual EXclusion). If another task wanted to access the global structure myGlobal, it should use the same semaphore to wrap the changes. This protects the updating of the shared structure. The area being protected is often called a critical region.

../../../_images/semaphore_code.png

Let’s look at what happens if we did not do this. Say writer1 (priority 1) and writer2 (priority 2) tasks both modify this structure and reader reads and acts on this struct. Let’s say writer1 is running and writer2 is blocked. Here is a situation where without the semaphore protection, corruption can occur.

../../../_images/semaphore_bad_case.png

Now the request is to have bufferC read into bufferB…no one asked for that! If the semaphore was used, writer1 would have completed updating the entire global first. Please note this is a somewhat contrived example, but hopefully clearly shows why managing critical regions is important.

When this semaphore was created, it could have been binary (since the count will only be 0 or 1) and the initial count should have been 1 (to allow the first Semaphore_pend() to succeed).

Note

The Gate module can also be used for mutual exclusion. For such short critical regions, you can also disable/restore interrupts (Hwi_disable() & Hwi_restore()).

Timing Services#

  • Timer: Module that allows management of hardware timers.

  • Clock: TI-RTOS7, by default, uses a timer to drive timing services (e.g. Task_sleep(), etc.). Applications can plug functions into the Clock module that will be called at the rate they request. Your plugged-in Clock functions can be periodic or one-shot.

  • Seconds: Unified front-end to the device’s RTC timer.

Memory Managers#

TI-RTOS7 offers many types of memory managers. Here is an overview of the main heap implementations:

Heap

Description

Reason to use

HeapMem

Variable size allocation

Very flexible

HeapBuf

Fixed size allocation

Fast and deterministic

HeapMultiBuf

Multiple fixed size allocation

Fast and deterministic

HeapMin

Variable size, growth only

Fast and deterministic (but cannot call free)

HeapTrack

Stacking diagnostic heap

Helps find memory leaks, corruption, etc.

The heaps sit underneath the Memory_alloc() and Memory_free() APIs.

By default, TI-RTOS7 creates a system (or default) heap. This heap is used when you pass in NULL for the IHeapHandle in Memory_alloc() and Memory_free(). The system heap is also used in malloc() and free() functions (the kernel replaces the RTS malloc() and free() functions). By default, the system heap is a HeapMem instance and the size is controlled by the settings in the linker command files. For example, here is the sizing of the system heap found in the linker file, this value is device specific and can change depending on your configuration.

../../../_images/systemheap_sizing_new.png

An application can have more than one heap in the application. A common usage is to have the system heap be HeapMem and then create a HeapBuf instance to manage fixed-blocks that can be allocated and freed quickly with no fragmentation (or to be more precise, no external fragmentation). We generally see that the system heap is left as a HeapMem instance because it may be hard to know where all the allocations (and the size of the allocations) in an application are occurring.

POSIX (Portable Operating System Interface) Support#

POSIX is an IEEE industry API standard for OS compatibility. The SimpleLink SDK provides support for POSIX APIs on top of TI-RTOS7 (as it does for FreeRTOS). For details about POSIX, please refer to IEEE POSIX. For a more detailed description of the POSIX support in SimpleLink SDKs, please refer to the POSIX Overview Workshop.

TI-RTOS7 Configuration and Examples#

For older TI-RTOS the kernel is built based on the settings in the kernel configuration file (also called the .cfg file). This file is basically a JavaScript file that can be edited as a text file or graphically (graphically is only available in CCS). As part of the application build, the .cfg file generates the kernel objects and libraries.

There are two different ways for an application to include the TI-RTOS kernel configuration file in the SimpleLink SDKs:

  • Application projects include the .cfg file

  • Application projects (e.g. TI Drivers examples) point to a TI-RTOS kernel configuration project

The end result of the two approaches are essentially the same. However with the update to TI-RTOS7 you will change RTOS settings in SysConfig instead of the .cfg file. For information about porting from TI-RTOS to TI-RTOS7, refer to the migration guide in the SDK Software Documentation. Refer to TI-RTOS to TI-RTOS7 Migration for more information regarding the migration process.

Kernel Projects#

The older TI-RTOS kernel is built via a provided project. However in the updated TI-RTOS7 the kernel is included in the “includes” section of the project browser. For example, here is the directory that contains the sysbios kernel configuration file.

../../../_images/kernel_directory_new.png

This project can be imported into CCS by selecting Project → Import CCS Projects… and navigating to the examples/rtos/device/drivers directory. Then select the desired example project to import, you will be given the option to import the project as freertos, or tirtos7, select tirtos7 in this case.

../../../_images/import_kernel_new.png

Here is what the imported example project looks like. You see the empty.syscfg file, which contains all the included settings you can change to use certain features associated with your device.

../../../_images/imported_project_new.png

For more details on how to change or create new TI-RTOS7 projects, please refer to the SimpleLink SDK User Guide. We do recommend that you start with a relevant SDK example project while developing your application. Starting with a relevant SDK example project will demonstrate how to configure and use TI drivers, among other useful code.

Examples#

Many of the examples use the POSIX layer to allow them to be used with TI-RTOS7 or FreeRTOS. All the TI-RTOS7 specific code is in the main_tirtos.c file (there is a main_freertos.c for FreeRTOS-based examples). The majority of the file uses the POSIX API, but at the end of main(), the call to start the TI-RTOS7 scheduler is made.

../../../_images/main_tirtos_new.png

The linker command file is also slightly different from the FreeRTOS one in how it sets up the dynamic heap.

Note

Even though the TI-RTOS7 examples use POSIX in the SimpleLink SDK, it is not required. You can use the native TI-RTOS7 APIs instead or alongside the POSIX APIs if you prefer. Using POSIX is recommended if you

  • want an application that is portable across different RTOSes

  • you have existing code that uses it

  • are comfortable with the slight overhead it introduces

Please refer to the demos\portable and demos\portableNative examples to see a comparison of using POSIX vs the native RTOS APIs.

Debugging Features and Tools#

TI-RTOS7 supports many powerful debugging features. A great resource when getting started with debugging is the BLE5-Stack Debugging Guide, the guide has a great overview of important features included in CCS for aiding developers debugging skills.

Runtime Object View#

Starting with CCS 7.1, Runtime Object View (let’s call it ROV2) is available. ROV2 is an improvement to ROV Classic capabilities. You get information like Task stack peaks, currently running thread, etc. It includes support for graphs. ROV2 has no overhead on the target. The tool is reading memory down on the target via emulation.

The “BIOS->Scan for errors…” is a fast and easy way to determine if the RTOS is in a bad state (e.g. blown stacks, corrupted data because of bad application pointers or buffer overflows, etc.). You can open ROV2 via “Tools->Runtime Object View”

../../../_images/rov2_dashboard_new.png

You can save your ROV2 session as a dashboard, so the next time you open ROV2, you can import that dashboard and get your customized view of the target.

../../../_images/imported_example_rov2_new.png

To load the dashboard, simply select the “Import a dashboard” and import the overview.rov.json.

../../../_images/open_dashboard.png

More information about ROV2 can be found here at

RTOS Analyzer#

The RTOS Analyzer tool in CCS allows you to visually see key items like the execution graph, CPU load, average/max/min execution times for code segments, etc. This is accomplished by logging records on the target. The typical use is to have the records maintained in buffers on the target which RTOS Analyzer reads while the target is halted. However there are also runtime reading capabilities for getting the log records off of the target (e.g. UART2).

Here is an example of execution of the mutex example in the SDK. The logging of TI-RTOS7 kernel log records can be turned on. All the context switches are logged on the target and interpreted in CCS.

../../../_images/execution_graph.png

Okay…this execution graph is a little boring, but you can see how easy it is to see what is happening. You can add in interrupt execution and pending/posting of semaphores to see even more granularity.

Lab: Getting started#

Software#

  • Any SimpleLink SDK

  • CCS as specified by your SimpleLink SDK Release Notes (please use Desktop version, not CCS Cloud)

Hardware#

  • Any supported SimpleLink LaunchPad™ Development Kit

The below steps will use SimpleLink CC13xx/CC26xx SDK along with the CC26X2R1-LAUNCHXL LaunchPad board. So some of the pictures/directory names/line numbers/sizes/etc. might be slightly different.

Making sure it works#

Open your Desktop Code Composer Studio and import the TI-RTOS7 empty project from your SimpleLink SDK inside of Resource Explorer. We’ll be changing the TI-RTOS7 kernel configuration file and this is not currently supported in CCS Cloud. Make sure to select the “CCS Compiler” version. (Note: the exact content or paths might be slightly different).

../../../_images/import_project_new.png

To test that the software and hardware pre-requisites are fulfilled we are going to build and run the project before going to the first task.

  • Our first mission is to build the imported project. Select the project in Project Explorer and build the project.

  • When the project is built, we are going to make sure that the hardware and debugger work. To start debugging, press Run → Debug, or press F11.

  • When the download is finished, press F8 or the green play button to run.

  • You should see the red LED on the device toggle on and off. The program will turn the red LED on and off at a one second interval, and repeat this pattern forever.

Warning

Note that the first time you build the project, the whole TI-RTOS7 kernel will also be built. This may take a minute or two, but is only done the first time. Subsequent builds will re-use the compiled kernel unless the configuration changes.

Task 1 - Replacing the contents of the empty project#

Double-click the empty.syscfg file in your project. This will open the SysCfg editor. Select the “GPIO” module name in the left-hand column and click “ADD” to add another GPIO to your board’s configuration. Name the new GPIO CONFIG_GPIO_LED_1 and select the green LED in the Use Hardware field.

../../../_images/syscfg_add_led_new.png

Next select the below text and replace the entire contents of main_tirtos.c (the RTOS you chose will be reflected in the naming of main_####). Then rebuild/reload/run the project. Also comment out, or remove the code within empty.c as we will not be using it. Once you have followed these steps you should get a flashing red LED.

Click me for code
empty project modified#
  /* TI-RTOS Header files */
  #include <xdc/std.h>
  #include <string.h>
  #include <ti/sysbios/BIOS.h>
  #include <ti/sysbios/knl/Task.h>
  #include <ti/sysbios/knl/Clock.h>
  
  #include <ti/drivers/GPIO.h>

  /* Driver configuration */
  #include "ti_drivers_config.h"
  #include <ti/log/Log.h>
  #include <ti/log/LogSinkBuf.h>

  /* Could be anything, like computing primes */
  #define FakeBlockingSlowWork()   myDelay(5000)
  extern void myDelay(int count);
  Task_Struct workTask;
  #pragma workTaskStack(8);
  #define STACKSIZE 1024
  static uint8_t workTaskStack[STACKSIZE];

  void doWork(void)
  {
  FakeBlockingSlowWork();
  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_OFF);

  FakeBlockingSlowWork(); /* Pretend to do something useful but time-consuming */
  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_ON);
  }

  Void workTaskFunc(UArg arg0, UArg arg1)
  {
  while (1) {

      /* Do work */
      doWork();

      /* Wait a while, because doWork should be a periodic thing, not continuous.*/
      myDelay(2000);
      Task_sleep(100 * (1000 / Clock_tickPeriod));
  }
  }

  /*
   *  ======== main ========
   *
   */

  int main(void)
  {
  Board_init();
  GPIO_init();

  /* Set up the LED task */
  Task_Params workTaskParams;
  Task_Params_init(&workTaskParams);
  workTaskParams.stackSize = STACKSIZE;
  workTaskParams.priority = 2;
  workTaskParams.stack = &workTaskStack;

  Task_construct(&workTask, workTaskFunc, &workTaskParams, NULL);

  /* Start kernel. */
  BIOS_start();

  return (0);
  }
/* Delay count in ms */
  void myDelay(int count)
  {
      uint32_t j;

      for (j = 0; j < 4010 * count; j++)
      {
          asm(" NOP");
      }
  }

Orienting ourselves in the code#

The Task 1 example comes preconfigured with one TI-RTOS7 Task already constructed in main(). This task is set up to use the workTaskFunc function as the task function, which in turn uses the GPIO Driver to toggle a LED.

The task is created using the Task_construct in main(). main() also initializes the hardware.

In main(), after BIOS_start() is called, it never returns, but instead gives control to the TI-RTOS7 scheduler which will call the Task functions of the constructed tasks (e.g. workTaskFunc). Normally, task functions will enter an infinite loop and never return, letting TI-RTOS7 switch to higher priority tasks, Swi or Hwi.

Task 2 - Debugging Tools#

We are going to take a look at some of the built-in features of CCS which can aid in the development of firmware running on TI-RTOS, and also give a better understanding of the multitasking.

Runtime Object View#

As discussed above, Runtime Object View (we’ll call it ROV2 here for short) can be used to get a snapshot of the whole RTOS. By default the information is only updated via JTAG when the target is halted. First we are going to halt the code as we are toggling the led.

  • Put a breakpoint on the FakeBlockingSlowWork() call on the doWork() function. Do this by double clicking on the area on the left of the line number.

../../../_images/set_breakpoint_new.png
  • Run so you hit that breakpoint. Next open ROV2 by going to Tools → Runtime Object View. You’ll be prompted to connect.

../../../_images/start_rov2.png
  • In ROV2, select the “Import a dashboard” icon and select the overview.rov.json file that is in the project.

../../../_images/open_dashboard.png

This dashboard shows which task is currently running on the system, what tasks are blocked, as well as what tasks are ready to run.

We can see that the workTaskFunc is currently running and we can also see that the stack usage for the task has peaked at 236 of 1024 bytes (STACKSIZE) bytes so far, so no risk of stack overflow. Note: depending on your device, compiler, and versions, the values will probably be different. We generally recommend you start with a larger stack size and then trim it down once everything is working properly.

../../../_images/overview_dashboard_new.png

ROV Viewer#

The items highlighted in yellow means it has changed since the last time it was read.

Task 3 - Sleeping well#

We can notice that we have a problem with one of our tasks hogging all CPU resources. Let’s take a look at our workTaskFunc.

Work, “sleep”, work.#
void doWork(void)
{
  FakeBlockingSlowWork();
  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_OFF);

  FakeBlockingSlowWork(); /* Pretend to do something useful but time-consuming */
  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_ON);
}

 void workTaskFunc(UArg arg0, UArg arg1)
 {
  while (1) {

      /* Do work */
      doWork();

      /* Wait a while, because doWork should be a periodic thing, not continuous.*/
      myDelay(2000);
      Task_sleep(100 * (1000 / Clock_tickPeriod));
  }
}

The only thing the task does is execute the doWork function and then goes back to “sleep”, except it never does go to sleep. The myDelay function (FakeBlockingSlowWork) is simply a function which burns CPU cycles in a loop. This is not the correct way to pass time in the RTOS.

One of the easiest ways to pass time in a task is to call Task_sleep(numTicks). Task_sleep will simply make the current task sleep for as many system ticks as is specified in the argument. The current tick rate of the system is needed in order to know how long you will sleep. This is a constant value available via the Clock_tickPeriod variable. The value is the amount of microseconds per clock tick.

Note

To use Clock_tickPeriod, remember to include the kernel Clock module header:

clock include#
#include <ti/sysbios/knl/Clock.h>

The value of this variable [µs/tick] is determined when the TI-RTOS7 .syscfg file is parsed. If Clock.tickPeriod = nn; is not present in the .syscfg file, the default value is used (1000µs or 1ms). Since the tick period can vary between projects, it’s useful to include the variable Clock_tickPeriod in calculations that depend on system clock ticks.

Note

The following are differences observed between the .cfg configuration and .syscfg configuration that may not be a direct migration:

.cfg Module

Difference between TI-RTOS and TI-RTOS7

Boot

Boot.syscfg exists in tirtos7, but .syscfg doesn’t need to add the Boot module unless non-default settings are used.

Task 3.1#

Add the Clock modules include file after the Task.h include in empty.c.

c#
#include <ti/sysbios/knl/Clock.h>
  

Task 3.2#

  • Replace the use of FakeBlockingSlowWork to sleep with Task_sleep and use it to sleep for 2 seconds (change Count to increase or decrease time in sleep).

  • How do you convert an argument in microseconds to an argument in system ticks?

Answer if you’re stumped
   int Count = 20000; //change to target value to change rate of which LED's blink. 
   //FakeBlockingSlowWork();
   Task_sleep(50 * (Count / Clock_tickPeriod));
   GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_OFF);
   
   //FakeBlockingSlowWork();
   Task_sleep(50 * (Count / Clock_tickPeriod));
   GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_ON);
  • Rebuild/reload/run. Let the code run for a while and have another look at the Execution Graph, does it look any different? Note: you may have to zoom out to see the transitions.

../../../_images/execution_graph_better.png

Now we see that Idle was given a chance to run. For low power devices, going into idle will allow the device to transition to lower power modes.

Note: if the task function does not show in the execution graph, you can close reference the task handle in ROVs Task Detailed view.

../../../_images/task_names_new.png

Note

You can control the number of kernel records via the LoggingSetup.sysbiosLoggerSize variable in the .syscfg file. Each record is ~20-48 bytes since they are stored as binary codes (not ASCII). In this example we had 1024 bytes, so we get around 30-35 records. For a more complex execution application, you’ll probably need more Log records for a detailed execution graph.

Task 4 - Executing urgent work#

Next we are going to expand on the original code by adding a doUrgentWork function and task. In our system, this will represent the most important work processing the system needs to do. This is more important than the work done by the workTask and should execute as quickly as possible.

Setting up the new task#

  • First copy, paste and rename the workTaskFunc function to create a new task function called urgentWorkTaskFunc.

  • Let urgentWorkTaskFunc call doUrgentWork.

  • Copy, paste and rename the Task_Struct and the task stack storage as well for urgentTask.

  • Construct the new task (copy and rename the parameters and the construction) and set the priority of the new task to 1. We’ll play with this later… Note: Higher priority number means higher priority.

Answer to save typing!
  /* TI-RTOS Header files */
  #include <xdc/std.h>
  #include <string.h>
  #include <ti/sysbios/BIOS.h>
  #include <ti/sysbios/knl/Task.h>
  #include <ti/sysbios/knl/Clock.h>

  #include <ti/drivers/GPIO.h>

  /* Driver configuration */
  #include "ti_drivers_config.h"
  #include <ti/log/Log.h>
  #include <ti/log/LogSinkBuf.h>

  /* Could be anything, like computing primes */
  #define FakeBlockingSlowWork()   myDelay(5000)
  #define FakeBlockingFastWork()   myDelay(2000)
  extern void myDelay(int count);
  Task_Struct workTask;
  Task_Struct urgentWorkTask;
  #pragma workTaskStack(8);
  #define STACKSIZE 1024
  static uint8_t workTaskStack[STACKSIZE];
  static uint8_t urgentWorkTaskStack[STACKSIZE];

  void doUrgentWork(void)
  {
  int Count = 10000;

  Task_sleep(50 * (Count / Clock_tickPeriod));
  GPIO_write(CONFIG_GPIO_LED_1, CONFIG_LED_OFF);

  Task_sleep(50 * (Count / Clock_tickPeriod));
  GPIO_write(CONFIG_GPIO_LED_1, CONFIG_LED_ON);

  }

  void doWork(void)
  {
  int Count = 20000;

  Task_sleep(50 * (Count / Clock_tickPeriod));
  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_OFF);

  Task_sleep(50 * (Count / Clock_tickPeriod));
  GPIO_write(CONFIG_GPIO_LED_0, CONFIG_LED_ON);
  }

  Void workTaskFunc(UArg arg0, UArg arg1)
  {
  while (1) {

      /* Do work */
      doWork();

      /* Wait a while, because doWork should be a periodic thing, not continuous.*/
      myDelay(2000);
      Task_sleep(5000 * (1000 / Clock_tickPeriod));
  }
  }

  Void urgentWorkTaskFunc(UArg arg0, UArg arg1)
  {
  while (1) {

      /* Do work */
      doUrgentWork();

      /* Wait a while, because doWork should be a periodic thing, not continuous.*/
      //myDelay(2500);
      Task_sleep(500 * (1000 / Clock_tickPeriod));
  }
  }

  /*
   *  ======== main ========
   *
   */

  int main(void)
  {
  Board_init();
  GPIO_init();

  /* Set up the LED task */
  Task_Params workTaskParams;
  Task_Params_init(&workTaskParams);
  workTaskParams.stackSize = STACKSIZE;
  workTaskParams.priority = 2;
  workTaskParams.stack = &workTaskStack;

  Task_construct(&workTask, workTaskFunc, &workTaskParams, NULL);

  workTaskParams.priority = 1;
  workTaskParams.stack = &urgentWorkTaskStack;

  Task_construct(&urgentWorkTask, urgentWorkTaskFunc, &workTaskParams, NULL);

  /* Start kernel. */
  BIOS_start();

  return (0);
  }
/* Delay count in ms */
  void myDelay(int count)
  {
      uint32_t j;

      for (j = 0; j < 4010 * count; j++)
      {
          asm(" NOP");
      }
  }

Note

A Task has some information associated with it. This is stored in the Task_Struct, which holds the variables the TI-RTOS7 kernel needs to act on the Task; for example, to make it pend on a Semaphore, place it in a Ready queue, or just check the current priority.

A Task also needs a Stack to place function-local variables. The stack is just an array of bytes that we tell TI-RTOS7 to use. When a specific Task is running, the CPU’s stack pointer will point into the memory area of this array. This is a part of how multi-threading is accomplished, because each Task thinks on a low level that it is operating independently. For example workTaskFunc uses workTaskStack for its local variables and function calls.

For more information on tasks, refer to the TI-RTOS7 kernel documentation in the <SimpleLink_SDK_Install_dir>/docs/documentation_overview.html file.

Quizzes#

Let’s see if you picked up some key points!

## Which LED is flashing at the desired rate? 1. [x] LED0 (Red) > You are correct! 1. [ ] LED1 (Green) > LED1 does not flash at the desired rate. ## Why is LED1 not running at the desired rate? 1. [x] The urgentWorkTaskFunc task has a lower priority > You are correct! The urgentWorkTaskFunc task is only running when the workTaskFunc task is sleeping. 1. [ ] I messed up > While entirely possible, the real reason is because the urgentWorkTaskFunc task has a lower priority! Believe in yourself and try again!

Let’s look at the execution graph again.

../../../_images/execution_graph_wrong_priority.png

Changing priority#

Run the program if you did not already, did you notice that the green LED takes longer to finish blinking when red LED blinks, despite the green LED using the urgentWorkTaskFunc? Change the urgentWorkTaskFunc priority from 1, to 3; then rebuild and run the project.

workTaskParams.priority = 3;
workTaskParams.stack = &urgentWorkTaskStack;

Task_construct(&urgentWorkTask, urgentWorkTaskFunc, &workTaskParams, NULL);

Do you see that the green LED now blinks consistently, and is no longer affected by the red LED; the urgentWorkTaskFunc timing is working as intended since it is now a higher priority than doUrgentWork and no longer gets “blocked”. It is always important to take in consideration if your tasks are created at the correct priority level.

Additional Resources#

Additional training and reference material for TI-RTOS7 is available in the following places:

Online#