Embedded Artists


We are the leading providers of products and services around prototyping, evaluation and OEM platforms using NXP's ARM-based microcontrollers.

LPC4088DM Using RTOS

The DMSupport library includes mbed's official mbed-rtos library to provide a real time operating system (RTOS for short) with all the advantages that brings.

The mbed-rtos library is described in the handbook with examples and links to the different classes so there is no point in repeating that information here.

There are a number of Threads already in use, not including the ones you add in your own program:

ThreadLocationTaskIdDescription
Idlembed-rtos0xff"Runs" when no other threads are running
mainmbed-rtos1The thread that runs your main() function
osTimerThreadmbed-rtos2Handles timer ticks in the system
RtosLogDMSupportdynamicSerializes and sends log messages
TouchDMSupportdynamicHandles the touch events coming from the touch controller and presents them to the user code
RendererDMBasicGUIdynamicSupport for the SlideShow application (see SlideShow Engine for more information
usbThreadUSBHostdynamicUsed by the USB Host stack to handle events on the USB bus
Eth RXEthernetInterfacedynamicReceive thread for the TCP/IP stack
Eth TXEthernetInterfacedynamicTransmit thread for the TCP/IP stack

Note that which threads are available or not depends on the configuration done in the dm_board_config.h file.

Using wait functions

The wait(), wait_ms() and wait_us() functions are implemented in a polling fashion like this:

wait_api.c

void wait_us(int us) {
    uint32_t start = us_ticker_read();
    while ((us_ticker_read() - start) < (uint32_t)us);
}


This means, of course, that the caller is blocked until the function returns. This is the expected behavior and it works very well in a system without an RTOS as there is nothing else to execute while waiting.

But in an RTOS it is different. There may be many other threads that can do stuff even if this one thread wants to wait. The thread calling the wait_us() function above might still be interrupted by other events in the system so that other threads can execute but it is telling the system to "give me all the execution time you can spare" and in effect this means that the idle-thread will not run at all while waiting.

The solution to this problem is to replace all calls to wait(), wait_ms() and wait_us() with calls to Thread::wait(). The Thread::wait() function will put the calling thread to sleep until the desired time has passed and then wake it up again. While sleeping the thread will not use any execution time at all and the idle-task will be running most of the time.

There are cases where it is ok to use the wait functions (e.g. when the delay is in a low number of microseconds) but for the most cases never use the wait(), wait_ms() and wait_us() functions and always use Thread::wait().

Which Thread is Running?

When trying to get the most out of the hardware and to get the best possible performance for your program it is important to know what is executing in the system.

A simple way to see which thread is executing is to override the rt_stk_check() function in rt_System.c and have a look at the data.

#include "rt_TypeDef.h"

extern "C" {
  extern struct OS_TSK os_tsk;
  
  void rt_stk_check (void)
  {
    switch (os_tsk.new_tsk->task_id) {
    case  1: ... ; break;  // main task
    case  2: ... ; break;  // osTimer tick
    case  3: ... ; break;
	...
    case 0xff: ...; break; // Idle task
    }
  }
}   // extern "C"


Note

Overriding the rt_stk_check() function works as the stack is checked on all task switches. Overriding it has the negative effect of disabling stack checks so make sure that the stacks are ok in your program before testing this.

Note

As with all interrupt functions, avoid any delays and don't do anything blocking. This is a core function in the operating system and doing anything wrong here crashes the system.

The Measure Time page describes how to use GPIOs and a logic analyzer to look at the system with minimal impact on performance. Using that in combination with the rt_stk_check() function above allows real time monitoring of the system:

#include "meas.h"
#include "rt_TypeDef.h"

extern "C" {
  extern struct OS_TSK os_tsk;
  
  void rt_stk_check (void)
  {
    CLR_MEAS_PIN_1();
    CLR_MEAS_PIN_2();
    CLR_MEAS_PIN_3();
    CLR_MEAS_PIN_4();

    switch (os_tsk.new_tsk->task_id) {
    case  1: SET_MEAS_PIN_1(); break;  // main task
    case  2: SET_MEAS_PIN_2(); break;  // osTimer tick
    case  3: SET_MEAS_PIN_3(); break;
	...
    case 0xff: SET_MEAS_PIN_4(); break; // Idle task
    }
  }
}   // extern "C"


Read more about the measuring functions and the requirements in Measure Time.

Using Signals

Sending messages between thread using signals is described here. Unfortunately that only describes how to send a signal if you have a pointer to the receiving thread. But what if you don't have that?

It is possible to use the osSignalSet() function to send a signal to a specific threadId. Like this:

#include "mbed.h"
#include "rtos.h"
 
DigitalOut led(LED1);

osThreadId ledThreadId;

void led_thread(void const *argument) {
    ledThreadId = Thread::gettid();
    while (true) {
        // Signal flags that are reported as event are automatically cleared.
        Thread::signal_wait(0x1);
        led = !led;
    }
}
 
int main (void) {
    Thread thread(led_thread);
    
    while (true) {
        Thread::wait(1000);
        osSignalSet(ledThreadId, 0x1);
    }
}


In the example above it would have been just as simple to use thread.signal_set(0x1) but have a look at the lpc4088_displaymodule_everything example:

Import programlpc4088_displaymodule_everything

Example using the support package for LPC4088 DisplayModule

In the usbTask() funcion in main.cpp the thread registers for signals when something happens on the USB bus:

main.cpp

  USBHost* host = USBHost::getHostInst();
  host->signalOnConnections(Thread::gettid(), USB_CONNECTION_EVENT);


The USBHost class keeps track of the thread ID and the wanted signal and can then send a signal like this:

USBHost.cpp

  if (listener != 0) {
    osSignalSet(listener, listenerSignal);
  }


The main() function

For non-RTOS programs the main() function should never return so if you have a program that registers for interrupts to toggle an LED then the main() function will have something like this at the end:

void main() {
  ...
  while (true) {
  }
}


When using an RTOS the main() function is like other threads and can be terminated. The following code is ok:

Thread* t1;
void main() {
  t1 = new Thread(...);
}


The t1 thread would be started and left to run, and the main() function would return and result in the main thread being terminated.


All wikipages