Blogs | Technical Articles | Videos

In the dynamic landscape of embedded systems, the foundational decisions developers make often determine the trajectory of the entire project. One such pivotal choice lies between employing a Real-Time Operating System (RTOS) or venturing down the direct and unlayered path of bare metal development. While the former offers a structured environment with built-in functionalities, the latter boasts unparalleled control over hardware, free from any OS-induced overhead. But how does one determine which is more suitable for a particular project? Through this article, we'll delve deep into the nuances of performance metrics, confront the intricacies of development complexity, and unravel the maze of power efficiency considerations inherent to each approach. By the journey's end, you'll be equipped with a clearer perspective, empowering you to make an informed choice for your next embedded venture.

In today's interconnected world, embedded systems are the unsung heroes behind countless devices we use daily. From the thermostat regulating our home's temperature to the heart-monitoring apparatus in a hospital, these systems quietly power our modern world. However, like all complex pieces of technology, embedded systems come with their own challenges. Developers may face intricate hardware-software interactions, compatibility issues, evolving standards, and much more. Add to this the critical need for these systems to be reliable, and you've got a vast and complex landscape to navigate. It's within this landscape that technical support emerges as a beacon, providing guidance, solutions, and expertise. This article delves deep into why technical support is so crucial in embedded systems development and how it can make or break the success of a project.

Embedded systems are integral to our increasingly digitized world. They act as the digital brains behind a vast array of devices and technology that we rely on every day. From controlling the air conditioning in our cars to managing network communications in our smartphones, these specialized computer systems are ubiquitous, silently working in the background to provide sophisticated functionality and automation.

A testament to the advancement of technology, embedded systems have evolved considerably over the years. They've transformed from simple one-task devices to complex systems capable of managing multiple concurrent tasks, network communications, and high-level computations, often in real time.

However, the increasing complexity and sophistication of embedded systems present unique challenges. The interplay of software and hardware elements, the need for effective memory management, power consumption, real-time requirements, security, and testing processes all contribute to a development landscape that requires specialized knowledge and skills.

Embedded software serves as the backbone of countless devices we rely on daily, from smartphones and IoT devices to automotive systems and medical equipment. Developers face a crucial decision when developing embedded software: choosing between commercial and open-source solutions. Understanding the nuances of these approaches is vital for making informed decisions. Commercial embedded software, developed by profit-oriented companies, offers reliability, comprehensive features, and dedicated support. In contrast, open-source embedded software is collaboratively developed, freely available, and promotes flexibility and customization. In this article, we will explore the advantages and considerations of both approaches, enabling developers and businesses to make informed choices aligned with their project requirements and objectives.

In the world of embedded systems development, where real-time responsiveness and efficient resource management are paramount, incorporating an RTOS (Real-Time Operating System) can be a game-changer. An RTOS acts as the backbone of embedded applications, offering a host of features and functionalities that ensure tasks are executed in a deterministic manner, critical events are handled promptly, and system resources are efficiently utilized. Whether you're working on a small microcontroller or a complex embedded application, harnessing the power of an RTOS can provide significant benefits. In this article, we will delve into ten compelling reasons why integrating an RTOS into your embedded systems projects is a decision that can greatly enhance performance, reliability, and scalability. From task management and scheduling to power optimization and community support, we'll explore the multitude of advantages an RTOS brings to the table, revolutionizing the way you design and develop embedded systems.

Storage devices have become an indispensable component of many embedded applications. To select the optimal medium for memory storage, it is essential to consider the specific requirements of your use case. The most prevalent storage mediums for embedded applications are NOR Flash Memory, NAND Flash Memory, SD/MMC Cards, Mass Storage Devices, and RAM.

NOR and NAND flash memory devices are both non-volatile storage mediums, which means they can retain data even without power. MultiMediaCard (MMC) and Secure Digital (SD) cards are flash memory cards that adhere to these standards, and they are also non-volatile memory devices. USB Mass Storage Device Class (USB MSC) is the most commonly used Mass storage device with Cs/FS. USB Drives are also non-volatile memory devices and are recognized by the host device as an external hard drive when connected. RAM storage mediums, on the other hand, are volatile, which means they can't maintain their stored data in the event of a power outage or failure.

Cesium RTOS, a continuation of the popular Micrium µC/ kernels, and FreeRTOS are two popular real-time kernels in the embedded software space. While they are functionally similar and have comparable execution metrics, there are advantages and disadvantages to each of the two product’s APIs. Let’s review some of these differences and make a case for why choosing Cesium RTOS may benefit your project’s overall long-term development and support.

