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:
Thread | Location | TaskId | Description |
---|---|---|---|
Idle | mbed-rtos | 0xff | "Runs" when no other threads are running |
main | mbed-rtos | 1 | The thread that runs your main() function |
osTimerThread | mbed-rtos | 2 | Handles timer ticks in the system |
RtosLog | DMSupport | dynamic | Serializes and sends log messages |
Touch | DMSupport | dynamic | Handles the touch events coming from the touch controller and presents them to the user code |
Renderer | DMBasicGUI | dynamic | Support for the SlideShow application (see SlideShow Engine for more information |
usbThread | USBHost | dynamic | Used by the USB Host stack to handle events on the USB bus |
Eth RX | EthernetInterface | dynamic | Receive thread for the TCP/IP stack |
Eth TX | EthernetInterface | dynamic | Transmit 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.