The Cortex-M0+ processor is an ultra low power 32-bit ARM processor optimized for embedded and IoT applications. It provides an efficient way to implement real-time operating systems (RTOS) like Keil RTX to enable multithreading and scheduling on resource constrained devices. The key aspects of using Keil RTX RTOS on Cortex-M0+ are: configuring RTX, creating threads, handling inter-thread communication and synchronization, and utilizing hardware timers. With careful planning, an RTOS can enable easier software design and more functionality even on Cortex-M0+ based devices.
Introduction to Keil RTX RTOS
Keil RTX is a royalty free RTOS from ARM designed specifically for ARM Cortex-M series processors. It is written in C and assembly and optimized for performance and small memory footprint. The key components of RTX include:
- Multi-thread management – Efficient scheduling and context switching between threads using preemptive multitasking.
- Inter-thread communication – Mechanisms like mailboxes, queues, semaphores, mutexes, event flags etc.
- Memory management – Dynamic memory allocation and deallocation.
- Time management – Software timers and time delay functions.
- Interrupt management – Deferred interrupt processing using kernel aware interrupts.
RTX has a modular architecture allowing only necessary components to be included reducing code size. Configuration is done using a GUI editor which generates required C header files. Small footprint (under 9KB), performance focus, and CMSIS-RTOS compatibility makes RTX well suited for Cortex-M0+ devices.
Configuring and Initializing RTX
The first step is to configure RTX as per application requirements using the configuration wizard in Keil uVision IDE. This generates files like rt_config.h, rt_memory.h etc. Next, RTX needs to be initialized by calling osKernelInitialize() which allocates kernel memory, initializes internal data structures, timers and interrupts. Additional configuration like thread stack sizes and priorities can be done by modifying rt_tmp_thread_stack or osPriority variables. With the default settings, RTX on Cortex-M0+ takes about 3KB of flash and 300 bytes of RAM.
Creating and Managing Threads
The basic unit of execution in RTX is a thread. A thread is defined by specifying a thread function, name, stack size, priority, startup argument and other attributes. Multiple threads can be created using the osThreadNew() API. A unique thread ID is returned which can be used to reference the thread. By default threads are created in suspended state and need to be started using osThreadResume().
Thread priority determines the scheduling order between threads. Higher priority threads preempt lower priority ones. Unique priorities from 0 (highest) to 31 (lowest) can be assigned. Special priorities like osPriorityRealtime can be used for time critical threads. Thread stack size is also an important consideration on memory constrained Cortex-M0+ devices.
RTX provides APIs for thread management like changing priority using osThreadSetPriority(), suspending using osThreadSuspend() and resume osThreadResume(), delaying osThreadDelay(), and terminating threads osThreadTerminate(). Care must be taken to avoid priority inversions, race conditions and resource access conflicts while designing multi-threaded applications.
Inter-thread Communication and Synchronization
RTX RTOS provides various inter-thread communication and synchronization methods like:
- Mailboxes – Threads can exchange data pointers and signals via mailboxes. This enables safe data exchange avoiding issues like missed signals.
- Queues – FIFO queues allow threads to exchange data similar to producer-consumer pattern. Multiple threads can wait on a queue unlike mailboxes.
- Semaphores – Counting semaphores help synchronize thread execution by controlling access to shared resources.
- Mutexes – Mutual exclusion objects allow only one thread to access a resource or code segment.
- Event Flags – Event flags provide synchronization based on flag bits set or cleared by threads.
These constructs along with priority based preemptive scheduling remove a lot of complexities associated with multithreaded design compared to bare-metal embedded programming.
Leveraging Hardware Timers
Cortex-M0+ MCUs have limited number of hardware timers suitable for time critical tasks. RTX simplifies utilizing them via its software timers and timed semaphores. Up to 8 software timers can be created using the osTimerNew() API which work based on a dedicated timer or SysTick. Short duration delays can also be generated using osDelay() instead of software timers.
Timed semaphores are special semaphores that auto expire after a timeout period. This helps implement timeouts when waiting on resources. With careful planning, hardware timers can be optimally used to implement real-time behavior in the application threads.
Handling Interrupts
By default interrupts are handled in the context of RTX threads by registering interrupt handlers using osKernelInitialize(). This adds context switch overhead to ISRs. For time critical interrupts that require very low latency handling, RTX supports “kernel-aware” interrupts. These ISRs are configured using the __MASKED attribute to prevent context switches and can directly access RTX kernel data.
Kernel aware interrupts need to avoid kernel API calls that could cause scheduling or synchronization issues. They are also responsible for waking up any threads that may need to run after ISR completion. With proper use of kernel aware interrupts, real-time performance can be improved on RTX RTOS.
Optimizing Footprint and Performance
Since Cortex-M0+ devices are highly resource constrained, it is important to optimize RTX memory usage and performance. Compiler settings like optimizing for size, stripping unnecessary locale information, and disabling float can reduce code size. Eliminating unused RTX configurations, features, thread stack sizes also helps. Including only the required RTX source files avoids unused code.
Performance optimization involves choosing best RTOS object types for the application, assigning proper thread priorities to avoid blocking, using kernel aware interrupts judiciously, and moving long running non-realtime tasks to separate low priority threads. With careful optimization, RTX RTOS can provide significant benefits for multithreading even on Cortex-M0+ devices.
Conclusion
Overall, Keil RTX RTOS provides an efficient way to implement multithreading on resource constrained Cortex-M0+ devices by handling thread management, scheduling, inter-thread communication, synchronization, and more in the RTOS kernel. With good upfront planning and design, developers can leverage RTX to build more modular, maintainable and extensible real-time embedded applications on Cortex-M0+ platforms.