Both µC/TCP-IP and Cs/NET, in accordance with RFC recommendations, are capable of sending probes, or keep-alive segments, over connections that have been idle for an extended period of time.

How long should a quiet TCP connection – meaning one over which no communication is taking place – remain open? The answer to this question, like many answers in the realm of TCP, depends on the network stack(s) being considered. In µC/TCP-IP and Cs/NET, the maximum amount of idle time is a configurable property, and application developers can specify a unique limit for each of their connections.

If the idle-time threshold for a particular connection is reached, µC/TCP-IP and Cs/NET are capable of probing the remote side of the connection by sending segments known as keep-alives. The idea of transmitting such segments is raised by RFC 1122, a "request for comment" that, along with many other RFCs, comprises the documentation for the Internet. 1122 states that developers of network stacks "MAY include 'keep-alives' in their TCP implementations." For stacks that provide this feature, the document specifies that application code must be allowed to turn keep-alives "on or off for each connection, and they MUST default to off."

Applications written around µC/OS-II, µC/OS-III, Cs/OS2, and Cs/OS3 can often use non-kernel-aware ISRs for rapid responses to certain events.

In the previous post in this series, I showed the format of what could be called a "typical" ISR for Micriµm's µC/OS-II, µC/OS-III, and Cesium Cs/OS2, Cs/OS3 kernels. Here, I'll review the format for an alternate type of ISR – one sometimes referred to as "non-kernel-aware." This format involves less overhead than the approach from the first post, but it is not applicable to all ISRs.

The non-kernel-aware format is intended for ISRs that don't require the use of kernel services – routines that, for example, don't place a message in a queue or signal a task using a semaphore. The assumption behind the non-kernel-aware format is that such routines will never result in preemption. In other words, because they don't involve any of the kernel's services, they will never make another task ready to run. ISRs of this type can forgo invoking the kernel routines that would otherwise be needed to facilitate a post-ISR switch to a new task.

For basic embedded network designs, a single Ethernet or Wi-Fi interface will suffice. As projects and consumer expectations grow, more complex connectivity requirements, such as multiple interfaces, become more prevalent. With our network stack, it is possible to meet this project requirement.

If you are familiar with µC/TCP-IP or Cs/NET's API function calls you’ve seen our example start-up code for creating a single interface over Ethernet. When dealing with multiple interfaces it is as simple as calling the same creation and startup process for each desired interface. The following pseudo-code will elaborate on this:

The flowchart shown below details how to setup a multiple-interface initialization for your application. Once the application initializes the core Network stack, a new network interface will be added. With Wi-Fi connections, subsequently scanning for wireless access points to join may be executed. For hidden networks (whose name is known to the application code already) the scan operation could be skipped. Once that is completed, the application must configure the interface for the desired IP host address, subnet mask, and default gateway.

Application developers are not always responsible for the kernel-specific code needed to properly handle interrupts under µC/OS-II, µC/OS-III, Cs/OS2, and Cs/OS3.

Real-time kernels can be viewed as task managers – software modules that allow application developers to write their code as a collection of different tasks. Most kernel-based applications, though, do not consist of tasks alone. Oftentimes, developers of kernel-based applications mix tasks with interrupt service routines, or ISRs, to meet their systems' requirements.

The proper format for an ISR within a kernel-based application varies somewhat according to the kernel being used. Developers who consult the books describing Micrium's µC/OS-II and µC/OS-III kernels learn that ISRs under these kernels should at least roughly resemble the example shown below. The function called near the midpoint of this routine, App_ISR(), represents the application's portion of the ISR, while the rest of the example's pseudo-code can be considered kernel-related.

In a multi-task application based on Micrium's µC/OS-II, µC/OS-III, or Cesium's Cs/OS2, Cs/OS3, the C stack is used in main() and task stacks are used thereafter.

When writing application code for one of Micrium or Cesium's real-time kernels, developers must declare a stack for each of their tasks. These task stacks are used by the kernel to save context (CPU registers) when passing control of the CPU from one task to another, and they are also ultimately used by the tasks' own code during function calls, in place of the C stack. Does the presence of these stacks, then, mean that the C stack is not needed in a system that incorporates Micrium or Cesium kernels? In the following post, we will answer this question and, in doing so, we will examine some important consequences that the kernels' stack usage holds for application code.

A Real-Time Operating System (RTOS) is software that manages the time of a microprocessor or microcontroller. Real-time systems are characterized by the fact that severe consequences can result if logical and timing correctness properties of the system are not met. A real-time multitasking application is a system in which several time-critical activities must be processed simultaneously. A real-time multitasking kernel (also called a real-time operating system, RTOS) is software that ensures that time-critical events are processed as efficiently as possible. The use of an RTOS generally simplifies the design process of a system by allowing the application to be divided into multiple independent elements called tasks.

Jean J. Labrosse, the founder of Micrium and author of the well-known µC/OS series of software and books, presents during the 2021 Embedded Online Conference. In his presentation, Jean demystifies Memory Protection Units, also known as MPUs. He talks about an RTOS without a Memory Protection Unit, an RTOS with process separation using an MPU, inter-process communications, and organizing memory in sections or segments using the IAR tools. Furthermore, a sample linker script is shown and explained to aid in further organizing memory for use with an MPU. As usual, a lengthy list of recommendations is given at the end of the presentation.

 

µC/FS includes several configuration files that allow you to customize for your application design.

This article will focus on the top-level configuration file of µC/FS. The others are specifically geared towards the inner layers of the file system and are not needed for all projects, such as SD Card, NAND device, etc. We will discuss the configuration file fs_cfg.h. The file systems configurable behavior is controlled by how the developer defines the following configuration values to reflect the application's needs.

It helps to ask yourself a few questions about your application:

  • What type of file system do I need? Do I need an embedded-sized File System or a full-sized FS line Linux, Windows, MAC, etc. Do I need a FAT File System? Transactional File System?
  • How much data am I looking to store and transfer around? If your embedded device is looking to handle more data than a FAT 32 file system can store you may have some difficulties.
  • Do I need to restrict access to the file system? Read-only or Read-Write functionality.
  • What device do I intend to run my file system on? SD Card, NAND or NOR Chip, RAMDisk implementation?

In Part I, we looked at setting up a UDP server. Now we will cover setting up a socket for a UDP server. 

The process for setting up a TCP server, although considerably more complex than a typical application using UDP as the underlying protocol, can be broken down into five steps.

In part one of this series, we examined the function calls necessary to set up a socket for a UDP server. In part I, we looked at the UDP half of the transport-layer protocols. Now we will consider the other protocol, TCP. The typical sequence followed in establishing TCP communication in a server application begins similarly to UDP. Where it deviates will be the focus of part II as we cover the new API’s.

Perhaps the most notable difference between a server based on UDP and one that relies on TCP is the latter typically requires a minimum of two sockets, whereas the former, requires the creation of one. At least two sockets for TCP is a result of this protocol's robust feature set; while UDP is often described as a "connection-less" protocol, TCP is best described as "connection-oriented". The creation of a data connection (essentially, state information for a sender and receiver) is maintained by two communicating devices. Thus, before a TCP client can communicate with a server, it must establish a connection. In application code, this process typically unfolds as illustrated below.

When designing a µC/TCP-IP based application, setting up a socket for a UDP server can be accomplished with a straightforward sequence of function calls.

When utilizing Micrium's network stack µC/TCP-IP to establish connections, you'll likely end up writing sockets code. Sockets, in networking jargon, represent a medium for sending and receiving data. Many basic Micrium examples are devoid of socket call implementation — this is due to stacks like µC/TCP-IP that carrying out a variety of basic operations, including the creation of responses to pings (or echo requests) automatically. As a developer, your own designs will typically require more than the basics. After establishing that your project has the essentials needed for network development, pinging your board for instance, sockets will be your vehicle for transmitting and receiving data.

When writing your application code around µC/TCP-IP, there’s a basic sequence to follow when setting up and initializing a socket. The variation between socket setup is dependent on which transport-layer protocol your socket is utilizing. There are two options provided by the TCP/IP protocol suite that constitute the foundation for transport-layer communication—UDP and TCP—each offering their own unique set of services. This article will focus on the initialization of UDP sockets, specifically, utilizing UDP sockets to implement the server side of a client-server application. In Part II, we'll discuss the analogous code for a TCP server.

Prerequisites

This is the second entry in a series on Dynamic Tick Mode for µC/OS-III V3.07 and above. If you have not done so, please read the first part “Dynamic Tick on µC/OS-III - Part 1: Getting Started”. This document assumes that the reader is familiar with the concepts described in Part 1.

There are a number of features in µC/OS-III which require time-keeping services. For example, we may want our task to delay for some amount of time before running again, or we may want it to wait on a semaphore but timeout if the semaphore is not available after some timeTime-keeping in µC/OS-IIIhas always been performed by means of a tick interrupt.

In part one of this series we covered a majority of µC/TCP-IP’s basic configurations. In this final blog of the series, we’ll examine window sizes and queues, both of which can affect the rate of data flowing in and out of a device.

Version V3.07.00 of µC/OS-III and V2.92.16 of µC/OS-II brought a few changes and improvements to the ARMv7-M port, which provides support for Cortex-M3, M4, and M7 architectures. The changes are simple Folder/File structure modifications to µC/CPU and µC/OS-III as well as additional configuration defines. Moreover, the new functionality of the port allows the user to specify a boundary for Kernel aware and Non-Kernel aware ISR's by taking advantage of the BASEPRI register.

TCP/IP communication involves a complex set of protocols that make configuring a TCP/IP stack challenging for new and experienced users. This is the first of a two-part series which will break down how to configure Micrium's µC/TCP-IP stack and provide details on how and why those settings matter. The series will be geared towards users implementing TCP sockets and not UDP. However, much of the content in the first post is applicable to all projects based on µC/TCP-IP, regardless of the underlying transport protocol.

Although it might seem complex at first glance, the concept behind a real-time operating system, or RTOS, is a fairly simple one.  An RTOS is little more than software that manages the time and resources of a CPU.  In an RTOS-based application, the RTOS ensures that the most important parts of the application run when they are ready.  This simple model happens to be a very effective one, which is why RTOSes are used in countless IoT and embedded systems.

In an upcoming webinar hosted by Beningo Embedded, Jean Labrosse will explain everything you need to know about an RTOS and expose both the benefits and drawbacks of RTOS-based systems in comparison with bare-metal code.  In this 45-minute session, Jean will also discuss some of the tools available to help developers gain insight into a running RTOS-based application.

When using the Micrium kernels, it’s possible for the C startup stack to be lost, thus reducing the amount of RAM available to your embedded application. This note shows how you can completely recover this valuable RAM for use as one of your task stacks.

Most embedded applications are written in C, and upon reset, the CPU is provided the address of the startup code (i.e., boot code) from the default vector table. On a Cortex-M, the vector table also contains the initial value of the stack pointer and is automatically assigned to the CPU’s main stack pointer (MSP). For the IAR toolchain (i.e., EWARM), the storage area for the initial stack is set up by the linker command file, and the stack space itself is called the CSTACK. Once the startup code completes, the MSP is generally left untouched and the CSTACK is thus used to process interrupt service routines (ISRs).

In EWARM, the size (in bytes) and storage for the CSTACK is defined in the linker command script as shown in the following statements (see your MCU’s .icf file for your project):

Memory-protection units (MPUs) have been available for years on processors such as the Cortex-M, and yet, embedded developers shy away from using them. Is it because they aren’t useful? Is it because MPUs are complex devices? Do they add too much overhead? In this article and the subsequent multi-part series, these questions and more will be answered.

In Part 1, I’ll cover MPU basics using generic concepts. Then, I’ll get into specifics using the MPU found in an ARM Cortex-M. Finally, I’ll show you how to organize your code by processes, how a process can communicate with one another, what happens when a process accesses memory or I/O outside of its assigned memory, and finally, offer some recommendations when using an MPU.

I’ll start off with a brief description of what an RTOS is and then show how an MPU fits into the picture. An RTOS (a.k.a., real-time kernel) is software that manages the time of a central processing unit (CPU) or a microprocessing unit (MPU) as efficiently as possible. Most RTOSs are written in C and require a small portion of code written in assembly language to adapt the RTOS to different CPU architectures.

Message queues can be used in a number of different ways in embedded software design. You can write fairly complex applications in which you might use message queues alone. And using only message queues can reduce the size of your code (i.e., memory footprint) because many of the other services (semaphores, time delays, and event flags) can be simulated.

Read the full article at Embedded Computing Design.

μC/OS-III has a rich set of built-in instrumentation that collects real-time performance data. This data can be used to provide invaluable insight into your kernel-based application, allowing you to have a better understanding of the run-time behavior of your system. Having this information readily available can, in some cases, uncover potential real-time programming errors and allow you to optimize your application.

Once your application performs satisfactorily and meets your real-time needs, you can compile out the instrumentation code, which would not only reduce code and data size but would also improve the kernel’s performance.

Download the white paper "µC/OS-III Performance Optimization ARM Cortex-M (Part 1)"

Download the white paper "µC/OS-III Performance Optimization ARM Cortex-M (Part 2)"

 

More and more embedded systems rely on the use of Real-Time Operating Systems (RTOSs) to: satisfy real-time requirements, reduce time-to-market, simplify development, increase code portability, and simplify development. Despite its many benefits, an RTOS also has its drawbacks, one of which is the possibility of introducing improperly assigned task priorities, stack overflows, starvation, deadlocks, priority inversions, and other hard-to-find bugs.

In this three-part series, Jean Labrosse looks at tools specifically designed to help RTOS-based application developers uncover some of these elusive bugs, identify issues and offer corrective actions. These tools are readily available yet often unknown to embedded developers.

Electronic Design recently published 11 myths about µC/OS as part of its "11 Myths" series. As µC/OS, a world-renowned embedded real-time operating system (RTOS), hits its 25th anniversary this year, it’s an ideal time to examine the kernel and address some of the many myths that have proliferated in the embedded market over the years.

1. The µC/OS kernels were community-developed.

Absolutely not. I wrote 100% of the code as well as a series of books describing the internals of the µC/OS kernels: µC/OS (1992), µC/OS-II (1998), and µC/OS-III (2008). The only help I got as an author was editing, but that’s typical for most authors. I did receive a lot of feedback from embedded developers, and of course evolved the code to satisfy some of those requests. But for the past couple of years, Micrium’s developers have been maintaining the code following the strict coding guidelines I established for Micrium.

In some ways, it’s hard for me to believe that the µC/OS kernel is 25 years old; perhaps mostly because I don’t want to believe it! The kernel has an interesting history, largely because it didn’t start as a commercial project, and yet 25 years on, I find myself still hard at work on µC/OS products. Now seems like a good time for a look back at the software’s origins, as many of the challenges that existed then are still experienced by developers today. The difference is that now the proven, certifiable real-time operating system (RTOS) µC/OS-II® and µC/OS-III® kernels are commercially available.

The μC/OS story started in 1989, when I joined Dynalco Controls in Fort Lauderdale, Florida, and began working on the design of a new microprocessor-based ignition control system for a large industrial reciprocating engine. I was convinced that an RTOS kernel would benefit this project. Initially, I wanted to use a kernel I had experience with, but budget requirements drove the selection of a less costly alternative. It quickly became apparent that I was paying for the seemingly cheaper RTOS with my time. I spent the next two months in contact with technical support, trying to determine why even the simplest applications would not run. It turned out I was one of the first customers for the kernel, meaning I ended up as an unintended beta tester.

Micrium's µC/OS-III kernel has a rich set of built-in instrumentation that collects real-time performance data. This data can be used to provide invaluable insight into your kernel-based application, allowing you to have a better understanding of the run-time behavior of your system. Having this information readily available can, in some cases, uncover potential real-time programming errors and allow you to optimize your application.

In Part I of this post we examined, via µC/Probe, a number of the statistics built into µC/OS-III, including those for stack usage, CPU usage (total and per-task), context-switch counts, and signaling times for task semaphores and queues.

In this post, we'll examine the kernel’s built-in ability to measure interrupt-disable and scheduler lock time on a per-task basis. Once again, we used µC/Probe to display, at run time, these values.

Micrium's µC/OS-III kernel has a rich set of built-in instrumentation that collects real-time performance data. This data can be used to provide invaluable insight into your kernel-based application, allowing you to have a better understanding of the run-time behavior of your system. Having this information readily available can, in some cases, uncover potential real-time programming errors and allow you to optimize your application. In this two-part series of posts, we will explore the statistics yielded by the kernel's instrumentation, and we'll also consider a unique way of visualizing this information.

In Part 1 of this two-part series, we looked at what stack overflows are and how to determine the size of a task stack. Now, we turn to detect stack overflows, as there are a number of techniques that can be used. Some make use of hardware, while some are performed entirely in software. They are listed in order of the most preferable to the least preferable, based on the likelihood of detecting the overflow. As we will see shortly, having the capability in hardware is preferable since stack overflows can be detected nearly immediately as they happen, which can help avoid those strange behaviors and aid in solving them faster.

Hardware stack overflow detection mechanisms generally trigger an exception handler. The exception handler typically saves the current program counter (PC) and possibly other CPU registers onto the current task’s stack. Of course, because the exception occurs when we are attempting to access data outside of the stack, the handler would overwrite some variables or another stack in your application, assuming there is RAM beyond the base of the overflowed stack.

In a previous blog, I showed how you can display stack usage of kernel-based applications using µC/Probe™. In this two-part series, I will describe the importance of sizing your stacks at design time and checking task stacks at run‑time to catch stack overflows. I will first explore how to determine the size of task stacks and then, in Part 2, I will go into ways that can be used to detect overflows.